diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f1ed16237092f4db40cae542e26c780a36e84fc5..60812faeeed4d111cc69eff158464a4742a6b852 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -15,10 +15,11 @@ linux64_ci:
   script:
     - mkdir build
     - cd build
+    - export CXXFLAGS=-Werror
     - cmake ${EXTRA_OPTION} ..
     - make -j 8
     - ctest -j 8 --output-on-failure
-    - valgrind --leak-check=full --error-exitcode=1 ./gmsh ../tutorial/t5.geo -2
+    - valgrind --leak-check=full --error-exitcode=1 ./gmsh ../tutorial/t5.geo -3
   tags:
     - linux64
     - docker
@@ -79,18 +80,15 @@ windows64_msvc_ci:
 
 .linux_official: &linux_official
   only:
-    - master@gmsh/asml
+    - master@gmsh/gmsh
   script:
     - mkdir build
     - cd build
     - /usr/local/bin/cmake -DGMSH_HOST=gmsh.info -DCMAKE_PREFIX_PATH='/usr/local/opencascade;/usr/local' -DENABLE_NATIVE_FILE_CHOOSER:bool=FALSE -DPETSC_ARCH=complex_mumps_seq -DPETSC_DIR=/home/geuzaine/src/petsc -DSLEPC_DIR=/home/geuzaine/src/slepc ${EXTRA_OPTION} ..
     - make package -j 1
     - PKG=`ls gmsh-*.tar*`
-    - mv ${PKG} ../${PKG/\.tar\.gz/\.tgz}
-  artifacts:
-    name: "$CI_JOB_NAME"
-    paths:
-       - "*.tgz"
+    - scp -o StrictHostKeyChecking=no -i /home/gitlab-runner/.ssh/id_rsa ${PKG} geuzaine@gmsh.info:.wwwgmsh/bin/Linux/${PKG/\.tar\.gz/\.tgz}
+    - /usr/local/bin/ctest -j 1 --output-on-failure
 
 linux64_official_snapshot:
   <<: *linux_official
@@ -130,7 +128,7 @@ linux64-sdk_official_release:
   only:
     - /^gmsh_.*$/
 
-.linux32_official_snapshot:
+linux32_official_snapshot:
   <<: *linux_official
   tags:
     - linux32
@@ -138,7 +136,7 @@ linux64-sdk_official_release:
   except:
     - tags
 
-.linux32_official_release:
+linux32_official_release:
   variables:
     EXTRA_OPTION: "-DGMSH_RELEASE=1"
   <<: *linux_official
@@ -148,7 +146,7 @@ linux64-sdk_official_release:
   only:
     - /^gmsh_.*$/
 
-.linux32-sdk_official_snapshot:
+linux32-sdk_official_snapshot:
   variables:
     EXTRA_OPTION: "-DENABLE_BUILD_DYNAMIC=1 -DINSTALL_SDK_README=1"
   <<: *linux_official
@@ -158,7 +156,7 @@ linux64-sdk_official_release:
   except:
     - tags
 
-.linux32-sdk_official_release:
+linux32-sdk_official_release:
   variables:
     EXTRA_OPTION: "-DGMSH_RELEASE=1 -DENABLE_BUILD_DYNAMIC=1 -DINSTALL_SDK_README=1"
   <<: *linux_official
@@ -174,17 +172,14 @@ linux64-sdk_official_release:
 
 .windows_official: &windows_official
   only:
-    - master@gmsh/asml
+    - master@gmsh/gmsh
   script:
     - md build
     - cd build
     - bash -c "/usr/bin/cmake -DGMSH_HOST=gmsh.info -DCMAKE_PREFIX_PATH='/usr/local/opencascade;/usr/local;/usr/mingw32/sys-root/mingw' -DCMAKE_C_COMPILER=/usr/bin/mingw32-gcc.exe -DCMAKE_CXX_COMPILER=/usr/bin/mingw32-g++.exe -DCMAKE_Fortran_COMPILER=/usr/bin/mingw32-gfortran.exe -DCMAKE_RC_COMPILER=/usr/bin/mingw32-windres.exe -DPETSC_ARCH=complex_mumps_seq -DPETSC_DIR=/home/geuzaine/src/petsc -DSLEPC_DIR=/home/geuzaine/src/slepc -DENABLE_OS_SPECIFIC_INSTALL=1 ${EXTRA_OPTION} .."
     - bash -c "/usr/bin/make package -j 2"
-    - bash -c "mv gmsh-*.zip .."
-  artifacts:
-    name: "%CI_JOB_NAME%"
-    paths:
-       - "*.zip"
+    - bash -c "/usr/bin/scp -o StrictHostKeyChecking=no -i /home/geuzaine/.ssh/id_rsa gmsh-*.zip geuzaine@gmsh.info:.wwwgmsh/bin/Windows/"
+    - bash -c "/usr/bin/ctest -j 2 --output-on-failure"
 
 windows64_official_snapshot:
   <<: *windows_official
@@ -224,7 +219,7 @@ windows64-sdk_official_release:
   only:
     - /^gmsh_.*$/
 
-.windows32_official_snapshot:
+windows32_official_snapshot:
   <<: *windows_official
   tags:
     - windows32
@@ -232,7 +227,7 @@ windows64-sdk_official_release:
   except:
     - tags
 
-.windows32_official_release:
+windows32_official_release:
   variables:
     EXTRA_OPTION: "-DGMSH_RELEASE=1"
   <<: *windows_official
@@ -242,7 +237,7 @@ windows64-sdk_official_release:
   only:
     - /^gmsh_.*$/
 
-.windows32-sdk_official_snapshot:
+windows32-sdk_official_snapshot:
   variables:
     EXTRA_OPTION: "-DENABLE_OS_SPECIFIC_INSTALL=0 -DENABLE_BUILD_DYNAMIC=1 -DINSTALL_SDK_README=1"
   <<: *windows_official
@@ -252,7 +247,7 @@ windows64-sdk_official_release:
   except:
     - tags
 
-.windows32-sdk_official_release:
+windows32-sdk_official_release:
   variables:
     EXTRA_OPTION: "-DGMSH_RELEASE=1 -DENABLE_OS_SPECIFIC_INSTALL=0 -DENABLE_BUILD_DYNAMIC=1 -DINSTALL_SDK_README=1"
   <<: *windows_official
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ab9920053f0a47e446d49a18a3560f8a3bdbe09..dc333dda9b7d7ae09617d4570fac3d010093af33 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -98,7 +98,7 @@ opt(ZIPPER "Enable Zip file compression/decompression" OFF)
 set(GMSH_MAJOR_VERSION 4)
 set(GMSH_MINOR_VERSION 2)
 set(GMSH_PATCH_VERSION 0)
-set(GMSH_EXTRA_VERSION "-asml")
+set(GMSH_EXTRA_VERSION "")
 set(GMSH_EXTRA_VERSION_TEXI "${GMSH_EXTRA_VERSION}")
 
 if(NOT GMSH_RELEASE)
@@ -117,7 +117,7 @@ endif()
 
 set(GMSH_VERSION "${GMSH_MAJOR_VERSION}.${GMSH_MINOR_VERSION}")
 set(GMSH_VERSION "${GMSH_VERSION}.${GMSH_PATCH_VERSION}${GMSH_EXTRA_VERSION}")
-set(GMSH_SHORT_LICENSE "ASML NETHERLANDS BV License")
+set(GMSH_SHORT_LICENSE "GNU General Public License")
 
 set(GMSH_API api/gmsh.h api/gmshc.h api/gmsh.h_cwrap)
 
@@ -1996,7 +1996,7 @@ if(NOT DISABLE_GMSH_TESTS)
     # compilers does not understand a full Cygwin-style path)
     FILE(RELATIVE_PATH TEST ${CMAKE_CURRENT_BINARY_DIR} ${TESTFILE})
     if(HAVE_OCC OR NOT ${TEST} MATCHES "boolean" OR NOT ${TEST} MATCHES "occ")
-      add_test(${TEST} ./gmsh ${TEST} -2 -nopopup -o ./tmp.msh)
+      add_test(${TEST} ./gmsh ${TEST} -3 -nopopup -o ./tmp.msh)
     endif()
   endforeach()
 endif()
diff --git a/LICENSE.txt b/LICENSE.txt
index b33addbb04c226d428a84d865a23b2eaec8136be..52b997e33479d4920de2f62985c4b03ef9ddc087 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,2 +1,362 @@
-This version of Gmsh is covered by the GMESH SPRL Software License
-Agreement for Gmsh Software Suite – ASML NETHERLANDS BV
+Gmsh is provided under the terms of the GNU General Public License
+(GPL), Version 2 or later, with the following exception:
+
+  The copyright holders of Gmsh give you permission to combine Gmsh
+  with code included in the standard release of TetGen (from Hang 
+  Si), Netgen (from Joachim Sch"oberl), METIS (from George Karypis
+  at the University of Minnesota), OpenCASCADE (from Open CASCADE
+  S.A.S) and ParaView (from Kitware, Inc.) under their respective
+  licenses. You may copy and distribute such a system following the
+  terms of the GNU GPL for Gmsh and the licenses of the other code
+  concerned, provided that you include the source code of that other
+  code when and as the GNU GPL requires distribution of source code.
+
+  Note that people who make modified versions of Gmsh are not
+  obligated to grant this special exception for their modified
+  versions; it is their choice whether to do so. The GNU General
+  Public License gives permission to release a modified version
+  without this exception; this exception also makes it possible to
+  release a modified version which carries forward this exception.
+
+End of exception.
+
+		    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/Mesh/tetgenBR.cxx b/Mesh/tetgenBR.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..fb69ffcee19ab19a8eed1fbc5769a85008e4e211
--- /dev/null
+++ b/Mesh/tetgenBR.cxx
@@ -0,0 +1,17369 @@
+// This file is part of Tetgen/BR, the Boundary Recovery code of TetGen
+//
+// Copyright 2015-2016 Weierstrass Institute for Applied Analysis and
+// Stochastics
+//
+// This file is relicensed under the terms of LICENSE.txt for use in Gmsh thanks
+// to a Software License Agreement between Weierstrass Institute for Applied
+// Analysis and Stochastics and GMESH SPRL.
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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); // Gmsh commented out, as leads to crashes
+  } 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.
+  size_t loopCount = 0;
+  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);
+
+    // Highest loopCount encountered in a valid testcase is 124. For values
+    // much higher than that, we are likely stuck in an infinite loop.
+    const size_t surelyHangingLoopCountThreshold = 1000000;
+    if (loopCount > surelyHangingLoopCountThreshold)
+    {
+      terminatetetgen(this, 1000);
+    }
+    loopCount++;
+
+  } // 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;
+
+  size_t loopCount = 0;
+  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();
+
+    // Highest loopCount encountered in a valid testcase is 4. For values
+    // much higher than that, we are likely stuck in an infinite loop.
+    const size_t surelyHangingLoopCountThreshold = 10000;
+    if (loopCount > surelyHangingLoopCountThreshold)
+    {
+      terminatetetgen(this, 1000);
+    }
+    loopCount++;
+
+  } // 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/Mesh/tetgenBR.h b/Mesh/tetgenBR.h
new file mode 100644
index 0000000000000000000000000000000000000000..86f7ec7d3f26e175f90d742ec6b24087133385b9
--- /dev/null
+++ b/Mesh/tetgenBR.h
@@ -0,0 +1,2696 @@
+// This file is part of Tetgen/BR, the Boundary Recovery code of TetGen
+//
+// Copyright 2015-2016 Weierstrass Institute for Applied Analysis and
+// Stochastics
+//
+// This file is relicensed under the terms of LICENSE.txt for use in Gmsh thanks
+// to a Software License Agreement between Weierstrass Institute for Applied
+// Analysis and Stochastics and GMESH SPRL.
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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.1;
+    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)
+{
+  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
diff --git a/benchmarks/occ/duplicate_degenerated_edges.geo b/benchmarks/occ/duplicate_degenerated_edges.geo
new file mode 100644
index 0000000000000000000000000000000000000000..051457a73b7354eaa78cf3f363bedadcbe0a3fa0
--- /dev/null
+++ b/benchmarks/occ/duplicate_degenerated_edges.geo
@@ -0,0 +1,17 @@
+SetFactory("OpenCASCADE");
+
+a() = ShapeFromFile("full_sphere.brep");
+
+Mesh.Algorithm = 2;
+Mesh.Algorithm3D = 2;
+Mesh.ElementOrder = 2;
+
+lc = 5;
+Mesh.CharacteristicLengthMin = lc;
+Mesh.CharacteristicLengthMax = lc;
+Mesh 3;
+
+nbtets = Mesh.NbTetrahedra;
+If(nbtets == 0)
+  Error("No tetrahedral elements created!");
+EndIf
diff --git a/contrib/Netgen/CMakeLists.txt b/contrib/Netgen/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..14881c1ee560c9191276ba1c2099a8d7151084d9
--- /dev/null
+++ b/contrib/Netgen/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Gmsh - Copyright (C) 1997-2019 C. Geuzaine, J.-F. Remacle
+#
+# See the LICENSE.txt file for license information. Please report all
+# issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
+
+set(gen libsrc/general)
+set(lin libsrc/linalg)
+set(gpr libsrc/gprim)
+set(mes libsrc/meshing)
+set(opt libsrc/opti)
+
+set(SRC
+  nglib_gmsh.cpp
+  ${gen}/array.cpp ${gen}/bitarray.cpp ${gen}/hashtabl.cpp 
+    ${gen}/symbolta.cpp ${gen}/table.cpp ${gen}/flags.cpp
+    ${gen}/spbita2d.cpp ${gen}/seti.cpp ${gen}/optmem.cpp ${gen}/sort.cpp 
+    ${gen}/mystring.cpp ${gen}/parthreads.cpp
+    ${gen}/dynamicmem.cpp ${gen}/ngexception.cpp ${gen}/profiler.cpp
+  ${lin}/densemat.cpp ${lin}/polynomial.cpp ${lin}/bfgs.cpp
+    ${lin}/linopt.cpp ${lin}/linsearch.cpp
+  ${gpr}/geom2d.cpp ${gpr}/geom3d.cpp ${gpr}/geomtest3d.cpp ${gpr}/adtree.cpp 
+    ${gpr}/transform3d.cpp ${gpr}/geomfuncs.cpp
+  ${mes}/meshclass.cpp ${mes}/adfront2.cpp ${mes}/adfront3.cpp 
+    ${mes}/geomsearch.cpp ${mes}/global.cpp ${mes}/meshtool.cpp
+    ${mes}/netrule2.cpp ${mes}/netrule3.cpp ${mes}/parser2.cpp 
+    ${mes}/parser3.cpp ${mes}/ruler2.cpp ${mes}/ruler3.cpp
+    ${mes}/meshtype.cpp ${mes}/improve2.cpp ${mes}/smoothing2.5.cpp
+    ${mes}/smoothing2.cpp ${mes}/improve3.cpp ${mes}/smoothing3.cpp
+    ${mes}/improve2gen.cpp ${mes}/meshing2.cpp ${mes}/meshing3.cpp
+    ${mes}/localh.cpp ${mes}/delaunay.cpp ${mes}/topology.cpp 
+    ${mes}/clusters.cpp ${mes}/tetrarls.cpp ${mes}/triarls.cpp 
+    ${mes}/quadrls.cpp ${mes}/meshfunc.cpp 
+    ${mes}/refine.cpp ${mes}/bisect.cpp ${mes}/boundarylayer.cpp 
+    ${mes}/specials.cpp ${mes}/msghandler.cpp ${mes}/pyramidrls.cpp
+    ${mes}/pyramid2rls.cpp ${mes}/prism2rls.cpp ${mes}/curvedelems.cpp
+    ${mes}/validate.cpp ${mes}/basegeom.cpp
+)
+
+file(GLOB_RECURSE HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h)
+append_gmsh_src(contrib/Netgen "${SRC};${HDR}")
diff --git a/contrib/Netgen/LICENSE b/contrib/Netgen/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..02d61a13e0dc9e0b6c0d3763375af86d2353d168
--- /dev/null
+++ b/contrib/Netgen/LICENSE
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 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.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, 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 library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+  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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+  If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be 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.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+  9. 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 Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+
+  11. 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 Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  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 library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/contrib/Netgen/README.txt b/contrib/Netgen/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..573548e036d9c6da4fc0c4f1e95553081dfc05e8
--- /dev/null
+++ b/contrib/Netgen/README.txt
@@ -0,0 +1,18 @@
+
+This directory contains Joachim Sch\"oberl's NETGEN mesh generator:
+
+The libsrc and nglib directories are unmodified copies taken from 
+"svn co https://netgen-mesher.svn.sourceforge.net/svnroot/netgen-mesher"
+(svn revision 469), with the following exceptions:
+
+* removed dllexport/dllimport #defines from
+     nglib/nglib.h
+     libsrc/interface/nginterface.h
+     libsrc/interface/mydefs.hpp
+* removed #include <config.h> from 
+     libsrc/includes/mystdlib.h
+
+The nglib_gmsh.cpp file in this directory reimplements the parts of
+nglib/nglib.cpp that Gmsh relies on.
+
+See the LICENSE file for license information.
diff --git a/contrib/Netgen/libsrc/Makefile.am b/contrib/Netgen/libsrc/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..8337b4b7f71b60988fe9bfaba1000d6a3e4ec3fe
--- /dev/null
+++ b/contrib/Netgen/libsrc/Makefile.am
@@ -0,0 +1,5 @@
+AM_CPPFLAGS = 
+
+METASOURCES = AUTO
+
+SUBDIRS = general gprim  linalg include meshing  interface csg geom2d occ stlgeom visualization
diff --git a/contrib/Netgen/libsrc/csg/Makefile.am b/contrib/Netgen/libsrc/csg/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..acf35b00d97db21689e9b82347062c2408ce27d3
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/Makefile.am
@@ -0,0 +1,26 @@
+noinst_HEADERS = algprim.hpp csgparser.hpp extrusion.hpp manifold.hpp \
+singularref.hpp surface.hpp brick.hpp curve2d.hpp gencyl.hpp	      \
+meshsurf.hpp solid.hpp triapprox.hpp csgeom.hpp edgeflw.hpp geoml.hpp \
+polyhedra.hpp specpoin.hpp csg.hpp explicitcurve2d.hpp identify.hpp   \
+revolution.hpp spline3d.hpp vscsg.hpp
+
+
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc/include  $(TCL_INCLUDES)
+METASOURCES = AUTO
+
+lib_LTLIBRARIES = libcsg.la  libcsgvis.la 
+
+
+libcsg_la_SOURCES = algprim.cpp brick.cpp   \
+bspline2d.cpp csgeom.cpp csgparser.cpp curve2d.cpp edgeflw.cpp	       \
+explicitcurve2d.cpp extrusion.cpp gencyl.cpp genmesh.cpp identify.cpp  \
+manifold.cpp meshsurf.cpp polyhedra.cpp revolution.cpp singularref.cpp \
+solid.cpp specpoin.cpp spline3d.cpp surface.cpp triapprox.cpp
+
+
+libcsgvis_la_SOURCES = vscsg.cpp csgpkg.cpp
+
+
+libcsgvis_la_LIBADD = libcsg.la
+#   $(top_builddir)/libsrc/geom2d/libgeom2d.la 
+
diff --git a/contrib/Netgen/libsrc/csg/algprim.cpp b/contrib/Netgen/libsrc/csg/algprim.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..09bad01327fa99cbab10f7817d700efe158f8340
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/algprim.cpp
@@ -0,0 +1,1727 @@
+#include <mystdlib.h>
+
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+
+  double 
+  QuadraticSurface :: CalcFunctionValue (const Point<3> & p) const
+  {
+    return p(0) * (cxx * p(0) + cxy * p(1) + cxz * p(2) + cx) +
+      p(1) * (cyy * p(1) + cyz * p(2) + cy) +
+      p(2) * (czz * p(2) + cz) + c1;
+  }
+
+  void 
+  QuadraticSurface :: CalcGradient (const Point<3> & p, Vec<3> & grad) const
+  {
+    grad(0) = 2 * cxx * p(0) + cxy * p(1) + cxz * p(2) + cx;
+    grad(1) = 2 * cyy * p(1) + cxy * p(0) + cyz * p(2) + cy;
+    grad(2) = 2 * czz * p(2) + cxz * p(0) + cyz * p(1) + cz;
+  }
+
+  void 
+  QuadraticSurface :: CalcHesse (const Point<3> & /* p */, Mat<3> & hesse) const
+  {
+    hesse(0,0) = 2 * cxx;
+    hesse(1,1) = 2 * cyy;
+    hesse(2,2) = 2 * czz;
+    hesse(0,1) = hesse(1,0) = cxy;
+    hesse(0,2) = hesse(2,0) = cxz;
+    hesse(1,2) = hesse(2,1) = cyz;
+  }
+
+
+  void QuadraticSurface :: Read (istream & ist)
+  {
+    ist >> cxx >> cyy >> czz >> cxy >> cxz >> cyz >> cx >> cy >> cz >> c1;
+  }
+
+  void QuadraticSurface :: Print (ostream & ost) const
+  {
+    ost << cxx << "  " << cyy << "  " << czz << "  "
+        << cxy << "  " << cxz << "  " << cyz << "  "
+        << cx << "  " << cy << "  " << cz << "  "
+        << c1;
+  }
+
+
+  void QuadraticSurface :: PrintCoeff (ostream & ost) const
+  {
+    ost << " cxx = " << cxx
+        << " cyy = " << cyy
+        << " czz = " << czz
+        << " cxy = " << cxy
+        << " cxz = " << cxz
+        << " cyz = " << cyz
+        << " cx = " << cx
+        << " cy = " << cy
+        << " cz = " << cz
+        << " c1 = " << c1 << endl;
+  }
+
+
+
+  Point<3> QuadraticSurface :: GetSurfacePoint () const
+  {
+    MyError ("GetSurfacePoint called for QuadraticSurface");
+    return Point<3> (0, 0, 0);
+  }
+
+
+  Plane :: Plane (const Point<3> & ap, Vec<3> an)
+  {
+    eps_base = 1e-8;
+
+    p = ap;
+    n = an;
+    CalcData();
+  }
+
+  void Plane :: CalcData()
+  {
+    n.Normalize();
+    cxx = cyy = czz = cxy = cxz = cyz = 0;
+    cx = n(0); cy = n(1); cz = n(2);
+    c1 = - (cx * p(0) + cy * p(1) + cz * p(2));
+  }
+
+  Primitive * Plane :: Copy () const
+  {
+    return new Plane (p, n);
+  }
+
+  void Plane :: Transform (Transformation<3> & trans)
+  {
+    Point<3> hp;
+    Vec<3> hn;
+    trans.Transform (p, hp);
+    trans.Transform (n, hn);
+    p = hp;
+    n = hn;
+
+    CalcData();
+  }
+
+
+
+  void Plane :: GetPrimitiveData (const char *& classname, 
+                                  Array<double> & coeffs) const
+  {
+    classname = "plane";
+    coeffs.SetSize (6);
+    coeffs.Elem(1) = p(0);
+    coeffs.Elem(2) = p(1);
+    coeffs.Elem(3) = p(2);
+    coeffs.Elem(4) = n(0);
+    coeffs.Elem(5) = n(1);
+    coeffs.Elem(6) = n(2);
+  }
+
+  void Plane :: SetPrimitiveData (Array<double> & coeffs)
+  {
+    p(0) = coeffs.Elem(1);
+    p(1) = coeffs.Elem(2);
+    p(2) = coeffs.Elem(3);
+    n(0) = coeffs.Elem(4);
+    n(1) = coeffs.Elem(5);
+    n(2) = coeffs.Elem(6);
+
+    CalcData();
+  }
+
+  Primitive * Plane :: CreateDefault ()
+  {
+    return new Plane (Point<3> (0,0,0), Vec<3> (0,0,1));
+  }
+
+
+  int Plane :: IsIdentic (const Surface & s2, int & inv, double eps) const
+  {
+    const Plane * ps2 = dynamic_cast<const Plane*>(&s2);
+
+    if(ps2)
+      {
+        Point<3> pp2 = ps2->GetSurfacePoint();
+        Vec<3> n2 = s2.GetNormalVector(pp2);
+
+        if(fabs(n*n2) < 1.-eps_base)
+          return 0;
+
+        if (fabs (s2.CalcFunctionValue(p)) > eps) return 0;
+      }
+    else
+      {
+        if (fabs (s2.CalcFunctionValue(p)) > eps) return 0;
+        Vec<3> hv1, hv2;
+        hv1 = n.GetNormal ();
+        hv2 = Cross (n, hv1);
+      
+        Point<3> hp = p + hv1;
+        if (fabs (s2.CalcFunctionValue(hp)) > eps) return 0;
+        hp = p + hv2;
+        if (fabs (s2.CalcFunctionValue(hp)) > eps) return 0;
+      }
+
+    Vec<3> n1, n2;
+    n1 = GetNormalVector (p);
+    n2 = s2.GetNormalVector (p);
+    inv = (n1 * n2 < 0);
+    return 1;
+  }
+
+
+
+  void Plane :: DefineTangentialPlane (const Point<3> & ap1, const Point<3> & ap2)
+  {
+    Surface::DefineTangentialPlane (ap1, ap2);
+  }
+
+
+  void Plane :: ToPlane (const Point<3> & p3d, 
+                         Point<2> & pplane, 
+                         double h, int & zone) const
+  {
+    Vec<3> p1p;
+
+    p1p = p3d - p1;
+    p1p /= h;
+    pplane(0) = p1p * ex;
+    pplane(1) = p1p * ey;
+    zone = 0;
+  }
+
+  void Plane :: FromPlane (const Point<2> & pplane, Point<3> & p3d, double h) const
+  {
+    p3d = p1 + (h * pplane(0)) * ex + (h * pplane(1)) * ey;
+  }
+
+
+  void Plane :: Project (Point<3> & p3d) const
+  {
+    double val = Plane::CalcFunctionValue (p3d);
+    p3d -= val * n;
+  }
+
+  INSOLID_TYPE Plane :: BoxInSolid (const BoxSphere<3> & box) const
+  {
+    int i;
+    double val;
+    Point<3> pp;
+
+    val = Plane::CalcFunctionValue (box.Center());
+    if (val > box.Diam() / 2) return IS_OUTSIDE;
+    if (val < -box.Diam() / 2) return IS_INSIDE;
+
+    if (val > 0)
+      {
+        /*
+          double modify = 
+          ((box.MaxX()-box.MinX()) * fabs (cx) + 
+          (box.MaxY()-box.MinY()) * fabs (cy) + 
+          (box.MaxZ()-box.MinZ()) * fabs (cz)) / 2;
+        */
+        Vec<3> vdiag = box.PMax() - box.PMin();
+        double modify = (vdiag(0) * fabs (cx) + 
+                         vdiag(1) * fabs (cy) + 
+                         vdiag(2) * fabs (cz) ) / 2;
+
+        if (val - modify < 0)
+          return DOES_INTERSECT;
+        return IS_OUTSIDE;
+
+        // only outside or intersect possible
+        for (i = 0; i < 8; i++)
+          {
+            pp = box.GetPointNr (i);
+            val = Plane::CalcFunctionValue (pp);
+            if (val < 0) 
+              return DOES_INTERSECT;
+          }
+        return IS_OUTSIDE;
+      }
+    else
+      {
+        /*
+          double modify = 
+          ((box.MaxX()-box.MinX()) * fabs (cx) + 
+          (box.MaxY()-box.MinY()) * fabs (cy) + 
+          (box.MaxZ()-box.MinZ()) * fabs (cz)) / 2;
+        */
+        Vec<3> vdiag = box.PMax() - box.PMin();
+        double modify =  (vdiag(0) * fabs (cx) + 
+                          vdiag(1) * fabs (cy) + 
+                          vdiag(2) * fabs (cz) ) / 2;
+        if (val + modify > 0)
+          return DOES_INTERSECT;
+        return IS_INSIDE;
+
+
+        // only inside or intersect possible
+        for (i = 0; i < 8; i++)
+          {
+            pp = box.GetPointNr (i);
+            val = Plane::CalcFunctionValue (pp);
+            if (val > 0) 
+              return DOES_INTERSECT;
+          }
+        return IS_INSIDE;
+      }
+
+
+
+    /*
+      for (i = 1; i <= 8; i++)
+      {
+      box.GetPointNr (i, p);
+      val = CalcFunctionValue (p);
+      if (val > 0) inside = 0;
+      if (val < 0) outside = 0;
+      }
+
+      if (inside) return IS_INSIDE;
+      if (outside) return IS_OUTSIDE;
+      return DOES_INTERSECT;
+    */
+  }
+
+
+
+  // double Plane :: CalcFunctionValue (const Point<3> & p3d) const
+  // {
+  //   return cx * p3d(0) + cy * p3d(1) + cz * p3d(2) + c1;
+  // }
+
+  void Plane :: CalcGradient (const Point<3> & /* p */, Vec<3> & grad) const
+  {
+    grad(0) = cx;
+    grad(1) = cy;
+    grad(2) = cz;
+  }
+
+  void Plane :: CalcHesse (const Point<3> & /* p */, Mat<3> & hesse) const
+  {
+    hesse = 0;
+  }
+
+  double Plane :: HesseNorm () const
+  {
+    return 0;
+  }
+
+
+  Point<3> Plane :: GetSurfacePoint () const
+  {
+    return p;
+  }
+
+
+  void Plane :: GetTriangleApproximation 
+  (TriangleApproximation & tas, 
+   const Box<3> & boundingbox, double /* facets */) const
+  {
+    // find triangle, such that
+    // boundingbox \cap plane is contained in it
+
+    Point<3> c = boundingbox.Center();
+    double r = boundingbox.Diam();
+
+    Project (c);
+    Vec<3> t1 = n.GetNormal();
+    Vec<3> t2 = Cross (n, t1);
+
+    t1.Normalize();
+    t2.Normalize();
+
+    tas.AddPoint (c + (-0.5 * r) * t2 + (sqrt(0.75) * r) * t1);
+    tas.AddPoint (c + (-0.5 * r) * t2 + (-sqrt(0.75) * r) * t1);
+    tas.AddPoint (c +  r * t2);
+
+    tas.AddTriangle (TATriangle (0, 0, 1, 2));
+  }
+
+
+
+
+  Sphere :: Sphere (const Point<3> & ac, double ar)
+  {
+    c = ac;
+    r = ar;
+    invr = 1.0/r;
+
+    cxx = cyy = czz = 0.5 / r;
+    cxy = cxz = cyz = 0;
+    cx = - c(0) / r;
+    cy = - c(1) / r;
+    cz = - c(2) / r;
+    c1 = (c(0) * c(0) + c(1) * c(1) + c(2) * c(2)) / (2 * r) - r / 2;
+  }
+
+  void Sphere :: GetPrimitiveData (const char *& classname, Array<double> & coeffs) const
+  {
+    classname = "sphere";
+    coeffs.SetSize (4);
+    coeffs.Elem(1) = c(0);
+    coeffs.Elem(2) = c(1);
+    coeffs.Elem(3) = c(2);
+    coeffs.Elem(4) = r;
+  }
+
+  void Sphere :: SetPrimitiveData (Array<double> & coeffs)
+  {
+    c(0) = coeffs.Elem(1);
+    c(1) = coeffs.Elem(2);
+    c(2) = coeffs.Elem(3);
+    r = coeffs.Elem(4);
+
+    invr = 1.0/r;
+    cxx = cyy = czz = 0.5 / r;
+    cxy = cxz = cyz = 0;
+    cx = - c(0) / r;
+    cy = - c(1) / r;
+    cz = - c(2) / r;
+    c1 = (c(0) * c(0) + c(1) * c(1) + c(2) * c(2)) / (2 * r) - r / 2;
+  }
+
+  Primitive * Sphere :: CreateDefault ()
+  {
+    return new Sphere (Point<3> (0,0,0), 1);
+  }
+
+
+
+  Primitive * Sphere :: Copy () const
+  {
+    return new Sphere (c, r);
+  }
+
+  void Sphere :: Transform (Transformation<3> & trans)
+  {
+    Point<3> hp;
+    trans.Transform (c, hp);
+    c = hp;
+
+    cxx = cyy = czz = 0.5 / r;
+    cxy = cxz = cyz = 0;
+    cx = - c(0) / r;
+    cy = - c(1) / r;
+    cz = - c(2) / r;
+    c1 = (c(0) * c(0) + c(1) * c(1) + c(2) * c(2)) / (2 * r) - r / 2;
+  }
+
+
+  double Sphere :: CalcFunctionValue (const Point<3> & point) const
+  {
+    return 0.5* (invr * Abs2 (point-c) - r);
+  }
+
+
+  int Sphere :: IsIdentic (const Surface & s2, int & inv, double eps) const
+  {
+    const Sphere * sp2 = dynamic_cast<const Sphere*>  (&s2);
+
+    if (!sp2) return 0;
+
+    if (Dist (sp2->c, c) > eps) return 0;
+    if (fabs (sp2->r - r) > eps) return 0;
+
+    inv = 0;
+
+    return 1;
+  }
+
+
+  void Sphere :: DefineTangentialPlane (const Point<3> & ap1, const Point<3> & ap2)
+  {
+    Surface::DefineTangentialPlane (ap1, ap2);
+
+    ez = p1 - c;
+    ez /= ez.Length();
+
+    ex = p2 - p1;
+    ex -= (ex * ez) * ez;
+    ex /= ex.Length();
+
+    ey = Cross (ez, ex);
+  }
+
+
+  void Sphere :: ToPlane (const Point<3> & p, Point<2> & pplane, double h, int & zone) const
+  {
+    Vec<3> p1p;
+  
+    p1p = p - p1;
+  
+    /*
+      if (p1p * ez < -r)
+      {
+      zone = -1;
+      pplane = Point<2> (1E8, 1E8);
+      }
+      else
+      { 
+      zone = 0;
+      p1p /= h;
+      pplane(0) = p1p * ex;
+      pplane(1) = p1p * ey;
+      }
+    */
+
+    Point<3> p1top = c + (c - p1);
+
+    Vec<3> p1topp = p - p1top;
+    Vec<3> p1topp1 = p1 - p1top;
+    Vec<3> lam;
+    //  SolveLinearSystem (ex, ey, p1topp, p1topp1, lam);
+
+    Mat<3> m;
+    for (int i = 0; i < 3; i++)
+      {
+        m(i, 0) = ex(i);
+        m(i, 1) = ey(i);
+        m(i, 2) = p1topp(i);
+      }
+    m.Solve (p1topp1, lam);
+
+    pplane(0) = -lam(0) / h;
+    pplane(1) = -lam(1) / h;
+  
+    if (lam(2) > 2)
+      zone = -1;
+    else 
+      zone = 0;
+  }
+
+  void Sphere :: FromPlane (const Point<2> & pplane, Point<3> & p, double h) const
+  {
+    /*
+    //  Vec<3> p1p;
+    double z;
+    Point<2> pplane2 (pplane);
+
+    pplane2(0) *= h;
+    pplane2(1) *= h;
+    z = -r + sqrt (sqr (r) - sqr (pplane2(0)) - sqr (pplane2(1)));
+    //  p = p1;
+    p(0) = p1(0) + pplane2(0) * ex(0) + pplane2(1) * ey(0) + z * ez(0);
+    p(1) = p1(1) + pplane2(0) * ex(1) + pplane2(1) * ey(1) + z * ez(1);
+    p(2) = p1(2) + pplane2(0) * ex(2) + pplane2(1) * ey(2) + z * ez(2);
+    */
+
+    Point<2> pplane2 (pplane);
+
+    pplane2(0) *= h;
+    pplane2(1) *= h;
+
+    p(0) = p1(0) + pplane2(0) * ex(0) + pplane2(1) * ey(0);
+    p(1) = p1(1) + pplane2(0) * ex(1) + pplane2(1) * ey(1);
+    p(2) = p1(2) + pplane2(0) * ex(2) + pplane2(1) * ey(2);
+    Project (p);
+  }
+
+
+  void Sphere :: Project (Point<3> & p) const
+  {
+    Vec<3> v;
+    v = p - c;
+    v *= (r / v.Length());
+    p = c + v;
+  }
+
+
+  INSOLID_TYPE Sphere :: BoxInSolid (const BoxSphere<3> & box) const
+  {
+    double dist;
+    dist = Dist (box.Center(), c);
+
+    if (dist - box.Diam()/2 > r) return IS_OUTSIDE;
+    if (dist + box.Diam()/2 < r) return IS_INSIDE;
+    return DOES_INTERSECT;
+  }
+
+  double Sphere :: HesseNorm () const
+  {
+    return 2 / r;
+  }
+
+
+  Point<3> Sphere :: GetSurfacePoint () const
+  {
+    return c + Vec<3> (r, 0, 0);
+  }
+
+
+  void Sphere :: GetTriangleApproximation 
+  (TriangleApproximation & tas, 
+   const Box<3> & /* boundingbox */, double facets) const
+  {
+    int n = int(facets) + 1;  
+
+    for (int j = 0; j <= n; j++)
+      for (int i = 0; i <= n; i++)
+        {
+          double lg = 2 * M_PI * double (i) / n;
+          double bg = M_PI * (double(j) / n - 0.5);
+
+          Point<3> p(c(0) + r * cos(bg) * sin (lg),
+                     c(1) + r * cos(bg) * cos (lg),
+                     c(2) + r * sin(bg));
+          tas.AddPoint (p);
+        }
+
+    for (int j = 0; j < n; j++)
+      for (int i = 0; i < n; i++)
+        {
+          int pi = i + (n+1) * j;
+          tas.AddTriangle (TATriangle (0, pi, pi+1, pi+n+2));
+          tas.AddTriangle (TATriangle (0, pi, pi+n+2, pi+n+1));
+        }
+  }
+
+
+
+
+
+  Ellipsoid :: 
+  Ellipsoid (const Point<3> & aa,
+             const Vec<3> & av1, const Vec<3> & av2, const Vec<3> & av3)
+  {
+    a = aa;
+    v1 = av1;
+    v2 = av2;
+    v3 = av3;
+
+    CalcData();
+  }
+
+
+  void Ellipsoid :: CalcData ()
+  {
+    // f = (x-a, vl)^2 / |vl|^2 + (x-a, vs)^2 / |vs|^2 -1
+    // f = sum_{i=1}^3  (x-a,v_i)^2 / |vi|^4 - 1   =  sum (x-a,hv_i)^2
+  
+    Vec<3> hv1, hv2, hv3;
+    double lv1 = v1.Length2 ();
+    if (lv1 < 1e-32) lv1 = 1;
+    double lv2 = v2.Length2 ();
+    if (lv2 < 1e-32) lv2 = 1;
+    double lv3 = v3.Length2 ();
+    if (lv3 < 1e-32) lv3 = 1;
+
+    rmin = sqrt (min3 (lv1, lv2, lv3));
+
+    hv1 = (1.0 / lv1) * v1;
+    hv2 = (1.0 / lv2) * v2;
+    hv3 = (1.0 / lv3) * v3;
+
+    cxx = hv1(0) * hv1(0) + hv2(0) * hv2(0) + hv3(0) * hv3(0);
+    cyy = hv1(1) * hv1(1) + hv2(1) * hv2(1) + hv3(1) * hv3(1);
+    czz = hv1(2) * hv1(2) + hv2(2) * hv2(2) + hv3(2) * hv3(2);
+
+    cxy = 2 * (hv1(0) * hv1(1) + hv2(0) * hv2(1) + hv3(0) * hv3(1));
+    cxz = 2 * (hv1(0) * hv1(2) + hv2(0) * hv2(2) + hv3(0) * hv3(2));
+    cyz = 2 * (hv1(1) * hv1(2) + hv2(1) * hv2(2) + hv3(1) * hv3(2));
+
+    Vec<3> va (a);
+    c1 = sqr(va * hv1) + sqr(va * hv2) + sqr(va * hv3) - 1;
+  
+    Vec<3> v = -2 * (va * hv1) * hv1 - 2 * (va * hv2) * hv2  - 2 * (va * hv3) * hv3;
+    cx = v(0);
+    cy = v(1);
+    cz = v(2);
+  }
+
+
+  INSOLID_TYPE Ellipsoid :: BoxInSolid (const BoxSphere<3> & box) const
+  {
+    // double grad = 2.0 / rmin;
+    // double grad = 3*(box.Center()-a).Length() / (rmin*rmin*rmin);
+
+    double ggrad = 1.0 / (rmin*rmin);
+    Vec<3> g;
+    double val = CalcFunctionValue (box.Center());
+    CalcGradient (box.Center(), g);
+    double grad = g.Length();
+
+    double r = box.Diam() / 2;
+    double maxval = grad * r + ggrad * r * r;
+
+    //  (*testout) << "box = " << box << ", val = " << val << ", maxval = " << maxval << endl;
+
+    if (val > maxval) return IS_OUTSIDE;
+    if (val < -maxval) return IS_INSIDE;
+    return DOES_INTERSECT;
+  }
+
+
+  double Ellipsoid :: HesseNorm () const
+  {
+    return 1.0/ (rmin * rmin);
+  }
+
+  double Ellipsoid :: MaxCurvature () const
+  {
+    const double a2 = v1.Length2();
+    const double b2 = v2.Length2();
+    const double c2 = v3.Length2();
+
+    return max3 ( sqrt(a2)/min2(b2,c2), sqrt(b2)/min2(a2,c2), sqrt(c2)/min2(a2,b2) );
+  }
+
+  Point<3> Ellipsoid :: GetSurfacePoint () const
+  {
+    return a + v1;
+  }
+
+
+
+  void Ellipsoid :: GetTriangleApproximation 
+  (TriangleApproximation & tas, 
+   const Box<3> & /* boundingbox */, double facets) const
+  {
+    int n = int(facets) + 1;  
+
+    for (int j = 0; j <= n; j++)
+      for (int i = 0; i <= n; i++)
+        {
+          double lg = 2 * M_PI * double (i) / n;
+          double bg = M_PI * (double(j) / n - 0.5);
+
+          Point<3> p(a + 
+                     sin (bg) * v1 + 
+                     cos (bg) * sin (lg) * v2 +
+                     cos (bg) * cos (lg) * v3);
+
+          tas.AddPoint (p);
+        }
+
+    for (int j = 0; j < n; j++)
+      for (int i = 0; i < n; i++)
+        {
+          int pi = i + (n+1) * j;
+          tas.AddTriangle (TATriangle (0, pi, pi+1, pi+n+2));
+          tas.AddTriangle (TATriangle (0, pi, pi+n+2, pi+n+1));
+        }
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  Cylinder :: Cylinder (Array<double> & coeffs)
+  {
+    SetPrimitiveData(coeffs);
+  }
+
+  Cylinder :: Cylinder (const Point<3> & aa, const Point<3> & ab, double ar)
+  {
+    a = aa;
+    b = ab;
+    vab = (b - a);
+    vab /= vab.Length();
+    r = ar;
+
+    // ( <x,x> - 2 <x,a> + <a,a>
+    //   - <x,vab>^2 + 2 <x,vab> <a, vab> - <a, vab>^2
+    //   - r^2) / (2r) = 0
+
+    double hv;
+    cxx = cyy = czz = 0.5 / r;
+    cxy = cxz = cyz = 0;
+    cx = - a(0) / r;
+    cy = - a(1) / r;
+    cz = - a(2) / r;
+    c1 = (a(0) * a(0) + a(1) * a(1) + a(2) * a(2)) / (2 * r);
+    hv = a(0) * vab(0) + a(1) * vab(1) + a(2) * vab(2);
+    cxx -= vab(0) * vab(0) / (2 * r);
+    cyy -= vab(1) * vab(1) / (2 * r);
+    czz -= vab(2) * vab(2) / (2 * r);
+    cxy -= vab(0) * vab(1) / r;
+    cxz -= vab(0) * vab(2) / r;
+    cyz -= vab(1) * vab(2) / r;
+    cx += vab(0) * hv / r;
+    cy += vab(1) * hv / r;
+    cz += vab(2) * hv / r;
+    c1 -= hv * hv / (2 * r);
+    c1 -= r / 2;
+    //  PrintCoeff ();
+  }
+
+
+
+
+  void Cylinder :: GetPrimitiveData (const char *& classname, Array<double> & coeffs) const
+  {
+    classname = "cylinder";
+    coeffs.SetSize (7);
+    coeffs.Elem(1) = a(0);
+    coeffs.Elem(2) = a(1);
+    coeffs.Elem(3) = a(2);
+    coeffs.Elem(4) = b(0);
+    coeffs.Elem(5) = b(1);
+    coeffs.Elem(6) = b(2);
+    coeffs.Elem(7) = r;
+  }
+
+  void Cylinder :: SetPrimitiveData (Array<double> & coeffs)
+  {
+    a(0) = coeffs.Elem(1);
+    a(1) = coeffs.Elem(2);
+    a(2) = coeffs.Elem(3);
+    b(0) = coeffs.Elem(4);
+    b(1) = coeffs.Elem(5);
+    b(2) = coeffs.Elem(6);
+    r = coeffs.Elem(7);
+
+
+    vab = (b - a);
+    vab /= vab.Length();
+
+
+    double hv;
+    cxx = cyy = czz = 0.5 / r;
+    cxy = cxz = cyz = 0;
+    cx = - a(0) / r;
+    cy = - a(1) / r;
+    cz = - a(2) / r;
+    c1 = (a(0) * a(0) + a(1) * a(1) + a(2) * a(2)) / (2 * r);
+    hv = a(0) * vab(0) + a(1) * vab(1) + a(2) * vab(2);
+    cxx -= vab(0) * vab(0) / (2 * r);
+    cyy -= vab(1) * vab(1) / (2 * r);
+    czz -= vab(2) * vab(2) / (2 * r);
+    cxy -= vab(0) * vab(1) / r;
+    cxz -= vab(0) * vab(2) / r;
+    cyz -= vab(1) * vab(2) / r;
+    cx += vab(0) * hv / r;
+    cy += vab(1) * hv / r;
+    cz += vab(2) * hv / r;
+    c1 -= hv * hv / (2 * r);
+    c1 -= r / 2;
+  }
+
+  Primitive * Cylinder :: CreateDefault ()
+  {
+    return new Cylinder (Point<3> (0,0,0), Point<3> (1,0,0), 1);
+  }
+
+
+
+
+  Primitive * Cylinder :: Copy () const
+  {
+    return new Cylinder (a, b, r);
+  }
+
+
+  int Cylinder :: IsIdentic (const Surface & s2, int & inv, double eps) const
+  {
+    const Cylinder * cyl2 = dynamic_cast<const Cylinder*>  (&s2);
+
+    if (!cyl2) return 0;
+
+    if (fabs (cyl2->r - r) > eps) return 0;
+
+    Vec<3> v1 = b - a;
+    Vec<3> v2 = cyl2->a - a;
+
+    if ( fabs (v1 * v2) < (1-eps) * v1.Length() * v2.Length()) return 0;
+    v2 = cyl2->b - a;
+    if ( fabs (v1 * v2) < (1-eps) * v1.Length() * v2.Length()) return 0;
+
+    inv = 0;
+    return 1;
+  }
+
+
+
+  void Cylinder :: Transform (Transformation<3> & trans)
+  {
+    Point<3> hp;
+    trans.Transform (a, hp);
+    a = hp;
+    trans.Transform (b, hp);
+    b = hp;
+
+    vab = (b - a);
+    vab /= vab.Length();
+
+    // ( <x,x> - 2 <x,a> + <a,a>
+    //   - <x,vab>^2 + 2 <x,vab> <a, vab> - <a, vab>^2
+    //   - r^2) / (2r) = 0
+
+    double hv;
+    cxx = cyy = czz = 0.5 / r;
+    cxy = cxz = cyz = 0;
+    cx = - a(0) / r;
+    cy = - a(1) / r;
+    cz = - a(2) / r;
+    c1 = (a(0) * a(0) + a(1) * a(1) + a(2) * a(2)) / (2 * r);
+    hv = a(0) * vab(0) + a(1) * vab(1) + a(2) * vab(2);
+    cxx -= vab(0) * vab(0) / (2 * r);
+    cyy -= vab(1) * vab(1) / (2 * r);
+    czz -= vab(2) * vab(2) / (2 * r);
+    cxy -= vab(0) * vab(1) / r;
+    cxz -= vab(0) * vab(2) / r;
+    cyz -= vab(1) * vab(2) / r;
+    cx += vab(0) * hv / r;
+    cy += vab(1) * hv / r;
+    cz += vab(2) * hv / r;
+    c1 -= hv * hv / (2 * r);
+    c1 -= r / 2;
+    //  PrintCoeff ();
+  }
+
+
+
+
+
+
+
+
+
+  void Cylinder :: DefineTangentialPlane (const Point<3> & ap1, const Point<3> & ap2)
+  {
+    Surface::DefineTangentialPlane (ap1, ap2);
+
+    ez = Center (p1, p2) - a;
+    ez -= (ez * vab) * vab;
+    ez /= ez.Length();
+
+    ex = p2 - p1;
+    ex -= (ex * ez) * ez;
+    ex /= ex.Length();
+
+    ey = Cross (ez, ex);
+  }
+
+
+  void Cylinder :: ToPlane (const Point<3> & p, 
+                            Point<2> & pplane, 
+                            double h, int & zone) const
+  {
+    Point<3> cp1p2 = Center (p1, p2);
+    Project (cp1p2);
+  
+    Point<3> ccp1p2 = a + ( (cp1p2 - a) * vab ) * vab;
+
+    Vec<3> er = cp1p2 - ccp1p2;
+    er.Normalize();
+    Vec<3> ephi = Cross (vab, er);
+
+    double co, si;
+    Point<2> p1p, p2p, pp;
+
+    co = er * (p1 - ccp1p2);
+    si = ephi * (p1 - ccp1p2);
+    p1p(0) = r * atan2 (si, co);
+    p1p(1) = vab * (p1 - ccp1p2);
+
+    co = er * (p2 - ccp1p2);
+    si = ephi * (p2 - ccp1p2);
+    p2p(0) = r * atan2 (si, co);
+    p2p(1) = vab * (p2 - ccp1p2);
+  
+    co = er * (p - ccp1p2);
+    si = ephi * (p - ccp1p2);
+
+    double phi = atan2 (si, co);
+    pp(0) = r * phi;
+    pp(1) = vab * (p - ccp1p2);
+  
+    zone = 0;
+    if (phi > 1.57) zone = 1;
+    if (phi < -1.57) zone = 2;
+
+
+
+    Vec<2> e2x = p2p - p1p;
+    e2x /= e2x.Length();
+
+    Vec<2> e2y (-e2x(1), e2x(0));
+
+    Vec<2> p1pp = pp - p1p;
+
+
+    pplane(0) = (p1pp * e2x) / h;
+    pplane(1) = (p1pp * e2y) / h;
+
+    /*
+      (*testout) << "p1 = " << p1 << ",  p2 = " << p2 << endl;
+      (*testout) << "p = " << p << ",  pp = " << pp << ",  pplane = " << pplane << endl;
+    */
+
+    /*
+      Vec<3> p1p;
+
+      p1p = p - p1;
+
+      if (p1p * ez < -1 * r)
+      {
+      zone = -1;
+      pplane(0) = 1e8;
+      pplane(1) = 1e8;
+      }
+      else
+      {
+      zone = 0;
+      p1p /= h;
+      pplane(0) = p1p * ex;
+      pplane(1) = p1p * ey;
+      }
+    */
+  }
+
+  void Cylinder :: FromPlane (const Point<2> & pplane, Point<3> & p, double h) const
+  {
+    Point<2> pplane2 (pplane);
+
+    pplane2(0) *= h;
+    pplane2(1) *= h;
+
+    p(0) = p1(0) + pplane2(0) * ex(0) + pplane2(1) * ey(0);
+    p(1) = p1(1) + pplane2(0) * ex(1) + pplane2(1) * ey(1);
+    p(2) = p1(2) + pplane2(0) * ex(2) + pplane2(1) * ey(2);
+    Project (p);
+  }
+
+
+  void Cylinder :: Project (Point<3> & p) const
+  {
+    Vec<3> v;
+    Point<3> c;
+
+    c = a + ((p - a) * vab) * vab;
+    v = p - c;
+    v *= (r / v.Length());
+    p = c + v;
+  }
+  /*
+    int Cylinder :: RootInBox (const BoxSphere<3> & box) const
+    {
+    double dist;
+    dist = sqrt (2 * CalcFunctionValue(box.Center()) * r + r * r);
+    if (fabs (dist - r) > box.Diam()/2) return 0;
+    return 2;
+    }
+  */
+
+  INSOLID_TYPE Cylinder :: BoxInSolid (const BoxSphere<3> & box) const
+  {
+    double dist;
+    //  dist = sqrt (2 * CalcFunctionValue(box.Center()) * r + r * r);
+
+    dist =  (2 * CalcFunctionValue(box.Center()) * r + r * r);
+    if (dist <= 0) dist = 0;
+    else dist = sqrt (dist + 1e-16);
+
+    if (dist - box.Diam()/2 > r) return IS_OUTSIDE;
+    if (dist + box.Diam()/2 < r) return IS_INSIDE;
+    return DOES_INTERSECT;
+  }
+
+
+  double Cylinder :: HesseNorm () const
+  {
+    return 2 / r;
+  }
+
+  Point<3> Cylinder :: GetSurfacePoint () const
+  {
+    Vec<3> vr;
+    if (fabs (vab(0)) > fabs(vab(2)))
+      vr = Vec<3> (vab(1), -vab(0), 0);
+    else
+      vr = Vec<3> (0, -vab(2), vab(1));
+    
+    vr *= (r / vr.Length());
+    return a + vr;
+  }
+
+  void Cylinder :: GetTriangleApproximation 
+  (TriangleApproximation & tas, 
+   const Box<3> & /* boundingbox */, double facets) const
+  {
+    int n = int(facets) + 1;  
+
+    Vec<3> lvab = b - a;
+    Vec<3> n1 = lvab.GetNormal();
+    Vec<3> n2 = Cross (lvab, n1);
+  
+    n1.Normalize();
+    n2.Normalize();
+
+
+    for (int j = 0; j <= n; j++)
+      for (int i = 0; i <= n; i++)
+        {
+          double lg = 2 * M_PI * double (i) / n;
+          double bg = double(j) / n;
+
+          Point<3> p = a + (bg * lvab) 
+            + ((r * cos(lg)) * n1) 
+            + ((r * sin(lg)) * n2);
+
+          tas.AddPoint (p);
+        }
+
+    for (int j = 0; j < n; j++)
+      for (int i = 0; i < n; i++)
+        {
+          int pi = i + (n+1) * j;
+          tas.AddTriangle (TATriangle (0, pi, pi+1, pi+n+2));
+          tas.AddTriangle (TATriangle (0, pi, pi+n+2, pi+n+1));
+        }
+  }
+
+
+
+
+
+
+
+
+
+  EllipticCylinder :: 
+  EllipticCylinder (const Point<3> & aa,
+                    const Vec<3> & avl, const Vec<3> & avs)
+  {
+    a = aa;
+    if(avl.Length2() > avs.Length2())
+      {
+        vl = avl;
+        vs = avs;
+      }
+    else
+      {
+        vl = avs;
+        vs = avl;
+      }
+
+    CalcData();
+  }
+
+  EllipticCylinder :: EllipticCylinder (Array<double> & coeffs)
+  {
+    SetPrimitiveData(coeffs);
+  }
+
+
+
+  void EllipticCylinder :: GetPrimitiveData (const char *& classname, Array<double> & coeffs) const
+  {
+    classname = "ellipticcylinder";
+    coeffs.SetSize (9);
+    coeffs[0] = a(0);
+    coeffs[1] = a(1);
+    coeffs[2] = a(2);
+    coeffs[3] = vl(0);
+    coeffs[4] = vl(1);
+    coeffs[5] = vl(2);
+    coeffs[6] = vs(0);
+    coeffs[7] = vs(1);
+    coeffs[8] = vs(2);
+  }
+
+  void EllipticCylinder :: SetPrimitiveData (Array<double> & coeffs)
+  {
+    a(0) = coeffs[0];
+    a(1) = coeffs[1];
+    a(2) = coeffs[2];
+    vl(0) = coeffs[3];
+    vl(1) = coeffs[4];
+    vl(2) = coeffs[5];
+    vs(0) = coeffs[6];
+    vs(1) = coeffs[7];
+    vs(2) = coeffs[8];
+
+    CalcData();
+  }
+
+
+
+  void EllipticCylinder :: CalcData ()
+  {
+    // f = (x-a, vl)^2 / |vl|^2 + (x-a, vs)^2 / |vs|^2 -1
+
+    Vec<3> hvl, hvs;
+    double lvl = vl.Length2 ();
+    if (lvl < 1e-32) lvl = 1;
+    double lvs = vs.Length2 ();
+    if (lvs < 1e-32) lvs = 1;
+
+    hvl = (1.0 / lvl) * vl;
+    hvs = (1.0 / lvs) * vs;
+
+    cxx = hvl(0) * hvl(0) + hvs(0) * hvs(0);
+    cyy = hvl(1) * hvl(1) + hvs(1) * hvs(1);
+    czz = hvl(2) * hvl(2) + hvs(2) * hvs(2);
+
+    cxy = 2 * (hvl(0) * hvl(1) + hvs(0) * hvs(1));
+    cxz = 2 * (hvl(0) * hvl(2) + hvs(0) * hvs(2));
+    cyz = 2 * (hvl(1) * hvl(2) + hvs(1) * hvs(2));
+
+    Vec<3> va (a);
+    c1 = pow(va * hvl,2) + pow(va * hvs,2) - 1;
+  
+    Vec<3> v = -2 * (va * hvl) * hvl - 2 * (va * hvs) * hvs;
+    cx = v(0);
+    cy = v(1);
+    cz = v(2);
+  }
+
+
+  INSOLID_TYPE EllipticCylinder :: BoxInSolid (const BoxSphere<3> & box) const
+  {
+    double grad = 2.0 / vs.Length ();
+    double ggrad = 1.0 / vs.Length2 ();
+
+    double val = CalcFunctionValue (box.Center());
+    double r = box.Diam() / 2;
+    double maxval = grad * r + ggrad * r * r;
+
+    // (*testout) << "box = " << box << ", val = " << val << ", maxval = " << maxval << endl;
+
+    if (val > maxval) return IS_OUTSIDE;
+    if (val < -maxval) return IS_INSIDE;
+    return DOES_INTERSECT;
+  }
+
+
+  double EllipticCylinder :: HesseNorm () const
+  {
+    return 1.0/min(vs.Length2 (),vl.Length2());
+  }
+
+  double EllipticCylinder :: MaxCurvature () const
+  {
+    double aa = vs.Length();
+    double bb = vl.Length();
+
+    return max2(bb/(aa*aa),aa/(bb*bb));
+  }
+
+  double EllipticCylinder :: MaxCurvatureLoc (const Point<3> & /* c */, 
+                                              double /* rad */) const
+  {
+    // saubere Loesung wird noch notwendig !!!
+    double aa = vs.Length();
+    double bb = vl.Length();
+    return max2(bb/(aa*aa),aa/(bb*bb));
+  }
+
+
+
+  Point<3> EllipticCylinder :: GetSurfacePoint () const
+  {
+    return a + vl;
+  }
+
+
+
+  void EllipticCylinder :: GetTriangleApproximation 
+  (TriangleApproximation & tas, 
+   const Box<3> & /* boundingbox */, double facets) const
+  {
+    int n = int(facets) + 1;  
+
+    Vec<3> axis = Cross (vl, vs);
+
+    for (int j = 0; j <= n; j++)
+      for (int i = 0; i <= n; i++)
+        {
+          double lg = 2 * M_PI * double (i) / n;
+          double bg = double(j) / n;
+
+          Point<3> p = a + (bg * axis)
+            + cos(lg) * vl + sin(lg) * vs;
+
+          tas.AddPoint (p);
+        }
+
+    for (int j = 0; j < n; j++)
+      for (int i = 0; i < n; i++)
+        {
+          int pi = i + (n+1) * j;
+          tas.AddTriangle (TATriangle (0, pi, pi+1, pi+n+2));
+          tas.AddTriangle (TATriangle (0, pi, pi+n+2, pi+n+1));
+        }
+  }
+
+
+
+
+
+
+
+
+
+
+  Cone :: Cone (const Point<3> & aa, const Point<3> & ab, 
+                double ara, double arb)
+  {
+    a = aa;
+    b = ab;
+    ra = ara;
+    rb = arb;
+
+    CalcData();
+    // Print (cout);
+  }
+
+
+  Primitive * Cone :: CreateDefault ()
+  {
+    return new Cone (Point<3> (0,0,0), Point<3> (1,0,0), 0.5, 0.2);
+  }
+
+
+
+
+  void Cone :: GetPrimitiveData (const char *& classname, Array<double> & coeffs) const
+  {
+    classname = "cone";
+    coeffs.SetSize (8);
+    coeffs.Elem(1) = a(0);
+    coeffs.Elem(2) = a(1);
+    coeffs.Elem(3) = a(2);
+    coeffs.Elem(4) = b(0);
+    coeffs.Elem(5) = b(1);
+    coeffs.Elem(6) = b(2);
+    coeffs.Elem(7) = ra;
+    coeffs.Elem(8) = rb;
+  }
+
+  void Cone :: SetPrimitiveData (Array<double> & coeffs)
+  {
+    a(0) = coeffs.Elem(1);
+    a(1) = coeffs.Elem(2);
+    a(2) = coeffs.Elem(3);
+    b(0) = coeffs.Elem(4);
+    b(1) = coeffs.Elem(5);
+    b(2) = coeffs.Elem(6);
+    ra = coeffs.Elem(7);
+    rb = coeffs.Elem(8);
+
+    CalcData();
+  }
+
+  void Cone :: CalcData ()
+  {
+
+    minr = (ra < rb) ? ra : rb;
+
+    vab = b - a;
+    vabl = vab.Length();
+
+    Vec<3> va (a);
+
+    //
+    //   f = r(P)^2 - R(z(P))^2
+    //
+    //   z(P) = t0vec * P + t0 = (P-a, b-a)/(b-a,b-a)
+    //   R(z(P)) = t1vec * P + t1 = rb * z + ra * (1-z)
+    //   r(P)^2 =||P-a||^2 - ||a-b||^2 z^2k
+
+    cosphi = vabl / sqrt (vabl*vabl+sqr(ra-rb));
+
+    t0vec = vab;
+    t0vec /= (vabl * vabl);
+    t0 = -(va * vab) / (vabl * vabl);
+
+    t1vec = t0vec;
+    t1vec *= (rb - ra);
+    t1 = ra + (rb - ra) * t0; 
+
+    cxx = cyy = czz = 1;
+    cxy = cxz = cyz = 0;
+
+    cxx = 1 - (vab*vab) * t0vec(0) * t0vec(0) - t1vec(0) * t1vec(0);
+    cyy = 1 - (vab*vab) * t0vec(1) * t0vec(1) - t1vec(1) * t1vec(1);
+    czz = 1 - (vab*vab) * t0vec(2) * t0vec(2) - t1vec(2) * t1vec(2);
+  
+    cxy = -2 * (vab * vab) * t0vec(0) * t0vec(1) - 2 * t1vec(0) * t1vec(1);
+    cxz = -2 * (vab * vab) * t0vec(0) * t0vec(2) - 2 * t1vec(0) * t1vec(2);
+    cyz = -2 * (vab * vab) * t0vec(1) * t0vec(2) - 2 * t1vec(1) * t1vec(2);
+
+    cx = -2 * a(0) - 2 * (vab * vab) * t0 * t0vec(0) - 2 * t1 * t1vec(0);
+    cy = -2 * a(1) - 2 * (vab * vab) * t0 * t0vec(1) - 2 * t1 * t1vec(1);
+    cz = -2 * a(2) - 2 * (vab * vab) * t0 * t0vec(2) - 2 * t1 * t1vec(2);
+
+    c1 = va.Length2() - (vab * vab) * t0 * t0 - t1 * t1;
+
+
+    double maxr = max2(ra,rb);
+    cxx /= maxr; cyy /= maxr; czz /= maxr;
+    cxy /= maxr; cxz /= maxr; cyz /= maxr;
+    cx /= maxr; cy /= maxr; cz /= maxr;
+    c1 /= maxr;
+
+
+    // (*testout) << "t0vec = " << t0vec << " t0 = " << t0 << endl;
+    // (*testout) << "t1vec = " << t1vec << " t1 = " << t1 << endl;
+    // PrintCoeff (*testout);
+  }
+
+
+  INSOLID_TYPE Cone :: BoxInSolid (const BoxSphere<3> & box) const
+  {
+    Vec<3> cv(box.Center());
+
+    double rzp = cv * t1vec + t1;
+    double dist = sqrt (CalcFunctionValue(box.Center()) *max2(ra,rb) + rzp * rzp) - rzp;
+
+    dist *= cosphi;
+    INSOLID_TYPE res = DOES_INTERSECT;
+
+    if (dist - box.Diam() > 0) res = IS_OUTSIDE;
+    if (dist + box.Diam() < 0) res = IS_INSIDE;
+
+    return res;
+  }
+
+
+  double Cone :: HesseNorm () const
+  {
+    // cout << "2/minr = " << 2/minr << ",  cxx .. = " << cxx << ", " << cyy << ", " << czz << endl;
+    return 2 / minr;
+  }
+
+
+  double Cone ::  LocH (const Point<3> & p, double /* x */, 
+                        double /* c */, double hmax) const
+  {
+    //double bloch = Surface::LocH (p, x, c, hmax);
+    Vec<3> g;
+    CalcGradient (p, g);
+
+    double lam = Abs(g);
+    double meancurv = 
+      -( 2  * g(0)*g(1)*cxy - 2 * czz * (g(0)*g(0)+g(1)*g(1))
+         +2 * g(1)*g(2)*cyz - 2 * cxx * (g(1)*g(1)+g(2)*g(2))
+         +2 * g(0)*g(2)*cxz - 2 * cyy * (g(0)*g(0)+g(2)*g(2))) / (3*lam*lam*lam);
+
+    // cout << "type = " << typeid(*this).name() << ", baseh = " << bloch << ", meancurv = " << meancurv << endl;
+    // return bloch;
+  
+    meancurv = fabs (meancurv);
+    if (meancurv < 1e-20) meancurv = 1e-20;
+
+    // cout << "c = " << c << ", safety = " << mparam.curvaturesafety << endl;
+    double hcurv = 1.0/(4*meancurv*mparam.curvaturesafety);
+
+    return min2 (hmax, hcurv);
+  }
+
+
+  Point<3> Cone :: GetSurfacePoint () const
+  {
+    Vec<3> vr = vab.GetNormal ();
+  
+    vr *= (ra / vr.Length());
+    return a + vr;
+  }
+
+
+
+
+
+  void Cone :: GetTriangleApproximation 
+  (TriangleApproximation & tas, 
+   const Box<3> & /* boundingbox */, double facets) const
+  {
+    int i, j;
+    double lg, bg;
+    int n = int(facets) + 1;  
+
+    Vec<3> lvab = b - a;
+    Vec<3> n1 = lvab.GetNormal();
+    Vec<3> n2 = Cross (lvab, n1);
+  
+    n1.Normalize();
+    n2.Normalize();
+
+
+    for (j = 0; j <= n; j++)
+      for (i = 0; i <= n; i++)
+        {
+          lg = 2 * M_PI * double (i) / n;
+          bg = double(j) / n;
+
+          Point<3> p = a + (bg * lvab) 
+            + (( (ra+(rb-ra)*bg)  * cos(lg)) * n1) 
+            + (( (ra+(rb-ra)*bg)  * sin(lg)) * n2);
+
+          tas.AddPoint (p);
+        }
+
+    for (j = 0; j < n; j++)
+      for (i = 0; i < n; i++)
+        {
+          int pi = i + (n+1) * j;
+          tas.AddTriangle (TATriangle (0, pi, pi+1, pi+n+2));
+          tas.AddTriangle (TATriangle (0, pi, pi+n+2, pi+n+1));
+        }
+  }
+
+
+
+
+
+
+
+
+
+  /// Torus 
+  /// Lorenzo Codecasa (codecasa@elet.polimi.it)
+  /// April 27th, 2005 
+  ///
+  Torus :: Torus (const Point<3> & ac, const Vec<3> & an, double aR, double ar)
+  {
+    c = ac;
+    n = an;
+    n.Normalize();
+    R = aR;
+    r = ar;
+  }
+
+  void Torus :: GetPrimitiveData (const char *& classname, Array<double> & coeffs) const
+  {
+    classname = "torus";
+    coeffs.SetSize (8);
+    coeffs.Elem(1) = c(0);
+    coeffs.Elem(2) = c(1);
+    coeffs.Elem(3) = c(2);
+    coeffs.Elem(4) = n(0);
+    coeffs.Elem(5) = n(1);
+    coeffs.Elem(6) = n(2);
+    coeffs.Elem(7) = R;
+    coeffs.Elem(8) = r;
+  }
+
+  void Torus :: SetPrimitiveData (Array<double> & coeffs)
+  {
+    c(0) = coeffs.Elem(1);
+    c(1) = coeffs.Elem(2);
+    c(2) = coeffs.Elem(3);
+    n(0) = coeffs.Elem(4);
+    n(1) = coeffs.Elem(5);
+    n(2) = coeffs.Elem(6);
+    R = coeffs.Elem(7);
+    r = coeffs.Elem(8);
+  }
+
+  Primitive * Torus :: CreateDefault ()
+  {
+    return new Torus (Point<3> (0,0,0), Vec<3> (0,0,1), 2, 1);
+  }
+
+  Primitive * Torus :: Copy () const
+  {
+    return new Torus (c, n, R, r);
+  }
+
+  void Torus :: Transform (Transformation<3> & trans)
+  {
+    Point<3> hc;
+    trans.Transform (c, hc);
+    c = hc;
+  
+    Vec<3> hn;
+    trans.Transform (n, hn);
+    n = hn;
+  }
+
+  int Torus :: IsIdentic (const Surface & s2, int & inv, double eps) const
+  {
+    const Torus * torus2 = dynamic_cast<const Torus*>  (&s2);
+
+    if (!torus2) return 0;
+
+    if (fabs (torus2->R - R) > eps) return 0;
+  
+    if (fabs (torus2->r - r) > eps) return 0;
+
+    Vec<3> v2 = torus2->n - n;
+    if ( v2 * v2 > eps ) return 0;
+  
+    v2 = torus2->c - c;
+    if ( v2 * v2 > eps ) return 0;
+
+    inv = 0;
+    return 1;
+  }
+
+  double Torus :: CalcFunctionValue (const Point<3> & point) const
+  {
+    /*
+    // original version
+    Vec<3> v1 = point - c;
+    double a1 = Abs2 (v1);         // v1(0) * v1(0) + v1(1) * v1(1) + v1(2) * v1(2);
+    double a2 = n * v1;            // n(0) * v1(0) + n(1) * v1(1) + n(2) * v1(2);
+    double a3 = a1 + R * R - r * r;
+    double a4 = Abs2 (n);          // n(0) * n(0) + n(1) * n(1) + n(2) * n(2);
+
+    return ( a3 * a3 -4 * R * R * ( a1 - a2 * a2 / a4 ) ) / ( R * R * R );
+    */
+
+    
+    // JS, April 2011
+    Vec<3> v1 = point-c;
+    double abs2 = Abs2(v1);
+    double tau = v1 * n;
+    double rho = sqrt (abs2 - tau*tau);
+    return sqr (R - rho) + tau*tau - r*r;
+
+    // double val2 = sqr (tau*tau + sqr (R - rho) -r*r) / (R*R*R);
+  }
+
+  void Torus :: CalcGradient (const Point<3> & point, Vec<3> & grad) const
+  {
+    /*
+    Vec<3> v1 = point - c;
+    double a1 = v1(0) * v1(0) + v1(1) * v1(1) + v1(2) * v1(2);
+    double a2 = n(0) * v1(0) + n(1) * v1(1) + n(2) * v1(2);
+    double a3 = a1 - R * R - r * r;
+    double a4 = n(0) * n(0) + n(1) * n(1) + n(2) * n(2);
+    grad(0) = ( 4 * a3 * v1(0) + 8 * R * R * a2 / a4 * n(0) ) / ( R * R * R );
+    grad(1) = ( 4 * a3 * v1(1) + 8 * R * R * a2 / a4 * n(1) ) / ( R * R * R );
+    grad(2) = ( 4 * a3 * v1(2) + 8 * R * R * a2 / a4 * n(2) ) / ( R * R * R );
+    */
+
+    Vec<3> v1 = point-c;
+    double abs2 = Abs2(v1);
+    double tau = v1 * n;
+    double rho = sqrt (abs2 - tau*tau);
+    double func = sqr (R - rho) + tau*tau - r*r;
+
+    Vec<3> gradabs2 = 2 * v1;
+    Vec<3> gradtau = n;
+    Vec<3> gradrho = 0.5 / rho * (gradabs2 - 2 * tau * gradtau);
+    grad = -2 * (R - rho) * gradrho  + 2 * tau * gradtau;
+  }
+
+  void Torus :: CalcHesse (const Point<3> & point, Mat<3> & hesse) const
+  {
+    Surface::CalcHesse (point, hesse);
+    return;
+
+    Vec<3> v1 = point - c;
+    double a1 = v1(0) * v1(0) + v1(1) * v1(1) + v1(2) * v1(2);
+    double a3 = a1 - R * R - r * r;
+    double a4 = n(0) * n(0) + n(1) * n(1) + n(2) * n(2);
+    hesse(0,0) = ( 4 * a3 + 8 * (v1(0) * v1(0) + (R * n(0)) * (R * n(0)) / a4 ) ) / ( R * R * R );
+    hesse(1,1) = ( 4 * a3 + 8 * (v1(1) * v1(1) + (R * n(1)) * (R * n(1)) / a4 ) ) / ( R * R * R );
+    hesse(2,2) = ( 4 * a3 + 8 * (v1(2) * v1(2) + (R * n(2)) * (R * n(2)) / a4 ) ) / ( R * R * R );
+    hesse(0,1) = hesse(1,0) = 8 * (v1(0) * v1(1) + (R * n(0)) * (R * n(1)) / a4 ) / ( R * R * R );
+    hesse(1,2) = hesse(2,1) = 8 * (v1(1) * v1(2) + (R * n(1)) * (R * n(2)) / a4) / ( R * R * R );
+    hesse(0,2) = hesse(2,0) = 8 * (v1(0) * v1(2) + (R * n(0)) * (R * n(2)) / a4) / ( R * R * R );
+  }
+
+  double Torus :: HesseNorm () const
+  {	
+    return 4/(r*r);
+    // return  ( 2 / r + 2 / ( R - r ) );
+  }
+
+  Point<3> Torus :: GetSurfacePoint () const
+  {
+    Vec<3> vn = n.GetNormal();
+    return c + ( R + r ) * vn.Normalize();
+  }
+
+  /// void Torus :: DefineTangentialPlane (const Point<3> & ap1, const Point<3> & ap2)
+  /// {
+  /// }
+
+  /// void Torus :: ToPlane (const Point<3> & p, 
+  ///			  Point<2> & pplane, 
+  ///			  double h, int & zone) const
+  /// {
+  /// }
+
+  /// void Torus :: FromPlane (const Point<2> & pplane, Point<3> & p, double h) const
+  /// {
+  /// }
+
+  /// void Torus :: Project (Point<3> & p) const
+  /// {
+  /// }
+
+  INSOLID_TYPE Torus :: BoxInSolid (const BoxSphere<3> & box) const
+  {
+    Vec<3> v1 = box.Center() - c;
+    double a1 = Abs2(v1);        // v1(0) * v1(0) + v1(1) * v1(1) + v1(2) * v1(2);
+    double a2 = n * v1;          // n(0) * v1(0) + n(1) * v1(1) + n(2) * v1(2);
+    double a4 = Abs2(n);         // n(0) * n(0) + n(1) * n(1) + n(2) * n(2);
+ 
+    double dist = sqrt( a1 + R * R - 2 * R * sqrt( a1 - a2 * a2 / a4) );
+
+    if (dist - box.Diam()/2 > r) return IS_OUTSIDE;
+    if (dist + box.Diam()/2 < r) return IS_INSIDE;
+    return DOES_INTERSECT;
+  }
+
+  void Torus :: GetTriangleApproximation (TriangleApproximation & tas, 
+                                          const Box<3> & /* boundingbox */, double facets) const
+  {
+    int N = int(facets) + 1;  
+
+    Vec<3> lvab = n ;
+    lvab.Normalize();
+  
+    Vec<3> n1 = lvab.GetNormal();
+    n1.Normalize();
+  
+    Vec<3> n2 = Cross(lvab, n1);
+    n2.Normalize();
+  
+    for (int j = 0; j <= N; j++)
+      for (int i = 0; i <= N; i++)
+	{
+          double lg = 2 * M_PI * double (i) / N;
+          double bg = 2 * M_PI * double(j) / N;
+	
+          Point<3> p = c + ( R + r * cos(lg) ) * ( cos(bg) * n1 + sin(bg) * n2 ) + r * sin(lg) * n;
+          tas.AddPoint (p);
+	}
+	
+    for (int j = 0; j < N; j++)
+      for (int i = 0; i < N; i++)
+	{
+          int pi = i + (N+1) * j;
+          tas.AddTriangle (TATriangle (0, pi, pi+1, pi+N+2));
+          tas.AddTriangle (TATriangle (0, pi, pi+N+2, pi+N+1));
+	}
+  } 
+  
+  void Torus :: Read (istream & ist)
+  {
+    ist >> c(0) >> c(1) >> c(2) >> n(0) >> n(1) >> n(2) >> R >> r;
+  }
+
+  void Torus :: Print (ostream & ost) const
+  {
+    ost << c(0) << "  " << c(1) << "  " << c(2) << "  "
+        << n(0) << "  " << n(1) << "  " << n(2) << "  "
+        << R    << "  " << r    << endl;
+  }
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/csg/algprim.hpp b/contrib/Netgen/libsrc/csg/algprim.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..82cd58e7881615e13bb4ec04d10088f5eda5c23a
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/algprim.hpp
@@ -0,0 +1,446 @@
+#ifndef FILE_ALGPRIM
+#define FILE_ALGPRIM
+
+
+/**************************************************************************/
+/* File:   algprim.hpp                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   1. Dez. 95                                                     */
+/**************************************************************************/
+
+namespace netgen
+{
+
+  /*
+
+  Quadric Surfaces (Plane, Sphere, Cylinder)
+  
+  */
+
+
+  /**
+     A quadric surface.
+     surface defined by
+     cxx x^2 + cyy y^2 + czz z^2 + cxy x y + cxz x z + cyz y z +
+     cx x + cy y + cz z + c1 = 0.
+  **/
+  class QuadraticSurface : public  OneSurfacePrimitive
+  {
+  protected:
+    double cxx, cyy, czz, cxy, cxz, cyz, cx, cy, cz, c1;
+
+  public:
+    virtual double CalcFunctionValue (const Point<3> & point) const;
+    virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const;
+    virtual void CalcHesse (const Point<3> & point, Mat<3> & hesse) const;
+    /*
+      virtual int RootInBox (const Box<3> & box) 
+      const { return 0; }
+      virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) 
+      const { return DOES_INTERSECT; }
+    */
+    virtual double HesseNorm () const { return cxx + cyy + czz; }
+
+    virtual Point<3> GetSurfacePoint () const;
+
+
+    virtual void Print (ostream & ist) const;
+    virtual void Read (istream & ist);
+    void PrintCoeff (ostream & ost) const;
+  };
+
+
+  /// A Plane (i.e., the plane and everything behind it).
+  class Plane : public QuadraticSurface
+  {
+    /// a point in the plane
+    Point<3> p;
+    /// outward normal vector 
+    Vec<3> n;
+
+    double eps_base;
+
+  public:
+    ///
+    Plane (const Point<3> & ap, Vec<3> an);
+
+    virtual void GetPrimitiveData (const char *& classname, 
+				   Array<double> & coeffs) const;
+    virtual void SetPrimitiveData (Array<double> & coeffs);
+    static Primitive * CreateDefault ();
+
+    virtual Primitive * Copy () const;
+    virtual void Transform (Transformation<3> & trans);
+
+
+    virtual int IsIdentic (const Surface & s2, int & inv, double eps) const;
+
+    ///
+    virtual void DefineTangentialPlane (const Point<3> & ap1, 
+					const Point<3> & ap2);
+    ///
+    virtual void ToPlane (const Point<3> & p3d, 
+			  Point<2> & pplane, double h,
+			  int & zone) const;
+    ///
+    virtual void FromPlane (const Point<2> & pplane, 
+			    Point<3> & p3d, 
+			    double h) const;
+    ///
+    virtual void Project (Point<3> & p) const;
+
+    ///
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+
+    ///
+    inline virtual double CalcFunctionValue (const Point<3> & p3d) const
+    {return cx * p3d(0) + cy * p3d(1) + cz * p3d(2) + c1;}
+    ///
+    virtual void CalcGradient (const Point<3> & point, 
+			       Vec<3> & grad) const;
+    ///
+    virtual void CalcHesse (const Point<3> & point, 
+			    Mat<3> & hesse) const;
+    ///
+    virtual double HesseNorm () const;
+    ///
+    virtual Point<3> GetSurfacePoint () const;
+    ///
+    virtual void GetTriangleApproximation 
+    (TriangleApproximation & tas, 
+     const Box<3> & boundingbox, double facets) const;
+  protected:
+    void CalcData();
+  };
+
+  // typedef Plane Plane;
+
+
+  ///
+  class Sphere : public QuadraticSurface
+  {
+    ///
+    Point<3> c;
+    ///
+    double r, invr;
+  public:
+    ///
+    Sphere (const Point<3> & ac, double ar);
+
+    virtual void GetPrimitiveData (const char *& classname, 
+				   Array<double> & coeffs) const;
+    virtual void SetPrimitiveData (Array<double> & coeffs);
+    static Primitive * CreateDefault ();
+
+    virtual Primitive * Copy () const;
+    virtual void Transform (Transformation<3> & trans);
+
+    virtual double CalcFunctionValue (const Point<3> & point) const;
+
+
+    virtual int IsIdentic (const Surface & s2, int & inv, double eps) const;
+
+    ///
+    virtual void DefineTangentialPlane (const Point<3> & ap1, 
+					const Point<3> & ap2);
+    ///
+    virtual void ToPlane (const Point<3> & p3d, 
+			  Point<2> & pplane, double h,
+			  int & zone) const;
+    ///
+    virtual void FromPlane (const Point<2> & pplane, 
+			    Point<3> & p, double h) const;
+    ///
+    virtual void Project (Point<3> & p) const;
+
+    ///
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    ///
+    virtual double HesseNorm () const;
+    ///
+    virtual Point<3> GetSurfacePoint () const;
+    ///
+    const Point<3> & Center () const { return c; }
+    ///
+    double Radius () const { return r; }
+
+    ///
+    virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					   const Box<3> & bbox, 
+					   double facets) const;
+  };
+
+
+  ///
+  class Cylinder : public QuadraticSurface
+  {
+    ///
+    Point<3> a, b;
+    ///
+    double r;
+    ///
+    Vec<3> vab;
+
+  public:
+    Cylinder (const Point<3> & aa, const Point<3> & ab, double ar);
+    Cylinder (Array<double> & coeffs);
+
+    virtual void GetPrimitiveData (const char *& classname, Array<double> & coeffs) const;
+    virtual void SetPrimitiveData (Array<double> & coeffs);
+    static Primitive * CreateDefault ();
+
+    virtual Primitive * Copy () const;
+    virtual void Transform (Transformation<3> & trans);
+
+    ///
+    virtual int IsIdentic (const Surface & s2, int & inv, double eps) const;
+    ///
+    virtual void DefineTangentialPlane (const Point<3> & ap1, 
+					const Point<3> & ap2);
+    ///
+    virtual void ToPlane (const Point<3> & p, 
+			  Point<2> & pplane, 
+			  double h,
+			  int & zone) const;
+    ///
+    virtual void FromPlane (const Point<2> & pplane, 
+			    Point<3> & p, 
+			    double h) const;
+    ///
+    virtual void Project (Point<3> & p) const;
+
+    ///
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    ///
+    virtual double HesseNorm () const;
+    ///
+    virtual Point<3> GetSurfacePoint () const;
+    ///
+    virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					   const Box<3> & bbox, 
+					   double facets) const;
+  };
+
+
+
+
+
+  ///
+  class EllipticCylinder : public QuadraticSurface
+  {
+  private:
+    ///
+    Point<3> a;
+    ///
+    Vec<3> vl, vs;
+    ///
+    Vec<3> vab, t0vec, t1vec;
+    ///
+    double vabl, t0, t1;
+  public:
+    ///
+    EllipticCylinder (const Point<3> & aa,
+		      const Vec<3> & avl, const Vec<3> & avs);
+    EllipticCylinder (Array<double> & coeffs);
+
+
+    // static Primitive * CreateDefault ();
+    virtual void GetPrimitiveData (const char *& classname, Array<double> & coeffs) const;
+    virtual void SetPrimitiveData (Array<double> & coeffs);
+
+    ///
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    ///
+    virtual double HesseNorm () const;
+    ///
+    virtual Point<3> GetSurfacePoint () const;
+
+    virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					   const Box<3> & bbox, 
+					   double facets) const;
+
+  
+    virtual double MaxCurvature () const;
+
+    virtual double MaxCurvatureLoc (const Point<3> & /* c */ , 
+				    double /* rad */) const;
+
+
+  private:
+    void CalcData();
+  };
+
+
+
+
+
+
+  ///
+  class Ellipsoid : public QuadraticSurface
+  {
+  private:
+    ///
+    Point<3> a;
+    ///
+    Vec<3> v1, v2, v3;
+    ///
+    double rmin;
+  public:
+    ///
+    Ellipsoid (const Point<3> & aa,
+	       const Vec<3> & av1, 
+	       const Vec<3> & av2,
+	       const Vec<3> & av3);
+    ///
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    ///
+    virtual double HesseNorm () const;
+    ///
+    virtual double MaxCurvature () const;
+    ///
+    virtual Point<3> GetSurfacePoint () const;
+
+    virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					   const Box<3> & bbox, 
+					   double facets) const;
+
+  private:
+    void CalcData();
+  };
+
+
+
+
+
+
+
+
+  ///
+  class Cone : public QuadraticSurface
+  {
+    ///
+    Point<3> a, b;
+    ///
+    double ra, rb, minr;
+    ///
+    Vec<3> vab, t0vec, t1vec;
+    ///
+    double vabl, t0, t1;
+    double cosphi;
+  public:
+    ///
+    Cone (const Point<3> & aa, const Point<3> & ab, double ara, double arb);
+    ///
+    static Primitive * CreateDefault ();
+    virtual void GetPrimitiveData (const char *& classname, Array<double> & coeffs) const;
+    virtual void SetPrimitiveData (Array<double> & coeffs);
+
+    ///
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    ///
+    virtual double HesseNorm () const;
+
+    virtual double LocH (const Point<3> & p, double x, 
+			 double c, double hmax) const;
+
+    ///
+    virtual Point<3> GetSurfacePoint () const;
+
+    virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					   const Box<3> & bbox, 
+					   double facets) const;
+
+  private:
+    void CalcData();
+  };
+
+
+
+
+
+
+
+
+  /** Torus 
+  /// Lorenzo Codecasa (codecasa@elet.polimi.it)
+  /// April 27th, 2005 
+  */
+  class Torus : public OneSurfacePrimitive
+  { 
+    /// center of the torus
+    Point<3> c;
+    /// vector normal to the symmetry plane of the torus
+    Vec<3> n;
+    /// Large radius of the torus
+    double R;
+    /// Small radius of the torus
+    double r;
+  
+  public:
+    /// OK
+    Torus (const Point<3> & ac, const Vec<3> & an, double aR, double ar);
+    /// OK
+    const Point<3> & Center () const { return c; }
+    /// OK
+    const Vec<3> & NormalToPlane () const { return n; }
+    /// OK
+    double LargeRadius () const { return R; }
+    /// OK
+    double SmallRadius () const { return r; }
+    /// OK
+    virtual double CalcFunctionValue (const Point<3> & point) const;
+    /// OK
+    virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const;
+    /// OK
+    virtual void CalcHesse (const Point<3> & point, Mat<3> & hesse) const;
+    /// OK
+    virtual double HesseNorm () const;
+    /// OK
+    virtual Point<3> GetSurfacePoint () const;
+    /// OK
+    virtual void GetPrimitiveData (const char *& classname, 
+				   Array<double> & coeffs) const;
+    /// OK			 
+    virtual void SetPrimitiveData (Array<double> & coeffs);
+    /// OK
+    static Primitive * CreateDefault ();
+    /// OK
+    virtual Primitive * Copy () const;
+    /// OK
+    virtual void Transform (Transformation<3> & trans);
+    /// OK
+    virtual int IsIdentic (const Surface & s2, int & inv, double eps) const;
+    /// OK
+    /// virtual void DefineTangentialPlane (const Point<3> & ap1, 
+    //				      const Point<3> & ap2);
+    /// OK
+    /// virtual void ToPlane (const Point<3> & p3d, 
+    ///			Point<2> & pplane, 
+    ///			double h, int & zone) const;
+    /// OK
+    /// virtual void FromPlane (const Point<2> & pplane, 
+    //			  Point<3> & p, double h) const;
+    /// OK
+    /// virtual void Project (Point<3> & p) const;
+    /// OK
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    /// OK
+    virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					   const Box<3> & bbox, 
+					   double facets) const;
+    /// OK		 
+    virtual void Print (ostream & ist) const;
+    /// OK
+    virtual void Read (istream & ist);
+  };
+
+  /// ...end
+
+
+}
+
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/brick.cpp b/contrib/Netgen/libsrc/csg/brick.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..54c23b174051e4262daf1ffa8bb72e2d347fd1ae
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/brick.cpp
@@ -0,0 +1,526 @@
+#include <mystdlib.h>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+namespace netgen
+{
+
+Parallelogram3d :: Parallelogram3d (Point<3> ap1, Point<3> ap2, Point<3> ap3)
+{
+  p1 = ap1;
+  p2 = ap2;
+  p3 = ap3;
+
+  CalcData();
+}
+
+Parallelogram3d ::~Parallelogram3d ()
+{
+  ;
+}
+
+void Parallelogram3d :: SetPoints (Point<3> ap1, 
+				   Point<3> ap2, 
+				   Point<3> ap3)
+{
+  p1 = ap1;
+  p2 = ap2;
+  p3 = ap3;
+
+  CalcData();
+}
+
+void Parallelogram3d :: CalcData()
+{
+  v12 = p2 - p1;
+  v13 = p3 - p1;
+  p4 = p2 + v13;
+
+  n = Cross (v12, v13);
+  n.Normalize();
+}
+
+int Parallelogram3d :: 
+IsIdentic (const Surface & s2, int & inv, double eps) const
+{
+  int id = 
+    (fabs (s2.CalcFunctionValue (p1)) <= eps) &&
+    (fabs (s2.CalcFunctionValue (p2)) <= eps) &&
+    (fabs (s2.CalcFunctionValue (p3)) <= eps);
+
+  if (id)
+    {
+      Vec<3> n2;
+      n2 = s2.GetNormalVector(p1);
+      inv = (n * n2) < 0;
+    }
+  return id;
+}
+
+
+double Parallelogram3d :: CalcFunctionValue (const Point<3> & point) const
+{
+  return n * (point - p1);
+}
+
+void Parallelogram3d :: CalcGradient (const Point<3> & /* point */, 
+				      Vec<3> & grad) const
+{
+  grad = n;
+}
+
+void Parallelogram3d :: CalcHesse (const Point<3> & /* point */, Mat<3> & hesse) const
+{
+  hesse = 0;
+}
+
+double Parallelogram3d :: HesseNorm () const
+{
+  return 0;
+}
+
+Point<3> Parallelogram3d :: GetSurfacePoint () const
+{
+  return p1;
+}
+
+void Parallelogram3d :: Print (ostream & str) const
+{
+  str << "Parallelogram3d " << p1 << " - " << p2 << " - " << p3 << endl;
+}
+
+  
+void Parallelogram3d :: 
+GetTriangleApproximation (TriangleApproximation & tas, 
+			  const Box<3> & /* bbox */, 
+			  double /* facets */) const
+{
+  tas.AddPoint (p1);
+  tas.AddPoint (p2);
+  tas.AddPoint (p3);
+  tas.AddPoint (p4);
+  tas.AddTriangle (TATriangle (0, 0, 1, 2));
+  tas.AddTriangle (TATriangle (0, 2, 1, 3));
+}
+
+
+
+
+
+
+
+
+
+
+Brick :: Brick (Point<3> ap1, Point<3> ap2, 
+		Point<3> ap3, Point<3> ap4)
+{
+  faces.SetSize (6);
+  surfaceids.SetSize (6);
+  surfaceactive.SetSize(6);
+
+  p1 = ap1; p2 = ap2;
+  p3 = ap3; p4 = ap4;
+
+  for (int i = 0; i < 6; i++)
+    {
+      faces[i] = new Plane (Point<3>(0,0,0), Vec<3> (0,0,1));
+      surfaceactive[i] = 1;
+    }
+
+  CalcData();
+}
+
+Brick :: ~Brick ()
+{
+  for (int i = 0; i < 6; i++)
+    delete faces[i];
+}
+
+Primitive * Brick :: CreateDefault ()
+{
+  return new Brick (Point<3> (0,0,0),
+		    Point<3> (1,0,0),
+		    Point<3> (0,1,0),
+		    Point<3> (0,0,1));
+}
+
+
+
+Primitive * Brick :: Copy () const
+{
+  return new Brick (p1, p2, p3, p4);
+}
+
+void  Brick :: Transform (Transformation<3> & trans)
+{
+  trans.Transform (p1);
+  trans.Transform (p2);
+  trans.Transform (p3);
+  trans.Transform (p4);
+
+  CalcData();
+}
+
+
+
+
+
+
+
+
+
+INSOLID_TYPE Brick :: BoxInSolid (const BoxSphere<3> & box) const
+{
+  /*
+  int i;
+  double maxval;
+  for (i = 1; i <= 6; i++)
+    {
+      double val = faces.Get(i)->CalcFunctionValue (box.Center());
+      if (i == 1 || val > maxval)
+	maxval = val;
+    }
+  
+  if (maxval > box.Diam()) return IS_OUTSIDE;
+  if (maxval < -box.Diam()) return IS_INSIDE;
+  return DOES_INTERSECT;
+  */
+
+  bool inside = 1;
+  bool outside = 0;
+
+  Point<3> p[8];
+  for (int j = 0; j < 8; j++)
+    p[j] = box.GetPointNr(j);
+
+  for (int i = 0; i < 6; i++)
+    {
+      bool outsidei = 1;
+      for (int j = 0; j < 8; j++)
+	{
+	  // Point<3> p = box.GetPointNr (j);
+	  double val = faces[i]->Plane::CalcFunctionValue (p[j]);
+
+	  if (val > 0)  inside = 0;
+	  if (val < 0)  outsidei = 0;
+	}
+      if (outsidei) outside = 1;
+    }
+
+  if (outside) return IS_OUTSIDE;
+  if (inside) return IS_INSIDE;
+  return DOES_INTERSECT;
+}
+
+INSOLID_TYPE Brick :: PointInSolid (const Point<3> & p,
+			   double eps) const
+{
+  double maxval = faces[0] -> Plane::CalcFunctionValue (p);
+  for (int i = 1; i < 6; i++)
+    {
+      double val = faces[i] -> Plane::CalcFunctionValue (p);
+      if (val > maxval) maxval = val;
+    }
+
+  if (maxval > eps) return IS_OUTSIDE;
+  if (maxval < -eps) return IS_INSIDE;
+  return DOES_INTERSECT;
+}
+
+
+INSOLID_TYPE Brick :: VecInSolid (const Point<3> & p,
+				  const Vec<3> & v,
+				  double eps) const
+{
+  INSOLID_TYPE result = IS_INSIDE;
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      INSOLID_TYPE hres = faces[i]->VecInSolid(p, v, eps);
+      if (hres == IS_OUTSIDE || result == IS_OUTSIDE) result = IS_OUTSIDE;
+      else if (hres == DOES_INTERSECT || result == DOES_INTERSECT) result = DOES_INTERSECT;
+      else result = IS_INSIDE;
+    }
+  return result;
+
+  /*
+  INSOLID_TYPE is = IS_INSIDE;
+  Vec<3> grad;
+  double scal;
+
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      if (faces[i] -> PointOnSurface (p, eps))
+	{
+	  GetSurface(i).CalcGradient (p, grad);
+	  scal = v * grad;
+	  
+	  if (scal >= eps) 
+	    is = IS_OUTSIDE;
+	  if (scal >= -eps && is == IS_INSIDE)
+	    is = DOES_INTERSECT;
+	}
+    }
+  return is;
+  */
+
+  /*
+  Point<3> p2 = p + 1e-2 * v;
+  return PointInSolid (p2, eps);
+  */
+}
+
+
+
+
+
+INSOLID_TYPE Brick :: VecInSolid2 (const Point<3> & p,
+				    const Vec<3> & v1,
+				    const Vec<3> & v2,
+				    double eps) const
+{
+  INSOLID_TYPE result = IS_INSIDE;
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      INSOLID_TYPE hres = faces[i]->VecInSolid2(p, v1, v2, eps);
+      if (hres == IS_OUTSIDE || result == IS_OUTSIDE) result = IS_OUTSIDE;
+      else if (hres == DOES_INTERSECT || result == DOES_INTERSECT) result = DOES_INTERSECT;
+      else result = IS_INSIDE;
+    }
+  return result;
+}
+
+INSOLID_TYPE Brick :: VecInSolid3 (const Point<3> & p,
+				    const Vec<3> & v1,
+				    const Vec<3> & v2,
+				    double eps) const
+{
+  INSOLID_TYPE result = IS_INSIDE;
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      INSOLID_TYPE hres = faces[i]->VecInSolid3(p, v1, v2, eps);
+      if (hres == IS_OUTSIDE || result == IS_OUTSIDE) result = IS_OUTSIDE;
+      else if (hres == DOES_INTERSECT || result == DOES_INTERSECT) result = DOES_INTERSECT;
+      else result = IS_INSIDE;
+    }
+  return result;
+}
+
+INSOLID_TYPE Brick :: VecInSolid4 (const Point<3> & p,
+				    const Vec<3> & v,
+				    const Vec<3> & v2,
+				    const Vec<3> & m,
+				    double eps) const
+{
+  INSOLID_TYPE result = IS_INSIDE;
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      INSOLID_TYPE hres = faces[i]->VecInSolid4(p, v, v2, m, eps);
+      if (hres == IS_OUTSIDE || result == IS_OUTSIDE) result = IS_OUTSIDE;
+      else if (hres == DOES_INTERSECT || result == DOES_INTERSECT) result = DOES_INTERSECT;
+      else result = IS_INSIDE;
+    }
+  return result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void Brick :: 
+GetPrimitiveData (const char *& classname, Array<double> & coeffs) const
+{
+  classname = "brick";
+  coeffs.SetSize(12);
+  coeffs.Elem(1) = p1(0);
+  coeffs.Elem(2) = p1(1);
+  coeffs.Elem(3) = p1(2);
+
+  coeffs.Elem(4) = p2(0);
+  coeffs.Elem(5) = p2(1);
+  coeffs.Elem(6) = p2(2);
+
+  coeffs.Elem(7) = p3(0);
+  coeffs.Elem(8) = p3(1);
+  coeffs.Elem(9) = p3(2);
+
+  coeffs.Elem(10) = p4(0);
+  coeffs.Elem(11) = p4(1);
+  coeffs.Elem(12) = p4(2);
+}
+
+void Brick :: SetPrimitiveData (Array<double> & coeffs)
+{
+  p1(0) = coeffs.Elem(1);
+  p1(1) = coeffs.Elem(2);
+  p1(2) = coeffs.Elem(3);
+
+  p2(0) = coeffs.Elem(4);
+  p2(1) = coeffs.Elem(5);
+  p2(2) = coeffs.Elem(6);
+
+  p3(0) = coeffs.Elem(7);
+  p3(1) = coeffs.Elem(8);
+  p3(2) = coeffs.Elem(9);
+
+  p4(0) = coeffs.Elem(10);
+  p4(1) = coeffs.Elem(11);
+  p4(2) = coeffs.Elem(12);
+
+  CalcData();
+}
+
+
+
+void Brick :: CalcData()
+{
+  v12 = p2 - p1;
+  v13 = p3 - p1;
+  v14 = p4 - p1;
+
+  Point<3> pi[8];
+  int i1, i2, i3;
+  int i, j;
+  
+  i = 0;
+  for (i3 = 0; i3 <= 1; i3++)
+    for (i2 = 0; i2 <= 1; i2++)
+      for (i1 = 0; i1 <= 1; i1++)
+	{
+	  pi[i] = p1 + i1 * v12 + i2 * v13 + i3 * v14;
+	  i++;
+	}
+
+  static int lface[6][4] =
+  { { 1, 3, 2, 4 },
+    { 5, 6, 7, 8 },
+    { 1, 2, 5, 6 },
+    { 3, 7, 4, 8 },
+    { 1, 5, 3, 7 },
+    { 2, 4, 6, 8 } };
+  
+  Array<double> data(6);
+  for (i = 0; i < 6; i++)
+    {
+      const Point<3> lp1 = pi[lface[i][0]-1];
+      const Point<3> lp2 = pi[lface[i][1]-1];
+      const Point<3> lp3 = pi[lface[i][2]-1];
+
+      Vec<3> n = Cross ((lp2-lp1), (lp3-lp1));
+      n.Normalize();
+      
+      for (j = 0; j < 3; j++)
+	{
+	  data[j] = lp1(j);
+	  data[j+3] = n(j);
+	}
+      faces[i] -> SetPrimitiveData (data);
+      /* 
+	 {
+	 faces.Elem(i+1) -> SetPoints
+	 (pi[lface[i][0]-1],
+	 pi[lface[i][1]-1],
+	 pi[lface[i][2]-1]);
+	 }
+      */
+    }
+}
+
+
+void Brick :: Reduce (const BoxSphere<3> & box)
+{
+  double val;
+  // Point<3> p;
+  Point<3> p[8];
+  for(int j=0;j<8;j++)
+    p[j]=box.GetPointNr(j);
+
+  for (int i = 0; i < 6; i++)
+    {
+      bool hasout = 0;
+      bool hasin = 0;
+      for (int j = 0; j < 8; j++)
+	{
+	  // p = box.GetPointNr (j);
+	  val = faces[i]->Plane::CalcFunctionValue (p[j]);
+	  if (val > 0)  hasout = 1;
+	  else if (val < 0)  hasin = 1;
+	  if (hasout && hasin) break;
+	}
+      surfaceactive[i] =  hasout && hasin;
+    }
+}
+
+void Brick :: UnReduce ()
+{ 
+  for (int i = 0; i < 6; i++)
+    surfaceactive[i] = 1;
+}
+
+
+
+OrthoBrick :: OrthoBrick (const Point<3> & ap1, const Point<3> & ap2)
+  : Brick (ap1, 
+	   Point<3> (ap2(0), ap1(1), ap1(2)),
+	   Point<3> (ap1(0), ap2(1), ap1(2)),
+	   Point<3> (ap1(0), ap1(1), ap2(2)))
+{
+  pmin = ap1;
+  pmax = ap2;
+}
+	 
+INSOLID_TYPE OrthoBrick :: BoxInSolid (const BoxSphere<3> & box) const
+{
+  if (pmin(0) > box.PMax()(0) ||
+      pmin(1) > box.PMax()(1) ||
+      pmin(2) > box.PMax()(2) ||
+      pmax(0) < box.PMin()(0) ||
+      pmax(1) < box.PMin()(1) ||
+      pmax(2) < box.PMin()(2))
+    return IS_OUTSIDE;
+
+  if (pmin(0) < box.PMin()(0) &&
+      pmin(1) < box.PMin()(1) &&
+      pmin(2) < box.PMin()(2) &&
+      pmax(0) > box.PMax()(0) &&
+      pmax(1) > box.PMax()(1) &&
+      pmax(2) > box.PMax()(2))
+    return IS_INSIDE;
+
+  return DOES_INTERSECT;
+}
+
+
+void OrthoBrick :: Reduce (const BoxSphere<3> & box)
+{
+  surfaceactive.Elem(1) =
+    (box.PMin()(2) < pmin(2)) && (pmin(2) < box.PMax()(2));
+  surfaceactive.Elem(2) =
+    (box.PMin()(2) < pmax(2)) && (pmax(2) < box.PMax()(2));
+
+  surfaceactive.Elem(3) =
+    (box.PMin()(1) < pmin(1)) && (pmin(1) < box.PMax()(1));
+  surfaceactive.Elem(4) =
+    (box.PMin()(1) < pmax(1)) && (pmax(1) < box.PMax()(1));
+
+  surfaceactive.Elem(5) =
+    (box.PMin()(0) < pmin(0)) && (pmin(0) < box.PMax()(0));
+  surfaceactive.Elem(6) =
+    (box.PMin()(0) < pmax(0)) && (pmax(0) < box.PMax()(0));
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/brick.hpp b/contrib/Netgen/libsrc/csg/brick.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..25b003e0e88453cd63d12eff2ba163a0e2074830
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/brick.hpp
@@ -0,0 +1,126 @@
+#ifndef FILE_BRICK
+#define FILE_BRICK
+
+
+/**************************************************************************/
+/* File:   brick.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   11. Mar. 98                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+
+  /*
+
+  brick geometry, has several surfaces
+  
+  */
+
+
+
+  class Parallelogram3d : public Surface
+  {
+    Point<3> p1, p2, p3, p4;
+    Vec<3> v12, v13;
+    Vec<3> n;
+
+  public:
+    Parallelogram3d (Point<3> ap1, Point<3> ap2, Point<3> ap3);
+    virtual ~Parallelogram3d ();
+
+    void SetPoints (Point<3> ap1, Point<3> ap2, Point<3> ap3);
+
+    virtual int IsIdentic (const Surface & s2, int & inv, double eps) const; 
+
+    virtual double CalcFunctionValue (const Point<3> & point) const;
+    virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const;
+    virtual void CalcHesse (const Point<3> & point, Mat<3> & hesse) const;
+    virtual double HesseNorm () const;
+
+    virtual Point<3> GetSurfacePoint () const;
+    virtual void Print (ostream & str) const;
+  
+    virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					   const Box<3> & boundingbox, 
+					   double facets) const;
+
+  protected:
+    void CalcData();
+  };
+
+
+  class Brick : public Primitive
+  {
+    Point<3> p1, p2, p3, p4;
+    Vec<3> v12, v13, v14;
+    // Array<OneSurfacePrimitive*> faces;
+    Array<Plane*> faces;
+
+  public:
+    Brick (Point<3> ap1, Point<3> ap2, Point<3> ap3, Point<3> ap4);
+    virtual ~Brick ();
+    static Primitive * CreateDefault ();
+
+    virtual Primitive * Copy () const;
+    virtual void Transform (Transformation<3> & trans);
+
+
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+
+    virtual INSOLID_TYPE PointInSolid (const Point<3> & p,
+				       double eps) const;
+    virtual INSOLID_TYPE VecInSolid (const Point<3> & p,
+				     const Vec<3> & v,
+				     double eps) const;
+    virtual INSOLID_TYPE VecInSolid2 (const Point<3> & p,
+				      const Vec<3> & v1,
+				      const Vec<3> & v2,
+				      double eps) const;
+
+    virtual INSOLID_TYPE VecInSolid3 (const Point<3> & p,
+				      const Vec<3> & v1,
+				      const Vec<3> & v2,
+				      double eps) const;
+
+    virtual INSOLID_TYPE VecInSolid4 (const Point<3> & p,
+				      const Vec<3> & v,
+				      const Vec<3> & v2,
+				      const Vec<3> & m,
+				      double eps) const;
+
+
+    virtual int GetNSurfaces() const 
+    { return 6; }
+    virtual Surface & GetSurface (int i) 
+    { return *faces[i]; }
+    virtual const Surface & GetSurface (int i) const
+    { return *faces[i]; }
+
+
+    virtual void GetPrimitiveData (const char *& classname, Array<double> & coeffs) const;
+    virtual void SetPrimitiveData (Array<double> & coeffs);
+
+    virtual void Reduce (const BoxSphere<3> & box);
+    virtual void UnReduce ();
+
+  protected:
+    void CalcData();
+  };
+
+
+  class OrthoBrick : public Brick 
+  {
+  protected:
+    Point<3> pmin, pmax;
+  public:
+    OrthoBrick (const Point<3> & ap1, const Point<3> & ap2);
+  
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    virtual void Reduce (const BoxSphere<3> & box);
+  };
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/bspline2d.cpp b/contrib/Netgen/libsrc/csg/bspline2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..93b5e89621d955619ec35655cc4547e3126d0cbc
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/bspline2d.cpp
@@ -0,0 +1,242 @@
+#include <mystdlib.h>
+
+#include <csg.hpp>
+
+namespace netgen
+{
+
+BSplineCurve2d :: BSplineCurve2d ()
+{
+  redlevel = 0;
+}
+
+
+void BSplineCurve2d :: AddPoint (const Point<2> & apoint)
+{
+  points.Append (apoint);
+  intervallused.Append (0);
+}
+
+bool BSplineCurve2d :: Inside (const Point<2> & p, double & dist) const
+{
+  Point<2> hp = p;
+  double t = ProjectParam (p);
+  hp = Eval(t);
+  Vec<2> v = EvalPrime (t);
+
+  Vec<2> n (v(0), -v(1));
+  
+  cout << "p = " << p << ", hp = " << hp << endl;
+  dist = Dist (p, hp);
+  double scal = (hp-p) * n;
+  cout << "scal = " << scal << endl;
+
+  return scal >= 0;
+}
+  
+double BSplineCurve2d :: ProjectParam (const Point<2> & p) const
+{
+  double t, dt, mindist, mint = 0.0;
+  int n1;
+  
+  mindist = 1e10;
+  dt = 0.2;
+  for (n1 = 1; n1 <= points.Size(); n1++)
+    if (intervallused.Get(n1) == 0)
+      for (t = n1; t <= n1+1; t += dt)
+        if (Dist (Eval(t), p) < mindist)
+          {
+	    mint = t;
+	    mindist = Dist (Eval(t), p);
+          }
+    
+  if (mindist > 1e9) 
+    {
+      for (t = 0; t <= points.Size(); t += dt)
+	if (Dist (Eval(t), p) < mindist)
+	  {
+	    mint = t;
+	    mindist = Dist (Eval(t), p);
+	  }   
+    }
+
+  while (Dist (Eval (mint-dt), p) < mindist)
+    {
+      mindist = Dist (Eval (mint-dt), p);
+      mint -= dt;
+    }
+  while (Dist (Eval (mint+dt), p) < mindist)
+    {
+      mindist = Dist (Eval (mint+dt), p);
+      mint += dt;
+    }
+
+
+  return NumericalProjectParam (p, mint-dt, mint+dt);  
+}
+  
+  
+// t \in (n1, n2)  
+  
+Point<2> BSplineCurve2d :: Eval (double t) const
+{
+  int n, n1, n2, n3, n4;
+  double loct, b1, b2, b3, b4;
+  Point<2> hp;
+
+  static int cnt = 0;
+  cnt++;
+  if (cnt % 100000 == 0) (*mycout) << "cnt = " << cnt << endl;
+  
+  n = int(t);   
+  loct = t - n;
+  
+  b1 = 0.25 * (1 - loct) * (1 - loct);
+  b4 = 0.25 * loct * loct;
+  b2 = 0.5 - b4;
+  b3 = 0.5 - b1;
+  
+  n1 = (n + 10 * points.Size() -1) % points.Size() + 1;
+  n2 = n1+1;
+  if (n2 > points.Size()) n2 = 1;
+  n3 = n2+1;
+  if (n3 > points.Size()) n3 = 1;
+  n4 = n3+1;
+  if (n4 > points.Size()) n4 = 1;
+
+  //  (*mycout) << "t = " << t << " n = " << n << " loct = " << loct 
+  //      << " n1 = " << n1 << endl;
+
+  
+  hp(0) = b1 * points.Get(n1)(0) + b2 * points.Get(n2)(0) +
+    b3 * points.Get(n3)(0) + b4 * points.Get(n4)(0);
+  hp(1) = b1 * points.Get(n1)(1) + b2 * points.Get(n2)(1) +
+    b3 * points.Get(n3)(1) + b4 * points.Get(n4)(1);
+  return hp;
+}
+  
+Vec<2> BSplineCurve2d :: EvalPrime (double t) const
+{
+  int n, n1, n2, n3, n4;
+  double loct, db1, db2, db3, db4;
+  Vec<2> hv;
+  
+  n = int(t);   
+  loct = t - n;
+  
+  db1 = 0.5 * (loct - 1);
+  db4 = 0.5 * loct;
+  db2 = -db4;
+  db3 = -db1;
+  
+  n1 = (n + 10 * points.Size() -1) % points.Size() + 1;
+  n2 = n1+1;
+  if (n2 > points.Size()) n2 = 1;
+  n3 = n2+1;
+  if (n3 > points.Size()) n3 = 1;
+  n4 = n3+1;
+  if (n4 > points.Size()) n4 = 1;
+  
+  hv(0) = db1 * points.Get(n1)(0) + db2 * points.Get(n2)(0) +
+    db3 * points.Get(n3)(0) + db4 * points.Get(n4)(0);
+  hv(1) = db1 * points.Get(n1)(1) + db2 * points.Get(n2)(1) +
+    db3 * points.Get(n3)(1) + db4 * points.Get(n4)(1);
+  return hv;
+}
+
+Vec<2> BSplineCurve2d :: EvalPrimePrime (double t) const
+{
+  int n, n1, n2, n3, n4;
+  double ddb1, ddb2, ddb3, ddb4;
+  Vec<2> hv;
+  
+  n = int(t);   
+  //  double loct = t - n;
+  
+  ddb1 = 0.5;
+  ddb4 = 0.5;
+  ddb2 = -0.5;
+  ddb3 = -0.5;
+  
+  n1 = (n + 10 * points.Size() -1) % points.Size() + 1;
+  n2 = n1+1;
+  if (n2 > points.Size()) n2 = 1;
+  n3 = n2+1;
+  if (n3 > points.Size()) n3 = 1;
+  n4 = n3+1;
+  if (n4 > points.Size()) n4 = 1;
+  
+  hv(0) = ddb1 * points.Get(n1)(0) + ddb2 * points.Get(n2)(0) +
+    ddb3 * points.Get(n3)(0) + ddb4 * points.Get(n4)(0);
+  hv(1) = ddb1 * points.Get(n1)(1) + ddb2 * points.Get(n2)(1) +
+    ddb3 * points.Get(n3)(1) + ddb4 * points.Get(n4)(1);
+  return hv;
+}
+  
+
+int BSplineCurve2d :: SectionUsed (double t) const
+{
+  int n1 = int(t);   
+  n1 = (n1 + 10 * points.Size() - 1) % points.Size() + 1;
+  return (intervallused.Get(n1) == 0);
+}
+
+void BSplineCurve2d :: Reduce (const Point<2> & p, double rad)
+{
+  int n1, n;
+  int j; 
+  double minx, miny, maxx, maxy;
+  
+  //  (*testout) << "Reduce: " << p << "," << rad << endl;
+  
+  redlevel++;
+  
+  for (n1 = 1; n1 <= points.Size(); n1++)
+    {
+      if (intervallused.Get(n1) != 0) continue;
+    
+      minx = maxx = points.Get(n1)(0);
+      miny = maxy = points.Get(n1)(1);
+    
+      n = n1;
+      for (j = 1; j <= 3; j++)
+	{
+	  n++;
+	  if (n > points.Size()) n = 1;
+	  if (points.Get(n)(0) < minx) minx = points.Get(n)(0);
+	  if (points.Get(n)(1) < miny) miny = points.Get(n)(1);
+	  if (points.Get(n)(0) > maxx) maxx = points.Get(n)(0);
+	  if (points.Get(n)(1) > maxy) maxy = points.Get(n)(1);
+	}
+      
+      if (minx > p(0) + rad || maxx < p(0) - rad ||
+	  miny > p(1) + rad || maxy < p(1) - rad)
+	{
+	  intervallused.Elem(n1) = redlevel;
+	  //      (*testout) << 0;
+	}
+      else
+	{
+	  //      (*testout) << 1;
+	  intervallused.Elem(n1) = 0;
+	}
+    }
+  //  (*testout) << endl;
+}
+
+void BSplineCurve2d :: UnReduce () 
+{
+  int i;
+  for (i = 1; i <= intervallused.Size(); i++)
+    if (intervallused.Get(i) == redlevel)
+      intervallused.Set (i, 0);
+  redlevel--;
+}
+  
+void BSplineCurve2d :: Print (ostream & ost) const
+{
+  ost << "SplineCurve: " << points.Size() << " points." << endl;
+  for (int i = 1; i <= points.Size(); i++)
+    ost << "P" << i << " = " << points.Get(i) << endl;
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/csg.hpp b/contrib/Netgen/libsrc/csg/csg.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8ddf8d5087ad998f1f1fe55e512d9d10c4914262
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csg.hpp
@@ -0,0 +1,44 @@
+#ifndef FILE_CSG
+#define FILE_CSG
+
+/* *************************************************************************/
+/* File:   geoml.hpp                                                        */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   21. Jun. 98                                                     */
+/* *************************************************************************/
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+#include <meshing.hpp>
+
+// #include <geometry2d.hpp>
+#include "../gprim/spline.hpp"
+#include "../gprim/splinegeometry.hpp"
+
+
+
+#include "surface.hpp"
+#include "solid.hpp"
+#include "identify.hpp"
+#include "singularref.hpp"
+#include "csgeom.hpp"
+#include "csgparser.hpp"
+
+
+#include "triapprox.hpp"
+#include "algprim.hpp"
+#include "brick.hpp"
+#include "spline3d.hpp"
+#include "manifold.hpp"
+#include "curve2d.hpp"
+#include "explicitcurve2d.hpp"
+#include "gencyl.hpp"
+#include "polyhedra.hpp"
+#include "extrusion.hpp"
+#include "revolution.hpp"
+#include "specpoin.hpp"
+#include "edgeflw.hpp"
+#include "meshsurf.hpp"
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/csgeom.cpp b/contrib/Netgen/libsrc/csg/csgeom.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ec8125da63fc6b550f9a8e6ce9dfb021e899154f
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgeom.cpp
@@ -0,0 +1,1500 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+
+
+
+  int CSGeometry :: changeval = 0;
+
+
+
+  TopLevelObject ::  
+  TopLevelObject (Solid * asolid,
+		  Surface * asurface)
+  {
+    solid = asolid;
+    surface = asurface;
+
+    SetRGB (0, 0, 1);
+    SetTransparent (0);
+    SetVisible (1); 
+    SetLayer (1);
+
+    if (!surface)
+      maxh = solid->GetMaxH();
+    else
+      maxh = surface->GetMaxH();
+
+    SetBCProp (-1);
+
+    bcname = "default";
+  }
+
+  void TopLevelObject :: GetData (ostream & ost)
+  {
+    ost << red << " " << green << " " << blue << " " 
+	<< transp << " " << visible << " ";
+  }
+
+  void TopLevelObject :: SetData (istream & ist)
+  {
+    ist >> red >> green >> blue >> transp >> visible;
+  }
+
+
+ 
+  Box<3> CSGeometry::default_boundingbox (Point<3> (-1000, -1000, -1000),
+					  Point<3> ( 1000,  1000,  1000));
+
+
+  CSGeometry :: CSGeometry ()
+    : boundingbox (default_boundingbox),
+      identicsurfaces (100), ideps(1e-9), filename("")
+  {
+    ;
+  }
+
+  CSGeometry :: CSGeometry (const string & afilename)
+    : boundingbox (default_boundingbox),
+      identicsurfaces (100), ideps(1e-9), filename(afilename)
+  {
+    changeval++;
+  }
+
+  CSGeometry :: ~CSGeometry ()
+  {
+    Clean();
+  }
+
+
+  void CSGeometry :: Clean ()
+  {
+    Array< Solid* > to_delete;
+    
+    for (int i = 0; i < solids.Size(); i++)
+      if(!to_delete.Contains(solids[i]->S1()))
+	to_delete.Append(solids[i]->S1());
+    for (int i = 0; i < solids.Size(); i++)
+      if(!to_delete.Contains(solids[i]))
+	to_delete.Append(solids[i]);
+    for(int i = 0; i < to_delete.Size(); i++)
+      delete to_delete[i];    
+    
+    /*
+    for (int i = 0; i < solids.Size(); i++)
+      delete solids[i]->S1();
+    for (int i = 0; i < solids.Size(); i++)
+      delete solids[i];
+    */
+
+    solids.DeleteAll ();
+
+    for (int i = 0; i < splinecurves2d.Size(); i++)
+      delete splinecurves2d[i];
+    splinecurves2d.DeleteAll();
+    
+    /*
+    for (int i = 0; i < surfaces.Size(); i++)
+      delete surfaces[i];
+    surfaces.DeleteAll ();
+    */
+    for(int i = 0; i<delete_them.Size(); i++)
+      delete delete_them[i];
+    delete_them.DeleteAll();
+    surfaces.DeleteAll();
+  
+    for (int i = 0; i < toplevelobjects.Size(); i++)
+      delete toplevelobjects[i];
+    toplevelobjects.DeleteAll ();
+    for (int i = 0; i < triapprox.Size(); i++)
+      delete triapprox[i];
+    triapprox.DeleteAll();
+
+    for(int i = 0; i < identifications.Size(); i++)
+      delete identifications[i];
+    identifications.DeleteAll();
+
+    for (int i = 0; i < singfaces.Size(); i++)
+      delete singfaces[i];
+    singfaces.DeleteAll();
+    for (int i = 0; i < singedges.Size(); i++)
+      delete singedges[i];
+    singedges.DeleteAll();
+    for (int i = 0; i < singpoints.Size(); i++)
+      delete singpoints[i];
+    singpoints.DeleteAll();
+
+    changeval++;
+  }
+
+
+  extern int CSGGenerateMesh (CSGeometry & geom, 
+			      Mesh *& mesh, MeshingParameters & mparam,
+			      int perfstepsstart, int perfstepsend);
+
+
+  int CSGeometry :: GenerateMesh (Mesh*& mesh, MeshingParameters & mparam,
+				 int perfstepsstart, int perfstepsend)
+  {
+    return CSGGenerateMesh (*this, mesh, mparam, perfstepsstart, perfstepsend);
+  }
+
+
+  const Refinement & CSGeometry :: GetRefinement () const
+  {
+    // cout << "get CSGeometry - Refinement" << endl;
+    // should become class variables
+    RefinementSurfaces * ref = new RefinementSurfaces(*this);
+    ref -> Set2dOptimizer(new MeshOptimize2dSurfaces(*this));
+    return *ref;
+  }
+
+  class WritePrimitivesIt : public SolidIterator
+  {
+    ostream & ost;
+  public:
+    WritePrimitivesIt (ostream & aost) : ost(aost) { ; }
+    virtual ~WritePrimitivesIt () { ; }
+
+    virtual void Do (Solid * sol);
+  };
+
+  void WritePrimitivesIt :: Do (Solid * sol) 
+  {
+    Primitive * prim = sol->GetPrimitive();
+    if (prim)
+      {
+	const char * classname;
+	Array<double> coeffs;
+
+	prim -> GetPrimitiveData (classname, coeffs);
+
+	if (sol->Name())
+	  ost << "primitive " 
+	      << sol->Name() << " "
+	      << classname << "  " << coeffs.Size();
+	for (int i = 0; i < coeffs.Size(); i++)
+	  ost << " " << coeffs[i];
+	ost << endl;
+      }
+  }
+
+
+  void CSGeometry :: Save (string filename) const
+  {
+    fstream ost (filename.c_str());
+    Save (ost);
+  }
+  
+  void CSGeometry :: Save (ostream & ost) const
+  {
+    ost << "boundingbox "
+	<< boundingbox.PMin()(0) << " "
+	<< boundingbox.PMin()(1) << " "
+	<< boundingbox.PMin()(2) << " "
+	<< boundingbox.PMax()(0) << " "
+	<< boundingbox.PMax()(1) << " "
+	<< boundingbox.PMax()(2) << endl;
+
+
+    WritePrimitivesIt wpi(ost);
+    IterateAllSolids (wpi, 1);
+
+    for (int i = 0; i < solids.Size(); i++)
+      {
+	if (!solids[i]->GetPrimitive())
+	  {
+	    ost << "solid " << solids.GetName(i) << " ";
+	    solids[i] -> GetSolidData (ost);
+	    ost << endl;
+	  }
+      }
+
+    for (int i = 0; i < GetNTopLevelObjects(); i++)
+      {
+	TopLevelObject * tlo = GetTopLevelObject (i);
+	ost << "toplevel ";
+	if (tlo -> GetSurface())
+	  ost << "surface " << tlo->GetSolid()->Name() << " "
+	      << tlo->GetSurface()->Name() << " ";
+	else
+	  ost << "solid " << tlo->GetSolid()->Name() << " ";
+	tlo->GetData(ost);
+	ost << endl;
+      }
+
+    for (int i = 0; i < identifications.Size(); i++)
+      {
+	ost << "identify ";
+	identifications[i] -> GetData (ost);
+	ost << endl;
+      }
+
+    ost << "end" << endl;
+  }
+
+ 
+  void CSGeometry :: Load (istream & ist)
+  {
+    //  CSGeometry * geo = new CSGeometry;
+  
+    char key[100], name[100], classname[100], sname[100];
+    int ncoeff, i, j;
+    Array<double> coeff;
+
+    while (ist.good())
+      {
+	ist >> key;
+	if (strcmp (key, "boundingbox") == 0)
+	  {
+	    Point<3> pmin, pmax;
+	    ist >> pmin(0) >> pmin(1) >> pmin(2);
+	    ist >> pmax(0) >> pmax(1) >> pmax(2);
+	    SetBoundingBox (Box<3> (pmin, pmax));
+	  }
+	if (strcmp (key, "primitive") == 0)
+	  {
+	    ist >> name >> classname >> ncoeff;
+	    coeff.SetSize (ncoeff);
+	    for (i = 0; i < ncoeff; i++)
+	      ist >> coeff[i];
+
+	    Primitive * nprim = Primitive::CreatePrimitive (classname);
+	    nprim -> SetPrimitiveData (coeff);
+	    Solid * nsol = new Solid (nprim);
+
+	    for (j = 0; j < nprim->GetNSurfaces(); j++)
+	      {
+		sprintf (sname, "%s,%d", name, j);
+		AddSurface (sname, &nprim->GetSurface(j));
+		nprim -> SetSurfaceId (j, GetNSurf());
+	      }
+	    SetSolid (name, nsol);
+	  }
+	else if (strcmp (key, "solid") == 0)
+	  {
+	    ist >> name;
+	    Solid * nsol = Solid::CreateSolid (ist, solids);
+
+	    cout << " I have found solid " << name << " = ";
+	    nsol -> GetSolidData (cout);
+	    cout << endl;
+
+	    SetSolid (name, nsol);
+	  }
+	else if (strcmp (key, "toplevel") == 0)
+	  {
+	    char type[20], solname[50], surfname[50];
+	    const Solid * sol = NULL;
+	    const Surface * surf = NULL;
+	    int nr;
+
+	    ist >> type;
+	    if (strcmp (type, "solid") == 0)
+	      {
+		ist >> solname;
+		sol = GetSolid (solname);
+	      }
+	    if (strcmp (type, "surface") == 0)
+	      {
+		ist >> solname >> surfname;
+		sol = GetSolid (solname);
+		surf = GetSurface (surfname);
+	      }
+	    nr = SetTopLevelObject ((Solid*)sol, (Surface*)surf);
+	    GetTopLevelObject (nr) -> SetData (ist);
+	  }
+	else if (strcmp (key, "identify") == 0)
+	  {
+	    char type[10], surfname1[50], surfname2[50];
+	    const Surface * surf1;
+	    const Surface * surf2;
+
+
+	    ist >> type >> surfname1 >> surfname2;
+	    surf1 = GetSurface(surfname1);
+	    surf2 = GetSurface(surfname2);
+	  
+	    AddIdentification (new PeriodicIdentification 
+			       (GetNIdentifications(),
+				*this, surf1, surf2));
+	  }
+	else if (strcmp (key, "end") == 0)
+	  break;
+      }
+
+    changeval++;
+  }
+
+
+
+  void CSGeometry :: SaveSurfaces (ostream & out) const
+  {
+    if(singfaces.Size() > 0 || singedges.Size() > 0 || singpoints.Size() > 0)
+      {
+	PrintMessage(3,"Singular faces/edges/points => no csg-information in .vol file");
+	return;
+      }
+
+
+    
+    Array<double> coeffs;
+    const char * classname;
+
+    out << "csgsurfaces " << GetNSurf() << "\n";
+    for(int i=0; i<GetNSurf(); i++)
+      {
+	const OneSurfacePrimitive * sp = dynamic_cast< const OneSurfacePrimitive * > (GetSurface(i));
+	const ExtrusionFace * ef = dynamic_cast< const ExtrusionFace * > (GetSurface(i));
+	const RevolutionFace * rf = dynamic_cast< const RevolutionFace * > (GetSurface(i));
+	const DummySurface * dummyf = dynamic_cast< const DummySurface * > (GetSurface(i));
+
+
+	if(sp)
+	  {
+	    sp->GetPrimitiveData(classname,coeffs);
+	
+	    out << classname << " ";
+	  }
+	else if(ef)
+	  {
+	    out << "extrusionface ";
+	    ef->GetRawData(coeffs);
+	  }
+	else if(rf)
+	  {
+	    out << "revolutionface ";
+	    rf->GetRawData(coeffs);
+	  }
+	else if(dummyf)
+	  {
+	    out << "dummy ";
+            coeffs.SetSize(0);
+	  }
+	else
+	  throw NgException ("Cannot write csg surface. Please, contact developers!");
+      
+	
+	out << coeffs.Size() << "\n";
+	for(int j=0; j<coeffs.Size(); j++)
+	  out << coeffs[j] << " ";
+	    
+	out << "\n";
+      }
+  }
+
+  void CSGeometry :: LoadSurfaces (istream & in)
+  {
+    Array<double> coeffs;
+    string classname;
+    int nsurfaces,size;
+
+    in >> classname;
+    
+    if(classname == "csgsurfaces")
+      in >> nsurfaces;
+    else
+      nsurfaces = atoi(classname.c_str());
+    
+    Point<3> dummypoint(0,0,0);
+    Vec<3> dummyvec(0,0,0);
+    double dummydouble(0.1);
+
+    for(int i=0; i<nsurfaces; i++)
+      {
+	in >> classname;
+	in >> size;
+
+	coeffs.SetSize(size);
+
+	for(int j=0; j<size; j++)
+	  in >> coeffs[j];
+
+	if(classname == "plane")
+	  {
+	    Plane * plane = new Plane(dummypoint,dummyvec);
+	    plane->SetPrimitiveData(coeffs);
+
+	    AddSurface(plane);
+	    delete_them.Append(plane);
+	  }
+
+	else if(classname == "sphere")
+	  {
+	    Sphere * sphere = new Sphere(dummypoint,dummydouble);
+	    sphere->SetPrimitiveData(coeffs);
+
+	    AddSurface(sphere);
+	    delete_them.Append(sphere);
+	  }
+
+	else if(classname == "cylinder")
+	  {
+	    Cylinder * cylinder = new Cylinder(coeffs);
+
+	    AddSurface(cylinder);
+	    delete_them.Append(cylinder);
+	  }
+
+	else if(classname == "ellipticcylinder")
+	  {
+	    EllipticCylinder * cylinder = new EllipticCylinder(coeffs);
+	    AddSurface(cylinder);
+	    delete_them.Append(cylinder);
+	  }
+
+
+	else if(classname == "torus")
+	  {
+	    Torus * torus = new Torus(dummypoint,dummyvec,dummydouble, dummydouble);
+	    torus->SetPrimitiveData(coeffs);
+	    AddSurface(torus);
+	    delete_them.Append(torus);
+	  }
+
+
+	else if(classname == "cone")
+	  {
+	    Cone * cone = new Cone(dummypoint,dummypoint,dummydouble,dummydouble);
+	    cone->SetPrimitiveData(coeffs);
+
+	    AddSurface(cone);
+	    delete_them.Append(cone);
+	  }
+
+	else if(classname == "extrusionface")
+	  {
+	    ExtrusionFace * ef =
+	      new ExtrusionFace(coeffs);
+
+	    AddSurface(ef);
+	    delete_them.Append(ef);
+	  }
+
+	else if(classname == "revolutionface")
+	  {
+	    RevolutionFace * rf =
+	      new RevolutionFace(coeffs);
+
+	    AddSurface(rf);
+	    delete_them.Append(rf);
+	  }
+
+	else if(classname == "dummy")
+	  {
+	    Surface * surf = new DummySurface();
+            
+	    AddSurface(surf);
+	    delete_them.Append(surf);
+	  }
+
+      }    
+  }
+    
+
+  void CSGeometry :: SaveToMeshFile (ostream & ost) const
+  {
+    SaveSurfaces (ost);
+  }
+
+
+
+
+  void CSGeometry :: AddSurface (Surface * surf)
+  {
+    static int cntsurfs = 0;
+    cntsurfs++;
+    char name[15];
+    sprintf (name, "nnsurf%d", cntsurfs);
+    AddSurface (name, surf);
+  }
+ 
+  void CSGeometry :: AddSurface (char * name, Surface * surf)
+  { 
+    (*testout) << "Adding surface " << name << endl;
+    surfaces.Set (name, surf); 
+    surf->SetName (name);
+    changeval++; 
+  }
+
+  void CSGeometry :: AddSurfaces (Primitive * prim)
+  {
+    for (int i = 0; i < prim->GetNSurfaces(); i++)
+      {
+	AddSurface (&prim->GetSurface(i));
+	prim->SetSurfaceId (i, GetNSurf()-1);
+	surf2prim.Append (prim);
+      }
+  }
+
+  const Surface * CSGeometry :: GetSurface (const char * name) const
+  {
+    if (surfaces.Used(name))
+      return surfaces.Get(name);
+    else
+      return NULL;
+  }
+
+  /*
+  const Surface * CSGeometry :: GetSurface (int i) const
+  {
+    if (i >= 0 && i < surfaces.Size()) 
+      return surfaces[i];
+    else
+      throw NgException ("CSGeometry::GetSurface out of range");
+  }
+  */
+
+
+
+
+  void CSGeometry :: SetSolid (const char * name, Solid * sol)
+  {
+    Solid * oldsol = NULL;
+
+    if (solids.Used (name))
+      oldsol = solids.Get(name);
+
+    solids.Set (name, sol);
+    sol->SetName (name);
+
+    if (oldsol)
+      {
+	if (oldsol->op != Solid::ROOT ||
+	    sol->op != Solid::ROOT)
+	  {
+	    cerr << "Setsolid: old or new no root" << endl;
+	  }
+	oldsol -> s1 = sol -> s1;
+      }
+    changeval++;
+  }
+
+  const Solid * CSGeometry :: GetSolid (const char * name) const
+  {
+    if (solids.Used(name))
+      return solids.Get(name);
+    else
+      return NULL;
+  }
+
+  
+
+
+
+  const Solid * CSGeometry :: GetSolid (const string & name) const
+  {
+    if (solids.Used(name.c_str()))
+      return solids.Get(name.c_str());
+    else
+      return NULL;
+  }
+
+
+
+
+  void CSGeometry :: SetSplineCurve (const char * name, SplineGeometry<2> * spl)
+  {
+    splinecurves2d.Set(name,spl);
+  }
+  void CSGeometry :: SetSplineCurve (const char * name, SplineGeometry<3> * spl)
+  {
+    splinecurves3d.Set(name,spl);
+  }
+
+
+  const SplineGeometry<2> * CSGeometry :: GetSplineCurve2d (const string & name) const
+  {
+    if (splinecurves2d.Used(name.c_str()))
+      return splinecurves2d.Get(name.c_str());
+    else
+      return NULL;
+  }
+  const SplineGeometry<3> * CSGeometry :: GetSplineCurve3d (const string & name) const
+  {
+    if (splinecurves3d.Used(name.c_str()))
+      return splinecurves3d.Get(name.c_str());
+    else
+      return NULL;
+  }
+
+
+
+    /*
+  class RemoveDummyIterator : public SolidIterator
+  {
+  public:
+  
+    RemoveDummyIterator() { ; }
+    virtual ~RemoveDummyIterator() { ; }
+    virtual void Do(Solid * sol);
+  };
+
+  void RemoveDummyIterator :: Do(Solid * sol)
+  {
+    cerr << "remove dummy iterator is obsolete" << endl;
+
+    if ( (sol->op == Solid::SUB || sol->op == Solid::SECTION || 
+	  sol->op == Solid::UNION)
+	 && sol->s1->op == Solid::DUMMY)
+      sol->s1 = sol->s1->s1;
+    if ( (sol->op == Solid::SECTION || sol->op == Solid::UNION)
+	 && sol->s2->op == Solid::DUMMY)
+      sol->s2 = sol->s2->s1;
+  }
+    */
+
+
+
+
+
+
+  int CSGeometry :: SetTopLevelObject (Solid * sol, Surface * surf)
+  {
+    return toplevelobjects.Append (new TopLevelObject (sol, surf)) - 1;
+  }
+
+  TopLevelObject * CSGeometry :: 
+  GetTopLevelObject (const Solid * sol, const Surface * surf)
+  {
+    for (int i = 0; i < toplevelobjects.Size(); i++)
+      {
+	if (toplevelobjects[i]->GetSolid() == sol &&
+	    toplevelobjects[i]->GetSurface() == surf)
+	  return (toplevelobjects[i]);
+      }
+    return NULL;
+  }
+
+  void CSGeometry :: RemoveTopLevelObject (Solid * sol, Surface * surf)
+  {
+    for (int i = 0; i < toplevelobjects.Size(); i++)
+      {
+	if (toplevelobjects[i]->GetSolid() == sol &&
+	    toplevelobjects[i]->GetSurface() == surf)
+	  {
+	    delete toplevelobjects[i];
+	    toplevelobjects.DeleteElement (i+1);
+	    changeval++;
+	    break;
+	  }
+      }
+  }
+
+  void CSGeometry :: AddIdentification (Identification * ident)
+  {
+    identifications.Append (ident);
+  }
+
+  void CSGeometry :: SetFlags (const char * solidname, const Flags & flags)
+  {
+    Solid * solid = solids.Elem(solidname);
+    Array<int> surfind;
+
+    int i;
+    double maxh = flags.GetNumFlag ("maxh", -1);
+    if (maxh > 0 && solid)
+      {
+	solid->GetSurfaceIndices (surfind);
+
+	for (i = 0; i < surfind.Size(); i++)
+	  {
+	    if (surfaces[surfind[i]]->GetMaxH() > maxh)
+	      surfaces[surfind[i]] -> SetMaxH (maxh);
+	  }
+
+	solid->SetMaxH (maxh);
+      }
+
+    if ( flags.StringFlagDefined ("bcname") )
+      {
+	solid->GetSurfaceIndices (surfind);
+	string bcn = flags.GetStringFlag("bcname", "default");
+	for (i = 0; i < surfind.Size(); i++)
+	  {
+	    if(surfaces[surfind[i]]->GetBCName() == "default")
+	      surfaces[surfind[i]]->SetBCName(bcn);
+	  }
+      }
+
+    if (flags.StringListFlagDefined ("bcname"))
+      {
+	const Array<char*> & bcname = flags.GetStringListFlag("bcname");
+
+	Polyhedra * polyh;
+	if(solid->S1())
+	  polyh = dynamic_cast<Polyhedra *>(solid->S1()->GetPrimitive());
+	else
+	  polyh = dynamic_cast<Polyhedra *>(solid->GetPrimitive());
+
+	if(polyh)
+	  {
+	    Array < Array<int> * > polysurfs;
+	    polyh->GetPolySurfs(polysurfs);
+	    if(bcname.Size() != polysurfs.Size())
+	      cerr << "WARNING: solid \"" << solidname << "\" has " << polysurfs.Size()
+		   << " surfaces and should get " << bcname.Size() << " bc-names!" << endl;
+	    
+	    for ( i = 0; i < min2(polysurfs.Size(),bcname.Size()); i++)
+	      {
+		for (int j = 0; j < polysurfs[i]->Size(); j++)
+		  {
+		    if(surfaces[(*polysurfs[i])[j]]->GetBCName() == "default")
+		      surfaces[(*polysurfs[i])[j]]->SetBCName(bcname[i]);
+		  }
+		delete polysurfs[i];
+	      }
+	  }
+	else
+	  {
+	    solid->GetSurfaceIndices (surfind);
+	    if(bcname.Size() != surfind.Size())
+	      cerr << "WARNING: solid \"" << solidname << "\" has " << surfind.Size()
+		   << " surfaces and should get " << bcname.Size() << " bc-names!" << endl;
+	    
+	    for (i = 0; i < min2(surfind.Size(),bcname.Size()); i++)
+	      {
+		if(surfaces[surfind[i]]->GetBCName() == "default")
+		  surfaces[surfind[i]]->SetBCName(bcname[i]);
+	      }
+	  }
+      }
+
+    if (flags.NumFlagDefined ("bc"))
+      {
+	solid->GetSurfaceIndices (surfind);
+	int bc = int (flags.GetNumFlag("bc", -1));
+	for (i = 0; i < surfind.Size(); i++)
+	  {
+	    if (surfaces[surfind[i]]->GetBCProperty() == -1)
+	      surfaces[surfind[i]]->SetBCProperty(bc);
+	  }
+      }
+   
+    if (flags.NumListFlagDefined ("bc"))
+      {
+	const Array<double> & bcnum = flags.GetNumListFlag("bc");
+
+	Polyhedra * polyh;
+	if(solid->S1())
+	  polyh = dynamic_cast<Polyhedra *>(solid->S1()->GetPrimitive());
+	else
+	  polyh = dynamic_cast<Polyhedra *>(solid->GetPrimitive());
+
+	if(polyh)
+	  {
+	    Array < Array<int> * > polysurfs;
+	    polyh->GetPolySurfs(polysurfs);
+	    if(bcnum.Size() != polysurfs.Size())
+	      cerr << "WARNING: solid \"" << solidname << "\" has " << polysurfs.Size()
+		   << " surfaces and should get " << bcnum.Size() << " bc-numbers!" << endl;
+	    
+	    for ( i = 0; i < min2(polysurfs.Size(),bcnum.Size()); i++)
+	      {
+		for (int j = 0; j < polysurfs[i]->Size(); j++)
+		  {
+		    if ( surfaces[(*polysurfs[i])[j]]->GetBCProperty() == -1 )
+		      surfaces[(*polysurfs[i])[j]]->SetBCProperty(int(bcnum[i]));
+		  }
+		delete polysurfs[i];
+	      }
+	  }
+	else
+	  {
+	    solid->GetSurfaceIndices (surfind);
+	    if(bcnum.Size() != surfind.Size())
+	      cerr << "WARNING: solid \"" << solidname << "\" has " << surfind.Size()
+		   << " surfaces and should get " << bcnum.Size() << " bc-numbers!" << endl;
+	    
+	    for (i = 0; i < min2(surfind.Size(),bcnum.Size()); i++)
+	      {
+		if (surfaces[surfind[i]]->GetBCProperty() == -1)
+		  surfaces[surfind[i]]->SetBCProperty(int(bcnum[i]));
+	      }
+	  }
+      }
+
+  }
+
+  void CSGeometry :: FindIdenticSurfaces (double eps)
+  {
+    int inv;
+    int nsurf = GetNSurf();
+
+    isidenticto.SetSize(nsurf);
+    for (int i = 0; i < nsurf; i++)
+      isidenticto[i] = i;
+  
+    //(*testout) << "jetzt!" << endl;
+    for (int i = 0; i < nsurf; i++)
+      for (int j = i+1; j < nsurf; j++)
+	{
+	  //(*testout) << "surf" << i << " surf" << j << endl;
+	  if (GetSurface(j) -> IsIdentic (*GetSurface(i), inv, eps))
+	    {
+	      INDEX_2 i2(i, j);
+	      identicsurfaces.Set (i2, inv);
+	      isidenticto[j] = isidenticto[i];
+	      //(*testout) << "surfaces " << i2 << " are identic" << endl;
+	    }
+	}
+
+    (*testout) << "identicmap:" << endl;
+    for (int i = 0; i < isidenticto.Size(); i++)
+      (*testout) << i << " -> " << isidenticto[i] << endl;
+
+    /*    
+    for (int i = 0; i < nsurf; i++)
+      GetSurface(i)->Print (*testout);
+    */
+  }
+  
+
+  
+  void CSGeometry ::
+  GetSurfaceIndices (const Solid * sol, 
+		     const BoxSphere<3> & box, 
+		     Array<int> & locsurf) const
+  {
+    ReducePrimitiveIterator rpi(box);
+    UnReducePrimitiveIterator urpi;
+
+    ((Solid*)sol) -> IterateSolid (rpi);
+    sol -> GetSurfaceIndices (locsurf);
+    ((Solid*)sol) -> IterateSolid (urpi);
+
+    for (int i = locsurf.Size()-1; i >= 0; i--)
+      {
+	bool indep = 1;
+	for (int j = 0; j < i; j++)
+	  if (locsurf[i] == locsurf[j])
+	    {
+	      indep = 0;
+	      break;
+	    }
+
+	if (!indep) locsurf.Delete(i);
+      }
+  }
+
+
+
+  
+  void CSGeometry ::
+  GetIndependentSurfaceIndices (const Solid * sol, 
+				const BoxSphere<3> & box, 
+				Array<int> & locsurf) const
+  {
+    ReducePrimitiveIterator rpi(box);
+    UnReducePrimitiveIterator urpi;
+
+    ((Solid*)sol) -> IterateSolid (rpi);
+    sol -> GetSurfaceIndices (locsurf);
+    ((Solid*)sol) -> IterateSolid (urpi);
+
+    for (int i = 0; i < locsurf.Size(); i++)
+      locsurf[i] = isidenticto[locsurf[i]];
+
+    for (int i = locsurf.Size()-1; i >= 0; i--)
+      {
+	bool indep = 1;
+	for (int j = 0; j < i; j++)
+	  if (locsurf[i] == locsurf[j])
+	    {
+	      indep = 0;
+	      break;
+	    }
+
+	if (!indep) locsurf.Delete(i);
+      }
+
+
+    /*
+    // delete identified
+    for (int i = locsurf.Size()-1; i >= 0; i--)
+      {
+	bool indep = 1;
+	for (int j = 0; j < i; j++)
+	  {
+	    if (identicsurfaces.Used (INDEX_2::Sort (locsurf[i], locsurf[j])) !=
+		(isidenticto[locsurf[i]] == isidenticto[locsurf[j]]))
+	      {
+		cerr << "different result" << endl;
+		exit(1);
+	      }
+
+	    if (isidenticto[locsurf[i]] == isidenticto[locsurf[j]])
+	      {
+		indep = 0;
+		break;
+	      }
+	  }
+	if (!indep)
+	  locsurf.Delete(i);
+      }
+
+    for (int i = 0; i < locsurf.Size(); i++)
+      locsurf[i] = isidenticto[locsurf[i]];
+    */
+  }
+
+
+  void CSGeometry ::
+  GetIndependentSurfaceIndices (const Solid * sol, 
+				const Point<3> & p, Vec<3> & v,
+				Array<int> & locsurf) const
+  {
+    cout << "very dangerous" << endl;
+    Point<3> p2 = p + 1e-2 * v;
+    BoxSphere<3> box (p2, p2);
+    box.Increase (1e-3);
+    box.CalcDiamCenter();
+    GetIndependentSurfaceIndices (sol, box, locsurf);
+  }
+
+
+  void CSGeometry ::
+  GetIndependentSurfaceIndices (Array<int> & locsurf) const
+  {
+    for (int i = 0; i < locsurf.Size(); i++)
+      locsurf[i] = isidenticto[locsurf[i]];
+
+    for (int i = locsurf.Size()-1; i >= 0; i--)
+      {
+	bool indep = 1;
+	for (int j = 0; j < i; j++)
+	  if (locsurf[i] == locsurf[j])
+	    {
+	      indep = 0;
+	      break;
+	    }
+
+	if (!indep) locsurf.Delete(i);
+      }
+  }
+
+
+
+
+
+
+
+
+
+  void CSGeometry :: 
+  CalcTriangleApproximation(double detail, double facets)
+  {
+    PrintMessage (1, "Calc Triangle Approximation");
+
+    try
+      {
+    //    FindIdenticSurfaces (1e-6);
+  
+    int ntlo = GetNTopLevelObjects();
+
+    for (int i = 0; i < triapprox.Size(); i++)
+      delete triapprox[i];
+    triapprox.SetSize (ntlo);
+
+    Array<int> surfind;
+    IndexSet iset(GetNSurf());
+
+    for (int i = 0; i < ntlo; i++)
+      {
+	Solid * sol;
+	Surface * surf;
+	GetTopLevelObject (i, sol, surf);
+
+	sol -> CalcSurfaceInverse ();
+
+	TriangleApproximation * tams = new TriangleApproximation();
+	triapprox[i] = tams;
+
+	// sol -> GetSurfaceIndices (surfind);
+	for (int j = 0; j < GetNSurf(); j++)
+	  // for (int jj = 0; jj < surfind.Size(); jj++)
+	  {
+	    // int j = surfind[jj];
+
+	    PrintMessageCR (3, "Surface ", j, "/", GetNSurf());
+	    // PrintMessageCR (3, "Surface ", j, "/", surfind.Size());
+
+	    if (surf && surf != GetSurface(j))
+	      continue;
+
+	    TriangleApproximation tas;
+	    GetSurface (j) -> GetTriangleApproximation (tas, boundingbox, facets);
+
+	    int oldnp = tams -> GetNP();
+
+	    if (!tas.GetNP())
+	      continue;
+
+	    for (int k = 0; k < tas.GetNP(); k++)
+	      {
+		tams -> AddPoint (tas.GetPoint(k));
+                Vec<3> n = GetSurface(j) -> GetNormalVector (tas.GetPoint(k)); 
+		n.Normalize();
+		if (GetSurface(j)->Inverse()) n *= -1;
+		tams -> AddNormal (n);
+	      }
+	  
+	    BoxSphere<3> surfbox;
+
+	    if (tas.GetNP())
+	      surfbox.Set (tas.GetPoint(0));
+	    for (int k = 1; k < tas.GetNP(); k++)
+	      surfbox.Add (tas.GetPoint(k));
+	    surfbox.Increase (1e-6);
+	    surfbox.CalcDiamCenter();
+
+	    Solid * surflocsol = sol -> GetReducedSolid (surfbox);
+	    if (!surflocsol)
+	      continue;
+
+	    for (int k = 0; k < tas.GetNT(); k++)
+	      {
+		const TATriangle & tri = tas.GetTriangle (k);
+
+		// check triangle
+		BoxSphere<3> box;
+		box.Set (tas.GetPoint (tri[0]));
+		box.Add (tas.GetPoint (tri[1]));
+		box.Add (tas.GetPoint (tri[2]));
+		box.Increase (1e-6);
+		box.CalcDiamCenter();
+
+
+		Solid * locsol = surflocsol -> GetReducedSolid (box);
+		
+		if (locsol)
+		  {
+		    TATriangle tria(j, 
+				    tri[0] + oldnp,
+				    tri[1] + oldnp,
+				    tri[2] + oldnp);
+                    
+                    // tams -> AddTriangle (tria);
+
+		    RefineTriangleApprox (locsol, j, box, detail, 
+					  tria, *tams, iset, 1);
+
+		    delete locsol;
+		  }
+	      }
+	  }
+
+	tams->RemoveUnusedPoints ();
+	PrintMessage (2, "Object ", i, " has ", tams->GetNT(), " triangles");
+      }
+      }
+    catch (exception)
+      {
+	cerr << "*************************************************************" << endl
+	     << "****   out of memory problem in CSG visualization        ****" << endl
+	     << "****   Restart netgen, and disable                       ****" << endl
+	     << "****   'Draw Geometry' in Geometry -> CSG Options        ****" << endl
+	     << "****   before loading the geometry                       ****" << endl
+	     << "****   meshing will still work !                         ****" << endl
+	     << "*************************************************************" << endl;
+	exit(1);
+      }
+    Change();
+  }
+
+
+
+  void CSGeometry ::
+  RefineTriangleApprox (Solid * locsol, 
+			int surfind,
+			const BoxSphere<3> & box, 
+			double detail,
+			const TATriangle & tria, 
+			TriangleApproximation & tams,
+			IndexSet & iset,
+                        int level)
+  {
+    // if (level > 10) return;
+
+    //tams.AddTriangle (tria);
+    //(*testout) << "tria " << tams.GetPoint(tria[0]) << " - " << tams.GetPoint(tria[1]) << " - " << tams.GetPoint(tria[2]) 
+    //       << " ( " << tria[0] << " " << tria[1] << " " << tria[2] << ")" <<endl;
+    //return;
+
+    int pinds[6];
+    ArrayMem<int,500> surfused(GetNSurf());
+  
+    ReducePrimitiveIterator rpi(box);
+    UnReducePrimitiveIterator urpi;
+
+    locsol -> IterateSolid (rpi);
+    //  locsol -> GetSurfaceIndices (lsurfi);
+
+
+    //    IndexSet iset(GetNSurf());
+    locsol -> GetSurfaceIndices (iset);
+    const Array<int> & lsurfi = iset.GetArray();
+
+    locsol -> IterateSolid (urpi);
+
+    int surfii = -1;
+    for (int i = 0; i < lsurfi.Size(); i++)
+      if (lsurfi[i] == surfind)
+	{
+	  surfii = i;
+	  break;
+	}
+
+    if (surfii == -1)
+      return;
+
+    int cntindep = 0;
+
+    for (int i = 0; i < lsurfi.Size(); i++)
+      {
+	int linkto = isidenticto[lsurfi[i]];
+	surfused[linkto] = 0;
+      }
+
+    for (int i = 0; i < lsurfi.Size(); i++)
+      {
+	int linkto = isidenticto[lsurfi[i]];
+	if (!surfused[linkto])
+	  {
+	    surfused[linkto] = 1;
+	    cntindep++;
+	  }
+      }
+
+    int inverse = surfaces[surfind]->Inverse();
+
+    if (cntindep == 1)
+      {
+	tams.AddTriangle (tria);
+	//(*testout) << "pos1 " << tams.GetPoint(tria[0]) << " - " << tams.GetPoint(tria[1]) << " - " << tams.GetPoint(tria[2]) << endl;
+	return;
+      }
+
+    if (cntindep == 2)
+      {
+	// just 2 surfaces:
+	// if smooth, project inner points to edge and finish
+
+	int otherind = -1;
+
+	for (int i = 0; i < lsurfi.Size(); i++)
+	  {
+	    INDEX_2 i2 (lsurfi[i], surfind);
+	    i2.Sort();
+	  
+	    if (i != surfii && !identicsurfaces.Used(i2))
+	      otherind = lsurfi[i];
+	  }
+
+	double kappa = GetSurface(otherind)-> MaxCurvature ();
+
+	if (kappa * box.Diam() < 0.1)
+	  {
+	    int pnums[6];
+	    static int between[3][3] =
+	      { { 1, 2, 3 },
+		{ 0, 2, 4 },
+		{ 0, 1, 5 } };
+	    int onsurface[3];
+
+	    for (int j = 0; j < 3; j++)
+	      {
+		int pi = tria[j];
+		pnums[j] = pi;
+
+
+		onsurface[j] =  
+		  !locsol->IsStrictIn (tams.GetPoint (pi), 1e-6) &&
+		  locsol->IsIn (tams.GetPoint (pi), 1e-6);
+		
+		//
+		/*
+		static int nos=0;
+		if(!onsurface[j])
+		  {
+		    nos++;
+		    cout << "NOT ON SURFACE!! "<< nos << endl;
+		  }
+		*/
+	      }
+	  
+	    for (int j = 0; j < 3; j++)
+	      {
+		int lpi1 = between[j][0];
+		int lpi2 = between[j][1];
+		int lpin = between[j][2];
+		if (onsurface[lpi1] == onsurface[lpi2])
+		  pnums[lpin] = -1;
+		else
+		  {
+		    const Point<3> & p1 = tams.GetPoint (pnums[lpi1]);
+		    const Point<3> & p2 = tams.GetPoint (pnums[lpi2]);
+		    double f1 = GetSurface(otherind)->CalcFunctionValue (p1);
+		    double f2 = GetSurface(otherind)->CalcFunctionValue (p2);
+
+		    Point<3> pn;
+
+		    double l2(100),l1(100);
+		    if ( fabs (f1-f2) > 1e-20 )
+		      {
+			l2 = -f1/(f2-f1);
+			l1 = f2/(f2-f1);
+			pn = Point<3>(l1 * p1(0) + l2 * p2(0),
+				      l1 * p1(1) + l2 * p2(1),
+				      l1 * p1(2) + l2 * p2(2));
+		      }
+		    else
+		      pn = p1;
+
+// 		    if(fabs(pn(0)) > 4 || fabs(pn(1)) > 4 || fabs(pn(2)) > 4)
+// 		      {
+// 			cout << "p1 " << p1 << " p2 " << p2 
+// 			     << " f1 " << f1 << " f2 " << f2
+// 			     << " l1 " << l1 << " l2 " << l2 
+// 			     << " pn " << pn << endl;
+
+// 		      }
+
+		    
+		    //GetSurface (surfind)->Project (pn);
+		    
+		    pnums[lpin] = tams.AddPoint (pn);
+
+		    GetSurface (surfind)->Project (pn);
+		    
+		    Vec<3> n;
+		    n = GetSurface (surfind)->GetNormalVector (pn);
+		    if (inverse) n *= -1;
+		    tams.AddNormal(n);
+		  }
+	      }
+	  
+	    int vcase = 0;
+	    if (onsurface[0]) vcase++;
+	    if (onsurface[1]) vcase+=2;
+	    if (onsurface[2]) vcase+=4;
+	  
+	    static int trias[8][6] =
+	      { { 0, 0, 0,   0, 0, 0 },
+		{ 1, 6, 5,   0, 0, 0 },
+		{ 2, 4, 6,   0, 0, 0 },
+		{ 1, 2, 4,   1, 4, 5 },
+		{ 3, 5, 4,   0, 0, 0 },
+		{ 1, 6, 4,   1, 4, 3 },
+		{ 2, 3, 6,   3, 5, 6 },
+		{ 1, 2, 3,   0, 0, 0 } };
+	    static int ntrias[4] =
+	      { 0, 1, 2, 1 };
+
+	    int nvis = 0;
+	    for (int j = 0; j < 3; j++)
+	      if (onsurface[j])
+		nvis++;
+
+	    for (int j = 0; j < ntrias[nvis]; j++)
+	      {
+		TATriangle ntria(tria.SurfaceIndex(),
+				 pnums[trias[vcase][3*j]-1],
+				 pnums[trias[vcase][3*j+1]-1],
+				 pnums[trias[vcase][3*j+2]-1]);
+		//(*testout) << "pos2 " << tams.GetPoint(ntria[0]) << " - " << tams.GetPoint(ntria[1]) << " - " << tams.GetPoint(ntria[2]) << endl
+		//	   << "( " << ntria[0] << " - " << ntria[1] << " - " << ntria[2] << ")" << endl;
+		tams.AddTriangle (ntria);
+	      }
+
+	    /* saturn changes:
+
+	    int pvis[3];
+	    for (j = 0; j < 3; j++)
+	    pvis[j] = !locsol->IsStrictIn (tams.GetPoint (j+1), 1e-6) &&
+	    locsol->IsIn (tams.GetPoint (j+1), 1e-6);
+	  
+	    int newpi[3];
+	    for (j = 0; j < 3; j++)
+	    {
+	    int pi1 = j;
+	    int pi2 = (j+1) % 3;
+	    int pic = j;
+
+	    if (pvis[pi1] != pvis[pi2])
+	    {
+	    Point<3> hp = Center (tams.GetPoint (tria.PNum (pi1+1)),
+	    tams.GetPoint (tria.PNum (pi2+1)));
+
+	    newpi[j] = tams.AddPoint (hp);
+	    Vec<3> n = tams.GetNormal (pi1);
+	    tams.AddNormal (n);
+	    }
+	    else
+	    newpi[j] = 0;
+	    }
+
+	    int nvis = 0;
+	    for (j = 0; j <= nvis; j++)
+	    if (pvis[j]) nvis++;
+
+	    int si = tria.SurfaceIndex();
+	    switch (nvis)
+	    {
+	    case 0:
+	    break;
+	    case 1:
+	    {
+	    int visj;
+	    for (j = 0; j < 3; j++)
+	    if (pvis[j]) visj = j;
+	    int pivis = tria.PNum (visj+1);
+	    int pic1 = newpi[(visj+1)%3];
+	    int pic2 = newpi[(visj+2)%3];
+		
+	    cout << pivis << "," << pic1 << "," << pic2 << endl;
+		
+	    tams.AddTriangle (TATriangle (si, pivis, pic1,pic2));
+	    break;
+	    }
+	    case 2:
+	    {
+	    int nvisj;
+	    for (j = 0; j < 3; j++)
+	    if (!pvis[j]) nvisj = j;
+
+	    int pivis1 = tria.PNum ((nvisj+1)%3+1);
+	    int pivis2 = tria.PNum ((nvisj+2)%3+1);
+	    int pic1 = newpi[nvisj];
+	    int pic2 = newpi[(nvisj+2)%3];
+
+	    tams.AddTriangle (TATriangle (si, pivis1, pic1,pic2));
+	    tams.AddTriangle (TATriangle (si, pivis1, pic1,pivis2));
+	    break;
+	    }
+	    case 3:
+	    {
+	    tams.AddTriangle (tria);
+	    break;
+	    }
+	    }
+
+	    */
+	    return;
+	  }
+      }
+
+    // bisection
+    if (box.Diam() < detail)
+      {
+	//cout << "returning" << endl;
+	return;
+      }
+
+    for (int i = 0; i < 3; i++)
+      pinds[i] = tria[i];
+  
+    static int between[3][3] =
+      { { 0, 1, 5 },
+	{ 0, 2, 4 },
+	{ 1, 2, 3 } };
+  
+    for (int i = 0; i < 3; i++)
+      {
+	// int pi1 = tria[between[i][0]];
+
+	Point<3> newp = Center (tams.GetPoint (tria[between[i][0]]),
+				tams.GetPoint (tria[between[i][1]]));
+	Vec<3> n;
+	
+	GetSurface(surfind)->Project (newp);
+
+	n = GetSurface(surfind)->GetNormalVector (newp);
+      
+	pinds[between[i][2]] = tams.AddPoint (newp);
+	if (inverse) n *= -1;
+	tams.AddNormal (n);
+      }
+  
+    static int trias[4][4] =
+      { { 0, 5, 4 },
+	{ 5, 1, 3 },
+	{ 4, 3, 2 },
+	{ 3, 4, 5 } };
+ 
+    for (int i = 0; i < 4; i++)
+      {
+	TATriangle ntri(surfind,
+			pinds[trias[i][0]],
+			pinds[trias[i][1]],
+			pinds[trias[i][2]]);
+
+	// check triangle
+	BoxSphere<3> nbox;
+	nbox.Set (tams.GetPoint (ntri[0]));
+	nbox.Add (tams.GetPoint (ntri[1]));
+	nbox.Add (tams.GetPoint (ntri[2]));
+	nbox.Increase (1e-8);
+	nbox.CalcDiamCenter();
+
+	Solid * nsol = locsol -> GetReducedSolid (nbox);
+
+	if (nsol)
+	  {
+	    RefineTriangleApprox (nsol, surfind, nbox, 
+				  detail, ntri, tams, iset, level+1);
+	  
+	    delete nsol;
+	  }
+      }
+  }
+
+
+
+
+  class ClearVisitedIt : public SolidIterator
+  {
+  public:
+    ClearVisitedIt () { ; }
+    virtual ~ClearVisitedIt () { ; }
+
+    virtual void Do (Solid * sol)
+    { 
+      sol -> visited = 0;
+    }
+  };
+
+
+  void CSGeometry :: 
+  IterateAllSolids (SolidIterator & it, bool only_once) const
+  {
+    if (only_once)
+      {
+	ClearVisitedIt clit;
+	for (int i = 0; i < solids.Size(); i++)
+	  solids[i] -> IterateSolid (clit, 0);
+      }
+
+    for (int i = 0; i < solids.Size(); i++)
+      solids[i] -> IterateSolid (it, only_once);
+  }
+
+
+  double CSGeometry ::  MaxSize () const
+  {
+    double maxs, mins;
+    maxs = max3 (boundingbox.PMax()(0), 
+		 boundingbox.PMax()(1), 
+		 boundingbox.PMax()(2));
+    mins = min3 (boundingbox.PMin()(0), 
+		 boundingbox.PMin()(1), 
+		 boundingbox.PMin()(2));
+    return max2 (maxs, -mins) * 1.1;
+  }
+}
diff --git a/contrib/Netgen/libsrc/csg/csgeom.hpp b/contrib/Netgen/libsrc/csg/csgeom.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..64badd492c574701042bada3c2b222ce928fafa6
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgeom.hpp
@@ -0,0 +1,327 @@
+#ifndef FILE_CSGEOM
+#define FILE_CSGEOM
+
+/**************************************************************************/
+/* File:   csgeom.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   27. Nov. 97                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+  /**
+     Constructive Solid Geometry
+  */
+
+
+  class TriangleApproximation;
+  class TATriangle;
+
+
+  /**
+     A top level object is an entity to be meshed.
+     I can be either a solid, or one surface patch of a solid.
+  */
+  class TopLevelObject
+  {
+    Solid * solid;
+    Surface * surface;
+
+    double red, blue, green;
+    bool visible, transp;
+    double maxh;
+    string material;
+    int layer;
+    int bc;     // for surface patches, only
+    string bcname;
+
+  public:
+    TopLevelObject (Solid * asolid,
+		    Surface * asurface = NULL);
+
+    const Solid * GetSolid() const { return solid; }
+    Solid * GetSolid() { return solid; }
+
+    const Surface * GetSurface () const { return surface; }
+    Surface  * GetSurface () { return surface; }
+
+    void GetData (ostream & ost);
+    void SetData (istream & ist);
+
+    void SetMaxH (double amaxh) { maxh = amaxh; } 
+    double GetMaxH () const { return maxh; }
+
+    void SetRGB (double ared, double agreen, double ablue)
+    {
+      red = ared;
+      green = agreen;
+      blue = ablue;
+    }
+
+    double GetRed () const { return red; }
+    double GetGreen () const { return green; }
+    double GetBlue () const { return blue; }
+
+    void SetTransparent (bool atransp) 
+    { transp = atransp; }
+    bool GetTransparent () const { return transp; }
+
+    void SetVisible (bool avisible)
+    { visible = avisible; }
+    bool GetVisible () const { return visible; }
+
+    const string GetMaterial () const { return material; }
+    void SetMaterial (const string & mat) { material = mat; }
+
+    int GetLayer () const { return layer; }
+    void SetLayer (int alayer) { layer = alayer; }
+
+    void SetBCProp (int abc) { bc = abc; }
+    int GetBCProp () const { return bc; }
+
+    void SetBCName (string abc) { bcname = abc; }
+    const string GetBCName () const { return bcname; }
+  };
+
+
+
+
+
+  /**
+     CSGeometry has the whole geometric information
+  */
+  class CSGeometry : public NetgenGeometry
+  {
+  private:
+    /// all surfaces
+    SYMBOLTABLE<Surface*> surfaces;
+
+  public:
+    /// primitive of surface
+    Array<const Primitive*> surf2prim;
+
+  private:
+    Array<Surface*> delete_them;
+
+    /// all named solids
+    SYMBOLTABLE<Solid*> solids;
+
+    /// all 2d splinecurves
+    SYMBOLTABLE< SplineGeometry<2>* > splinecurves2d;
+    /// all 3d splinecurves
+    SYMBOLTABLE< SplineGeometry<3>* > splinecurves3d;
+
+    /// all top level objects: solids and surfaces
+    Array<TopLevelObject*> toplevelobjects;
+
+    /// additional points specified by user
+    Array<Point<3> > userpoints;
+    Array<double> userpoints_ref_factor;
+
+    mutable Array<Point<3> > identpoints;
+
+    /// triangular approximation of top level objects
+    Array<TriangleApproximation*> triapprox;
+
+    /// increment, if geometry is changed
+    static int changeval;
+  
+    /// bounding box of geometry
+    Box<3> boundingbox;
+
+    /// bounding box, if not set by input file
+    static Box<3> default_boundingbox;
+
+    /// identic surfaces are stored by pair of indizes, val = inverse
+    INDEX_2_HASHTABLE<int> identicsurfaces;
+    Array<int> isidenticto;
+    /// identification of boundaries (periodic, thin domains, ...)
+
+    double ideps;
+
+    /// filename of inputfile
+    string filename;
+
+  public:
+    CSGeometry ();
+    CSGeometry (const string & afilename);
+    virtual ~CSGeometry ();
+
+    void Clean ();
+
+    virtual void Save (string filename) const;
+    void Save (ostream & ost) const;
+    void Load (istream & ist);
+
+    void SaveSurfaces (ostream & out) const;
+    void LoadSurfaces (istream & in);
+
+    virtual void SaveToMeshFile (ostream & ost) const;
+
+    int GetChangeVal() { return changeval; }
+    void Change() { changeval++; }
+
+    void AddSurface (Surface * surf);
+    void AddSurface (char * name, Surface * surf);
+    void AddSurfaces (Primitive * prim);
+
+    int GetNSurf () const { return surfaces.Size(); }
+    const Surface * GetSurface (const char * name) const;
+    const Surface * GetSurface (int i) const
+    { return surfaces[i]; }
+
+    void SetSolid (const char * name, Solid * sol);
+    const Solid * GetSolid (const char * name) const;
+    const Solid * GetSolid (const string & name) const;
+    int GetNSolids () const { return solids.Size(); }
+    const Solid * GetSolid (int i) const { return solids[i]; }
+    const SYMBOLTABLE<Solid*> & GetSolids () const { return solids; }
+
+
+    void SetSplineCurve (const char * name, SplineGeometry<2> * spl);
+    void SetSplineCurve (const char * name, SplineGeometry<3> * spl);
+    const SplineGeometry<2> * GetSplineCurve2d (const string & name) const;
+    const SplineGeometry<3> * GetSplineCurve3d (const string & name) const;
+    
+
+    void SetFlags (const char * solidname, const Flags & flags);
+
+
+    int GetNTopLevelObjects () const
+    { return toplevelobjects.Size(); }
+    int SetTopLevelObject (Solid * sol, Surface * surf = NULL);
+    void GetTopLevelObject (int nr, Solid *& sol, Surface *& surf)
+    {
+      sol = toplevelobjects[nr]->GetSolid();
+      surf = toplevelobjects[nr]->GetSurface();
+    }
+    void GetTopLevelObject (int nr, const Solid *& sol, const Surface *& surf) const
+    {
+      sol = toplevelobjects[nr]->GetSolid();
+      surf = toplevelobjects[nr]->GetSurface();
+    }
+
+    TopLevelObject * GetTopLevelObject (const Solid * sol, const Surface * surf = NULL);
+    TopLevelObject * GetTopLevelObject (int nr) const
+    { return toplevelobjects[nr]; }
+    // const TopLevelObject * GetTopLevelObject (int nr) const
+    // { return toplevelobjects[nr]; }
+    void RemoveTopLevelObject (Solid * sol, Surface * surf = NULL); 
+
+
+    void AddUserPoint (const Point<3> & p, double ref_factor = 0)
+    { userpoints.Append (p); userpoints_ref_factor.Append (ref_factor); }
+    int GetNUserPoints () const
+    { return userpoints.Size(); }
+    const Point<3> & GetUserPoint (int nr) const
+    { return userpoints[nr]; }
+    double GetUserPointRefFactor (int nr) const
+    { return userpoints_ref_factor[nr]; }
+  
+    void AddIdentPoint (const Point<3> & p) const
+    { identpoints.Append(p);}
+    int GetNIdentPoints (void) const
+    { return identpoints.Size();}
+    const Point<3> & GetIdentPoint(int nr) const
+    { return identpoints[nr]; }
+    void DeleteIdentPoints(void) const
+    { identpoints.DeleteAll();}
+
+
+    // quick implementations:
+    Array<SingularFace*> singfaces;
+    Array<SingularEdge*> singedges;
+    Array<SingularPoint*> singpoints;
+    Array<Identification*> identifications;
+
+    int GetNIdentifications (void) const { return identifications.Size(); }
+    void AddIdentification (Identification * ident);
+
+
+    ///
+    void CalcTriangleApproximation(double detail, double facets);
+
+    ///
+    void FindIdenticSurfaces (double eps);
+    ///
+    void GetSurfaceIndices (const Solid * sol, 
+			    const BoxSphere<3> & box, 
+			    Array<int> & locsurf) const;
+    ///
+    void GetIndependentSurfaceIndices (const Solid * sol, 
+				       const BoxSphere<3> & box, 
+				       Array<int> & locsurf) const;
+    ///
+    void GetIndependentSurfaceIndices (const Solid * sol, 
+				       const Point<3> & p, Vec<3> & v,
+				       Array<int> & locsurf) const;
+    ///
+    void GetIndependentSurfaceIndices (Array<int> & locsurf) const;
+
+    ///
+    int GetSurfaceClassRepresentant (int si) const
+    { return isidenticto[si]; }
+
+    ///
+    const TriangleApproximation * GetTriApprox (int msnr)
+    {
+      if (msnr < triapprox.Size())
+	return triapprox[msnr];
+      return 0;
+    }
+  
+
+    void IterateAllSolids (SolidIterator & it, bool only_once = false) const;
+
+    void RefineTriangleApprox (Solid * locsol, 
+			       int surfind,
+			       const BoxSphere<3> & box, 
+			       double detail,
+			       const TATriangle & tria, 
+			       TriangleApproximation & tams,
+			       IndexSet & iset,
+			       int level);
+
+    const Box<3> & BoundingBox () const { return boundingbox; }
+
+    void SetBoundingBox (const Box<3> & abox)
+    {
+      boundingbox = abox;
+    }
+
+
+    static void SetDefaultBoundingBox (const Box<3> & abox)
+    {
+      default_boundingbox = abox;
+    }
+
+    double MaxSize () const;
+
+    void SetIdEps(double eps){ideps = eps;}
+    double GetIdEps(void) const {return ideps;}
+
+    class BCModification {
+    public:
+      int si;
+      int tlonr;
+      int bcnr;
+      string * bcname;
+    };
+
+    Array<BCModification> bcmodifications;
+
+    virtual int GenerateMesh (Mesh*& mesh, MeshingParameters & mparam, 
+			      int perfstepsstart, int perfstepsend);
+
+    virtual const Refinement & GetRefinement () const; 
+  };
+
+
+  
+
+
+}
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/csg/csgparser.cpp b/contrib/Netgen/libsrc/csg/csgparser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c2728bcdf30b763068153b91b162b09178e58773
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgparser.cpp
@@ -0,0 +1,1390 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+  static kwstruct defkw[] =
+    {
+      { TOK_RECO,    "algebraic3d" },
+      { TOK_SOLID,   "solid" },
+      { TOK_TLO,     "tlo" },
+      { TOK_CURVE2D, "curve2d" },
+      { TOK_CURVE3D, "curve3d" },
+      { TOK_BOUNDINGBOX, "boundingbox" },
+      { TOK_OR,      "or" },
+      { TOK_AND,     "and" },
+      { TOK_NOT,     "not" },
+      { TOK_SINGULAR, "singular" },
+      { TOK_EDGE,     "edge" },
+      { TOK_FACE,     "face" },
+      { TOK_POINT,    "point" },
+      { TOK_IDENTIFY, "identify" },
+      { TOK_CLOSESURFACES, "closesurfaces" },
+      { TOK_CLOSEEDGES, "closeedges" },
+      { TOK_PERIODIC,  "periodic" },
+      { TOK_BOUNDARYCONDITION, "boundarycondition" },
+      { TOK_BOUNDARYCONDITIONNAME, "boundaryconditionname" },
+      { TOK_DEFINE, "define" },
+      { TOK_CONSTANT, "constant" },
+      { TOKEN_TYPE(0), 0 }
+    };
+
+  static primstruct defprim[] =
+    {
+      { TOK_PLANE,     "plane" },
+      { TOK_SPHERE,    "sphere" },
+      { TOK_CYLINDER,  "cylinder" },
+      { TOK_CONE,      "cone" },
+      { TOK_ELLIPTICCYLINDER, "ellipticcylinder" },
+      { TOK_ELLIPSOID, "ellipsoid" },
+      { TOK_ORTHOBRICK, "orthobrick" },
+      { TOK_POLYHEDRON, "polyhedron" },
+      { TOK_TORUS,      "torus" },
+
+      { TOK_TUBE,      "tube" },
+      { TOK_GENCYL,    "gencyl" },
+      { TOK_EXTRUSION,  "extrusion" },
+      { TOK_REVOLUTION, "revolution" },
+
+      { TOK_TRANSLATE, "translate" },
+      { TOK_MULTITRANSLATE, "multitranslate" },
+      { TOK_ROTATE,   "rotate" },
+      { TOK_MULTIROTATE, "multirotate" },
+      { PRIMITIVE_TYPE(0), 0 }
+    };
+
+  static CSGeometry * geom;
+
+
+  CSGScanner :: CSGScanner (istream & ascanin)
+  {
+    scanin = &ascanin;
+    token = TOK_END;
+    num_value = 0;
+    linenum = 1;
+  }
+
+
+  void CSGScanner :: ReadNext ()
+  {
+    char ch;
+  
+
+    // scan whitespaces
+    do
+      { 
+	scanin->get(ch);
+
+	//if (ch == '\n') 
+	//  linenum++;
+
+	// end of file reached
+	if (scanin->eof())
+	  {
+	    token = TOK_END;
+	    return;
+	  }
+	if (ch == '\n') 
+	  linenum++;
+
+
+	// skip comment line
+	if (ch == '#')
+	  {
+	    while (ch != '\n')
+	      {
+		scanin->get(ch);
+		if (scanin->eof())
+		  {
+		    token = TOK_END;
+		    return;
+		  }
+	      }
+	    linenum++;
+	  }	
+      }
+    while (isspace(ch));
+  
+    switch (ch)
+      {
+      case '(': case ')': 
+      case '[': case ']': 
+      case '-':
+      case '=': case ',': case ';':
+	{
+	  token = TOKEN_TYPE (ch);
+	  break;
+	}
+  
+      default:
+	{
+	  if (isdigit (ch) || ch == '.')
+	    {
+	      scanin->putback (ch);
+	      (*scanin) >> num_value;
+	      token = TOK_NUM;
+	      return;
+	    }
+
+	  if (isalpha (ch))
+	    {
+	      string_value = string (1, ch);
+	      scanin->get(ch);
+	      while (isalnum(ch) || ch == '_')
+		{
+		  string_value += ch;
+		  scanin->get(ch);
+		}
+	      scanin->putback (ch);
+	    }
+
+	  int nr = 0;
+	  while (defkw[nr].kw)
+	    {
+	      if (string_value == defkw[nr].name)
+		{
+		  token = defkw[nr].kw;
+		  return;
+		}
+	      nr++;
+	    }
+
+	  nr = 0;
+	  while (defprim[nr].kw)
+	    {
+	      if (string_value == defprim[nr].name)
+		{
+		  token = TOK_PRIMITIVE;
+		  prim_token = defprim[nr].kw;
+		  return;
+		}
+	      nr++;
+	    }
+
+	  token = TOK_STRING;
+	}
+      }
+  }
+
+  void CSGScanner :: Error (const string & err)
+  {
+    stringstream errstr;
+    errstr << "Parsing error in line " << linenum << ": " << endl << err << endl;
+    throw string(errstr.str());
+  }
+
+
+  /*
+    Solid = Term { OR Term }
+    Term  = Primary { AND Primary }
+    Primary = PRIM | IDENT | ( Solid ) | NOT Primary
+  */
+
+  void ParseChar (CSGScanner & scan, char ch)
+  {
+    if (scan.GetToken() != TOKEN_TYPE(ch)) 
+      scan.Error (string ("token '") + string(1, ch) + string("' expected"));
+    scan.ReadNext();
+  }
+  
+  double ParseNumber(CSGScanner & scan)
+  {
+    if (scan.GetToken() == '-')
+      {
+	scan.ReadNext();
+	return -ParseNumber (scan);
+      }
+    if (scan.GetToken() != TOK_NUM) scan.Error ("number expected");
+    double val = scan.GetNumValue();
+    scan.ReadNext();
+    return val;
+  }
+
+  Vec<3> ParseVector (CSGScanner & scan)
+  {
+    Vec<3> v;
+    v(0) = ParseNumber (scan);
+    ParseChar (scan, ',');
+    v(1) = ParseNumber (scan);
+    ParseChar (scan, ',');
+    v(2) = ParseNumber (scan);
+    return v;
+  }
+
+
+  CSGScanner & operator>> (CSGScanner & scan, char ch)
+  {
+    if (scan.GetToken() != TOKEN_TYPE(ch)) 
+      scan.Error (string ("token '") + string(1, ch) + string("' expected"));
+    scan.ReadNext();
+    return scan;
+  }
+
+  CSGScanner & operator>> (CSGScanner & scan, double & d)
+  {
+    d = ParseNumber (scan);
+    return scan;
+  }
+
+  CSGScanner & operator>> (CSGScanner & scan, int & i)
+  {
+    i = int (ParseNumber (scan));
+    return scan;
+  }
+
+  CSGScanner & operator>> (CSGScanner & scan, Point<3> & p)
+  {
+    scan >> p(0) >> ',' >> p(1) >> ',' >> p(2);
+    return scan;
+  }
+
+  CSGScanner & operator>> (CSGScanner & scan, Vec<3> & v)
+  {
+    scan >> v(0) >> ',' >> v(1) >> ',' >> v(2);
+    return scan;
+  }
+
+
+  Solid * ParseSolid (CSGScanner & scan);
+  Solid * ParseTerm (CSGScanner & scan);
+  Solid * ParsePrimary (CSGScanner & scan);
+ 
+
+  Solid * ParsePrimary (CSGScanner & scan)
+  {
+    if (scan.GetToken() == TOK_PRIMITIVE)
+      {
+	switch (scan.GetPrimitiveToken())
+	  {
+	  case TOK_PLANE:
+	    {
+	      Point<3> p;
+	      Vec<3> v;
+	      
+	      scan.ReadNext();
+	      scan >> '(' >> p >> ';' >> v >> ')';
+
+	      OneSurfacePrimitive * surf = new Plane ( p, v );
+	      geom->AddSurfaces (surf);
+	      return new Solid (surf);
+	    }
+
+	  case TOK_CYLINDER:
+	    {
+	      Point<3> pa, pb;
+	      double r;
+	      
+	      scan.ReadNext();
+	      scan >> '(' >> pa >> ';' >> pb >> ';' >> r >> ')';
+
+	      OneSurfacePrimitive * surf = new Cylinder ( pa, pb, r );
+	      geom->AddSurfaces (surf);
+	      return new Solid (surf);
+	    }
+
+	  case TOK_ELLIPTICCYLINDER:
+	    {
+	      Point<3> pa;
+	      Vec<3> vl, vs;
+	      
+	      scan.ReadNext();
+	      scan >> '(' >> pa >> ';' >> vl >> ';' >> vs >> ')';
+
+	      OneSurfacePrimitive * surf = new EllipticCylinder ( pa, vl, vs);
+	      geom->AddSurfaces (surf);
+	      return new Solid (surf);
+	    }
+
+
+	  case TOK_ELLIPSOID:
+	    {
+	      Point<3> pa;
+	      Vec<3> v1, v2, v3;
+	      
+	      scan.ReadNext();
+	      scan >> '(' >> pa >> ';' >> v1 >> ';' >> v2 >> ';' >> v3 >> ')';
+
+	      OneSurfacePrimitive * surf = new Ellipsoid ( pa, v1, v2, v3);
+	      geom->AddSurfaces (surf);
+	      return new Solid (surf);
+	    }
+
+
+	  case TOK_CONE:
+	    {
+	      Point<3> pa, pb;
+	      double ra, rb;
+	      
+	      scan.ReadNext();
+	      scan >> '(' >> pa >> ';' >> ra >> ';' >> pb >> ';' >> rb >> ')';
+
+	      OneSurfacePrimitive * surf = new Cone ( pa, pb, ra, rb );
+	      geom->AddSurfaces (surf);
+	      return new Solid (surf);
+	    }
+
+
+
+	  case TOK_SPHERE:
+	    {
+	      Point<3> p;
+	      double r;
+	      
+	      scan.ReadNext();
+	      scan >> '(' >> p >> ';' >> r >> ')';
+
+	      OneSurfacePrimitive * surf = new Sphere ( p, r );
+	      geom->AddSurfaces (surf);
+	      return new Solid (surf);
+	    }
+
+	  case TOK_ORTHOBRICK:
+	    {
+	      Point<3> pa, pb;
+	      
+	      scan.ReadNext();
+	      scan >> '(' >> pa >> ';' >> pb >> ')';
+	      
+
+	      Primitive * nprim = new OrthoBrick (pa, pb);
+	      geom->AddSurfaces (nprim);
+	      return new Solid (nprim);
+	    } 
+
+	  case TOK_POLYHEDRON:
+	    {
+	      // Added by Dalibor Lukas, October 15, 2003
+
+	      Point<3> p;
+	      //int pi1, pi2, pi3, pi4;
+	      
+	      scan.ReadNext();
+	      ParseChar (scan, '(');
+	      
+	      Polyhedra * polyhedron = new Polyhedra;
+
+	      // scanning the points
+	      while (1)
+		{
+		  p = Point<3> (ParseVector (scan));
+		  ParseChar (scan, ';');
+
+		  polyhedron->AddPoint(p);
+
+		  if (scan.GetToken() == ';')
+		    {
+		      scan.ReadNext();
+		      break;
+		    }
+		}
+
+	      // scanning the faces
+	      int inputface = 0;
+	      while (1)
+		{
+		  Array<int> pnums,cleaned_pnums;
+		  for(int i=0; i<3; i++)
+		    {
+		      pnums.Append((int) (ParseNumber (scan)));
+		      if(i<2) 
+			ParseChar (scan, ',');
+		    }
+
+		  if (scan.GetToken() == TOK_COMMA)
+		    {
+		      ParseChar (scan, ',');
+		      pnums.Append((int) (ParseNumber (scan)));	
+		    }
+
+		  for(int i=0; i<pnums.Size(); i++)
+		    if(!cleaned_pnums.Contains(pnums[i]))
+		      cleaned_pnums.Append(pnums[i]);
+
+		  if(cleaned_pnums.Size() == 3)
+		    {
+		      polyhedron->AddFace(cleaned_pnums[0]-1,
+					  cleaned_pnums[1]-1,
+					  cleaned_pnums[2]-1,
+					  inputface);
+		    }
+		  else if(cleaned_pnums.Size() == 4)
+		    {
+		      polyhedron->AddFace(cleaned_pnums[0]-1,
+					  cleaned_pnums[1]-1,
+					  cleaned_pnums[2]-1,
+					  inputface);
+		      polyhedron->AddFace(cleaned_pnums[0]-1,
+					  cleaned_pnums[2]-1,
+					  cleaned_pnums[3]-1,
+					  inputface);
+		    }
+		  else
+		    {
+		      ostringstream msg;
+		      msg << "Something wrong with polyhedron face:";
+		      for(int i=0; i<pnums.Size(); i++)
+			msg << " " << pnums[i];
+		      throw NgException(msg.str());
+		    }
+		  
+		      
+
+		  if (scan.GetToken() == ')')
+		    {
+		      scan.ReadNext();
+		      break;
+		    }
+		  scan.ReadNext();
+		  inputface++;
+		}
+
+	      geom->AddSurfaces (polyhedron);
+	      return new Solid (polyhedron);
+	    }
+
+
+	  case TOK_REVOLUTION:
+	    {
+	      Point<3> p0,p1;
+
+	      scan.ReadNext();
+	      scan >> '(' >> p0 >> ';' >> p1 >> ';';
+
+	      string spline = scan.GetStringValue();
+	      
+	      scan.ReadNext();
+	      scan >> ')';
+	      
+	      if(!geom->GetSplineCurve2d(spline))
+		{
+		  scan.Error ( string("2D Spline curve not found: ") + spline );
+		  break;
+		}
+
+	      Primitive * nprim = new Revolution(p0,p1,
+						 *(geom->GetSplineCurve2d(spline)));
+
+	      geom->AddSurfaces (nprim);
+	      return new Solid(nprim);
+	    }
+
+
+	  case TOK_EXTRUSION: 
+	    {   
+	      scan.ReadNext();
+	      scan >> '(';
+	      string epath = scan.GetStringValue();
+	      scan.ReadNext();
+	      scan >> ';';
+	      string profile = scan.GetStringValue();
+
+	      
+	      scan.ReadNext();
+	      Vec<3> z_dir;
+	      scan >> ';' >> z_dir(0) >> ',' >> z_dir(1) >> ',' >> z_dir(2) >> ')';
+	      
+	      if(!geom->GetSplineCurve2d(profile))
+		{
+		  scan.Error ( string("2D Spline curve not found: ") + profile );
+		  break;
+		}
+	      if(!geom->GetSplineCurve3d(epath))
+		{
+		  scan.Error ( string("2D Spline curve not found: ") + epath );
+		  break;
+		}
+	      
+	      Primitive * nprim = new Extrusion(*(geom->GetSplineCurve3d(epath)),
+						*(geom->GetSplineCurve2d(profile)),
+						z_dir);
+	      geom->AddSurfaces (nprim);
+	      return new Solid(nprim);
+	    }
+
+
+	  /// Torus 
+    	  /// Lorenzo Codecasa (codecasa@elet.polimi.it)
+    	  /// April 27th, 2005 
+	  ///
+	  /// begin...
+	  case TOK_TORUS:
+	    {     
+	      Point<3> pc; 
+	      Vec<3> vn;
+	      double R, r;
+	      
+	      scan.ReadNext();
+	      scan >> '(' >> pc >> ';' >> vn >> ';' >> R >> ';' >> r >> ')';
+
+	      OneSurfacePrimitive * surf = new Torus ( pc, vn, R, r );
+	      geom->AddSurfaces (surf);
+	      return new Solid (surf);
+	    }
+	  /// ..end
+
+
+
+
+	  case TOK_TRANSLATE: 
+	    {
+	      Vec<3> v;
+	      scan.ReadNext();
+
+	      ParseChar (scan, '(');
+	      v = ParseVector (scan);
+	      ParseChar (scan, ';');
+	      
+	      Solid * sol1 = ParseSolid (scan);
+
+	      ParseChar (scan, ')');
+
+	      Solid * nsol = sol1 -> Copy(*geom);
+	      Transformation<3> trans(v);
+	      nsol -> Transform (trans);
+	      return nsol;
+	    }
+
+
+	  case TOK_ROTATE: 
+	    {
+	      Point<3> c;
+	      Vec<3> v;
+	      scan.ReadNext();
+
+	      scan >> '(' >> c >> ';' >> v >> ';';
+
+	      Solid * sol1 = ParseSolid (scan);
+
+	      ParseChar (scan, ')');
+
+	      Solid * nsol = sol1 -> Copy(*geom);
+	      Transformation<3> trans(c,v(0),v(1),v(2));
+	      nsol -> Transform (trans);
+	      return nsol;
+	    }
+
+
+	  case TOK_MULTITRANSLATE: 
+	    {
+	      Vec<3> v;
+	      int n;
+	      
+	      scan.ReadNext();
+
+	      scan >> '(' >> v >> ';' >> n >> ';';
+
+	      Solid * sol1 = ParseSolid (scan);
+	      
+	      scan >> ')';
+	      
+	      Solid * hsol = sol1;
+	      for (int i = 1; i <= n; i++)
+		{
+		  Solid * nsol = sol1 -> Copy(*geom);
+		  Transformation<3> trans(double(i) * v);
+		  
+		  nsol -> Transform (trans);
+		  hsol = new Solid (Solid::UNION, hsol, nsol); 
+		}
+	      return hsol;
+	    }
+
+
+	  case TOK_MULTIROTATE: 
+	    {
+	      Point<3> c;
+	      Vec<3> v;
+	      int n;
+	      
+	      scan.ReadNext();
+
+	      scan >> '(' >> c >> ';' >> v >> ';' >> n >> ';';
+	      Solid * sol1 = ParseSolid (scan);
+	      scan >> ')';
+
+	      Transformation<3> trans(c, v(0), v(1), v(2));
+	      Transformation<3> multi(Vec<3>(0,0,0));
+	      Transformation<3> ht;
+
+	      Solid * hsol = sol1;
+	      for (int i = 1; i <= n; i++)
+		{
+		  Solid * nsol = sol1 -> Copy(*geom);
+
+		  nsol -> Transform (multi);
+		  hsol = new Solid (Solid::UNION, hsol, nsol); 
+
+		  ht=multi;
+		  multi.Combine (trans, ht);
+		}
+	      return hsol;
+	    }
+
+
+	  default:
+	    {
+	      scan.Error (string ("unknown primary ") + scan.GetStringValue());
+	    }
+
+	  }
+      }
+
+    else if (scan.GetToken() == TOK_STRING &&
+	     geom->GetSolid(scan.GetStringValue()))
+
+      {
+	Solid * sol = const_cast<Solid*> (geom->GetSolid(scan.GetStringValue()));
+	scan.ReadNext();
+	return sol;
+      }
+
+    else if (scan.GetToken() == TOK_NOT)
+
+      {
+	scan.ReadNext();
+	Solid * sol1 = ParsePrimary (scan);
+	return new Solid (Solid::SUB, sol1);
+      }
+
+    else if (scan.GetToken() == '(')
+
+      {
+	scan.ReadNext();
+	Solid * sol1 = ParseSolid (scan);
+	scan.ReadNext();
+	return sol1;
+      }
+
+    scan.Error (string ("not a primary, name = ")+
+		scan.GetStringValue());
+    return 0;
+  }
+
+
+
+  Solid * ParseTerm (CSGScanner & scan)
+  {
+    Solid * sol = ParsePrimary(scan);
+    while (scan.GetToken() == TOK_AND)
+      {
+	scan.ReadNext();
+	Solid * sol2 = ParsePrimary(scan);
+	sol = new Solid (Solid::SECTION, sol, sol2);
+      }
+    return sol;
+  }
+
+
+  Solid * ParseSolid (CSGScanner & scan)
+  {
+    Solid * sol = ParseTerm(scan);
+    while (scan.GetToken() == TOK_OR)
+      {
+	scan.ReadNext();
+	Solid * sol2 = ParseTerm(scan);
+	sol = new Solid (Solid::UNION, sol, sol2);
+      }
+    return sol;
+  }
+
+
+  template <int D>
+  void LoadSpline (SplineGeometry<D> & spline, CSGScanner & scan)
+  {
+    double hd;
+    Point<D> x;
+    int nump, numseg;
+    
+    //scan.ReadNext();
+    scan >> nump >> ';';
+    
+    hd = 1;
+    spline.geompoints.SetSize(nump);
+    for(int i = 0; i<nump; i++)
+      {
+	if(D==2)
+	  scan >> x(0) >> ',' >> x(1) >> ';';
+	else if(D==3)
+	  scan >> x(0) >> ',' >> x(1) >> ',' >> x(2) >> ';';
+	
+	spline.geompoints[i] = GeomPoint<D>(x,hd);
+      }
+    
+    scan >> numseg;// >> ';';
+
+    spline.splines.SetSize(numseg);
+
+  int pnums,pnum1,pnum2,pnum3;
+    
+
+  for(int i = 0; i<numseg; i++)
+    {
+      scan >> ';' >> pnums >> ',';
+      if (pnums == 2)
+	{
+	  scan >> pnum1 >> ',' >> pnum2;// >> ';';
+	  spline.splines[i] = new LineSeg<D>(spline.geompoints[pnum1-1],
+					     spline.geompoints[pnum2-1]);
+	}
+      else if (pnums == 3)
+	{
+	  scan >> pnum1 >> ',' >> pnum2 >> ',' 
+	       >> pnum3;// >> ';';
+	  spline.splines[i] = new SplineSeg3<D>(spline.geompoints[pnum1-1],
+						spline.geompoints[pnum2-1],
+						spline.geompoints[pnum3-1]);
+	}
+      else if (pnums == 4)
+	{
+	  scan >> pnum1 >> ',' >> pnum2 >> ',' 
+	       >> pnum3;// >> ';';
+	  spline.splines[i] = new CircleSeg<D>(spline.geompoints[pnum1-1],
+					       spline.geompoints[pnum2-1],
+					       spline.geompoints[pnum3-1]);
+	}
+    }
+  }
+
+
+
+
+  void ParseFlags (CSGScanner & scan, Flags & flags)
+  {
+    while (scan.GetToken() == '-')
+      {
+	scan.ReadNext();
+	string name = scan.GetStringValue();
+	scan.ReadNext();
+	if (scan.GetToken() == '=')
+	  {
+	    scan.ReadNext();
+	    if (scan.GetToken() == TOK_STRING)
+	      {
+		flags.SetFlag (name.c_str(), scan.GetStringValue().c_str());
+		scan.ReadNext();
+	      }
+	    else if (scan.GetToken() == '[')
+	      {
+		scan.ReadNext();
+
+		if(scan.GetToken() == '-' || scan.GetToken() == TOK_NUM)
+		  {
+		    Array<double> vals;
+		    vals.Append (ParseNumber(scan));
+		    while (scan.GetToken() == ',')
+		      {
+			scan.ReadNext();
+			vals.Append (ParseNumber(scan));
+		      }
+		    ParseChar (scan, ']');
+		    flags.SetFlag (name.c_str(), vals);
+		  }
+		else
+		  { // string list
+		    Array<char*> vals;
+		    string val = scan.GetStringValue();
+		    vals.Append(new char[val.size()+1]);
+		    strcpy(vals.Last(),val.c_str());
+		    scan.ReadNext();
+
+		    while (scan.GetToken() == ',')
+		      {
+			scan.ReadNext();
+			val = scan.GetStringValue();
+			vals.Append(new char[val.size()+1]);
+			strcpy(vals.Last(),val.c_str());
+			scan.ReadNext();
+		      }
+		    ParseChar (scan, ']');
+		    flags.SetFlag (name.c_str(), vals);
+		    for(int i=0; i<vals.Size(); i++)
+		      delete [] vals[i];
+		  }
+	      }
+	    else if (scan.GetToken() == TOK_NUM)
+	      {
+		flags.SetFlag (name.c_str(), scan.GetNumValue());
+		scan.ReadNext();
+	      }
+	  }     
+	else
+	  {
+	    flags.SetFlag (name.c_str());
+	  }
+      }
+  }
+
+
+  /*
+    Main parsing function for CSG geometry
+  */
+  CSGeometry * ParseCSG (istream & istr)
+  {
+    CSGScanner scan(istr);
+    
+    geom = new CSGeometry;
+
+    scan.ReadNext();
+    if (scan.GetToken() != TOK_RECO)  // keyword 'algebraic3d'
+      return 0;
+
+    scan.ReadNext();
+
+    try
+      {
+	while (1)
+	  {
+	    if (scan.GetToken() == TOK_END) break;
+	    
+	    if (scan.GetToken() == TOK_SOLID)
+	      {
+		scan.ReadNext();
+		if (scan.GetToken() != TOK_STRING)
+		  scan.Error ("name identifier expected");
+		string solidname = scan.GetStringValue();
+
+		scan.ReadNext();
+
+		ParseChar (scan, '=');
+		Solid * solid = ParseSolid (scan);
+
+		Flags flags;
+		ParseFlags (scan, flags);
+
+		geom->SetSolid (solidname.c_str(), new Solid (Solid::ROOT, solid)); 
+		geom->SetFlags (solidname.c_str(), flags); 
+		
+		ParseChar (scan, ';');
+		
+		PrintMessage (4, "define solid ", solidname);
+	      }
+
+	    else if (scan.GetToken() == TOK_TLO)
+
+	      { // a TopLevelObject definition
+
+		scan.ReadNext();
+		
+		string name = scan.GetStringValue();
+		scan.ReadNext();
+
+		if (scan.GetToken() != TOK_STRING)
+
+		  { // a solid TLO
+
+		    Flags flags;
+		    ParseFlags (scan, flags);
+		    
+		    ParseChar (scan, ';');
+		    if (!geom->GetSolid (name))
+		      scan.Error ("Top-Level-Object "+name+" not defined");
+
+		    int tlonr = 
+		      geom->SetTopLevelObject ((Solid*)geom->GetSolid(name));
+		    TopLevelObject * tlo = geom->GetTopLevelObject (tlonr);
+
+		    if (flags.NumListFlagDefined ("col"))
+		      {
+			const Array<double> & col =
+			  flags.GetNumListFlag ("col");
+			tlo->SetRGB (col[0], col[1], col[2]);
+		      }
+
+		    if (flags.GetDefineFlag ("transparent"))
+		      tlo->SetTransparent (1);
+
+		    tlo->SetMaterial (flags.GetStringFlag ("material", ""));
+		    tlo->SetLayer (int(flags.GetNumFlag ("layer", 1)));
+		    if (flags.NumFlagDefined ("maxh"))
+		      tlo->SetMaxH (flags.GetNumFlag("maxh", 1e10));
+		  }
+
+		else
+		  
+		  { // a surface TLO
+
+		    string surfname = scan.GetStringValue();
+		    scan.ReadNext();
+
+		    Flags flags;
+		    ParseFlags (scan, flags);
+		    
+		    ParseChar (scan, ';');
+
+		    Array<int> si;
+		    geom->GetSolid(surfname)->GetSurfaceIndices(si);
+		    int tlonr = 
+		      geom->SetTopLevelObject ((Solid*)geom->GetSolid(name),
+					       (Surface*)geom->GetSurface(si.Get(1)));
+		    TopLevelObject * tlo = geom->GetTopLevelObject (tlonr);
+		    if (flags.NumListFlagDefined ("col"))
+		      {
+			const Array<double> & col = flags.GetNumListFlag ("col");
+			tlo->SetRGB (col.Get(1), col.Get(2), col.Get(3));
+		      }
+		    if (flags.GetDefineFlag ("transparent"))
+		      tlo->SetTransparent (1);
+
+		    if (flags.NumFlagDefined ("maxh"))
+		      tlo->SetMaxH (flags.GetNumFlag("maxh", 1e10));
+		    tlo->SetLayer (int(flags.GetNumFlag ("layer", 1)));
+		    tlo->SetBCProp (int(flags.GetNumFlag ("bc", -1)));
+		    if ( flags.StringFlagDefined("bcname") )
+		      tlo->SetBCName ( flags.GetStringFlag ("bcname", "default") );
+		  }
+	      }
+	    
+	    else if (scan.GetToken() == TOK_IDENTIFY)
+
+	      {
+		
+		scan.ReadNext();
+		switch (scan.GetToken())
+		  {
+		  case TOK_CLOSESURFACES:
+		    {
+		      scan.ReadNext();
+		      
+		      string name1 = scan.GetStringValue();
+		      scan.ReadNext();
+		      
+		      string name2 = scan.GetStringValue();
+		      scan.ReadNext();
+
+		      Flags flags;
+		      ParseFlags (scan, flags);
+		      
+		      ParseChar (scan, ';');
+		      
+		      
+		      Array<int> si1, si2;
+		      geom->GetSolid(name1)->GetSurfaceIndices(si1);
+		      geom->GetSolid(name2)->GetSurfaceIndices(si2);
+
+		      const TopLevelObject * domain = 0;
+		      if (flags.StringFlagDefined ("tlo"))
+			{
+			  domain = 
+			    geom->GetTopLevelObject (geom->GetSolid(flags.GetStringFlag ("tlo","")));
+			  if (!domain) 
+			    scan.Error ("identification needs undefined tlo");
+			}
+
+		      geom->AddIdentification 
+			(new CloseSurfaceIdentification 
+			 (geom->GetNIdentifications()+1, *geom, 
+			  geom->GetSurface (si1[0]), geom->GetSurface (si2[0]),
+			  domain,
+			  flags));
+
+		      break;
+		    }
+		    
+		  case TOK_PERIODIC:
+		    {
+		      scan.ReadNext();
+		      
+		      string name1 = scan.GetStringValue();
+		      scan.ReadNext();
+
+		      string name2 = scan.GetStringValue();
+		      scan.ReadNext();
+
+		      ParseChar (scan, ';');
+
+		      
+		      Array<int> si1, si2;
+		      geom->GetSolid(name1)->GetSurfaceIndices(si1);
+		      geom->GetSolid(name2)->GetSurfaceIndices(si2);
+		      
+		      geom->AddIdentification 
+			(new PeriodicIdentification 
+			 (geom->GetNIdentifications()+1,
+			  *geom,
+			  geom->GetSurface (si1.Get(1)),
+			  geom->GetSurface (si2.Get(1))));
+		      break;
+		    }
+
+		  default:
+		    scan.Error ("keyword 'closesurfaces' or 'periodic' expected");
+		  }
+		
+	      }
+
+	    else if (scan.GetToken() == TOK_SINGULAR)
+
+	      {
+		
+		scan.ReadNext();
+		switch (scan.GetToken())
+		  {
+		  case TOK_FACE:
+		    {
+		      scan.ReadNext();
+		      
+		      string name1 = scan.GetStringValue();  // tlo
+		      scan.ReadNext();
+		      
+		      string name2 = scan.GetStringValue();
+		      scan.ReadNext();
+		      
+		      Flags flags;
+		      ParseFlags (scan, flags);
+		      int factor = int(flags.GetNumFlag("factor",1)); 
+		      // cout << "Singular Face with factor " << factor << endl; 
+		      PrintMessageCR (3, "Singular Face  with factor ", factor);
+
+		      ParseChar (scan, ';');
+		      
+		      const Solid * sol = geom->GetSolid(name2);
+
+		      if(!sol)
+			scan.Error ("unknown solid in singular face definition");
+		      else
+			for (int i = 0; i < geom->GetNTopLevelObjects(); i++)
+			  if (name1 == geom->GetTopLevelObject (i)->GetSolid()->Name())
+			    geom->singfaces.Append (new SingularFace (i+1, sol,factor));
+
+		      break;
+		    }
+
+		  case TOK_EDGE:
+		    {
+		      scan.ReadNext();
+		      
+		      string name1 = scan.GetStringValue();
+		      scan.ReadNext();
+		      
+		      string name2 = scan.GetStringValue();
+		      scan.ReadNext();
+		      
+		      Flags flags;
+		      ParseFlags (scan, flags);
+		      int factor = int(flags.GetNumFlag("factor",1));
+		      double maxhinit = flags.GetNumFlag("maxh",-1);
+		      ParseChar (scan, ';');
+		      
+		      const Solid * s1 = geom->GetSolid(name1);
+		      const Solid * s2 = geom->GetSolid(name2);
+		      PrintMessageCR (3, "Singular Edge  with factor ", factor);
+
+		      int domnr = -1;
+		      if (flags.StringFlagDefined ("tlo"))
+			{
+			  const Solid * sol =
+			    geom->GetSolid(flags.GetStringFlag ("tlo",""));
+			  
+			  for (int i = 0; i < geom->GetNTopLevelObjects(); i++)
+			    if (geom->GetTopLevelObject(i)->GetSolid() == sol)
+			      domnr = i;
+			  
+			  // cout << "domnr = " << domnr;
+			}
+
+		      if(!s1 || !s2)
+			scan.Error ("unknown solid ins singular edge definition");
+		      else
+			geom->singedges.Append (new SingularEdge (1, domnr, 
+								  *geom, s1, s2, factor,
+								  maxhinit));
+		      break;
+		    }
+
+		  case TOK_POINT:
+		    {
+		      scan.ReadNext();
+		      
+		      string name1 = scan.GetStringValue();
+		      scan.ReadNext();
+		      string name2 = scan.GetStringValue();
+		      scan.ReadNext();
+		      string name3 = scan.GetStringValue();
+		      scan.ReadNext();
+		      
+		      Flags flags;
+		      ParseFlags (scan, flags);
+		      int factor = int(flags.GetNumFlag("factor",1)); 
+		      ParseChar (scan, ';');
+		      
+		      const Solid * s1 = geom->GetSolid(name1);
+		      const Solid * s2 = geom->GetSolid(name2);
+		      const Solid * s3 = geom->GetSolid(name3);
+		      // cout << "Singular Point with factor " << factor << endl; 
+		      PrintMessageCR (3, "Singular Point  with factor ", factor);
+		      geom->singpoints.Append (new SingularPoint (1, s1, s2, s3, factor));
+		      break;
+		    }
+		  default:
+		    scan.Error ("keyword 'face' or 'edge' or 'point' expected");
+		  }
+	      }
+
+	    
+	    else if (scan.GetToken() == TOK_POINT)
+	      {
+		Point<3> p;
+
+		scan.ReadNext();
+		ParseChar (scan, '(');
+		p = Point<3> (ParseVector (scan));
+		ParseChar (scan, ')');
+
+
+		Flags flags;
+		ParseFlags (scan, flags);
+		int factor = int(flags.GetNumFlag("factor",0)); 
+
+		ParseChar (scan, ';');
+
+		geom->AddUserPoint (p, factor);
+	      }
+
+	    else if (scan.GetToken() == TOK_BOUNDINGBOX)
+	      {
+		Point<3> p1, p2;
+		
+		scan.ReadNext();
+		ParseChar (scan, '(');
+		p1 = Point<3> (ParseVector (scan));
+		ParseChar (scan, ';');
+		p2 = Point<3> (ParseVector (scan));
+		ParseChar (scan, ')');
+		ParseChar (scan, ';');
+
+		geom->SetBoundingBox (Box<3> (p1, p2));
+	      }
+
+	    else if (scan.GetToken() == TOK_CURVE2D)
+	      {
+		scan.ReadNext();
+
+		
+		if (scan.GetToken() != TOK_STRING)
+		  scan.Error ("name identifier expected");
+		string curvename = scan.GetStringValue();
+
+		scan.ReadNext();
+
+		ParseChar (scan, '=');
+		ParseChar (scan, '(');
+		
+		SplineGeometry<2> * newspline = new SplineGeometry<2>;
+		// newspline->CSGLoad(scan);
+		LoadSpline (*newspline, scan);
+
+		ParseChar (scan, ')');
+		ParseChar (scan, ';');
+
+		geom->SetSplineCurve(curvename.c_str(),newspline);
+
+		PrintMessage (4, "define 2d curve ", curvename);
+	      }
+
+	    else if (scan.GetToken() == TOK_CURVE3D)
+	      {
+		scan.ReadNext();
+
+		
+		if (scan.GetToken() != TOK_STRING)
+		  scan.Error ("name identifier expected");
+		string curvename = scan.GetStringValue();
+
+		scan.ReadNext();
+
+		ParseChar (scan, '=');
+		ParseChar (scan, '(');
+		
+		SplineGeometry<3> * newspline = new SplineGeometry<3>;
+		// newspline->CSGLoad(scan);
+		LoadSpline (*newspline, scan);
+
+		ParseChar (scan, ')');
+		ParseChar (scan, ';');
+
+		geom->SetSplineCurve(curvename.c_str(),newspline);
+
+		PrintMessage (4, "define 3d curve ", curvename);
+	      }
+
+	    else if (scan.GetToken() == TOK_BOUNDARYCONDITION)
+	      {
+		scan.ReadNext();
+		
+		string name1 = scan.GetStringValue();
+		scan.ReadNext();
+		
+		string name2 = scan.GetStringValue();
+		scan.ReadNext();
+		
+		int num = int (ParseNumber (scan));
+		ParseChar (scan, ';');
+
+
+		CSGeometry::BCModification bcm;
+		bcm.bcname = NULL;
+		Array<int> si;
+		
+		geom->GetSolid(name1)->GetSurfaceIndices(si);
+		if(si.Size() == 0)
+		  {
+		    string errstring = "solid \""; errstring += name1; errstring += "\" has no surfaces";
+		    scan.Error (errstring);
+		  }
+	
+		bcm.tlonr = -1;
+		int i;	
+		for (i = 0; i < geom->GetNTopLevelObjects(); i++)
+		  if (string (geom->GetTopLevelObject(i)->GetSolid()->Name())
+		      == name2)
+		    {
+		      bcm.tlonr = i;
+		      break;
+		    }
+		if(bcm.tlonr == -1)
+		  {
+		    string errstring = "tlo \""; errstring += name2; errstring += "\" not found";
+		    scan.Error(errstring);
+		  }
+		
+		
+		bcm.bcnr = num;
+		for (i = 0; i < si.Size(); i++)
+		  {
+		    bcm.si = si[i];
+		    geom->bcmodifications.Append (bcm);
+		  }
+	      }
+	    
+	    else if (scan.GetToken() == TOK_BOUNDARYCONDITIONNAME)
+	      {
+		scan.ReadNext();
+		
+		string name1 = scan.GetStringValue();
+		scan.ReadNext();
+		
+		string name2 = scan.GetStringValue();
+		scan.ReadNext();
+
+		string bcname = scan.GetStringValue();
+		scan.ReadNext();
+		ParseChar(scan, ';');
+		
+
+		CSGeometry::BCModification bcm;
+		bcm.bcname = NULL;
+
+
+		Array<int> si;
+		
+		geom->GetSolid(name1)->GetSurfaceIndices(si);
+		if(si.Size() == 0)
+		  {
+		    string errstring = "solid \""; errstring += name1; errstring += "\" has no surfaces";
+		    scan.Error (errstring);
+		  }
+	
+		bcm.tlonr = -1;
+		int i;	
+		for (i = 0; i < geom->GetNTopLevelObjects(); i++)
+		  if (string (geom->GetTopLevelObject(i)->GetSolid()->Name())
+		      == name2)
+		    {
+		      bcm.tlonr = i;
+		      break;
+		    }
+		if(bcm.tlonr == -1)
+		  {
+		    string errstring = "tlo \""; errstring += name2; errstring += "\" not found";
+		    scan.Error(errstring);
+		  }
+		
+		
+		bcm.bcnr = -1;
+		for (i = 0; i < si.Size(); i++)
+		  {
+		    bcm.si = si[i];
+		    geom->bcmodifications.Append (bcm);
+		    geom->bcmodifications.Last().bcname = new string(bcname);
+		  }
+	      }
+	    
+	    else if (scan.GetToken() == TOK_DEFINE)
+	      {
+		scan.ReadNext();
+		string name;
+		double val;
+		
+		switch (scan.GetToken())
+		  {
+		  case TOK_CONSTANT:
+		    scan.ReadNext();
+		      
+		    name = scan.GetStringValue();
+		    scan.ReadNext();
+
+		    ParseChar(scan, '=');
+		    val = ParseNumber(scan);
+
+		    if(name == "identprec")
+		      geom->SetIdEps(val);
+		    
+		    
+
+		    break;
+		  default:
+		    scan.Error ("keyword 'constant' expected");
+		  }
+	      }
+
+
+	    else
+	      {
+		cout << "read unidentified token " << scan.GetToken() 
+		     << " (as char: \"" << char(scan.GetToken()) << "\")"
+		     << " string = " << scan.GetStringValue() << endl;
+		scan.ReadNext();
+	      }
+	  }
+      }
+    catch (string errstr)
+      {
+	cout << "caught error " << errstr << endl;
+	throw NgException (errstr);
+      }
+
+
+
+    (*testout) << geom->GetNTopLevelObjects() << " TLOs:" << endl;
+    for (int i = 0; i < geom->GetNTopLevelObjects(); i++)
+      {
+	const TopLevelObject * tlo = geom->GetTopLevelObject(i);
+	if (tlo->GetSolid())
+	  (*testout) << i << ": " << *tlo->GetSolid() << endl;
+      }
+
+    (*testout) << geom->GetNSurf() << " Surfaces" << endl;
+    for (int i = 0; i < geom->GetNSurf(); i++)
+      (*testout) << i << ": " << *geom->GetSurface(i) << endl;
+
+    return geom;
+    /*
+      do
+      {
+      scan.ReadNext();
+      if (scan.GetToken() == TOK_STRING)
+      cout << "found string " << scan.GetStringValue() << endl;
+      else
+      cout << "token = " << int(scan.GetToken()) << endl;
+      }
+      while (scan.GetToken() != TOK_END);
+    */
+  }
+
+
+};
+
diff --git a/contrib/Netgen/libsrc/csg/csgparser.hpp b/contrib/Netgen/libsrc/csg/csgparser.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dfbd24ac048df191232a81e2b8167ae73e271f12
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgparser.hpp
@@ -0,0 +1,101 @@
+#ifndef _CSGPARSER_HPP
+#define _CSGPARSER_HPP
+
+
+namespace netgen
+{
+
+  enum TOKEN_TYPE
+    { 
+      TOK_MINUS = '-', TOK_LP = '(', OK_RP = ')', TOK_LSP = '[', TOK_RSP = ']',
+      TOK_EQU = '=', TOK_COMMA = ',', TOK_SEMICOLON = ';',
+      TOK_NUM = 100, TOK_STRING, TOK_NAMED_SOLID, TOK_PRIMITIVE, 
+      TOK_OR, TOK_AND, TOK_NOT, 
+      TOK_SINGULAR, TOK_EDGE, TOK_POINT, TOK_FACE, TOK_IDENTIFY, TOK_CLOSESURFACES,
+      TOK_CLOSEEDGES, TOK_PERIODIC,
+      TOK_SOLID, TOK_RECO, TOK_TLO, TOK_CURVE2D, TOK_CURVE3D, TOK_BOUNDINGBOX,
+      TOK_BOUNDARYCONDITION, TOK_BOUNDARYCONDITIONNAME,
+      TOK_DEFINE, TOK_CONSTANT,
+      TOK_END };
+
+  struct kwstruct
+  {
+    TOKEN_TYPE kw; 
+    const char * name;
+  };
+
+  enum PRIMITIVE_TYPE
+    {
+      TOK_SPHERE = 1, TOK_CYLINDER, TOK_PLANE, TOK_ELLIPTICCYLINDER, 
+      TOK_ELLIPSOID, TOK_CONE, 
+      TOK_ORTHOBRICK, TOK_POLYHEDRON, 
+      TOK_TORUS,
+      TOK_TUBE, TOK_GENCYL, TOK_EXTRUSION, TOK_REVOLUTION,
+
+      TOK_TRANSLATE, TOK_MULTITRANSLATE, TOK_ROTATE, TOK_MULTIROTATE
+    };
+
+  struct primstruct
+  {
+    PRIMITIVE_TYPE kw; 
+    const char * name;
+  };
+
+
+  class CSGScanner
+  {
+    TOKEN_TYPE token;
+    PRIMITIVE_TYPE prim_token;
+    double num_value;
+    string string_value;
+    
+    int linenum;
+    istream * scanin;
+
+  public:
+
+    CSGScanner (istream & ascanin);
+
+    TOKEN_TYPE GetToken() const
+    { return token; }
+
+    double GetNumValue() const
+    { return num_value; }
+
+    const string & GetStringValue() const
+    { return string_value; }
+
+    char GetCharValue() const
+    { return string_value[0]; }
+
+    PRIMITIVE_TYPE GetPrimitiveToken() const
+    { return prim_token; }
+  
+    void ReadNext();
+
+    /*
+    CSGScanner & Parse (char ch);
+    CSGScanner & Parse (int & i);
+    CSGScanner & Parse (double & d);
+    CSGScanner & Parse (Point<3> & p);
+    CSGScanner & Parse (Vec<3> & p);
+    */
+    void Error (const string & err);
+  };
+
+
+  
+  CSGScanner & operator>> (CSGScanner & scan, char ch);
+  CSGScanner & operator>> (CSGScanner & scan, double & d);
+  CSGScanner & operator>> (CSGScanner & scan, int & i);
+  CSGScanner & operator>> (CSGScanner & scan, Point<3> & p);
+  CSGScanner & operator>> (CSGScanner & scan, Vec<3> & v);
+  
+
+
+}
+
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/csg/csgpkg.cpp b/contrib/Netgen/libsrc/csg/csgpkg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ce6bf1a4eb46c89856d19893a1c8989e7e7f649
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgpkg.cpp
@@ -0,0 +1,700 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+#include <incvis.hpp>
+#include <visual.hpp>
+
+
+#include "vscsg.hpp"
+
+
+extern "C" int Ng_CSG_Init (Tcl_Interp * interp);
+
+
+
+namespace netgen
+{
+  extern DLL_HEADER NetgenGeometry * ng_geometry;
+  extern DLL_HEADER AutoPtr<Mesh> mesh;
+
+  static VisualSceneGeometry vsgeom;
+ 
+  char * err_needscsgeometry = (char*) "This operation needs an CSG geometry";
+  extern char * err_needsmesh;
+  extern char * err_jobrunning;
+
+ 
+
+
+  int Ng_ParseGeometry (ClientData clientData,
+			Tcl_Interp * interp,
+			int argc, tcl_const char *argv[])
+  {
+    CSGeometry * csgeom = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (csgeom)
+      {
+	double detail = atof (Tcl_GetVar (interp, "::geooptions.detail", 0));
+	double facets = atof (Tcl_GetVar (interp, "::geooptions.facets", 0));
+      
+	if (atoi (Tcl_GetVar (interp, "::geooptions.drawcsg", 0)))
+	  csgeom->CalcTriangleApproximation(detail, facets);
+      }
+    return TCL_OK;
+  }
+
+
+
+
+  int Ng_GeometryOptions (ClientData clientData,
+			  Tcl_Interp * interp,
+			  int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+
+
+    const char * command = argv[1];
+
+    if (strcmp (command, "get") == 0)
+      {
+	if (geometry)
+	  {
+	    char buf[20];
+	    Point3d pmin = geometry->BoundingBox ().PMin();
+	    Point3d pmax = geometry->BoundingBox ().PMax();
+	    
+	    sprintf (buf, "%5.1lf", pmin.X());
+	    Tcl_SetVar (interp, "::geooptions.minx", buf, 0);
+	    sprintf (buf, "%5.1lf", pmin.Y());
+	    Tcl_SetVar (interp, "::geooptions.miny", buf, 0);
+	    sprintf (buf, "%5.1lf", pmin.Z());
+	    Tcl_SetVar (interp, "::geooptions.minz", buf, 0);
+	    
+	    sprintf (buf, "%5.1lf", pmax.X());
+	    Tcl_SetVar (interp, "::geooptions.maxx", buf, 0);
+	    sprintf (buf, "%5.1lf", pmax.Y());
+	    Tcl_SetVar (interp, "::geooptions.maxy", buf, 0);
+	    sprintf (buf, "%5.1lf", pmax.Z());
+	    Tcl_SetVar (interp, "::geooptions.maxz", buf, 0);
+	  }
+      }
+    else if (strcmp (command, "set") == 0)
+      {
+        Point<3> pmin (atof (Tcl_GetVar (interp, "::geooptions.minx", 0)),
+                       atof (Tcl_GetVar (interp, "::geooptions.miny", 0)),
+                       atof (Tcl_GetVar (interp, "::geooptions.minz", 0)));
+        Point<3> pmax (atof (Tcl_GetVar (interp, "::geooptions.maxx", 0)),
+                       atof (Tcl_GetVar (interp, "::geooptions.maxy", 0)),
+                       atof (Tcl_GetVar (interp, "::geooptions.maxz", 0)));
+	Box<3> box (pmin, pmax);
+	if (geometry)
+	  geometry -> SetBoundingBox (box);
+	CSGeometry::SetDefaultBoundingBox (box);
+      }
+
+    return TCL_OK;
+  }
+
+
+
+
+
+  // attempt of a simple modeller
+
+  int Ng_CreatePrimitive (ClientData clientData,
+			  Tcl_Interp * interp,
+			  int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+
+    tcl_const char * classname = argv[1];
+    tcl_const char * name = argv[2];
+
+    cout << "Create primitive, class = " << classname
+	 << ", name = " << name << endl;
+
+    Primitive * nprim = Primitive::CreatePrimitive (classname);
+    Solid * nsol = new Solid (nprim);
+
+    char sname[100];
+    for (int j = 1; j <= nprim->GetNSurfaces(); j++)
+      {
+	sprintf (sname, "%s,%d", name, j);
+	geometry -> AddSurface (sname, &nprim->GetSurface(j));
+	nprim -> SetSurfaceId (j, geometry->GetNSurf());
+      }
+
+    geometry->SetSolid (name, nsol);
+
+    return TCL_OK;
+  }
+
+
+  int Ng_SetPrimitiveData (ClientData clientData,
+			   Tcl_Interp * interp,
+			   int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+
+    tcl_const char * name = argv[1];
+    tcl_const char * value = argv[2];
+
+    Array<double> coeffs;
+
+
+    cout << "Set primitive data, name = " << name
+	 << ", value = " << value  << endl;
+
+
+    istringstream vst (value);
+    double val;
+    while (!vst.eof())
+      {
+	vst >> val;
+	coeffs.Append (val);
+      }
+
+    ((Primitive*)
+     geometry->GetSolid (name)->GetPrimitive())->SetPrimitiveData (coeffs);
+
+    return TCL_OK;
+  }
+
+
+
+  int Ng_SetSolidData (ClientData clientData,
+		       Tcl_Interp * interp,
+		       int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+
+    tcl_const char * name = argv[1];
+    tcl_const char * val = argv[2];
+
+    cout << "Set Solid Data, name = " << name
+	 << ", value = " << val << endl;
+
+    istringstream vst (val);
+
+    Solid * nsol = Solid::CreateSolid (vst, geometry->GetSolids());
+    geometry->SetSolid (name, nsol);
+
+    return TCL_OK;
+  }
+
+
+  int Ng_GetPrimitiveData (ClientData clientData,
+			   Tcl_Interp * interp,
+			   int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+
+    tcl_const char * name = argv[1];
+    tcl_const char * classnamevar = argv[2];
+    tcl_const char * valuevar = argv[3];
+
+    const char * classname;
+
+    Array<double> coeffs;
+
+    geometry->GetSolid (name)->GetPrimitive()->GetPrimitiveData (classname, coeffs);
+
+    ostringstream vst;
+    for (int i = 1; i <= coeffs.Size(); i++)
+      vst << coeffs.Get(i) << " ";
+
+    cout << "GetPrimitiveData, name = " << name
+	 << ", classnamevar = " << classnamevar
+	 << ", classname = " << classname << endl
+	 << " valuevar = " << valuevar
+	 << ", values = " << vst.str() << endl;
+
+    Tcl_SetVar  (interp, classnamevar, (char*)classname, 0);
+    Tcl_SetVar  (interp, valuevar, (char*)vst.str().c_str(), 0);
+
+    return TCL_OK;
+  }
+
+  int Ng_GetSolidData (ClientData clientData,
+		       Tcl_Interp * interp,
+		       int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+    tcl_const char * name = argv[1];
+    tcl_const char * valuevar = argv[2];
+
+    ostringstream vst;
+
+    const Solid * sol = geometry->GetSolid (name);
+    sol->GetSolidData (vst);
+
+    cout << "GetSolidData, name = " << name << ", data = " << vst.str() << endl;
+
+    Tcl_SetVar  (interp, valuevar, (char*)vst.str().c_str(), 0);
+
+    return TCL_OK;
+  }
+
+
+  int Ng_GetPrimitiveList (ClientData clientData,
+			   Tcl_Interp * interp,
+			   int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+
+    tcl_const char * valuevar = argv[1];
+    int i;
+
+    stringstream vst;
+
+    for (i = 1; i <= geometry->GetNSolids(); i++)
+      {
+	const Solid * sol = geometry->GetSolid(i);
+	if (sol->GetPrimitive())
+	  vst << sol->Name() << " ";
+      }
+
+    cout << "primnames = " << vst.str() << endl;
+
+    Tcl_SetVar  (interp, valuevar, (char*)vst.str().c_str(), 0);
+
+    return TCL_OK;
+  }
+
+
+
+  int Ng_GetSurfaceList (ClientData clientData,
+			 Tcl_Interp * interp,
+			 int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+
+    tcl_const char * valuevar = argv[1];
+    int i;
+
+    stringstream vst;
+
+    for (i = 1; i <= geometry->GetNSurf(); i++)
+      {
+	const Surface * surf = geometry->GetSurface(i);
+	vst << surf->Name() << " ";
+      }
+
+    cout << "surfnames = " << vst.str() << endl;
+
+    Tcl_SetVar  (interp, valuevar, (char*)vst.str().c_str(), 0);
+
+    return TCL_OK;
+  }
+
+
+  int Ng_GetSolidList (ClientData clientData,
+		       Tcl_Interp * interp,
+		       int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+    tcl_const char * valuevar = argv[1];
+    int i;
+
+    stringstream vst;
+
+    for (i = 1; i <= geometry->GetNSolids(); i++)
+      {
+	const Solid * sol = geometry->GetSolid(i);
+	if (!sol->GetPrimitive())
+	  vst << sol->Name() << " ";
+      }
+
+    cout << "solnames = " << vst.str() << endl;
+
+    Tcl_SetVar  (interp, valuevar, (char*)vst.str().c_str(), 0);
+
+    return TCL_OK;
+  }
+
+
+  int Ng_TopLevel (ClientData clientData,
+		   Tcl_Interp * interp,
+		   int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+
+    int i;
+    /*
+      for (i = 0; i < argc; i++)
+      cout << argv[i] << ", ";
+      cout << endl;
+    */
+
+    if (strcmp (argv[1], "getlist") == 0)
+      {
+	stringstream vst;
+
+	for (i = 0; i < geometry->GetNTopLevelObjects(); i++)
+	  {
+	    const Solid * sol;
+	    const Surface * surf;
+	    geometry->GetTopLevelObject (i, sol, surf);
+
+	    if (!surf)
+	      vst << "{ " << sol->Name() << " } ";
+	    else
+	      vst << "{ " << sol->Name() << " " << surf->Name() << " } ";
+	  }
+
+	tcl_const char * valuevar = argv[2];
+	Tcl_SetVar  (interp, valuevar, (char*)vst.str().c_str(), 0);
+      }
+
+    if (strcmp (argv[1], "set") == 0)
+      {
+	tcl_const char * solname = argv[2];
+	tcl_const char * surfname = argv[3];
+	Solid * sol = (Solid*)geometry->GetSolid (solname);
+	Surface * surf = (Surface*)geometry->GetSurface (surfname);
+	geometry->SetTopLevelObject (sol, surf);
+      }
+
+    if (strcmp (argv[1], "remove") == 0)
+      {
+	tcl_const char * solname = argv[2];
+	tcl_const char * surfname = argv[3];
+	Solid * sol = (Solid*)geometry->GetSolid (solname);
+	Surface * surf = (Surface*)geometry->GetSurface (surfname);
+	geometry->RemoveTopLevelObject (sol, surf);
+      }
+
+    if (strcmp (argv[1], "setprop") == 0)
+      {
+	tcl_const char * solname = argv[2];
+	tcl_const char * surfname = argv[3];
+	tcl_const char * propvar = argv[4];
+	Solid * sol = (Solid*)geometry->GetSolid (solname);
+	Surface * surf = (Surface*)geometry->GetSurface (surfname);
+	TopLevelObject * tlo = geometry->GetTopLevelObject (sol, surf);
+
+	if (!tlo) return TCL_OK;
+
+	char varname[50];
+	sprintf (varname, "%s(red)", propvar);
+	double red = atof (Tcl_GetVar (interp, varname, 0));
+	sprintf (varname, "%s(blue)", propvar);
+	double blue = atof (Tcl_GetVar (interp, varname, 0));
+	sprintf (varname, "%s(green)", propvar);
+	double green = atof (Tcl_GetVar (interp, varname, 0));
+	tlo -> SetRGB (red, green, blue);
+
+	sprintf (varname, "%s(visible)", propvar);
+	tlo -> SetVisible (bool(atoi (Tcl_GetVar (interp, varname, 0))));
+	sprintf (varname, "%s(transp)", propvar);
+	tlo -> SetTransparent (bool(atoi (Tcl_GetVar (interp, varname, 0))));
+      }
+
+    if (strcmp (argv[1], "getprop") == 0)
+      {
+	tcl_const char * solname = argv[2];
+	tcl_const char * surfname = argv[3];
+	tcl_const char * propvar = argv[4];
+
+	Solid * sol = (Solid*)geometry->GetSolid (solname);
+	Surface * surf = (Surface*)geometry->GetSurface (surfname);
+	TopLevelObject * tlo = geometry->GetTopLevelObject (sol, surf);
+
+	if (!tlo) return TCL_OK;
+
+	char varname[50], varval[10];
+
+	sprintf (varname, "%s(red)", propvar);
+	sprintf (varval, "%lf", tlo->GetRed());
+	Tcl_SetVar (interp, varname, varval, 0);
+
+	sprintf (varname, "%s(green)", propvar);
+	sprintf (varval, "%lf", tlo->GetGreen());
+	Tcl_SetVar (interp, varname, varval, 0);
+
+	sprintf (varname, "%s(blue)", propvar);
+	sprintf (varval, "%lf", tlo->GetBlue());
+	Tcl_SetVar (interp, varname, varval, 0);
+
+	sprintf (varname, "%s(visible)", propvar);
+	sprintf (varval, "%d", tlo->GetVisible());
+	Tcl_SetVar (interp, varname, varval, 0);
+
+	sprintf (varname, "%s(transp)", propvar);
+	sprintf (varval, "%d", tlo->GetTransparent());
+	Tcl_SetVar (interp, varname, varval, 0);
+      }
+
+
+    return TCL_OK;
+  }
+
+
+
+
+  int Ng_SingularEdgeMS (ClientData clientData,
+			 Tcl_Interp * interp,
+			 int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+    if (!mesh.Ptr())
+      {
+	Tcl_SetResult (interp, err_needsmesh, TCL_STATIC);
+	return TCL_ERROR;
+      }
+    if (multithread.running)
+      {
+	Tcl_SetResult (interp, err_jobrunning, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+    double globh = mparam.maxh;
+    for (int i = 1; i <= geometry->singedges.Size(); i++)
+      geometry->singedges.Get(i)->SetMeshSize (*mesh, globh);
+    return TCL_OK;
+  }
+
+
+  int Ng_SingularPointMS (ClientData clientData,
+			  Tcl_Interp * interp,
+			  int argc, tcl_const char *argv[])
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (!geometry)
+      {
+	Tcl_SetResult (interp, err_needscsgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+    double globh = mparam.maxh;
+    for (int i = 1; i <= geometry->singpoints.Size(); i++)
+      geometry->singpoints.Get(i)->SetMeshSize (*mesh, globh);
+    return TCL_OK;
+  }
+
+
+
+  int Ng_SelectSurface (ClientData clientData,
+			Tcl_Interp * interp,
+			int argc, tcl_const char *argv[])
+  {
+    int surfnr = atoi (argv[1]);
+    vsgeom.SelectSurface (surfnr);
+    return TCL_OK;
+  }
+
+
+  class CSGeometryRegister : public GeometryRegister
+  {
+  public:
+    virtual NetgenGeometry * Load (string filename) const;
+    virtual NetgenGeometry * LoadFromMeshFile (istream & ist) const;
+    virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const;
+  };
+
+	extern CSGeometry * ParseCSG (istream & istr);
+
+  NetgenGeometry *  CSGeometryRegister :: Load (string filename) const
+  {
+    const char * cfilename = filename.c_str();
+    if (strcmp (&cfilename[strlen(cfilename)-3], "geo") == 0)
+      {
+	PrintMessage (1, "Load CSG geometry file ", cfilename);
+	
+
+	ifstream infile(cfilename);
+
+	CSGeometry * hgeom = ParseCSG (infile);
+	if (!hgeom)
+	  throw NgException ("geo-file should start with 'algebraic3d'");
+
+	hgeom -> FindIdenticSurfaces(1e-8 * hgeom->MaxSize()); 
+	return hgeom;
+      }
+
+    if (strcmp (&cfilename[strlen(cfilename)-3], "ngg") == 0)
+      {
+	PrintMessage (1, "Load new CSG geometry file ", cfilename);
+
+	ifstream infile(cfilename);
+	CSGeometry * hgeom = new CSGeometry("");
+	hgeom -> Load (infile);
+
+	return hgeom;
+      }
+
+
+    
+    return NULL;
+  }
+
+  NetgenGeometry * CSGeometryRegister :: LoadFromMeshFile (istream & ist) const 
+  {
+    string auxstring;
+    if (ist.good())
+      {
+	ist >> auxstring;
+	if (auxstring == "csgsurfaces")
+	  {
+	    CSGeometry * geometry = new CSGeometry ("");
+	    geometry -> LoadSurfaces(ist);
+	    return geometry;
+	  }
+	// else
+	// ist.putback (auxstring);
+      }
+    return NULL;
+  }
+
+  VisualScene * CSGeometryRegister :: GetVisualScene (const NetgenGeometry * geom) const
+  {
+    CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+    if (geometry)
+      {
+	vsgeom.SetGeometry (geometry);
+	return &vsgeom;
+      }
+    return NULL;
+  }
+
+
+}
+
+
+using namespace netgen;
+
+int Ng_CSG_Init (Tcl_Interp * interp)
+{
+  geometryregister.Append (new CSGeometryRegister);
+  
+
+
+  Tcl_CreateCommand (interp, "Ng_ParseGeometry", Ng_ParseGeometry,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  // geometry
+  Tcl_CreateCommand (interp, "Ng_CreatePrimitive", Ng_CreatePrimitive,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  Tcl_CreateCommand (interp, "Ng_SetPrimitiveData", Ng_SetPrimitiveData,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  Tcl_CreateCommand (interp, "Ng_GetPrimitiveData", Ng_GetPrimitiveData,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  Tcl_CreateCommand (interp, "Ng_GetPrimitiveList", Ng_GetPrimitiveList,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+
+  Tcl_CreateCommand (interp, "Ng_GetSurfaceList", Ng_GetSurfaceList,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+
+
+  Tcl_CreateCommand (interp, "Ng_SetSolidData", Ng_SetSolidData,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  Tcl_CreateCommand (interp, "Ng_GetSolidData", Ng_GetSolidData,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  Tcl_CreateCommand (interp, "Ng_GetSolidList", Ng_GetSolidList,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+
+  Tcl_CreateCommand (interp, "Ng_TopLevel", Ng_TopLevel,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  Tcl_CreateCommand (interp, "Ng_GeometryOptions", Ng_GeometryOptions,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  Tcl_CreateCommand (interp, "Ng_SingularEdgeMS", Ng_SingularEdgeMS,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+  
+  Tcl_CreateCommand (interp, "Ng_SingularPointMS", Ng_SingularPointMS,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+    Tcl_CreateCommand (interp, "Ng_SelectSurface", Ng_SelectSurface,
+		       (ClientData)NULL,
+		       (Tcl_CmdDeleteProc*) NULL);
+
+
+  return TCL_OK;
+}
+
+
+
diff --git a/contrib/Netgen/libsrc/csg/curve2d.cpp b/contrib/Netgen/libsrc/csg/curve2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7091e87af9556c16e10084815036a0f28d39a498
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/curve2d.cpp
@@ -0,0 +1,78 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <csg.hpp>
+
+namespace netgen
+{
+CircleCurve2d :: CircleCurve2d (const Point<2> & acenter, double arad)
+  {
+  center = acenter;
+  rad = arad;
+  }
+  
+void CircleCurve2d :: Project (Point<2> & p) const
+  {
+  Vec<2> v = p - center;
+  v *= rad/v.Length();
+  p = center + v;
+  }
+
+void CircleCurve2d :: NormalVector (const Point<2> & p, Vec<2> & n) const
+  {
+  n = p - center;
+  n /= n.Length();
+  }
+
+
+
+
+
+
+QuadraticCurve2d ::  QuadraticCurve2d ()
+{
+  cxx = cyy = cxy = cx = cy = c = 0;
+}
+
+void QuadraticCurve2d :: Read (istream & ist)
+{
+  ist >> cxx >> cyy >> cxy >> cx >> cy >> c;
+}
+
+
+void QuadraticCurve2d :: Project (Point<2> & p) const
+{
+  double f, x, y, gradx, grady, grad2;
+  int its = 0;
+
+  x = p(0);
+  y = p(1);
+
+  do
+    {
+      f = cxx * x * x + cyy * y * y + cxy * x * y + cx * x + cy * y + c;
+      gradx = 2 * cxx * x + cxy * y + cx;
+      grady = 2 * cyy * y + cxy * x + cy;
+      grad2 = gradx * gradx + grady * grady;
+      
+      x -= f * gradx / grad2;
+      y -= f * grady / grad2;
+
+      //      (*mycout) << "x = " << x << " y = " << y << " f = " << f << endl;
+      its++;
+    }
+  while (fabs (f) > 1e-8 && its < 20);
+  if (its >= 20)
+    cerr << "QuadraticCurve2d::Project:  many iterations, f = " << f << endl;
+  p(0) = x;
+  p(1) = y;
+}
+
+
+void QuadraticCurve2d :: NormalVector (const Point<2> & p, Vec<2> & n) const
+{
+  n(0) = 2 * cxx * p(0) + cxy * p(1) + cx;
+  n(1) = 2 * cyy * p(1) + cxy * p(0) + cy;
+  n.Normalize();
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/curve2d.hpp b/contrib/Netgen/libsrc/csg/curve2d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..066a40adb76d8670c4e490cf31ac3c605264c5aa
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/curve2d.hpp
@@ -0,0 +1,67 @@
+#ifndef FILE_CURVE2D
+#define FILE_CURVE2D
+
+/**************************************************************************/
+/* File:   curve2d.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   24. Jul. 96                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+
+  /*
+
+  2D Curve repesentation
+
+  */
+
+
+
+  ///
+  class Curve2d : public Manifold
+  {
+  public:
+    ///
+    virtual void Project (Point<2> & p) const = 0;
+    ///
+    virtual void NormalVector (const Point<2> & p, Vec<2> & n) const = 0;
+  };
+  
+  ///
+  class CircleCurve2d : public Curve2d
+  {
+    ///
+    Point<2> center;
+    ///
+    double rad;
+  public:
+    ///
+    CircleCurve2d (const Point<2> & acenter, double arad);
+    ///
+    virtual void Project (Point<2> & p) const;
+    ///
+    virtual void NormalVector (const Point<2> & p, Vec<2> & n) const;
+  };
+  
+  ///
+  class QuadraticCurve2d : public Curve2d
+  {
+    ///
+    double cxx, cyy, cxy, cx, cy, c;
+  public:
+    ///
+    QuadraticCurve2d ();
+    ///
+    void Read (istream & ist);
+    ///
+    virtual void Project (Point<2> & p) const;
+    ///
+    virtual void NormalVector (const Point<2> & p, Vec<2> & n) const;
+  };
+
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/edgeflw.cpp b/contrib/Netgen/libsrc/csg/edgeflw.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c9c5c1667cd786e7a7c2c8bd83dd7ece430bb9cd
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/edgeflw.cpp
@@ -0,0 +1,1850 @@
+#include <mystdlib.h>
+#include <meshing.hpp>
+#include <csg.hpp>
+
+// #undef DEVELOP
+// #define DEVELOP
+
+namespace netgen
+{
+
+  EdgeCalculation :: 
+  EdgeCalculation (const CSGeometry & ageometry,
+		   Array<SpecialPoint> & aspecpoints)
+    : geometry(ageometry), specpoints(aspecpoints)
+  {
+    Box<3> bbox = geometry.BoundingBox();
+
+    searchtree = new Point3dTree (bbox.PMin(), bbox.PMax());
+    meshpoint_tree = new Point3dTree (bbox.PMin(), bbox.PMax());
+
+    for (int i = 0; i < specpoints.Size(); i++)
+      searchtree->Insert (specpoints[i].p, i);
+
+    ideps = 1e-9;
+  }
+
+  EdgeCalculation :: ~EdgeCalculation()
+  {
+    delete searchtree;
+    delete meshpoint_tree;
+  }
+
+
+  void EdgeCalculation :: Calc(double h, Mesh & mesh)
+  {
+    static int timer = NgProfiler::CreateTimer ("CSG: mesh edges");
+    NgProfiler::RegionTimer reg (timer);
+
+
+    PrintMessage (1, "Find edges");
+    PushStatus ("Find edges");
+
+    for (int i = 1; i <= mesh.GetNP(); i++)
+      meshpoint_tree->Insert (mesh.Point(i), i);
+
+
+    // add all special points before edge points (important for periodic identification)
+    // JS, Jan 2007
+    const double di=1e-7*geometry.MaxSize();
+    Array<int> locsearch;
+
+    for (int i = 0; i < specpoints.Size(); i++)
+      if (specpoints[i].unconditional)
+	{
+	  Point<3> p = specpoints[i].p;
+          meshpoint_tree -> GetIntersecting (p-Vec<3> (di,di,di),
+                                             p+Vec<3> (di,di,di), locsearch);
+          
+	  if (locsearch.Size() == 0)
+            {
+              PointIndex pi = mesh.AddPoint (p, specpoints[i].GetLayer(), FIXEDPOINT);
+              meshpoint_tree -> Insert (p, pi); 
+            }
+        }
+
+    /*
+      // slow version
+    for (int i = 0; i < specpoints.Size(); i++)
+      if (specpoints[i].unconditional)
+	{
+	  Point<3> p = specpoints[i].p;
+	  bool found = false;
+	  for (int j = 1; j <= mesh.GetNP(); j++)
+	    if (Dist (p, mesh.Point(j)) < 1e-8)
+	      found = true;
+	  if (!found)
+	    mesh.AddPoint (p, specpoints[i].GetLayer(), FIXEDPOINT);
+	}
+    */
+
+
+
+    CalcEdges1 (h, mesh);
+    SplitEqualOneSegEdges (mesh);
+    FindClosedSurfaces (h, mesh);
+    PrintMessage (3, cntedge, " edges found");
+
+    PopStatus ();
+  }
+
+
+
+
+
+  void EdgeCalculation :: CalcEdges1 (double h, Mesh & mesh)
+  {
+    Array<int> hsp(specpoints.Size());
+    Array<int> glob2hsp(specpoints.Size());
+    Array<int> startpoints, endpoints;
+
+
+    int pos, ep;
+    int layer;
+
+    Point<3> p, np; 
+    int pi1, s1, s2, s1_orig, s2_orig;
+
+    Array<Point<3> > edgepoints;
+    Array<double> curvelength;
+    int copyedge = 0, copyfromedge = -1, copyedgeidentification = -1;
+
+    Array<int> locsurfind, locind;
+
+    int checkedcopy = 0;
+
+    // double size = geometry.MaxSize(); 
+    // double epspointdist2 = sqr (size) * 1e-12;
+    
+
+    // copy special points to work with
+    for (int i = 0; i < specpoints.Size(); i++)
+      {
+	hsp[i] = i;
+	glob2hsp[i] = i;
+      }
+
+    //for(int i=0; i<hsp.Size(); i++)
+    //  (*testout) << "hsp["<<i<<"] ... " << specpoints[hsp[i]].p << endl;
+     
+
+    cntedge = 0;
+    INDEX_2_HASHTABLE<int> identification_used(100);  // identification i already used for startpoint j
+
+    mesh.GetIdentifications().Delete();
+    
+    TABLE<int> specpoint2surface(specpoints.Size());
+    if (geometry.identifications.Size())
+      {
+	for (int i = 0; i < specpoints.Size(); i++)
+	  for (int j = 0; j < geometry.GetNSurf(); j++)
+	    if (geometry.GetSurface(j)->PointOnSurface (specpoints[i].p))
+	      specpoint2surface.Add (i, j);
+      }
+
+    TABLE<int> specpoint2tlo(specpoints.Size());
+    if (geometry.identifications.Size())
+      {
+	for (int i = 0; i < specpoints.Size(); i++)
+	  for (int j = 0; j < geometry.GetNTopLevelObjects(); j++)
+	    {
+	      const TopLevelObject * tlo = geometry.GetTopLevelObject (j);
+	      if (tlo->GetSolid() && tlo->GetSolid()->VectorIn (specpoints[i].p,specpoints[i].v))
+		//if (tlo->GetSolid() && tlo->GetSolid()->IsIn (specpoints[i].p))
+		{
+#ifdef DEVELOP
+		  (*testout) << "point " << specpoints[i].p << " v " <<specpoints[i].v <<" is in " << tlo->GetSolid()->Name() << endl;
+#endif
+		  specpoint2tlo.Add (i, j);
+		}
+	    }
+      }
+
+    for (int i = 0; i < specpoints.Size(); i++)
+      specpoints[i].nr = i;
+
+    while (hsp.Size())
+      {
+	SetThreadPercent(100 - 100 * double (hsp.Size()) / specpoints.Size());
+
+#ifdef DEVELOP
+	(*testout) << "hsp.Size() " << hsp.Size() << " specpoints.Size() " << specpoints.Size() << endl;
+	(*testout) << endl << "edge nr " << cntedge+1 << endl;
+#endif
+
+	edgepoints.SetSize (0);
+	curvelength.SetSize (0);
+      
+
+	pi1 = 0;
+	copyedge = 0;
+	// identifyable point available ?
+
+	
+	for (int i = 0; i < geometry.identifications.Size() && !pi1; i++)
+	  for (int j = checkedcopy; j < startpoints.Size() && !pi1; j++)
+	    {
+#ifdef DEVELOP
+	      (*testout) << "checking point " << specpoints[startpoints[j]].p
+			 << ", v = " << specpoints[startpoints[j]].v 
+			 << " for copying (i,j = " << i << ", " << j << ")" << endl;	  
+#endif
+ 	      if (geometry.identifications[i]->IdentifyableCandidate (specpoints[startpoints[j]]) &&
+		  geometry.identifications[i]->IdentifyableCandidate (specpoints[endpoints[j]]))
+		  
+	      
+		{
+		  int pi1cand = 0;
+		  double mindist = 1e10;
+		
+		  for (int k = 0; k < hsp.Size() && !pi1; k++)
+		    {
+		      //(*testout) << "   ? identifyable with " << specpoints[hsp[k]].p 
+		      //<< ", v = " << specpoints[hsp[k]].v
+		      //		 << endl;
+		      if (identification_used.Used (INDEX_2(i, startpoints[j])) ||
+			  identification_used.Used (INDEX_2(i, hsp[k])))
+			{
+			  //(*testout) << "failed at pos0" << endl;
+			  continue;
+			}
+		      
+		      if (geometry.identifications[i]
+			  ->Identifyable(specpoints[startpoints[j]], specpoints[hsp[k]], specpoint2tlo, specpoint2surface) ||
+			  geometry.identifications[i]
+			  ->Identifyable(specpoints[hsp[k]], specpoints[startpoints[j]], specpoint2tlo, specpoint2surface))
+			{
+#ifdef DEVELOP
+			  (*testout) << "identifyable: " << specpoints[hsp[k]].p << ", v = " << specpoints[hsp[k]].v
+				     << " and " << specpoints[startpoints[j]].p << ", v = " << specpoints[startpoints[j]].v 
+				     << " (identification " << i+1 << ")" << endl;
+#endif
+
+			  if (Dist (specpoints[startpoints[j]].p, specpoints[hsp[k]].p) < mindist)
+			    {
+			      mindist = Dist (specpoints[startpoints[j]].p, specpoints[hsp[k]].p);
+			      pi1cand = k+1;
+			    }
+			}
+		    }
+	
+	
+		  if (pi1cand)
+		    {
+		      pi1 = pi1cand;
+		      copyedge = 1;
+		      copyfromedge = j+1;
+		      copyedgeidentification = i+1;
+		    
+		      identification_used.Set (INDEX_2(i, startpoints[j]), 1);
+		      identification_used.Set (INDEX_2(i, hsp.Get(pi1)), 1);
+		    }
+		}
+	    }
+	
+      
+	// cannot copy from other ege ?
+	if (!pi1)
+	  checkedcopy = startpoints.Size();
+      
+	// unconditional special point available ?
+	if (!pi1)
+	  for (int i = 1; i <= hsp.Size(); i++)
+	    if (specpoints[hsp.Get(i)].unconditional == 1)
+	      {
+		pi1 = i;
+		break;
+	      }
+ 
+     
+	if (!pi1)
+	  {
+	    // no unconditional points available, choose first conitional
+	    pi1 = 1;	     
+	  }
+
+	layer = specpoints[hsp.Get(pi1)].GetLayer();
+      
+
+	if (!specpoints[hsp.Get(pi1)].unconditional)
+	  {
+	    specpoints[hsp.Elem(pi1)].unconditional = 1;
+	    for (int i = 1; i <= hsp.Size(); i++)
+	      if (i != pi1 && 
+		  Dist (specpoints[hsp.Get(pi1)].p, specpoints[hsp.Get(i)].p) < 1e-8*geometry.MaxSize() &&
+		  (specpoints[hsp.Get(pi1)].v + specpoints[hsp.Get(i)].v).Length() < 1e-4)
+		{
+		  // opposite direction
+		  specpoints[hsp.Elem(i)].unconditional = 1;
+		}
+	  }
+
+	cntedge++;
+	startpoints.Append (hsp.Get(pi1));
+
+#ifdef DEVELOP
+	(*testout) << "start followedge: p1 = " << specpoints[hsp.Get(pi1)].p 
+		   << ", v = " << specpoints[hsp.Get(pi1)].v << endl;
+#endif
+
+	FollowEdge (pi1, ep, pos, hsp, h, mesh,
+		    edgepoints, curvelength);
+
+
+	if (multithread.terminate)
+	  return;
+      
+	if (!ep)
+	  {
+	    // ignore starting point
+	    hsp.DeleteElement (pi1);
+	    cout << "yes, this happens" << endl;
+	    continue;
+	  }
+
+
+
+	endpoints.Append (hsp.Get(ep));
+
+
+	double elen = 0;
+	for (int i = 1; i <= edgepoints.Size()-1; i++)
+	  elen += Dist (edgepoints.Get(i), edgepoints.Get(i+1));
+
+
+	int shortedge = 0;
+	for (int i = 1; i <= geometry.identifications.Size(); i++)
+	  if (geometry.identifications.Get(i)->ShortEdge(specpoints[hsp.Get(pi1)], specpoints[hsp.Get(ep)]))
+	    shortedge = 1;
+	// (*testout) << "shortedge = " << shortedge << endl;
+
+
+	if (!shortedge)
+	  {
+	    mesh.RestrictLocalHLine (Point3d (specpoints[hsp.Get(pi1)].p), 
+				     Point3d (specpoints[hsp.Get(ep)].p), 
+				     elen / mparam.segmentsperedge);
+	  }
+      
+	s1 = specpoints[hsp.Get(pi1)].s1;
+	s2 = specpoints[hsp.Get(pi1)].s2;
+	s1_orig = specpoints[hsp.Get(pi1)].s1_orig;
+	s2_orig = specpoints[hsp.Get(pi1)].s2_orig;
+
+
+	// delete initial, terminal and conditional points
+
+#ifdef DEVELOP
+	(*testout) << "terminal point: p = " << specpoints[hsp.Get(ep)].p 
+		   << ", v = " << specpoints[hsp.Get(ep)].v << endl;      
+#endif
+
+	searchtree -> DeleteElement (hsp.Get(ep));
+	searchtree -> DeleteElement (hsp.Get(pi1));
+
+	if (ep > pi1)
+	  {
+	    glob2hsp[hsp[ep-1]] = -1;
+	    glob2hsp[hsp.Last()] = ep-1;
+	    hsp.DeleteElement (ep);
+
+	    glob2hsp[hsp[pi1-1]] = -1;
+	    glob2hsp[hsp.Last()] = pi1-1;
+	    hsp.DeleteElement (pi1);
+	  }
+	else
+	  {
+	    glob2hsp[hsp[pi1-1]] = -1;
+	    glob2hsp[hsp.Last()] = pi1-1;
+	    hsp.DeleteElement (pi1);
+
+	    glob2hsp[hsp[ep-1]] = -1;
+	    glob2hsp[hsp.Last()] = ep-1;
+	    hsp.DeleteElement (ep);
+	  }
+
+
+	for (int j = 1; j <= edgepoints.Size()-1; j++)
+	  {
+	    p = edgepoints.Get(j);
+	    np = Center (p, edgepoints.Get(j+1));
+	    double hd = Dist (p, np);
+ 
+
+	    Box<3> boxp (np - (1.2 * hd) * Vec<3> (1, 1, 1),
+			 np + (1.2 * hd) * Vec<3> (1, 1, 1));
+	    searchtree -> GetIntersecting (boxp.PMin(), boxp.PMax(), locind);	    
+
+	    for (int i = 0; i < locind.Size(); i++)
+	      {
+		if ( specpoints[locind[i]].HasSurfaces (s1, s2) &&
+		     specpoints[locind[i]].unconditional == 0)
+		  {
+		    searchtree -> DeleteElement (locind[i]);
+
+		    int li = glob2hsp[locind[i]];
+		    glob2hsp[locind[i]] = -1;
+		    glob2hsp[hsp.Last()] = li;
+		    hsp.Delete (li);
+		  }
+	      }
+
+
+	    /*
+	    for (int i = 1; i <= hsp.Size(); i++)
+	      if ( specpoints[hsp.Get(i)].HasSurfaces (s1, s2) &&
+		   specpoints[hsp.Get(i)].unconditional == 0 &&
+		   Dist2 (np, specpoints[hsp.Get(i)].p) < 1.2 * hd)
+		{
+		  searchtree -> DeleteElement (hsp.Get(i)+1);
+		  hsp.DeleteElement (i);
+		  i--;
+		}
+	    */
+	  }
+
+      
+	Array<Segment> refedges;
+	Array<bool> refedgesinv;
+      
+
+	AnalyzeEdge (s1_orig, s2_orig, s1, s2, pos, layer,
+		     edgepoints,
+		     refedges, refedgesinv);
+
+
+	for (int i = 0; i < refedges.Size(); i++)
+	  refedges[i].edgenr = cntedge;
+
+	
+#ifdef DEVELOP
+	(*testout) << "edge " << cntedge << endl
+		   << "startp: " << specpoints[startpoints.Last()].p 
+		   << ", v = " << specpoints[startpoints.Last()].v << endl
+		   << "copy = " << copyedge << endl
+		   << refedges.Size() << " refedges: ";
+	for (int i = 1; i <= refedges.Size(); i++)
+	  (*testout) << " " << refedges.Get(i).si;
+	(*testout) << endl;
+	if (refedgesinv.Size())
+	  (*testout) << "inv[1] = " << refedgesinv.Get(1) << endl;
+#endif
+
+	if (refedges.Size() == 0)
+	  throw NgException ("Problem in edge detection");
+
+      
+	if (!copyedge)
+	  {
+	    // (*testout) << "store edge" << endl;
+	    // int oldnseg = mesh.GetNSeg();
+
+	    if (!shortedge)
+	      StoreEdge (refedges, refedgesinv, 
+			 edgepoints, curvelength, layer, mesh);
+	    else
+	      StoreShortEdge (refedges, refedgesinv, 
+			      edgepoints, curvelength, layer, mesh);
+
+
+	    for(int i = 0; i < refedges.Size(); i++)
+	      {
+		refedges[i].surfnr1 = geometry.GetSurfaceClassRepresentant(refedges[i].surfnr1);
+		refedges[i].surfnr2 = geometry.GetSurfaceClassRepresentant(refedges[i].surfnr2);
+	      }
+
+
+	    /*
+	      for (int i = oldnseg+1; i <= mesh.GetNSeg(); i++)
+	      for (int j = 1; j <= oldnseg; j++)
+	      {
+	      const Point<3> & l1p1 = mesh.Point (mesh.LineSegment(i).p1);
+	      const Point<3> & l1p2 = mesh.Point (mesh.LineSegment(i).p2);
+	      const Point<3> & l2p1 = mesh.Point (mesh.LineSegment(j).p1);
+	      const Point<3> & l2p2 = mesh.Point (mesh.LineSegment(j).p2);
+	      Vec<3> vl1(l1p1, l1p2);
+	      for (double lamk = 0; lamk <= 1; lamk += 0.1)
+	      {
+	      Point<3> l2p = l1p1 + lamk * vl1;
+	      double dist = sqrt (MinDistLP2 (l2p1, l2p2, l2p));
+	      if (dist > 1e-12)
+	      mesh.RestrictLocalH (l2p, 3*dist);
+	      }
+	      }
+	    */
+	  }
+	else
+	  {
+	    CopyEdge (refedges, refedgesinv,
+		      copyfromedge, 
+		      specpoints[startpoints.Get(copyfromedge)].p,
+		      specpoints[endpoints.Get(copyfromedge)].p,
+		      edgepoints.Get(1), edgepoints.Last(),
+		      copyedgeidentification, 
+		      layer,
+		      mesh);
+	  }
+	
+
+	/*
+	  // not available ...
+	for (int i = 0; i < refedges.Size(); i++)
+	  {
+	    EdgeDescriptor ed;
+	    ed.SetSurfNr(0, refedges[i].surfnr1);
+	    ed.SetSurfNr(1, refedges[i].surfnr2);
+	    int hnr = mesh.AddEdgeDescriptor(ed);
+	    if (hnr != refedges[i].edgenr)
+	      {
+		cerr << "edgedescriptor index wrong: new : " << hnr << " old = " << refedges[i].edgenr << endl;
+	      }
+	  }
+	*/
+
+
+
+// 	for(int i=0; i<hsp.Size(); i++)
+// 	  {
+// 	    (*testout) << "pos2 hsp["<<i<<"] ... " << specpoints[hsp[i]].p << endl;
+// 	  }
+      }
+  }
+  
+
+
+
+
+
+  /*
+    If two or more edges share the same initial and end-points,
+    then they need at least two segments 
+  */
+  void EdgeCalculation ::
+  SplitEqualOneSegEdges (Mesh & mesh)
+    {
+    //    int i, j;
+    SegmentIndex si;
+    PointIndex pi;
+
+    Array<int> osedges(cntedge);
+    INDEX_2_HASHTABLE<int> osedgesht (cntedge+1);
+
+    osedges = 2;
+
+    // count segments on edges
+    for (si = 0; si < mesh.GetNSeg(); si++)
+      {
+	const Segment & seg = mesh[si];
+	if (seg.seginfo && seg.edgenr >= 1 && seg.edgenr <= cntedge)
+	  osedges.Elem(seg.edgenr)--;
+      }
+
+    // flag one segment edges
+    for (int i = 0; i < cntedge; i++)
+      osedges[i] = (osedges[i] > 0) ? 1 : 0;
+
+    for (si = 0; si < mesh.GetNSeg(); si++)
+      {
+	const Segment & seg = mesh[si];
+	if (seg.seginfo && seg.edgenr >= 1 && seg.edgenr <= cntedge)
+	  {
+	    if (osedges.Get(seg.edgenr))
+	      {
+		INDEX_2 i2(seg[0], seg[1]);
+		i2.Sort ();
+		if (osedgesht.Used (i2))
+		  osedgesht.Set (i2, 2);
+		else
+		  osedgesht.Set (i2, 1);
+	      }
+	  }
+      }
+
+
+    // one edge 1 segment, other 2 segments 
+    // yes, it happens !
+    point_on_edge_problem = 0;
+    for (int i = 1; i <= osedgesht.GetNBags(); i++)
+      for (int j = 1; j <= osedgesht.GetBagSize(i); j++)
+	{
+	  INDEX_2 i2; 
+	  int val;
+	  osedgesht.GetData (i, j, i2, val);
+
+	  const Point<3> & p1 = mesh[PointIndex(i2.I1())];
+	  const Point<3> & p2 = mesh[PointIndex(i2.I2())];
+	  Vec<3> v = p2 - p1;
+	  double vlen = v.Length();
+	  v /= vlen;
+	  for (pi = PointIndex::BASE; 
+	       pi < mesh.GetNP()+PointIndex::BASE; pi++)
+
+	    if (pi != i2.I1() && pi != i2.I2())
+	      {
+		const Point<3> & p = mesh[pi];
+		Vec<3> v2 = p - p1;
+		double lam = (v2 * v);
+		if (lam > 0 && lam < vlen)
+		  {
+		    Point<3> hp = p1 + lam * v;
+		    if (Dist (p, hp) < 1e-4 * vlen)
+		      {
+			PrintWarning ("Point on edge !!!");
+			cout << "seg: " << i2 << ", p = " << pi << endl;
+			osedgesht.Set (i2, 2);		      
+			point_on_edge_problem = 1;
+
+			(*testout) << "Point on edge" << endl
+				   << "seg = " << i2 << ", p = " << pi << endl
+				   << "pos = " << p << ", projected = " << hp << endl
+				   << "seg is = " << mesh.Point(i2.I1()) << " - " << mesh.Point(i2.I2()) << endl;
+		      }
+		  }
+	      }
+	}
+
+
+    // insert new points
+    osedges = -1;
+
+    int nseg = mesh.GetNSeg();
+    for (si = 0; si < nseg; si++)
+      {
+	const Segment & seg = mesh[si];
+	if (seg.seginfo && seg.edgenr >= 1 && seg.edgenr <= cntedge)
+	  {
+	    INDEX_2 i2(seg[0], seg[1]);
+	    i2.Sort ();
+	    if (osedgesht.Used (i2) &&
+		osedgesht.Get (i2) == 2 &&
+		osedges.Elem(seg.edgenr) == -1)
+	      {
+		Point<3> newp = Center (mesh[PointIndex(seg[0])],
+					mesh[PointIndex(seg[1])]);
+
+		ProjectToEdge (geometry.GetSurface(seg.surfnr1), 
+			       geometry.GetSurface(seg.surfnr2), 
+			       newp);
+
+		osedges.Elem(seg.edgenr) = 
+		  mesh.AddPoint (newp, mesh[PointIndex(seg[0])].GetLayer(), EDGEPOINT);
+		meshpoint_tree -> Insert (newp, osedges.Elem(seg.edgenr));
+	      }
+	  }
+      }
+    
+
+    for (int i = 1; i <= nseg; i++)
+      {
+	Segment & seg = mesh.LineSegment (i);
+	if (seg.edgenr >= 1 && seg.edgenr <= cntedge)
+	  {
+	    if (osedges.Get(seg.edgenr) != -1)
+	      {
+		Segment newseg = seg;
+		newseg[0] = osedges.Get(seg.edgenr);
+		seg[1] = osedges.Get(seg.edgenr);
+		mesh.AddSegment (newseg);
+	      }
+	  }
+      }
+
+  }
+
+
+
+  void EdgeCalculation :: 
+  FollowEdge (int pi1, int & ep, int & pos,
+	      const Array<int> & hsp,
+	      double h, const Mesh & mesh,
+	      Array<Point<3> > & edgepoints,
+	      Array<double> & curvelength)
+  {
+    int s1, s2, s1_rep, s2_rep;
+    double len, steplen, cursteplen, loch;
+    Point<3> p, np, pnp;
+    Vec<3> a1, a2, t;
+
+    Array<int> locind;
+
+    double size = geometry.MaxSize();  
+    double epspointdist2 = size * 1e-6;
+    epspointdist2 = sqr (epspointdist2);
+    int uselocalh = mparam.uselocalh;
+
+
+    s1_rep = specpoints[hsp.Get(pi1)].s1;
+    s2_rep = specpoints[hsp.Get(pi1)].s2;
+    s1 = specpoints[hsp.Get(pi1)].s1_orig;
+    s2 = specpoints[hsp.Get(pi1)].s2_orig;
+  
+    p = specpoints[hsp.Get(pi1)].p;
+    //ProjectToEdge (geometry.GetSurface(s1), 
+    //               geometry.GetSurface(s2), p);
+    geometry.GetSurface(s1) -> CalcGradient (p, a1);
+    geometry.GetSurface(s2) -> CalcGradient (p, a2);
+
+    t = Cross (a1, a2);
+    t.Normalize();
+
+    pos = (specpoints[hsp.Get(pi1)].v * t) > 0;
+    if (!pos) t *= -1;
+
+  
+    edgepoints.Append (p);
+    curvelength.Append (0);
+    len = 0;
+
+    // (*testout) << "geometry.GetSurface(s1) -> LocH (p, 3, 1, h) " << geometry.GetSurface(s1) -> LocH (p, 3, 1, h)
+    // << " geometry.GetSurface(s2) -> LocH (p, 3, 1, h) " << geometry.GetSurface(s2) -> LocH (p, 3, 1, h) << endl;
+
+    loch = min2 (geometry.GetSurface(s1) -> LocH (p, 3, 1, h), 
+		 geometry.GetSurface(s2) -> LocH (p, 3, 1, h));
+  
+  
+  
+    if (uselocalh)
+      {
+	double lh = mesh.GetH(p);
+	// (*testout) << "lh " << lh << endl;
+	if (lh < loch)
+	  loch = lh;
+      }
+
+    steplen = 0.1 * loch;
+  
+    do
+      {
+	if (multithread.terminate)
+	  return;
+      
+	if (fabs (p(0)) + fabs (p(1)) + fabs (p(2)) > 100000*size)
+	  {
+	    ep = 0;
+	    PrintWarning ("Give up line");
+	    break;
+	  }
+
+	if (steplen > 0.1 * loch) steplen = 0.1 * loch;
+      
+	steplen *= 2;
+	do
+	  {
+	    steplen *= 0.5;
+	    np = p + steplen * t;
+	    pnp = np;
+	    ProjectToEdge (geometry.GetSurface(s1), 
+			   geometry.GetSurface(s2), pnp);
+	  }
+	while (Dist (np, pnp) > 0.1 * steplen);
+
+      
+	cursteplen = steplen;
+	if (Dist (np, pnp) < 0.01 * steplen) steplen *= 2;
+      
+ 
+	np = pnp;
+	ep = 0;
+      
+	double hvtmin = 1.5 * cursteplen;
+      
+	Box<3> boxp (p - (2 * cursteplen) * Vec<3> (1, 1, 1),
+		     p + (2 * cursteplen) * Vec<3> (1, 1, 1));
+
+	searchtree -> GetIntersecting (boxp.PMin(), boxp.PMax(), locind);
+	
+	for (int i = 0; i < locind.Size(); i++)
+	  {
+	    Vec<3> hv = specpoints[locind[i]].p - p;
+	    if (hv.Length2() > 9 * cursteplen * cursteplen)
+	      continue;
+
+	    double hvt = hv * t;
+	    hv -= hvt * t;
+
+	    if (hv.Length() < 0.2 * cursteplen &&
+		hvt > 0 && 
+		//		  hvt < 1.5 * cursteplen &&
+		hvt < hvtmin && 
+		specpoints[locind[i]].unconditional == 1 &&
+		(specpoints[locind[i]].v + t).Length() < 0.4  ) 
+	      {
+		Point<3> hep = specpoints[locind[i]].p;
+		ProjectToEdge (geometry.GetSurface(s1), 
+			       geometry.GetSurface(s2), hep);            
+	      
+	      
+		if (Dist2 (hep, specpoints[locind[i]].p) < epspointdist2 )
+		  {
+		    geometry.GetSurface(s1) -> CalcGradient (hep, a1);
+		    geometry.GetSurface(s2) -> CalcGradient (hep, a2);
+		    Vec<3> ept = Cross (a1, a2);
+		    ept /= ept.Length();
+		    if (!pos) ept *= -1;
+		  
+		    if ( (specpoints[locind[i]].v + ept).Length() < 1e-4 )
+		      {
+			np = specpoints[locind[i]].p;
+
+			for (int jj = 0; jj < hsp.Size(); jj++)
+			  if (hsp[jj] == locind[i])
+			    ep = jj+1;
+			    
+			if (!ep) 
+			  cerr << "endpoint not found" << endl;
+			  //			ep = i;
+			hvtmin = hvt;
+			//			  break;
+		      }
+		  }
+	      }
+	  }
+
+
+
+
+	/*
+	for (int i = 1; i <= hsp.Size(); i++)
+	  {
+	    if (!boxp.IsIn (specpoints[hsp.Get(i)].p))
+	      continue;
+	  
+	    Vec<3> hv = specpoints[hsp.Get(i)].p - p;
+	    if (hv.Length2() > 9 * cursteplen * cursteplen)
+	      continue;
+
+	    double hvt = hv * t;
+	    hv -= hvt * t;
+	  
+	    if (hv.Length() < 0.2 * cursteplen &&
+		hvt > 0 && 
+		//		  hvt < 1.5 * cursteplen &&
+		hvt < hvtmin && 
+		specpoints[hsp.Get(i)].unconditional == 1 &&
+		(specpoints[hsp.Get(i)].v + t).Length() < 0.4  ) 
+	      {
+		Point<3> hep = specpoints[hsp.Get(i)].p;
+		ProjectToEdge (geometry.GetSurface(s1), 
+			       geometry.GetSurface(s2), hep);            
+	      
+	      
+		if (Dist2 (hep, specpoints[hsp.Get(i)].p) < epspointdist2 )
+		  {
+		    geometry.GetSurface(s1) -> CalcGradient (hep, a1);
+		    geometry.GetSurface(s2) -> CalcGradient (hep, a2);
+		    Vec<3> ept = Cross (a1, a2);
+		    ept /= ept.Length();
+		    if (!pos) ept *= -1;
+		  
+		    if ( (specpoints[hsp.Get(i)].v + ept).Length() < 1e-4 )
+		      {
+			np = specpoints[hsp.Get(i)].p;
+			ep = i;
+			hvtmin = hvt;
+			//			  break;
+		      }
+		  }
+	      }
+	  }
+	*/
+
+	loch = min2 (geometry.GetSurface(s1_rep) -> LocH (np, 3, 1, h), 
+		     geometry.GetSurface(s2_rep) -> LocH (np, 3, 1, h));
+        loch = max2 (loch, mparam.minh);
+
+	if (uselocalh)
+	  {
+	    double lh = mesh.GetH(np);
+	    if (lh < loch)
+	      loch = lh;
+	  }
+      
+      
+	len += Dist (p, np) / loch;
+	edgepoints.Append (np);
+	curvelength.Append (len);
+      
+	p = np;
+      
+	geometry.GetSurface(s1) -> CalcGradient (p, a1);
+	geometry.GetSurface(s2) -> CalcGradient (p, a2);
+	t = Cross (a1, a2);
+	t.Normalize();
+	if (!pos) t *= -1;
+      }
+    while (! ep);
+  }
+
+
+
+
+
+
+
+  void EdgeCalculation :: 
+  AnalyzeEdge (int s1, int s2, int s1_rep, int s2_rep, int pos, int layer,
+	       const Array<Point<3> > & edgepoints,
+	       Array<Segment> & refedges,
+	       Array<bool> & refedgesinv)
+  {
+    Segment seg;
+    Array<int> locsurfind, locsurfind2;
+
+    Array<int> edges_priority;
+
+    double size = geometry.MaxSize();
+    bool debug = 0;
+
+#ifdef DEVELOP
+    debug = 1;
+#endif
+    
+    if (debug)
+      {
+	(*testout) << "debug edge !!!" << endl;
+	(*testout) << "edgepoints = " << edgepoints << endl;
+	(*testout) << "s1, s2 = " << s1 << " - " << s2 << endl;
+	(*testout) << "s1_rep, s2_rep = " << s1_rep << " - " << s2_rep << endl;
+      }
+
+    refedges.SetSize(0);
+    refedgesinv.SetSize(0);
+    Point<3> hp = Center (edgepoints[0], edgepoints[1]);
+    ProjectToEdge (geometry.GetSurface(s1), geometry.GetSurface(s2), hp);
+    
+    if (debug)
+      *testout << "hp = " << hp << endl;
+
+    Vec<3> t, a1, a2;
+    geometry.GetSurface(s1) -> CalcGradient (hp, a1);
+    geometry.GetSurface(s2) -> CalcGradient (hp, a2);
+    t = Cross (a1, a2);
+    t.Normalize();
+    if (!pos) t *= -1;    
+
+
+  
+    for (int i = 0; i < geometry.GetNTopLevelObjects(); i++)
+      {
+	Solid * locsol;
+
+	if (geometry.GetTopLevelObject(i)->GetLayer() != layer) 
+	  continue;
+      
+	const Solid * sol = geometry.GetTopLevelObject(i)->GetSolid();
+	const Surface * surf = geometry.GetTopLevelObject(i)->GetSurface();
+
+	sol -> TangentialSolid (hp, locsol, locsurfind, size*ideps);
+
+	//*testout << "hp = " << hp << endl;
+	//(*testout) << "locsol: " << endl;
+	//if (locsol) locsol->Print(*testout);
+	//(*testout) << endl;
+
+
+	if (!locsol) continue;
+
+	BoxSphere<3> boxp (hp, hp);
+	boxp.Increase (1e-8*size);
+	boxp.CalcDiamCenter();
+      
+	ReducePrimitiveIterator rpi(boxp);
+	UnReducePrimitiveIterator urpi;
+      
+	((Solid*)locsol) -> IterateSolid (rpi);
+
+	locsol -> CalcSurfaceInverse ();
+      
+	if (!surf)
+	  {
+	    locsol -> GetTangentialSurfaceIndices (hp,locsurfind,ideps*size);
+	  }
+	else
+	  {
+	    /*
+	      if (fabs (surf->CalcFunctionValue (hp)) < 1e-6)
+	      continue;
+	    */
+	    locsurfind.SetSize(1);
+	    locsurfind[0] = -1;
+	    for (int j = 0; j < geometry.GetNSurf(); j++)
+	      if (geometry.GetSurface(j) == surf)
+		{
+		  locsurfind[0] = j;
+		  //		      geometry.GetSurfaceClassRepresentant(j);
+		  break;
+		}
+	  }
+
+	((Solid*)locsol) -> IterateSolid (urpi);
+
+      
+	if (debug)
+	  (*testout) << "edge of tlo " << i << ", has " << locsurfind.Size() << " faces." << endl;
+      
+
+	for (int j = locsurfind.Size()-1; j >= 0; j--)
+	  if (fabs (geometry.GetSurface(locsurfind[j])
+		    ->CalcFunctionValue (hp) ) > ideps*size)
+	    locsurfind.Delete(j);
+      
+	if (debug)
+	  (*testout) << locsurfind.Size() << " faces on hp" << endl;
+
+
+
+	for (int j = 0; j < locsurfind.Size(); j++)
+	  {      
+	    int lsi = locsurfind[j];
+	    int rlsi = geometry.GetSurfaceClassRepresentant(lsi);
+	  
+
+	    // n is outer normal to solid
+	    Vec<3> n = geometry.GetSurface(lsi) -> GetNormalVector (hp);
+            if (debug)
+              *testout << "n1 = " << n << endl;
+	    if (geometry.GetSurface (lsi)->Inverse())
+	      n *= -1;
+	  
+	    if (fabs (t * n) > 1e-4) continue;
+	    if (debug)
+	      {
+		(*testout) << "face " << locsurfind[j] << ", rep = " << rlsi 
+			   << " has (t*n) = " << (t*n) << endl;
+		(*testout) << "n = " << n << endl;
+	      }
+	  
+	    // rn is normal to class representant
+	    Vec<3> rn = geometry.GetSurface(rlsi) -> GetNormalVector (hp);
+	    if (debug)
+	      {
+		(*testout) << "rn = " << rn << endl;
+	      }
+	    
+	    //if( n*rn < 0)
+	    // rn *= -1;
+
+	    bool sameasref = ((n * rn) > 0);
+
+	    //m = Cross (t, rn);
+	    Vec<3> m = Cross (t, n); 
+	    if(!sameasref) m*=-1.;
+	    
+	    m.Normalize();
+
+	    
+	    if (debug)
+	      (*testout) << "m = " << m << endl;
+
+
+	    //bool founddirection = false;
+	    //int k;
+	    double eps = 1e-8*size;
+
+	    Array<bool> pre_ok(2);
+
+ 	    do
+ 	      {
+ 		eps *= 0.5;
+ 		pre_ok[0] = (locsol -> VectorIn2 (hp, m, n, eps) == IS_OUTSIDE &&
+			     locsol -> VectorIn2 (hp, m, -1. * n, eps) == IS_INSIDE);
+ 		pre_ok[1] = (locsol -> VectorIn2 (hp, -1.*m, n, eps) == IS_OUTSIDE &&
+			     locsol -> VectorIn2 (hp, -1.*m, -1. * n, eps) == IS_INSIDE);
+		
+		if (debug)
+		  {
+		    *testout << "eps = " << eps << endl;
+		    *testout << "in,1 = " << locsol -> VectorIn2 (hp, m, n, eps) << endl;
+		    *testout << "in,1 = " << locsol -> VectorIn2 (hp, m, -1. * n, eps) << endl;
+		    *testout << "in,1 = " << locsol -> VectorIn2 (hp, -1.*m, n, eps) << endl;
+		    *testout << "in,1 = " << locsol -> VectorIn2 (hp, -1.*m, -1. * n, eps) << endl;
+		  }
+ 	      }
+ 	    while(pre_ok[0] && pre_ok[1] && eps > 1e-16*size);
+
+            if (debug)
+              {
+                *testout << "eps = " << eps << ", size = " << size << endl;
+                *testout << "pre_ok[0,1] = " << pre_ok[0] << "," << pre_ok[1] << endl;
+              }
+
+	    eps = 1e-8*size;
+	    
+
+	    for (int k = 1; k <= 2; k ++)
+	      {
+		bool edgeinv = (k == 2);
+	      
+		if (debug)
+		  {
+		    (*testout) << "onface(" << hp << ", " << m << ")= " << flush;
+		    (*testout) << locsol->OnFace (hp, m, eps) << flush;
+		    (*testout) << " n " << n << flush;
+		    (*testout) << " vec2in = "
+			       << locsol -> VectorIn2 (hp, m, n, eps) << " and " 
+			       << locsol -> VectorIn2 (hp, m, -1 * n, eps) << endl;
+		  }
+
+		//	      if (locsol -> OnFace (hp, m))
+		
+
+		// one side must be inside, the other must be outside
+		bool ok = (pre_ok[k-1] || 
+			   (locsol -> VectorIn2 (hp, m, n, eps) == IS_OUTSIDE &&
+			    locsol -> VectorIn2 (hp, m, -1 * n, eps) == IS_INSIDE));
+
+		if (debug)
+		  (*testout) << "ok (before) " << ok <<  endl;
+
+		// compute second order approximation
+		// curve = hp + t m + t*t/2 m2
+		Vec<3> grad, m2;
+		Mat<3> hesse;
+		geometry.GetSurface(lsi) -> CalcGradient (hp, grad);
+		geometry.GetSurface(lsi) -> CalcHesse (hp, hesse);
+		double fac = -(m * (hesse * m)) / (grad * grad);
+		m2 = fac * grad;
+		// (*testout) << "hp = " << hp << ", m = " << m << ", m2 = " << m2 << endl;
+
+		Solid * locsol2;
+		locsol -> TangentialSolid3 (hp, m, m2, locsol2, locsurfind2, ideps*size);
+		if (!locsol2) ok = 0;
+		delete locsol2;
+
+
+		if (ok)
+		  {
+		    if (debug)
+		      (*testout) << "is true" << endl;
+		    int hi = 0;
+		    for (int l = 1; !hi && l <= refedges.Size(); l++)
+		      {
+			   if (refedges.Get(l).si == rlsi &&     // JS sept 2006
+			       // if (refedges.Get(l).si == lsi &&
+			       refedgesinv.Get(l) == edgeinv)
+			     {
+			       hi = l;
+			     }
+		      }
+		  
+		    if (!hi)
+		      {
+			 seg.si = rlsi;  // JS Sept 2006
+			 // seg.si = lsi;
+			seg.domin = -1;
+			seg.domout = -1;
+			seg.tlosurf = -1;
+			//seg.surfnr1 = s1_rep;
+			//seg.surfnr2 = s2_rep;
+			seg.surfnr1 = s1;
+			seg.surfnr2 = s2;
+			hi = refedges.Append (seg);
+			refedgesinv.Append (edgeinv);
+			edges_priority.Append((pre_ok[k-1]) ? 1 : 0);
+		      }
+		    else
+		      {
+			if(edges_priority[hi-1] / 10 == -i-1)
+			  edges_priority[hi-1] = 10*(i+1);
+			else
+			  edges_priority[hi-1] = -10*(i+1);
+		      }
+		  
+		    if (!surf)
+		      {
+			if (sameasref)
+			  refedges.Elem(hi).domin = i;
+			else 
+			  refedges.Elem(hi).domout = i;
+		      }
+		    else
+		      refedges.Elem(hi).tlosurf = i;
+
+		    if(pre_ok[k-1])
+		      edges_priority[hi-1] = 1;
+		    
+
+		    if (debug)
+		      (*testout) << "add ref seg:" 
+				 << "si = " << refedges.Get(hi).si
+				 << ", domin = " << refedges.Get(hi).domin
+				 << ", domout = " << refedges.Get(hi).domout
+				 << ", surfnr1/2 = " << refedges.Get(hi).surfnr1
+				 << ", " << refedges.Get(hi).surfnr2
+				 << ", inv = " << refedgesinv.Get(hi) 
+				 << ", refedgenr = " << hi
+				 << ", priority = " << edges_priority[hi-1]
+				 << ", hi = " << hi 
+				 << endl;
+		  }
+		else
+		  {
+		    if (debug)
+		      (*testout) << "is false" << endl;
+		  }
+		m *= -1;
+	      } 
+	  }
+	delete locsol;          
+      }
+
+   
+    if (debug)
+      {
+	*testout << "Refsegments, before delete: " << endl << refedges << endl;
+	*testout << "inv: " << endl << refedgesinv << endl;
+      }
+    
+    BitArray todelete(refedges.Size());
+    todelete.Clear();
+
+
+    for(int i=0; i<refedges.Size()-1; i++)
+      {
+	for(int j=i+1; !todelete.Test(i) && j<refedges.Size(); j++)
+	  {
+	    if(todelete.Test(j))
+	      continue;
+
+	    if(refedges[i].si == refedges[j].si &&
+	       refedges[i].domin == refedges[j].domin &&
+	       refedges[i].domout == refedges[j].domout &&
+	       geometry.GetSurfaceClassRepresentant(refedges[i].surfnr1) == geometry.GetSurfaceClassRepresentant(refedges[j].surfnr1) &&
+	       geometry.GetSurfaceClassRepresentant(refedges[i].surfnr2) == geometry.GetSurfaceClassRepresentant(refedges[j].surfnr2)
+	       // && refedgesinv[i] == refedgesinv[j] // JS, 20060802
+	       )
+	      {
+		if(debug)
+		  (*testout) << "equal segments: " << refedges[i] << " pri " << edges_priority[i] 
+			     << " tlosurf " << refedges[i].tlosurf
+			     << "\n and " << refedges[j] << " pri " << edges_priority[j]
+			     << " tlosurf " << refedges[i].tlosurf << endl;
+		
+		if(edges_priority[i] < 10 && edges_priority[i] < edges_priority[j])
+		  {
+		    todelete.Set(i);
+		  }
+		else if (edges_priority[j] < 10 && edges_priority[i] > edges_priority[j])
+		  {
+		    todelete.Set(j);
+		  }
+	      }
+	  }
+	  
+      }
+    
+    int num = refedges.Size();
+
+    for(int i=refedges.Size()-1; num>2 && i>=0; i--)
+      if(todelete.Test(i))
+	{
+	  refedges.Delete(i);
+	  refedgesinv.Delete(i);
+	  num--;
+	}
+
+    
+    if (debug)
+      {
+	*testout << "Refsegments: " << endl << refedges << endl;
+      }
+  }
+
+
+
+  void EdgeCalculation :: 
+  StoreEdge (const Array<Segment> & refedges,
+	     const Array<bool> & refedgesinv,
+	     const Array<Point<3> > & edgepoints,
+	     const Array<double> & curvelength,
+	     int layer,
+	     Mesh & mesh)
+  {
+  
+    // Calculate optimal element-length
+    int i, j, k;
+    PointIndex pi;
+    int ne;
+
+    double len, corr, lam;
+    PointIndex thispi, lastpi;
+    Point<3> p, np;
+    Segment seg;
+
+    const Surface * surf1 = geometry.GetSurface (refedges.Get(1).surfnr1);
+    const Surface * surf2 = geometry.GetSurface (refedges.Get(1).surfnr2);
+
+    (*testout) << "s1 " << refedges.Get(1).surfnr1 << " s2 " << refedges.Get(1).surfnr2
+	       << " rs1 " << geometry.GetSurfaceClassRepresentant(refedges.Get(1).surfnr1)
+	       << " rs2 " << geometry.GetSurfaceClassRepresentant(refedges.Get(1).surfnr2) << endl;
+
+    len = curvelength.Last();
+    ne = int (len + 0.5);
+    if (ne == 0) ne = 1;
+    if (Dist (edgepoints.Get(1), edgepoints.Last()) < 1e-8*geometry.MaxSize() && 
+	ne <= 6) 
+      ne = 6;
+    corr = len / ne;
+
+    // generate initial point
+    p = edgepoints.Get(1);
+    lastpi = -1;
+
+    /*
+    for (pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+      if (Dist (mesh[pi], p) < 1e-6)
+	{
+	  lastpi = pi;
+	  break;
+	}
+    */
+
+    const double di=1e-7*geometry.MaxSize();
+
+    Array<int> locsearch;
+    meshpoint_tree -> GetIntersecting (p-Vec<3> (di,di,di),
+				       p+Vec<3> (di,di,di), locsearch);
+    if (locsearch.Size())
+      lastpi = locsearch[0];
+				       
+
+
+    if (lastpi == -1)
+      {
+	lastpi = mesh.AddPoint (p, layer, FIXEDPOINT);
+	meshpoint_tree -> Insert (p, lastpi); 
+	// (*testout) << "test1, store point " << lastpi << ", p = " << p << endl;
+      }
+  
+    j = 1;
+    for (i = 1; i <= ne; i++)
+      {
+	while (curvelength.Get(j) < i * corr && j < curvelength.Size()) j++;
+
+
+	lam = (i * corr - curvelength.Get(j-1)) / 
+	  (curvelength.Get(j) - curvelength.Get(j-1));
+
+	np(0) = (1-lam) * edgepoints.Get(j-1)(0) + lam * edgepoints.Get(j)(0);
+	np(1) = (1-lam) * edgepoints.Get(j-1)(1) + lam * edgepoints.Get(j)(1);
+	np(2) = (1-lam) * edgepoints.Get(j-1)(2) + lam * edgepoints.Get(j)(2);
+      
+	thispi = -1;
+	if (i == ne)
+	  {
+	    /*
+	  for (pi = PointIndex::BASE; 
+	       pi < mesh.GetNP()+PointIndex::BASE; pi++)
+	    if (Dist(mesh[pi], np) < 1e-6)
+	      thispi = pi;
+	    */
+	    
+	    meshpoint_tree -> GetIntersecting (np-Vec<3> (di,di,di),
+					       np+Vec<3> (di,di,di), locsearch);
+	    if (locsearch.Size())
+	      thispi = locsearch[0];
+	  }
+
+	if (thispi == -1)
+	  {
+	    ProjectToEdge (surf1, surf2, np);
+	    thispi = mesh.AddPoint (np, layer, (i==ne) ? FIXEDPOINT : EDGEPOINT);
+	   
+	    meshpoint_tree -> Insert (np, thispi);
+	    // (*testout) << "test2, store point " << thispi << ", p = " << np << endl;
+	  }
+
+	for (k = 1; k <= refedges.Size(); k++)
+	  {
+	    if (refedgesinv.Get(k))
+	      {
+		seg[0] = lastpi;
+		seg[1] = thispi;
+	      }
+	    else
+	      {
+		seg[0] = thispi;
+		seg[1] = lastpi;
+	      }
+	    seg.si = refedges.Get(k).si;
+	    seg.domin = refedges.Get(k).domin;
+	    seg.domout = refedges.Get(k).domout;
+	    seg.tlosurf = refedges.Get(k).tlosurf;
+	    seg.edgenr = refedges.Get(k).edgenr;
+	    seg.surfnr1 = refedges.Get(k).surfnr1;
+	    seg.surfnr2 = refedges.Get(k).surfnr2;
+	    seg.seginfo = 0;
+	    if (k == 1) seg.seginfo = (refedgesinv.Get(k)) ? 2 : 1;
+	    mesh.AddSegment (seg);
+	    //(*testout) << "add seg " << mesh[seg.p1] << "-" << mesh[seg.p2] << endl;
+	    //(*testout) << "refedge " << k << " surf1 " << seg.surfnr1 << " surf2 " << seg.surfnr2 << " inv " << refedgesinv.Get(k) << endl;
+	  
+	    double maxh = min2 (geometry.GetSurface(seg.surfnr1)->GetMaxH(),
+				geometry.GetSurface(seg.surfnr2)->GetMaxH());
+			      
+	    if (seg.domin != -1)
+	      {
+		const Solid * s1 = 
+		  geometry.GetTopLevelObject(seg.domin) -> GetSolid();
+		maxh = min2 (maxh, s1->GetMaxH());
+		maxh = min2 (maxh, geometry.GetTopLevelObject(seg.domin)->GetMaxH());
+		mesh.RestrictLocalH (p, maxh);
+		mesh.RestrictLocalH (np, maxh);
+	      }
+	    if (seg.domout != -1)
+	      {
+		const Solid * s1 = 
+		  geometry.GetTopLevelObject(seg.domout) -> GetSolid();
+		maxh = min2 (maxh, s1->GetMaxH());
+		maxh = min2 (maxh, geometry.GetTopLevelObject(seg.domout)->GetMaxH());
+		mesh.RestrictLocalH (p, maxh);
+		mesh.RestrictLocalH (np, maxh);
+	      }
+	    if (seg.tlosurf != -1)
+	      {
+		double hi = geometry.GetTopLevelObject(seg.tlosurf) -> GetMaxH();
+		maxh = min2 (maxh, hi);
+		mesh.RestrictLocalH (p, maxh);
+		mesh.RestrictLocalH (np, maxh);
+	      }	  
+	  }
+      
+	p = np;
+	lastpi = thispi;
+      }
+
+#ifdef DEVELOP
+    (*testout) << " eplast = " << lastpi << " = " << p << endl;
+#endif
+  }
+  
+
+
+
+
+
+  void EdgeCalculation :: 
+  StoreShortEdge (const Array<Segment> & refedges,
+		  const Array<bool> & refedgesinv,
+		  const Array<Point<3> > & edgepoints,
+		  const Array<double> & curvelength,
+		  int layer,
+		  Mesh & mesh)
+  {
+  
+    // Calculate optimal element-length
+    PointIndex pi;
+    // int ne;
+    Segment seg;
+
+    /*
+      double len, corr, lam;
+      int thispi, lastpi;
+      Point<3> p, np;
+
+
+      const Surface * surf1 = geometry.GetSurface (refedges.Get(1).surfnr1);
+      const Surface * surf2 = geometry.GetSurface (refedges.Get(1).surfnr2);
+
+      len = curvelength.Last();
+      ne = int (len + 0.5);
+      if (ne == 0) ne = 1;
+      if (Dist2 (edgepoints[1], edgepoints.Last()) < 1e-8 && 
+      ne <= 6) 
+      ne = 6;
+      corr = len / ne;
+    */
+
+    // generate initial point
+    Point<3> p = edgepoints[0];
+    PointIndex pi1 = -1;
+    for (pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+
+      if (Dist (mesh[pi], p) < 1e-6*geometry.MaxSize())
+	{
+	  pi1 = pi;
+	  break;
+	}
+
+    if (pi1 == -1) 
+      {
+	pi1 = mesh.AddPoint (p, layer, FIXEDPOINT);
+	meshpoint_tree -> Insert (p, pi1);
+	// (*testout) << "test3, store point " << pi1 << ", p = " << p << endl;
+      }
+
+    p = edgepoints.Last();
+    PointIndex pi2 = -1;
+    for (pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+
+      if (Dist (mesh[pi], p) < 1e-6*geometry.MaxSize())
+	{
+	  pi2 = pi;
+	  break;
+	}
+    if (pi2==-1) 
+      {
+	pi2 = mesh.AddPoint (p, layer, FIXEDPOINT);
+	meshpoint_tree -> Insert (p, pi2);
+	// (*testout) << "test4, store point " << pi2 << ", p = " << p << endl;
+      }
+
+    /*
+  
+    j = 1;
+    for (i = 1; i <= ne; i++)
+    {
+    while (curvelength[j] < i * corr && j < curvelength.Size()) j++;
+      
+    lam = (i * corr - curvelength[j-1]) / 
+    (curvelength[j] - curvelength[j-1]);
+      
+    np(0) = (1-lam) * edgepoints[j-1](0) + lam * edgepoints[j](0);
+    np(1) = (1-lam) * edgepoints[j-1](1) + lam * edgepoints[j](1);
+    np(2) = (1-lam) * edgepoints[j-1](2) + lam * edgepoints[j](2);
+      
+      
+    thispi = 0;
+    if (i == ne)
+    for (j = 1; j <= mesh.GetNP(); j++)
+    if (Dist(mesh.Point(j), np) < 1e-6)
+    thispi = j;
+      
+    if (!thispi)
+    {
+    ProjectToEdge (surf1, surf2, np);
+    thispi = mesh.AddPoint (np);
+    }
+    */
+
+    // (*testout) << "short edge " << pi1 << " - " << pi2 << endl;
+  
+    for (int k = 1; k <= refedges.Size(); k++)
+      {
+	if (refedgesinv.Get(k))
+	  {
+	    seg[0] = pi1;
+	    seg[1] = pi2;
+	  }
+	else
+	  {
+	    seg[0] = pi2;
+	    seg[1] = pi1;
+	  }
+
+	seg.si = refedges.Get(k).si;
+	seg.domin = refedges.Get(k).domin;
+	seg.domout = refedges.Get(k).domout;
+	seg.tlosurf = refedges.Get(k).tlosurf;
+	seg.edgenr = refedges.Get(k).edgenr;
+	seg.surfnr1 = refedges.Get(k).surfnr1;
+	seg.surfnr2 = refedges.Get(k).surfnr2;
+	seg.seginfo = 0;
+	if (k == 1) seg.seginfo = (refedgesinv.Get(k)) ? 2 : 1;
+	mesh.AddSegment (seg);
+	//	  (*testout) << "add seg " << seg[0] << "-" << seg[1] << endl;
+      }
+  }
+  
+
+
+
+
+
+
+  void EdgeCalculation :: 
+  CopyEdge (const Array<Segment> & refedges,
+	    const Array<bool> & refedgesinv,
+	    int copyfromedge, 
+	    const Point<3> & fromstart, const Point<3> & fromend,
+	    const Point<3> & tostart, const Point<3> & toend,
+	    int copyedgeidentification, 
+	    int layer,
+	    Mesh & mesh)
+  {
+    int k;
+    PointIndex pi;
+
+    double size = geometry.MaxSize();
+    
+    // copy start and end points
+    for (int i = 1; i <= 2; i++)
+      {
+	Point<3> fromp =
+	  (i == 1) ? fromstart : fromend;
+	Point<3> top =
+	  (i == 1) ? tostart : toend;
+      
+	PointIndex frompi = -1;
+	PointIndex topi = -1;
+	for (pi = PointIndex::BASE; 
+	     pi < mesh.GetNP()+PointIndex::BASE; pi++)
+	  {
+	    if (Dist2 (mesh[pi], fromp) <= 1e-16*size)
+	      frompi = pi;
+	    if (Dist2 (mesh[pi], top) <= 1e-16*size)
+	      topi = pi;
+	  }
+
+	
+	if (topi == -1)
+	  {
+	    topi = mesh.AddPoint (top, layer, FIXEDPOINT);
+	    meshpoint_tree -> Insert (top, topi);
+	  }
+
+	const Identification & csi = 
+	  (*geometry.identifications.Get(copyedgeidentification));
+
+
+	if (csi.Identifyable (mesh[frompi], mesh[topi]))
+	  mesh.GetIdentifications().Add(frompi, topi, copyedgeidentification);
+	else if (csi.Identifyable (mesh[topi], mesh[frompi]))
+	  mesh.GetIdentifications().Add(topi, frompi, copyedgeidentification);
+	else
+	  {
+	    cerr << "edgeflw.cpp: should identify, but cannot";
+	    exit(1);
+	  }
+#ifdef DEVELOP
+	(*testout) << "adding identification " << mesh[frompi] << "; " << mesh[topi]
+		   << " (id " << copyedgeidentification <<")" << endl;
+#endif
+
+
+	/*
+	  (*testout) << "Add Identification from CopyEdge, p1 = " 
+	  << mesh[PointIndex(frompi)] << ", p2 = " 
+	  << mesh[PointIndex(topi)] << endl;
+
+	  mesh.GetIdentifications().Add(frompi, topi, copyedgeidentification);
+	*/
+      }
+
+    int oldns = mesh.GetNSeg();
+    for (int i = 1; i <= oldns; i++)
+      {
+	// real copy, since array might be reallocated !!
+	const Segment oldseg = mesh.LineSegment(i);
+	if (oldseg.edgenr != copyfromedge)
+	  continue;
+	if (oldseg.seginfo == 0)
+	  continue;
+
+	int pi1 = oldseg[0];
+	int pi2 = oldseg[1];
+
+	int npi1 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi1);
+	int npi2 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi2);
+
+	//(*testout) << "copy edge, pts = " << npi1 << " - " << npi2 << endl;
+
+	Segment seg;
+
+	for (k = 1; k <= refedges.Size(); k++)
+	  {
+	    bool inv = refedgesinv.Get(k);
+
+	    // other edge is inverse
+	    if (oldseg.seginfo == 1)
+	      inv = !inv;
+
+	    //	  (*testout) << "inv, now = " << inv << endl;
+
+	    if (inv)
+	      {
+		seg[0] = npi1;
+		seg[1] = npi2;
+	      }
+	    else
+	      {
+		seg[0] = npi2;
+		seg[1] = npi1;
+	      }
+	    seg.si = refedges.Get(k).si;
+	    seg.domin = refedges.Get(k).domin;
+	    seg.domout = refedges.Get(k).domout;
+	    seg.tlosurf = refedges.Get(k).tlosurf;
+	    seg.edgenr = refedges.Get(k).edgenr;
+	    seg.surfnr1 = refedges.Get(k).surfnr1;
+	    seg.surfnr2 = refedges.Get(k).surfnr2;
+	    seg.seginfo = 0;
+	    if (k == 1) seg.seginfo = refedgesinv.Get(k) ? 2 : 1;
+	    mesh.AddSegment (seg);
+	    //	  (*testout) << "copy seg " << seg[0] << "-" << seg[1] << endl;
+#ifdef DEVELOP
+
+	    (*testout) << "copy seg, face = " << seg.si << ": " 
+		       << " inv = " << inv << ", refinv = " << refedgesinv.Get(k)
+		       << mesh.Point(seg[0]) << ", " << mesh.Point(seg[1]) << endl;
+#endif
+
+	  }
+      
+      }   
+  }
+  
+
+
+
+
+
+
+  void EdgeCalculation :: 
+  FindClosedSurfaces (double h, Mesh & mesh)
+  {
+    // if there is no special point at a sphere, one has to add a segment pair
+  
+    int i, j; 
+    int nsol; 
+    int nsurf = geometry.GetNSurf();
+    int layer(0);
+
+    BitArray pointatsurface (nsurf);
+    Point<3> p1, p2;
+    Vec<3> nv, tv;
+    Solid * tansol;
+    Array<int> tansurfind;
+    //  const Solid * sol;
+
+    double size = geometry.MaxSize();
+    nsol = geometry.GetNTopLevelObjects();
+    
+
+    pointatsurface.Clear();
+  
+    /*
+      for (i = 1; i <= specpoints.Size(); i++)
+      {
+      int classrep;
+
+      classrep = geometry.GetSurfaceClassRepresentant (specpoints[i].s1);
+      pointatsurface.Set (classrep);
+      classrep = geometry.GetSurfaceClassRepresentant (specpoints[i].s2);
+      pointatsurface.Set (classrep);
+      //      pointatsurface.Set (specpoints[i].s1);
+      //      pointatsurface.Set (specpoints[i].s2);
+      }
+    */
+    for (i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	const Segment & seg = mesh.LineSegment(i);
+	int classrep;
+
+#ifdef DEVELOP      
+	(*testout) << seg.surfnr1 << ", " << seg.surfnr2 << ", si = " << seg.si << endl;
+#endif
+	classrep = geometry.GetSurfaceClassRepresentant (seg.si);
+
+	pointatsurface.Set (classrep);
+      }
+
+  
+    for (i = 0; i < nsurf; i++)
+      {
+	int classrep = geometry.GetSurfaceClassRepresentant (i);
+
+	if (!pointatsurface.Test(classrep))
+	  {
+	    const Surface * s = geometry.GetSurface(i);
+	    p1 = s -> GetSurfacePoint();
+	    nv = s -> GetNormalVector (p1);
+		    
+	    double hloc = 
+	      min2 (s->LocH (p1, 3, 1, h), mesh.GetH(p1));
+
+	    tv = nv.GetNormal ();
+	    tv *=  (hloc / tv.Length());
+	    p2 = p1 + tv;
+	    s->Project (p2);
+	  
+		    
+	    Segment seg1;
+	    seg1.si = i;
+	    seg1.domin = -1;
+	    seg1.domout = -1;
+
+	    Segment seg2;
+	    seg2.si = i;
+	    seg2.domin = -1;
+	    seg2.domout = -1;
+
+	    seg1.surfnr1 = i;
+	    seg2.surfnr1 = i;
+	    seg1.surfnr2 = i;
+	    seg2.surfnr2 = i;
+
+	    for (j = 0; j < nsol; j++)
+	      {
+		if (geometry.GetTopLevelObject(j)->GetSurface())
+		  continue;
+		  
+		const Solid * sol = geometry.GetTopLevelObject(j)->GetSolid();
+		sol -> TangentialSolid (p1, tansol, tansurfind, ideps*size);
+		layer = geometry.GetTopLevelObject(j)->GetLayer();
+
+		
+		if (tansol)
+		  {
+		    tansol -> GetSurfaceIndices (tansurfind);
+		
+		    if (tansurfind.Size() == 1 && tansurfind.Get(1) == i)
+		      {
+			if (!tansol->VectorIn(p1, nv))
+			  {
+			    seg1.domin = j;
+			    seg2.domin = j;
+			    seg1.tlosurf = j;
+			    seg2.tlosurf = j;
+			  }
+			else
+			  {
+			    seg1.domout = j;
+			    seg2.domout = j;
+			    seg1.tlosurf = j;
+			    seg2.tlosurf = j;
+			  }
+			//        seg.s2 = i;
+			//        seg.invs1 = surfaces[i] -> Inverse();
+			//        seg.invs2 = ! (surfaces[i] -> Inverse());
+		      }
+		    delete tansol;
+		  }
+	      }
+
+
+	    if (seg1.domin != -1 || seg1.domout != -1)
+	      {
+		mesh.AddPoint (p1, layer, EDGEPOINT);
+		mesh.AddPoint (p2, layer, EDGEPOINT);
+		seg1[0] = mesh.GetNP()-1;
+		seg1[1] = mesh.GetNP();
+		seg2[1] = mesh.GetNP()-1;
+		seg2[0] = mesh.GetNP();
+		seg1.geominfo[0].trignum = 1;
+		seg1.geominfo[1].trignum = 1;
+		seg2.geominfo[0].trignum = 1;
+		seg2.geominfo[1].trignum = 1;
+		mesh.AddSegment (seg1);
+		mesh.AddSegment (seg2);
+
+		PrintMessage (5, "Add line segment to smooth surface");
+
+#ifdef DEVELOP
+		(*testout) << "Add segment at smooth surface " << i;
+		if (i != classrep) (*testout) << ", classrep = " << classrep;
+		(*testout) << ": "
+			   << mesh.Point (mesh.GetNP()-1) << " - "
+			   << mesh.Point (mesh.GetNP()) << endl;
+#endif
+	      }
+	  }
+      }
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/csg/edgeflw.hpp b/contrib/Netgen/libsrc/csg/edgeflw.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4c2fc9495a72f4b2295dc0d732b92b4d5b7b208e
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/edgeflw.hpp
@@ -0,0 +1,110 @@
+#ifndef FILE_EDGEFLW
+#define FILE_EDGEFLW
+
+/**************************************************************************/
+/* File:   edgeflw.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+
+
+  /*
+  
+  Edge - following function and
+  Projection to edge of implicitly given edge
+
+  */
+ 
+
+  /**
+     Calculates edges.
+     The edges of a solid geometry are computed. Special
+     points have to be given.
+  */
+  extern void CalcEdges (const CSGeometry & geometry,
+			 const Array<SpecialPoint> & specpoints,
+			 double h, Mesh & mesh);
+
+
+
+
+
+  class EdgeCalculation
+  {
+    const CSGeometry & geometry;
+    Array<SpecialPoint> & specpoints;
+    Point3dTree * searchtree;
+    Point3dTree * meshpoint_tree;
+    int cntedge;
+
+    double ideps;
+
+  public:
+    EdgeCalculation (const CSGeometry & ageometry,
+		     Array<SpecialPoint> & aspecpoints);
+
+    ~EdgeCalculation();
+
+    void SetIdEps(const double epsin) {ideps = epsin;}
+
+    void Calc(double h, Mesh & mesh);
+
+
+  private:
+    void CalcEdges1 (double h, Mesh & mesh);
+  
+
+    void FollowEdge (int pi1, int & ep, int & pos,
+		     // const Array<SpecialPoint> & hsp,
+		     const Array<int> & hsp,
+		     double h, const Mesh & mesh,
+		     Array<Point<3> > & edgepoints,
+		     Array<double> & curvelength);
+		   
+
+    void AnalyzeEdge (int s1, int s2, int s1_rep, int s2_rep, int pos, int layer,
+		      const Array<Point<3> > & edgepoints,
+		      Array<Segment> & refedges,
+		      Array<bool> & refedgesinv);
+
+    void StoreEdge (const Array<Segment> & refedges,
+		    const Array<bool> & refedgesinv,
+		    const Array<Point<3> > & edgepoints,
+		    const Array<double> & curvelength,
+		    int layer,
+		    Mesh & mesh);
+
+    void StoreShortEdge (const Array<Segment> & refedges,
+			 const Array<bool> & refedgesinv,
+			 const Array<Point<3> > & edgepoints,
+			 const Array<double> & curvelength,
+			 int layer,
+			 Mesh & mesh);
+
+    void CopyEdge (const Array<Segment> & refedges,
+		   const Array<bool> & refedgesinv,
+		   int copyfromedge, 
+		   const Point<3> & fromstart, const Point<3> & fromend,
+		   const Point<3> & tostart, const Point<3> & toend,
+		   int copyedgeidentification,
+		   int layer,
+		   Mesh & mesh);
+
+  
+    void SplitEqualOneSegEdges (Mesh & mesh);
+    void FindClosedSurfaces (double h, Mesh & mesh);
+
+
+  public:
+    bool point_on_edge_problem;
+
+  };
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/explicitcurve2d.cpp b/contrib/Netgen/libsrc/csg/explicitcurve2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81c0e7ac55648be40bed209143b9e32d8b77186a
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/explicitcurve2d.cpp
@@ -0,0 +1,160 @@
+#include <mystdlib.h>
+#include <csg.hpp>
+
+namespace netgen
+{
+ExplicitCurve2d :: ExplicitCurve2d ()
+  {
+    ;
+  }
+  
+  
+void ExplicitCurve2d :: Project (Point<2> & p) const
+  {
+  double t;
+  t = ProjectParam (p);
+  p = Eval (t);
+  }
+
+double ExplicitCurve2d :: NumericalProjectParam (const Point<2> & p, double lb, double ub) const
+  {
+  double t(-1);
+  Vec<2> tan;
+  Vec<2> curv;
+  Point<2> cp;
+  double f, fl, fu;
+  int cnt;
+  
+  tan = EvalPrime (lb);
+  cp = Eval (lb);
+  fl = tan * (cp - p);
+  if (fl > 0)			// changed by wmf, originally fl >= 0
+    {
+      //      cerr << "tan = " << tan << " cp - p = " << (cp - p) << endl;
+      //      cerr << "ExplicitCurve2d::NumericalProject: lb wrong" << endl;
+      return 0;
+    }
+  
+  tan = EvalPrime (ub);
+  cp = Eval (ub);
+  fu = tan * (cp - p);
+  if (fu < 0)			// changed by wmf, originally fu <= 0
+    {
+      //    cerr << "tan = " << tan << " cp - p = " << (cp - p) << endl;
+      //    cerr << "ExplicitCurve2d::NumericalProject: ub wrong" << endl;
+    return 0;
+    }
+    
+  cnt = 0;
+  while (ub - lb > 1e-12 && fu - fl > 1e-12)
+    {
+    cnt++;
+    if (cnt > 50)
+      {
+      (*testout) << "Num Proj, cnt = " << cnt << endl;
+      }
+     
+    t = (lb * fu - ub * fl) / (fu - fl);
+    if (t > 0.9 * ub + 0.1 * lb) t = 0.9 * ub + 0.1 * lb;
+    if (t < 0.1 * ub + 0.9 * lb) t = 0.1 * ub + 0.9 * lb;
+    
+    tan = EvalPrime (t);
+    cp = Eval (t);
+    f = tan * (cp - p);
+    
+    if (f >= 0)
+      {
+      ub = t;
+      fu = f;
+      }
+    else
+      {
+      lb = t;
+      fl = f;
+      }
+    }
+    
+  return t;
+  }
+
+
+Vec<2> ExplicitCurve2d :: Normal (double t) const
+{
+  Vec<2> tan = EvalPrime (t);
+  tan.Normalize();
+  return Vec<2> (tan(1), -tan(0));
+}
+
+
+void ExplicitCurve2d :: NormalVector (const Point<2> & p, Vec<2> & n) const
+  {
+  double t = ProjectParam (p);
+  n = Normal (t);
+  }
+
+
+Point<2> ExplicitCurve2d :: CurvCircle (double t) const
+  {
+  Point<2> cp;
+  Vec<2> tan, n, curv;
+  double den;
+  
+  cp = Eval (t);
+  tan = EvalPrime (t);
+  n = Normal (t);
+  curv = EvalPrimePrime (t);
+  
+  den = n * curv;
+  if (fabs (den) < 1e-12)
+    return cp + 1e12 * n;  
+    
+  return cp + (tan.Length2() / den) * n;  
+  }
+
+
+double ExplicitCurve2d :: MaxCurvature () const
+  {
+  double t, tmin, tmax, dt;
+  double curv;
+  Vec<2> tan;
+  double maxcurv;
+
+  maxcurv = 0;  
+  
+  tmin = MinParam ();
+  tmax = MaxParam ();
+  dt = (tmax - tmin) / 1000;
+  for (t = tmin; t <= tmax+dt; t += dt)
+    if (SectionUsed (t))
+      {
+      tan = EvalPrime (t);
+      curv = fabs ( (Normal(t) * EvalPrimePrime(t)) / tan.Length2());
+      if (curv > maxcurv) maxcurv = curv; 
+      }
+  return maxcurv;
+  }  
+  
+double ExplicitCurve2d :: MaxCurvatureLoc (const Point<2> & p, double rad) const
+  {
+  double t, tmin, tmax, dt;
+  double curv;
+  Vec<2> tan;
+  double maxcurv;
+
+  maxcurv = 0;  
+  
+  tmin = MinParam ();
+  tmax = MaxParam ();
+  dt = (tmax - tmin) / 1000;
+  for (t = tmin; t <= tmax+dt; t += dt)
+    if (Dist (Eval(t), p) < rad)
+      {
+      tan = EvalPrime (t);
+      curv = fabs ( (Normal(t) * EvalPrimePrime(t)) / tan.Length2());
+      if (curv > maxcurv) maxcurv = curv; 
+      }
+    
+  return maxcurv;
+  }  
+  
+}
diff --git a/contrib/Netgen/libsrc/csg/explicitcurve2d.hpp b/contrib/Netgen/libsrc/csg/explicitcurve2d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..559030b2541c7302a7177cbc01f375adb6f77237
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/explicitcurve2d.hpp
@@ -0,0 +1,113 @@
+#ifndef FILE_EXPLICITCURVE2D
+#define FILE_EXPLICITCURVE2D
+
+/**************************************************************************/
+/* File:   explicitcurve2d.hh                                             */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   14. Oct. 96                                                    */
+/**************************************************************************/
+
+
+namespace netgen
+{
+
+  /*
+
+  Explicit 2D Curve repesentation
+
+  */
+
+
+
+  ///
+  class ExplicitCurve2d : public Curve2d
+  {
+  public:
+    ///
+    ExplicitCurve2d ();
+
+    ///
+    virtual void Project (Point<2> & p) const;
+    ///
+    virtual double ProjectParam (const Point<2> & p) const = 0;
+    ///
+    virtual double NumericalProjectParam (const Point<2> & p, double lb, double ub) const;
+    ///
+    virtual double MinParam () const = 0;
+    ///
+    virtual double MaxParam () const = 0;
+    ///
+    virtual Point<2> Eval (double t) const = 0;
+    ///
+    virtual Vec<2> EvalPrime (double t) const = 0;
+    ///
+    virtual Vec<2> Normal (double t) const;
+    ///
+    virtual void NormalVector (const Point<2> & p, Vec<2> & n) const;
+    ///
+    virtual Vec<2> EvalPrimePrime (double t) const = 0;
+
+    ///
+    virtual double MaxCurvature () const;
+    ///
+    virtual double MaxCurvatureLoc (const Point<2> & p, double rad) const;
+
+    ///
+    virtual Point<2> CurvCircle (double t) const;
+    ///
+    virtual void Print (ostream & /* str */) const { };
+  
+    ///
+    virtual int SectionUsed (double /* t */) const { return 1; }
+    ///
+    virtual void Reduce (const Point<2> & /* p */, double /* rad */) { };
+    ///
+    virtual void UnReduce () { };
+  }; 
+  
+  
+  ///
+  class BSplineCurve2d : public ExplicitCurve2d
+  {
+    ///
+    Array<Point<2> > points;
+    ///
+    Array<int> intervallused;
+    ///
+    int redlevel;
+  
+  public:
+    ///
+    BSplineCurve2d ();
+    ///
+    void AddPoint (const Point<2> & apoint);
+
+    bool Inside (const Point<2> & p, double & dist) const;
+  
+    ///
+    virtual double ProjectParam (const Point<2> & p) const;
+    ///
+    virtual double MinParam () const { return 0; }
+    ///
+    virtual double MaxParam () const { return points.Size(); }
+    ///
+    virtual Point<2> Eval (double t) const;
+    ///
+    virtual Vec<2> EvalPrime (double t) const;  
+    ///
+    virtual Vec<2> EvalPrimePrime (double t) const;
+    ///
+    virtual void Print (ostream & str) const;
+
+    ///
+    virtual int SectionUsed (double t) const;
+    ///
+    virtual void Reduce (const Point<2> & p, double rad);
+    ///
+    virtual void UnReduce ();
+  };  
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/extrusion.cpp b/contrib/Netgen/libsrc/csg/extrusion.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..29676395f8646d4a6a5d3cf9398142a8109ddee9
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/extrusion.cpp
@@ -0,0 +1,876 @@
+#include <mystdlib.h>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+
+  Array<Point<3> > project1, project2;
+
+
+
+  void ExtrusionFace :: Init(void)
+  {
+    p0.SetSize(path->GetNSplines());
+    x_dir.SetSize(path->GetNSplines());
+    y_dir.SetSize(path->GetNSplines());
+    z_dir.SetSize(path->GetNSplines());
+    loc_z_dir.SetSize(path->GetNSplines());
+    spline3_path.SetSize(path->GetNSplines());
+    line_path.SetSize(path->GetNSplines());
+
+    for(int i=0; i<path->GetNSplines(); i++)
+      {
+	spline3_path[i] = dynamic_cast < const SplineSeg3<3>* >(&path->GetSpline(i));
+	line_path[i] = dynamic_cast < const LineSeg<3>* >(&path->GetSpline(i));
+	
+	if(line_path[i])
+	  {
+	    y_dir[i] = line_path[i]->EndPI() - line_path[i]->StartPI();
+	    y_dir[i].Normalize();
+	    z_dir[i] = glob_z_direction;
+	    Orthogonalize(y_dir[i],z_dir[i]);
+	    x_dir[i] = Cross(y_dir[i],z_dir[i]);
+	    loc_z_dir[i] = z_dir[i];
+	  }
+	else
+	  {
+	    z_dir[i] = glob_z_direction;
+	    loc_z_dir[i] = glob_z_direction;
+	  }
+      }
+    
+    profile->GetCoeff(profile_spline_coeff);
+    latest_point3d = -1.111e30;
+  }
+
+  
+  ExtrusionFace :: ExtrusionFace(const SplineSeg<2> * profile_in,
+				 const SplineGeometry<3> * path_in,
+				 const Vec<3> & z_direction) :
+    profile(profile_in), path(path_in), glob_z_direction(z_direction)
+  {
+    deletable = false;
+
+    Init();
+  }
+
+  ExtrusionFace :: ExtrusionFace(const Array<double> & raw_data)
+  {
+    deletable = true;
+
+    int pos=0;
+
+    Array< Point<2> > p(3);
+
+    int ptype = int(raw_data[pos]); pos++;
+
+    for(int i=0; i<ptype; i++)
+      {
+	p[i](0) = raw_data[pos]; pos++;
+	p[i](1) = raw_data[pos]; pos++;
+      }
+    if(ptype == 2)
+      {
+	profile = new LineSeg<2>(GeomPoint<2>(p[0],1),
+				 GeomPoint<2>(p[1],1));
+      }
+    else if(ptype == 3)
+      {
+	profile = new SplineSeg3<2>(GeomPoint<2>(p[0],1),
+				    GeomPoint<2>(p[1],1),
+				    GeomPoint<2>(p[2],1));
+      }
+
+    path = new SplineGeometry<3>;
+    pos = const_cast< SplineGeometry<3> *>(path)->Load(raw_data,pos);
+
+    for(int i = 0; i < 3; i++)
+      {
+	glob_z_direction(i) = raw_data[pos]; 
+	pos++;
+      }
+    
+    Init();
+  }
+
+  ExtrusionFace :: ~ExtrusionFace()
+  {
+    if(deletable)
+      {
+	delete profile;
+	delete path;
+      }
+  }
+
+  
+  int ExtrusionFace :: IsIdentic (const Surface & s2, int & inv, double eps) const
+  {
+    const ExtrusionFace * ext2 = dynamic_cast<const ExtrusionFace*>(&s2);
+
+    if(!ext2) return 0;
+
+    if(ext2 == this)
+      return 1;
+
+    return 0;
+  } 
+  
+  void ExtrusionFace :: Orthogonalize(const Vec<3> & v1, Vec<3> & v2) const
+  {
+    v2 -= (v1*v2)*v1;
+    v2.Normalize();
+  }
+
+
+  void ExtrusionFace :: CalcProj(const Point<3> & point3d, Point<2> & point2d,
+				 int & seg, double & t) const
+  {
+    if (Dist2 (point3d, latest_point3d) < 
+        1e-25 * Dist2(path->GetSpline(0).StartPI(), path->GetSpline(0).EndPI()))
+      {
+	point2d = latest_point2d;
+	seg = latest_seg;
+	t = latest_t;
+	return;
+      }
+    
+    latest_point3d = point3d;
+
+    double cutdist = -1;
+    
+
+    Array<double> mindist(path->GetNSplines());
+
+    for(int i = 0; i < path->GetNSplines(); i++)
+      {
+	double auxcut = -1;
+	double auxmin = -1;
+
+	if(spline3_path[i])
+	  {
+	    Point<3> startp(path->GetSpline(i).StartPI());
+	    Point<3> endp(path->GetSpline(i).EndPI());
+	    Point<3> tanp(spline3_path[i]->TangentPoint());
+            
+            // lower bound for dist
+            auxmin = sqrt (MinDistTP2 (startp, endp, tanp, point3d)); 
+            
+            // upper bound for dist
+            auxcut = min2 (Dist (startp, point3d), Dist (endp, point3d));
+	  }
+	else if(line_path[i])
+	  {
+            auxmin = auxcut = sqrt (MinDistLP2 (path->GetSpline(i).StartPI(),
+                                                path->GetSpline(i).EndPI(),
+                                                point3d));
+	  }
+	
+	mindist[i] = auxmin;
+	
+	if(i==0 || auxcut < cutdist)
+	  cutdist = auxcut;
+      }
+	
+
+
+    Point<2> testpoint2d;
+    Point<3> testpoint3d;
+    
+    double minproj(-1);
+    bool minproj_set(false);
+
+
+
+    for(int i=0; i<path->GetNSplines(); i++)
+      {
+	if(mindist[i] > cutdist) continue;
+
+	double thist = CalcProj(point3d,testpoint2d,i);
+
+	testpoint3d = p0[i] + testpoint2d(0)*x_dir[i] + testpoint2d(1)*loc_z_dir[i];
+	double d = Dist2(point3d,testpoint3d);
+
+
+	if(!minproj_set || d < minproj)
+	  {
+	    minproj_set = true;
+	    minproj = d;
+	    point2d = testpoint2d;
+	    t = thist;
+	    seg = i;
+	    latest_seg = i;
+	    latest_t = t;
+	    latest_point2d = point2d;
+	  }
+      }
+  }
+
+  double ExtrusionFace :: CalcProj(const Point<3> & point3d, Point<2> & point2d,
+				   int seg) const
+  {
+    double t = -1;
+
+    if(line_path[seg])
+      {
+	point2d(0) = (point3d-line_path[seg]->StartPI())*x_dir[seg];
+	point2d(1) = (point3d-line_path[seg]->StartPI())*z_dir[seg];
+	double l = Dist(line_path[seg]->StartPI(),
+			line_path[seg]->EndPI());
+	t = min2(max2((point3d - line_path[seg]->StartPI()) * y_dir[seg],0.),
+		 l);	
+	p0[seg] = line_path[seg]->StartPI() + t*y_dir[seg];
+	t *= 1./l;
+      }
+    else if(spline3_path[seg])
+      {
+	spline3_path[seg]->Project(point3d,p0[seg],t);
+	
+	y_dir[seg] = spline3_path[seg]->GetTangent(t); 
+        y_dir[seg].Normalize();
+	loc_z_dir[seg] = z_dir[seg];
+	Orthogonalize(y_dir[seg],loc_z_dir[seg]);
+	x_dir[seg] = Cross(y_dir[seg],loc_z_dir[seg]);
+	Vec<3> dir = point3d-p0[seg];
+	point2d(0) = x_dir[seg]*dir;
+	point2d(1) = loc_z_dir[seg]*dir;	
+      }
+    return t;
+  }
+
+
+
+  double ExtrusionFace :: CalcFunctionValue (const Point<3> & point) const
+  {
+    Point<2> p;
+
+    double dummyd;
+    int dummyi;
+
+    CalcProj(point, p, dummyi, dummyd);
+
+    return 
+      profile_spline_coeff(0)*p(0)*p(0) + 
+      profile_spline_coeff(1)*p(1)*p(1) + 
+      profile_spline_coeff(2)*p(0)*p(1) + 
+      profile_spline_coeff(3)*p(0) + 
+      profile_spline_coeff(4)*p(1) + 
+      profile_spline_coeff(5);    
+  }
+
+
+
+
+  void ExtrusionFace :: CalcGradient (const Point<3> & point, Vec<3> & grad) const
+  {
+    Point<2> p2d;
+
+    double t_path;
+    int seg;
+    CalcProj (point, p2d, seg, t_path);
+
+
+    Point<3> phi;
+    Vec<3> phip, phipp, phi_minus_point;
+
+    path->GetSpline(seg).GetDerivatives(t_path, phi, phip, phipp);
+    phi_minus_point = phi-point;
+
+    Vec<3> grad_t = (1.0/(phipp*phi_minus_point + phip*phip)) * phip;
+    Vec<3> grad_xbar, grad_ybar;
+
+    Vec<3> hex, hey, hez, dex, dey, dez;
+    CalcLocalCoordinatesDeriv (seg, t_path, hex, hey, hez, dex, dey, dez);
+
+    grad_xbar = hex - (phi_minus_point*dex + hex*phip) * grad_t;
+    grad_ybar = hez - (phi_minus_point*dez + hez*phip) * grad_t;
+
+    double dFdxbar = 2.*profile_spline_coeff(0)*p2d(0) +
+      profile_spline_coeff(2)*p2d(1) + profile_spline_coeff(3);
+
+    double dFdybar = 2.*profile_spline_coeff(1)*p2d(1) +
+      profile_spline_coeff(2)*p2d(0) + profile_spline_coeff(4);
+    
+
+    grad = dFdxbar * grad_xbar + dFdybar * grad_ybar;    
+  }
+
+  void ExtrusionFace :: CalcHesse (const Point<3> & point, Mat<3> & hesse) const
+  {
+    const double eps = 1e-7*Dist(path->GetSpline(0).StartPI(),path->GetSpline(0).EndPI());
+    
+
+    Point<3> auxpoint1(point),auxpoint2(point);
+    Vec<3> auxvec,auxgrad1,auxgrad2;
+
+    for(int i=0; i<3; i++)
+      {
+	auxpoint1(i) -= eps;
+	auxpoint2(i) += eps;
+	CalcGradient(auxpoint1,auxgrad1);
+	CalcGradient(auxpoint2,auxgrad2);
+	auxvec = (1./(2.*eps)) * (auxgrad2-auxgrad1);
+	for(int j=0; j<3; j++)
+	  hesse(i,j) = auxvec(j);
+	auxpoint1(i) = point(i);
+	auxpoint2(i) = point(i);
+      }
+
+    /*
+    Vec<3> grad;
+    CalcGradient(point,grad);
+
+    Point<3> auxpoint(point);
+    Vec<3> auxvec,auxgrad;
+
+    for(int i=0; i<3; i++)
+      {
+	auxpoint(i) -= eps;
+	CalcGradient(auxpoint,auxgrad);
+	auxvec = (1./eps) * (grad-auxgrad);
+	for(int j=0; j<3; j++)
+	  hesse(i,j) = auxvec(j);
+	auxpoint(i) = point(i);
+      }
+    */
+
+    
+    for(int i=0; i<3; i++)
+      for(int j=i+1; j<3; j++)
+	hesse(i,j) = hesse(j,i) = 0.5*(hesse(i,j)+hesse(j,i));
+  }
+  
+
+
+  double ExtrusionFace :: HesseNorm () const
+  {
+    return fabs(profile_spline_coeff(0) + profile_spline_coeff(1)) +
+      sqrt(pow(profile_spline_coeff(0)+profile_spline_coeff(1),2)+4.*pow(profile_spline_coeff(2),2));
+  }
+
+  double ExtrusionFace :: MaxCurvature () const
+  {
+    double retval,actmax;
+    
+    retval = profile->MaxCurvature();
+    for(int i=0; i<path->GetNSplines(); i++)
+      {
+	actmax = path->GetSpline(i).MaxCurvature();
+	if(actmax > retval)
+	  retval = actmax;
+      }
+
+    return 2.*retval;
+  }
+
+
+  void ExtrusionFace :: Project (Point<3> & p) const
+  {
+    double dummyt;
+    int seg;
+    Point<2> p2d;
+
+    CalcProj(p,p2d,seg,dummyt);
+
+    profile->Project(p2d,p2d,profile_par);
+    
+    p = p0[seg] + p2d(0)*x_dir[seg] + p2d(1)*loc_z_dir[seg];
+
+    Vec<2> tangent2d = profile->GetTangent(profile_par);
+    profile_tangent = tangent2d(0)*x_dir[seg] + tangent2d(1)*y_dir[seg];
+  }
+
+
+  
+  Point<3> ExtrusionFace :: GetSurfacePoint () const
+  {
+    p0[0] = path->GetSpline(0).GetPoint(0.5);
+    if(!line_path[0])
+      {
+	y_dir[0] = path->GetSpline(0).GetTangent(0.5);
+	y_dir[0].Normalize();
+	loc_z_dir[0] = z_dir[0];
+	Orthogonalize(y_dir[0],loc_z_dir[0]);
+	x_dir[0] = Cross(y_dir[0],loc_z_dir[0]);
+      }
+
+    Point<2> locpoint = profile->GetPoint(0.5);
+
+    return p0[0] + locpoint(0)*x_dir[0] + locpoint(1)*loc_z_dir[0];
+  }
+  
+
+  bool ExtrusionFace :: BoxIntersectsFace(const Box<3> & box) const
+  {
+    Point<3> center = box.Center();
+
+    Project(center);
+
+    //(*testout) << "box.Center() " << box.Center() << " projected " << center << " diam " << box.Diam() 
+    //       << " dist " << Dist(box.Center(),center) << endl;
+
+    return (Dist(box.Center(),center) < 0.5*box.Diam());
+  }
+
+
+  void ExtrusionFace :: LineIntersections ( const Point<3> & p,
+					    const Vec<3> & v,
+					    const double eps,
+					    int & before,
+					    int & after,
+					    bool & intersecting ) const
+  {
+    Point<2> p2d;
+    Vec<2> v2d;
+
+    intersecting = false;
+
+    double segt;
+    int seg;
+
+    CalcProj(p,p2d,seg,segt);
+
+    if(seg == 0 && segt < 1e-20)
+      {
+	Vec<3> v1,v2;
+	v1 = path->GetSpline(0).GetTangent(0);
+	v2 = p-p0[seg];
+	if(v1*v2 < -eps)
+	  return;
+      }
+    if(seg == path->GetNSplines()-1 && 1.-segt < 1e-20)
+      {
+	Vec<3> v1,v2;
+	v1 = path->GetSpline(seg).GetTangent(1);
+	v2 = p-p0[seg];
+	if(v1*v2 > eps)
+	  return;
+      }
+
+    v2d(0) = v * x_dir[seg];
+    v2d(1) = v * loc_z_dir[seg];
+    
+    Vec<2> n(v2d(1),-v2d(0));
+    Array < Point<2> > ips;
+
+
+    profile->LineIntersections(v2d(1),
+			      -v2d(0),
+			      -v2d(1)*p2d(0) + v2d(0)*p2d(1),
+			      ips,eps);
+    int comp;
+
+    if(fabs(v2d(0)) >= fabs(v2d(1)))
+      comp = 0;
+    else
+      comp = 1;
+
+    //(*testout) << "p2d " << p2d;
+
+    for(int i=0; i<ips.Size(); i++)
+      {
+	//(*testout) << " ip " << ips[i];
+
+	double t = (ips[i](comp)-p2d(comp))/v2d(comp);
+
+	if(t < -eps)
+	  before++;
+	else if(t > eps)
+	  after++;
+	else
+	  intersecting = true;
+      }
+    //(*testout) << endl;
+  }
+
+  void ExtrusionFace :: Print (ostream & str) const{}
+
+  INSOLID_TYPE ExtrusionFace :: VecInFace ( const Point<3> & p,
+					    const Vec<3> & v,
+					    const double eps ) const
+  {
+    
+    Vec<3> normal1;
+    CalcGradient(p,normal1); normal1.Normalize();
+
+    double d1 = normal1*v;
+
+
+    if(d1 > eps)
+      return IS_OUTSIDE;
+    if(d1 < -eps)
+      return IS_INSIDE;
+    
+
+    return DOES_INTERSECT;
+
+    /*
+    Point<2> p2d;
+
+    double t_path;
+    int seg;
+    CalcProj(p,p2d,seg,t_path);
+
+    double t;
+    profile.Project(p2d,p2d,t);
+
+
+    
+    Vec<2> profile_tangent = profile.GetTangent(t);
+
+    double d;
+
+    Vec<3> normal1;
+    CalcGradient(p,normal1); normal1.Normalize();
+
+    double d1 = normal1*v;
+
+    Vec<2> v2d;
+
+    v2d(0) = v*x_dir[seg];
+    v2d(1) = v*loc_z_dir[seg];
+
+			    	    
+    Vec<2> normal(-profile_tangent(1),profile_tangent(0));
+    
+    //d = normal*v2d;
+    
+
+    d = d1;
+
+
+    if(d > eps)
+      return IS_OUTSIDE;
+    if(d < -eps)
+      return IS_INSIDE;
+    
+
+    return DOES_INTERSECT;
+    */
+  }
+
+
+  void ExtrusionFace :: GetTriangleApproximation (TriangleApproximation & tas, 
+						  const Box<3> & boundingbox, 
+						  double facets) const
+  {
+    int n = int(facets) + 1;
+
+    for(int k = 0; k < path -> GetNSplines(); k++)
+      {
+	for(int i = 0; i <= n; i++)
+	  {
+	    Point<3> origin = path -> GetSpline(k).GetPoint(double(i)/double(n));
+	    if(!line_path[k])
+	      {
+		y_dir[k] = path->GetSpline(k).GetTangent(double(i)/double(n));
+		y_dir[k].Normalize();
+	      }
+	    loc_z_dir[k] = z_dir[k];
+	    Orthogonalize(y_dir[k],loc_z_dir[k]);
+	    if(!line_path[k])
+	      x_dir[k] = Cross(y_dir[k],loc_z_dir[k]);
+	    
+	    for(int j = 0; j <= n; j++)
+	      {
+		Point<2> locp = profile->GetPoint(double(j)/double(n));
+		tas.AddPoint(origin + locp(0)*x_dir[k] + locp(1)*loc_z_dir[k]);
+	      }
+	  }
+      }
+    
+    for(int k = 0; k < path->GetNSplines(); k++)
+      for(int i = 0; i < n; i++)
+	for(int j = 0; j < n; j++)
+	  {
+	    int pi = k*(n+1)*(n+1) + (n+1)*i +j;
+	  
+	    tas.AddTriangle( TATriangle (0, pi,pi+1,pi+n+1) );
+	    tas.AddTriangle( TATriangle (0, pi+1,pi+n+1,pi+n+2) );
+	  }
+  }
+  
+
+  void ExtrusionFace :: GetRawData(Array<double> & data) const
+  {
+    data.DeleteAll();
+    profile->GetRawData(data);
+    path->GetRawData(data);
+    for(int i=0; i<3; i++)
+      data.Append(glob_z_direction[i]);
+  }
+
+
+  void ExtrusionFace :: 
+  CalcLocalCoordinates (int seg, double t, 
+                        Vec<3> & ex, Vec<3> & ey, Vec<3> & ez) const
+  {
+    ey = path->GetSpline(seg).GetTangent(t); 
+    ey /= ey.Length();
+    ex = Cross (ey, glob_z_direction);
+    ex /= ex.Length();
+    ez = Cross (ex, ey);
+  }
+
+  void ExtrusionFace :: 
+  CalcLocalCoordinatesDeriv (int seg, double t, 
+                             Vec<3> & ex, Vec<3> & ey, Vec<3> & ez,
+                             Vec<3> & dex, Vec<3> & dey, Vec<3> & dez) const
+  {
+    Point<3> point;
+    Vec<3> first, second;
+    path->GetSpline(seg).GetDerivatives (t, point, first, second);
+
+    ey = first;
+    ex = Cross (ey, glob_z_direction);
+    ez = Cross (ex, ey);
+    
+    dey = second;
+    dex = Cross (dey, glob_z_direction);
+    dez = Cross (dex, ey) + Cross (ex, dey);
+    
+    double lenx = ex.Length();
+    double leny = ey.Length();
+    double lenz = ez.Length();
+
+    ex /= lenx;
+    ey /= leny;
+    ez /= lenz;
+    
+    dex /= lenx;
+    dex -= (dex * ex) * ex;
+
+    dey /= leny;
+    dey -= (dey * ey) * ey;
+
+    dez /= lenz;
+    dez -= (dez * ez) * ez;
+  }
+  
+
+  Extrusion :: Extrusion(const SplineGeometry<3> & path_in,
+			 const SplineGeometry<2> & profile_in,
+			 const Vec<3> & z_dir) :
+    path(path_in), profile(profile_in), z_direction(z_dir)
+  {
+    surfaceactive.SetSize(0);
+    surfaceids.SetSize(0);
+
+    for(int j=0; j<profile.GetNSplines(); j++)
+      {
+	ExtrusionFace * face = new ExtrusionFace(&(profile.GetSpline(j)),
+						 &path,
+						 z_direction);
+	faces.Append(face);
+	surfaceactive.Append(true);
+	surfaceids.Append(0);
+      }
+
+  }
+
+
+  Extrusion :: ~Extrusion()
+  {
+    for(int i=0; i<faces.Size(); i++)
+      delete faces[i];
+  }
+
+
+
+
+
+  INSOLID_TYPE Extrusion :: BoxInSolid (const BoxSphere<3> & box) const
+  {
+    for(int i=0; i<faces.Size(); i++)
+      {
+	if(faces[i]->BoxIntersectsFace(box))
+	  return DOES_INTERSECT;
+      }
+
+    return PointInSolid(box.Center(),0);
+  }
+
+
+  INSOLID_TYPE Extrusion :: PointInSolid (const Point<3> & p,
+					  const double eps,
+					  Array<int> * const facenums) const
+  {
+    Vec<3> random_vec(-0.4561,0.7382,0.4970247);
+
+    int before(0), after(0);
+    bool intersects(false);
+    bool does_intersect(false);
+
+    for(int i=0; i<faces.Size(); i++)
+      {
+	faces[i]->LineIntersections(p,random_vec,eps,before,after,intersects);
+
+	//(*testout) << "intersects " << intersects << " before " << before << " after " << after << endl;
+	if(intersects)
+	  {
+	    if(facenums)
+	      {
+		facenums->Append(i);
+		does_intersect = true;
+	      }
+	    else
+	      return DOES_INTERSECT;
+	  }
+      }
+
+    if(does_intersect)
+      return DOES_INTERSECT;
+
+
+    if(before % 2 == 0)
+      return IS_OUTSIDE;
+
+    return IS_INSIDE;
+  }
+
+
+  INSOLID_TYPE Extrusion :: PointInSolid (const Point<3> & p,
+					  double eps) const
+  {
+    return PointInSolid(p,eps,NULL);    
+  }
+
+  INSOLID_TYPE Extrusion :: VecInSolid (const Point<3> & p,
+					const Vec<3> & v,
+					double eps) const
+  {
+    Array<int> facenums;
+    INSOLID_TYPE pInSolid = PointInSolid(p,eps,&facenums);
+
+    if(pInSolid != DOES_INTERSECT)
+      return pInSolid;
+
+
+    double d(0);
+
+    if(facenums.Size() == 1)
+      {
+	Vec<3> normal;
+	faces[facenums[0]]->CalcGradient(p,normal);
+	normal.Normalize();
+	d = normal*v;
+	
+	latestfacenum = facenums[0];
+      }
+    else if (facenums.Size() == 2)
+      {
+	Vec<3> checkvec;
+
+	Point<3> dummy(p);
+	faces[facenums[0]]->Project(dummy);
+	if(fabs(faces[facenums[0]]->GetProfilePar()) < 0.1)
+	  {
+	    int aux = facenums[0];
+	    facenums[0] = facenums[1]; facenums[1] = aux;
+	  }
+	
+	checkvec = faces[facenums[0]]->GetYDir();
+     
+	Vec<3> n0, n1;
+	faces[facenums[0]]->CalcGradient(p,n0);
+	faces[facenums[1]]->CalcGradient(p,n1);
+	n0.Normalize();
+	n1.Normalize();
+	
+
+	Vec<3> t = Cross(n0,n1);
+	if(checkvec*t < 0) t*= (-1.);
+	
+	Vec<3> t0 = Cross(n0,t);
+	Vec<3> t1 = Cross(t,n1);
+	
+	t0.Normalize();
+	t1.Normalize();
+	
+
+	const double t0v = t0*v;
+	const double t1v = t1*v;
+
+	if(t0v > t1v)
+	  {
+	    latestfacenum = facenums[0];
+	    d = n0*v;
+	  }
+	else
+	  {
+	    latestfacenum = facenums[1];
+	    d = n1*v;
+	  }
+
+	if(fabs(t0v) < eps && fabs(t1v) < eps)
+	  latestfacenum = -1;
+      }
+
+    else
+      {
+	cerr << "WHY ARE THERE " << facenums.Size() << " FACES?" << endl;
+      }
+
+    if(d > eps)
+      return IS_OUTSIDE;
+    if(d < -eps)
+      return IS_INSIDE;
+      
+    return DOES_INTERSECT;
+  }
+
+
+
+  // checks if lim s->0 lim t->0  p + t(v1 + s v2) in solid
+  INSOLID_TYPE Extrusion :: VecInSolid2 (const Point<3> & p,
+					 const Vec<3> & v1,
+					 const Vec<3> & v2,
+					 double eps) const
+  {
+    INSOLID_TYPE retval;
+    retval = VecInSolid(p,v1,eps);
+
+    // *testout << "extr, vecinsolid=" << int(retval) << endl;
+
+    if(retval != DOES_INTERSECT)
+      return retval;
+
+    if(latestfacenum >= 0)
+      return faces[latestfacenum]->VecInFace(p,v2,0);
+    else
+      return VecInSolid(p,v2,eps);
+  }
+
+  
+  int Extrusion :: GetNSurfaces() const
+  {
+    return faces.Size();
+  }
+
+  Surface & Extrusion :: GetSurface (int i)
+  {
+    return *faces[i];
+  }
+
+  const Surface & Extrusion :: GetSurface (int i) const
+  {
+    return *faces[i];
+  }
+
+
+  void Extrusion :: Reduce (const BoxSphere<3> & box)
+  {
+    for(int i = 0; i < faces.Size(); i++)
+      surfaceactive[i] = faces[i]->BoxIntersectsFace(box);
+  }
+
+  void Extrusion :: UnReduce ()
+  {
+    for(int i = 0; i < faces.Size(); i++)
+      surfaceactive[i] = true;
+  }
+
+
+}
diff --git a/contrib/Netgen/libsrc/csg/extrusion.hpp b/contrib/Netgen/libsrc/csg/extrusion.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..189639bd2ecfd9c21de84a5f7c25f4746cdd7302
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/extrusion.hpp
@@ -0,0 +1,155 @@
+#ifndef _EXTRUSION_HPP
+#define _EXTRUSION_HPP
+
+namespace netgen
+{
+
+  class Extrusion;
+
+  class ExtrusionFace : public Surface
+  {
+  private:
+    const SplineSeg<2> * profile;
+    const SplineGeometry<3> * path;
+    Vec<3> glob_z_direction;
+
+    bool deletable;
+  
+    Array< const SplineSeg3<3> * > spline3_path;
+    Array< const LineSeg<3> * > line_path;
+  
+    mutable Array < Vec<3> > x_dir, y_dir, z_dir, loc_z_dir;
+    mutable Array < Point<3> > p0;
+
+    mutable Vec<3> profile_tangent;
+    mutable double profile_par;
+  
+    mutable Vector profile_spline_coeff;
+
+    mutable int latest_seg;
+    mutable double latest_t;
+    mutable Point<2> latest_point2d;
+    mutable Point<3> latest_point3d;
+
+
+  private:
+    void Orthogonalize(const Vec<3> & v1, Vec<3> & v2) const;
+
+    void Init(void);
+
+  public:
+    double CalcProj(const Point<3> & point3d, Point<2> & point2d,
+		    int seg) const;
+    void CalcProj(const Point<3> & point3d, Point<2> & point2d,
+		  int & seg, double & t) const;
+
+  public:
+    ExtrusionFace(const SplineSeg<2> * profile_in,
+		  const SplineGeometry<3> * path_in,
+		  const Vec<3> & z_direction);
+
+    ExtrusionFace(const Array<double> & raw_data);
+    
+
+    ~ExtrusionFace();
+  
+    virtual int IsIdentic (const Surface & s2, int & inv, double eps) const; 
+  
+    virtual double CalcFunctionValue (const Point<3> & point) const;
+    virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const;
+    virtual void CalcHesse (const Point<3> & point, Mat<3> & hesse) const;
+    virtual double HesseNorm () const;
+
+    virtual double MaxCurvature () const;
+    //virtual double MaxCurvatureLoc (const Point<3> & /* c */ , 
+    //				  double /* rad */) const;
+
+    virtual void Project (Point<3> & p) const;
+
+    virtual Point<3> GetSurfacePoint () const;
+    virtual void Print (ostream & str) const;
+  
+    virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					   const Box<3> & boundingbox, 
+					   double facets) const;
+
+    const SplineGeometry<3> & GetPath(void) const {return *path;}
+    const SplineSeg<2> & GetProfile(void) const {return *profile;}
+
+    bool BoxIntersectsFace(const Box<3> & box) const;
+
+    void LineIntersections ( const Point<3> & p,
+			     const Vec<3> & v,
+			     const double eps,
+			     int & before,
+			     int & after,
+			     bool & intersecting ) const;
+
+    INSOLID_TYPE VecInFace ( const Point<3> & p,
+			     const Vec<3> & v,
+			     const double eps ) const;
+
+    const Vec<3> & GetYDir ( void ) const {return y_dir[latest_seg];}
+    const Vec<3> & GetProfileTangent (void) const {return profile_tangent;}
+    double GetProfilePar(void) const {return profile_par;}
+
+    void GetRawData(Array<double> & data) const;
+
+    void CalcLocalCoordinates (int seg, double t, 
+			       Vec<3> & ex, Vec<3> & ey, Vec<3> & ez) const;
+
+    void CalcLocalCoordinatesDeriv (int seg, double t, 
+				    Vec<3> & ex, Vec<3> & ey, Vec<3> & ez,
+				    Vec<3> & dex, Vec<3> & dey, Vec<3> & dez) const;
+
+  };
+
+
+
+  class Extrusion : public Primitive
+  {
+  private:
+    const SplineGeometry<3> & path;
+    const SplineGeometry<2> & profile;
+
+    const Vec<3> & z_direction;
+
+    Array<ExtrusionFace*> faces;
+
+    mutable int latestfacenum;
+
+  public:
+    Extrusion(const SplineGeometry<3> & path_in,
+	      const SplineGeometry<2> & profile_in,
+	      const Vec<3> & z_dir);
+    ~Extrusion();
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    virtual INSOLID_TYPE PointInSolid (const Point<3> & p,
+				       double eps) const;
+    INSOLID_TYPE PointInSolid (const Point<3> & p,
+			       double eps,
+			       Array<int> * const facenums) const;
+    virtual INSOLID_TYPE VecInSolid (const Point<3> & p,
+				     const Vec<3> & v,
+				     double eps) const;
+
+    // checks if lim s->0 lim t->0  p + t(v1 + s v2) in solid
+    virtual INSOLID_TYPE VecInSolid2 (const Point<3> & p,
+				      const Vec<3> & v1,
+				      const Vec<3> & v2,
+				      double eps) const;
+
+  
+    virtual int GetNSurfaces() const;
+    virtual Surface & GetSurface (int i = 0);
+    virtual const Surface & GetSurface (int i = 0) const;
+
+
+    virtual void Reduce (const BoxSphere<3> & box);
+    virtual void UnReduce ();
+
+  };
+
+}
+
+#endif //_EXTRUSION_HPP
diff --git a/contrib/Netgen/libsrc/csg/gencyl.cpp b/contrib/Netgen/libsrc/csg/gencyl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1305c1b87bf0e7542501ec76c932996c8a3025b9
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/gencyl.cpp
@@ -0,0 +1,179 @@
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+
+  GeneralizedCylinder :: GeneralizedCylinder (ExplicitCurve2d & acrosssection,
+                                              Point<3> ap, Vec<3> ae1, Vec<3> ae2)
+    : crosssection(acrosssection)
+  {
+    planep = ap;
+    planee1 = ae1;
+    planee2 = ae2;
+    planee3 = Cross (planee1, planee2);
+    (*testout) << "Vecs = " << planee1 << " " << planee2 << " " << planee3 << endl;
+  };
+  
+
+  void GeneralizedCylinder :: Project (Point<3> & p) const
+  {
+    Point<2> p2d;
+    double z;
+  
+    p2d = Point<2> (planee1 * (p - planep), planee2 * (p - planep));
+    z = planee3 * (p - planep);
+
+    crosssection.Project (p2d);
+  
+    p = planep + p2d(0) * planee1 + p2d(1) * planee2 + z * planee3;
+  }
+
+  int GeneralizedCylinder ::BoxInSolid (const BoxSphere<3> & box) const
+  {
+    Point<3> p3d;
+    Point<2> p2d, projp;
+    double t;
+    Vec<2> tan, n;
+  
+    p3d = box.Center();
+  
+    p2d = Point<2> (planee1 * (p3d - planep), planee2 * (p3d - planep));
+    t = crosssection.ProjectParam (p2d);
+  
+    projp = crosssection.Eval (t);
+    tan = crosssection.EvalPrime (t);
+    n(0) = tan(1);
+    n(1) = -tan(0);
+    
+    if (Dist (p2d, projp) < box.Diam()/2)
+      return 2;
+    
+    if (n * (p2d - projp) > 0) 
+      {
+        return 0;   
+      }
+    
+    return 1;
+  }
+
+  double GeneralizedCylinder :: CalcFunctionValue (const Point<3> & point) const
+  {
+    Point<2> p2d, projp;
+    double t;
+    Vec<2> tan, n;
+  
+  
+    p2d = Point<2> (planee1 * (point - planep), planee2 * (point - planep));
+    t = crosssection.ProjectParam (p2d);
+  
+    projp = crosssection.Eval (t);
+    tan = crosssection.EvalPrime (t);
+    n(0) = tan(1);
+    n(1) = -tan(0);
+    
+    n /= n.Length();
+    return n * (p2d - projp);
+  }
+  
+  void GeneralizedCylinder :: CalcGradient (const Point<3> & point, Vec<3> & grad) const
+  {
+    Point<2> p2d, projp;
+    double t;
+    Vec<2> tan, n;
+  
+  
+    p2d = Point<2> (planee1 * (point - planep), planee2 * (point - planep));
+    t = crosssection.ProjectParam (p2d);
+  
+    projp = crosssection.Eval (t);
+    tan = crosssection.EvalPrime (t);
+    n(0) = tan(1);
+    n(1) = -tan(0);
+    
+    n /= n.Length();
+    grad = n(0) * planee1 + n(1) * planee2;
+  }
+  
+  
+  void GeneralizedCylinder :: CalcHesse (const Point<3> & point, Mat<3> & hesse) const
+  {
+    Point<2> p2d, projp;
+    double t, dist, val;
+    Point<2> curvp;
+    Vec<2> curvpp;
+    Mat<2> h2d;
+    Mat<3,2> vmat;
+    int i, j, k, l;
+  
+    p2d = Point<2> (planee1 * (point - planep), planee2 * (point - planep));
+    t = crosssection.ProjectParam (p2d);
+
+    curvp = crosssection.CurvCircle (t);
+    curvpp = p2d-curvp;
+    dist = curvpp.Length();
+    curvpp /= dist;
+    
+    h2d(0, 0) = (1 - curvpp(0) * curvpp(0) ) / dist;  
+    h2d(0, 1) = h2d(1, 0) = (- curvpp(0) * curvpp(1) ) / dist;  
+    h2d(1, 1) = (1 - curvpp(1) * curvpp(1) ) / dist;  
+  
+    vmat(0,0) = planee1(0);
+    vmat(1,0) = planee1(1);
+    vmat(2,0) = planee1(2);
+    vmat(0,1) = planee2(0);
+    vmat(1,1) = planee2(1);
+    vmat(2,1) = planee2(2);
+  
+    for (i = 0; i < 3; i++)
+      for (j = 0; j < 3; j++)
+        {
+          val = 0;
+          for (k = 0; k < 2; k++)
+            for (l = 0; l < 2; l++)
+              val += vmat(i,k) * h2d(k,l) * vmat(j,l);
+          hesse(i,j) = val;
+        }
+  }
+
+
+  double GeneralizedCylinder :: HesseNorm () const
+  {
+    return crosssection.MaxCurvature();
+  }
+
+  double GeneralizedCylinder :: MaxCurvatureLoc (const Point<3> & c, double rad) const
+  {
+    Point<2> c2d = Point<2> (planee1 * (c - planep), planee2 * (c - planep));
+    return crosssection.MaxCurvatureLoc(c2d, rad);
+  }
+  
+
+  
+  Point<3> GeneralizedCylinder :: GetSurfacePoint () const
+  {
+    Point<2> p2d; 
+    p2d = crosssection.Eval(0);
+    return planep + p2d(0) * planee1 + p2d(1) * planee2;
+  }
+
+  void GeneralizedCylinder :: Reduce (const BoxSphere<3> & box)
+  {
+    Point<2> c2d = Point<2> (planee1 * (box.Center() - planep), 
+                             planee2 * (box.Center() - planep));
+    crosssection.Reduce (c2d, box.Diam()/2);
+  }
+
+  void GeneralizedCylinder :: UnReduce ()
+  {
+    crosssection.UnReduce ();
+  }
+
+  void GeneralizedCylinder :: Print (ostream & str) const
+  {
+    str << "Generalized Cylinder" << endl;
+    crosssection.Print (str);
+  }
+  
+}
diff --git a/contrib/Netgen/libsrc/csg/gencyl.hpp b/contrib/Netgen/libsrc/csg/gencyl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e5db270dec3d76d793c3e237a3a6eebc0dd2facb
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/gencyl.hpp
@@ -0,0 +1,70 @@
+#ifndef FILE_GENCYL
+#define FILE_GENCYL
+
+/**************************************************************************/
+/* File:   gencyl.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   14. Oct. 96                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+
+  /*
+  
+  Generalized Cylinder
+  
+  */
+
+
+  ///
+  class GeneralizedCylinder : public Surface
+  {
+    ///
+    ExplicitCurve2d & crosssection;
+    ///
+    Point<3> planep;
+    ///
+    Vec<3> planee1, planee2, planee3;
+  
+    ///  Vec<3> ex, ey, ez;
+    Vec2d e2x, e2y;
+    ///
+    Point<3> cp;
+  
+  public:
+    ///
+    GeneralizedCylinder (ExplicitCurve2d & acrosssection,
+			 Point<3> ap, Vec<3> ae1, Vec<3> ae2);
+  
+    ///
+    virtual void Project (Point<3> & p) const;
+  
+    ///
+    virtual int BoxInSolid (const BoxSphere<3> & box) const;
+    /// 0 .. no, 1 .. yes, 2 .. maybe
+  
+    virtual double CalcFunctionValue (const Point<3> & point) const;
+    ///
+    virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const;
+    ///
+    virtual void CalcHesse (const Point<3> & point, Mat<3> & hesse) const;
+    ///
+    virtual double HesseNorm () const;
+    ///
+    virtual double MaxCurvatureLoc (const Point<3> & c, double rad) const;
+    ///
+    virtual Point<3> GetSurfacePoint () const;
+    ///
+    virtual void Print (ostream & str) const;
+  
+    ///
+    virtual void Reduce (const BoxSphere<3> & box);
+    ///
+    virtual void UnReduce ();
+  };  
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/genmesh.cpp b/contrib/Netgen/libsrc/csg/genmesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5e891639b8dc52ee442acda706a9ec82fe760fc0
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/genmesh.cpp
@@ -0,0 +1,849 @@
+#include <mystdlib.h>
+
+
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+
+namespace netgen
+{
+  Array<SpecialPoint> specpoints;
+  static Array<MeshPoint> spoints;
+
+#define TCL_OK 0
+#define TCL_ERROR 1
+
+
+
+  static void FindPoints (CSGeometry & geom, Mesh & mesh)
+  {
+    PrintMessage (1, "Start Findpoints");
+
+    const char * savetask = multithread.task;
+    multithread.task = "Find points";
+
+    for (int i = 0; i < geom.GetNUserPoints(); i++)
+      {
+	mesh.AddPoint(geom.GetUserPoint (i));
+	mesh.Points().Last().Singularity (geom.GetUserPointRefFactor(i));
+	mesh.AddLockedPoint (PointIndex (i+1));
+      }
+
+    SpecialPointCalculation spc;
+
+    spc.SetIdEps(geom.GetIdEps());
+
+    if (spoints.Size() == 0)
+      spc.CalcSpecialPoints (geom, spoints);
+    
+    PrintMessage (2, "Analyze spec points");
+    spc.AnalyzeSpecialPoints (geom, spoints, specpoints);
+  
+    PrintMessage (5, "done");
+
+    (*testout) << specpoints.Size() << " special points:" << endl;
+    for (int i = 0; i < specpoints.Size(); i++)
+      specpoints[i].Print (*testout);
+
+    /*
+      for (int i = 1; i <= geom.identifications.Size(); i++)
+      geom.identifications.Elem(i)->IdentifySpecialPoints (specpoints);
+    */
+    multithread.task = savetask;
+  }
+
+
+
+
+
+
+  static void FindEdges (CSGeometry & geom, Mesh & mesh, const bool setmeshsize = false)
+  {
+    EdgeCalculation ec (geom, specpoints);
+    ec.SetIdEps(geom.GetIdEps());
+    ec.Calc (mparam.maxh, mesh);
+
+    for (int i = 0; i < geom.singedges.Size(); i++)
+      {
+	geom.singedges[i]->FindPointsOnEdge (mesh);
+	if(setmeshsize)
+	  geom.singedges[i]->SetMeshSize(mesh,10.*geom.BoundingBox().Diam());
+      }
+    for (int i = 0; i < geom.singpoints.Size(); i++)
+      geom.singpoints[i]->FindPoints (mesh);
+
+    for (int i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	//(*testout) << "segment " << mesh.LineSegment(i) << endl;
+	int ok = 0;
+	for (int k = 1; k <= mesh.GetNFD(); k++)
+	  if (mesh.GetFaceDescriptor(k).SegmentFits (mesh.LineSegment(i)))
+	    {
+	      ok = k;
+	      //(*testout) << "fits to " << k << endl;
+	    }
+
+	if (!ok)
+	  {
+	    ok = mesh.AddFaceDescriptor (FaceDescriptor (mesh.LineSegment(i)));
+	    //(*testout) << "did not find, now " << ok << endl;
+	  }
+
+	//(*testout) << "change from " << mesh.LineSegment(i).si;
+	mesh.LineSegment(i).si = ok;
+	//(*testout) << " to " << mesh.LineSegment(i).si << endl;
+      }
+
+    if (geom.identifications.Size())
+      {
+	PrintMessage (3, "Find Identifications");
+	for (int i = 0; i < geom.identifications.Size(); i++)
+	  {
+	    geom.identifications[i]->IdentifyPoints (mesh);
+	    //(*testout) << "identification " << i << " is " 
+	    //	       << *geom.identifications[i] << endl;
+	    
+	  }
+	for (int i = 0; i < geom.identifications.Size(); i++)
+	  geom.identifications[i]->IdentifyFaces (mesh);
+      }
+
+
+    // find intersecting segments
+    PrintMessage (3, "Check intersecting edges");
+    
+    Point3d pmin, pmax;
+    mesh.GetBox (pmin, pmax);
+    Box3dTree segtree (pmin, pmax);
+    
+    for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+      {
+	if (mesh[si].seginfo)
+	  {
+	    Box<3> hbox;
+	    hbox.Set (mesh[mesh[si][0]]);
+	    hbox.Add (mesh[mesh[si][1]]);
+	    segtree.Insert (hbox.PMin(), hbox.PMax(), si);
+	  }
+      }
+
+    Array<int> loc;
+    if (!ec.point_on_edge_problem)
+      for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+	{
+	  if (!mesh[si].seginfo) continue;
+
+	  Box<3> hbox;
+	  hbox.Set (mesh[mesh[si][0]]);
+	  hbox.Add (mesh[mesh[si][1]]);
+	  hbox.Increase (1e-6);
+	  segtree.GetIntersecting (hbox.PMin(), hbox.PMax(), loc);
+	  	  
+	  // for (SegmentIndex sj = 0; sj < si; sj++)
+	  for (int j = 0; j < loc.Size(); j++)
+	    {
+	      SegmentIndex sj = loc[j];
+	      if (sj >= si) continue;
+	      if (!mesh[si].seginfo || !mesh[sj].seginfo) continue;
+	      if (mesh[mesh[si][0]].GetLayer() != mesh[mesh[sj][1]].GetLayer()) continue;
+	      
+	      Point<3> pi1 = mesh[mesh[si][0]];
+	      Point<3> pi2 = mesh[mesh[si][1]];
+	      Point<3> pj1 = mesh[mesh[sj][0]];
+	      Point<3> pj2 = mesh[mesh[sj][1]];
+	      Vec<3> vi = pi2 - pi1;
+	      Vec<3> vj = pj2 - pj1;
+	      
+	      if (sqr (vi * vj) > (1.-1e-6) * Abs2 (vi) * Abs2 (vj)) continue;
+	      
+	      // pi1 + vi t = pj1 + vj s
+	      Mat<3,2> mat;
+	      Vec<3> rhs;
+	      Vec<2> sol;
+	      
+	      for (int jj = 0; jj < 3; jj++)
+		{ 
+		  mat(jj,0) = vi(jj); 
+		  mat(jj,1) = -vj(jj); 
+		  rhs(jj) = pj1(jj)-pi1(jj); 
+		}
+	      
+	      mat.Solve (rhs, sol);
+
+	      //(*testout) << "mat " << mat << endl << "rhs " << rhs << endl << "sol " << sol << endl;
+	      
+	      if (sol(0) > 1e-6 && sol(0) < 1-1e-6 &&
+		  sol(1) > 1e-6 && sol(1) < 1-1e-6 &&
+		  Abs (rhs - mat*sol) < 1e-6)
+		{
+		  Point<3> ip = pi1 + sol(0) * vi;
+		  
+		  //(*testout) << "ip " << ip << endl;
+
+		  Point<3> pip = ip;
+		  ProjectToEdge (geom.GetSurface (mesh[si].surfnr1),
+				 geom.GetSurface (mesh[si].surfnr2), pip);
+		  
+		  //(*testout) << "Dist (ip, pip_si) " << Dist (ip, pip) << endl;
+		  if (Dist (ip, pip) > 1e-6*geom.MaxSize()) continue;
+		  pip = ip;
+		  ProjectToEdge (geom.GetSurface (mesh[sj].surfnr1),
+				 geom.GetSurface (mesh[sj].surfnr2), pip);
+
+		  //(*testout) << "Dist (ip, pip_sj) " << Dist (ip, pip) << endl;
+		  if (Dist (ip, pip) > 1e-6*geom.MaxSize()) continue;
+		  
+		  
+		  
+		  cout << "Intersection at " << ip << endl;
+		  
+		  geom.AddUserPoint (ip);
+		  spoints.Append (MeshPoint (ip, mesh[mesh[si][0]].GetLayer()));
+		  mesh.AddPoint (ip);
+		  
+		  (*testout) << "found intersection at " << ip << endl;
+		  (*testout) << "sol = " << sol << endl;
+		  (*testout) << "res = " << (rhs - mat*sol) << endl;
+		  (*testout) << "segs = " << pi1 << " - " << pi2 << endl;
+		  (*testout) << "and = " << pj1 << " - " << pj2 << endl << endl;
+		}
+	    }
+	}  
+  }
+
+
+
+
+
+
+  static void MeshSurface (CSGeometry & geom, Mesh & mesh)
+  {
+    const char * savetask = multithread.task;
+    multithread.task = "Surface meshing";
+  
+    Array<Segment> segments;
+    int noldp = mesh.GetNP();
+
+    double starttime = GetTime();
+
+    // find master faces from identified
+    Array<int> masterface(mesh.GetNFD());
+    for (int i = 1; i <= mesh.GetNFD(); i++)
+      masterface.Elem(i) = i;
+  
+    Array<INDEX_2> fpairs;
+    bool changed;
+    do
+      {
+	changed = 0;
+	for (int i = 0; i < geom.identifications.Size(); i++)
+	  {
+	    geom.identifications[i]->GetIdentifiedFaces (fpairs);
+
+	    for (int j = 0; j < fpairs.Size(); j++)
+	      {
+		if (masterface.Get(fpairs[j].I1()) <
+		    masterface.Get(fpairs[j].I2()))
+		  {
+		    changed = 1;
+		    masterface.Elem(fpairs[j].I2()) =
+		      masterface.Elem(fpairs[j].I1());
+		  }
+		if (masterface.Get(fpairs[j].I2()) <
+		    masterface.Get(fpairs[j].I1()))
+		  {
+		    changed = 1;
+		    masterface.Elem(fpairs[j].I1()) =
+		      masterface.Elem(fpairs[j].I2());
+		  }
+	      }
+	  }
+      }
+    while (changed);
+
+
+    int bccnt=0;
+    for (int k = 0; k < geom.GetNSurf(); k++)
+      bccnt = max2 (bccnt, geom.GetSurface(k)->GetBCProperty());
+
+    for (int k = 1; k <= mesh.GetNFD(); k++)
+      {
+	bool increased = false;
+
+	FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+	const Surface * surf = geom.GetSurface(fd.SurfNr());
+
+	if (fd.TLOSurface() && 
+	    geom.GetTopLevelObject(fd.TLOSurface()-1) -> GetBCProp() > 0)
+	  fd.SetBCProperty (geom.GetTopLevelObject(fd.TLOSurface()-1) -> GetBCProp());
+	else if (surf -> GetBCProperty() != -1)
+	  fd.SetBCProperty (surf->GetBCProperty());
+	else
+	  {
+	    bccnt++;
+	    fd.SetBCProperty (bccnt);
+	    increased = true;
+	  }      
+
+	for (int l = 0; l < geom.bcmodifications.Size(); l++)
+	  {
+	    if (geom.GetSurfaceClassRepresentant (fd.SurfNr()) == 
+		geom.GetSurfaceClassRepresentant (geom.bcmodifications[l].si) &&
+		(fd.DomainIn() == geom.bcmodifications[l].tlonr+1 ||
+		 fd.DomainOut() == geom.bcmodifications[l].tlonr+1))
+	      {
+		if(geom.bcmodifications[l].bcname == NULL)
+		  fd.SetBCProperty (geom.bcmodifications[l].bcnr);
+		else
+		  {
+		    if(!increased)
+		      {
+			bccnt++;
+			fd.SetBCProperty (bccnt);
+			increased = true;
+		      }
+		  }
+	      }
+	  }
+      }
+
+    mesh.SetNBCNames( bccnt );
+
+    for (int k = 1; k <= mesh.GetNFD(); k++)
+      {
+	FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+	const Surface * surf = geom.GetSurface(fd.SurfNr());
+	if (fd.TLOSurface() )
+	  {
+	    int bcp = fd.BCProperty();
+	    string nextbcname = geom.GetTopLevelObject(fd.TLOSurface()-1) -> GetBCName();
+	    if ( nextbcname != "default" )
+	      mesh.SetBCName ( bcp - 1 , nextbcname );
+	  }
+	else // if (surf -> GetBCProperty() != -1)
+	  {
+	    int bcp = fd.BCProperty();
+	    string nextbcname = surf->GetBCName();
+	    if ( nextbcname != "default" )
+	      mesh.SetBCName ( bcp - 1, nextbcname );
+	  }
+      }
+    
+    for (int k = 1; k <= mesh.GetNFD(); k++)
+      {
+	FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+	fd.SetBCName ( mesh.GetBCNamePtr ( fd.BCProperty() - 1 ) );
+      }
+    
+
+    //!!
+    
+    for (int k = 1; k <= mesh.GetNFD(); k++)
+      {
+	FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+	//const Surface * surf = geom.GetSurface(fd.SurfNr());
+
+	for (int l = 0; l < geom.bcmodifications.Size(); l++)
+	  {
+	    if (geom.GetSurfaceClassRepresentant (fd.SurfNr()) == 
+		geom.GetSurfaceClassRepresentant (geom.bcmodifications[l].si) &&
+		(fd.DomainIn() == geom.bcmodifications[l].tlonr+1 ||
+		 fd.DomainOut() == geom.bcmodifications[l].tlonr+1) &&
+		geom.bcmodifications[l].bcname != NULL
+		)
+	      {
+		int bcp = fd.BCProperty();
+		mesh.SetBCName ( bcp - 1, *(geom.bcmodifications[l].bcname) );
+		fd.SetBCName ( mesh.GetBCNamePtr ( bcp - 1) );
+	      }
+	  }
+      }
+
+    for(int k = 0; k<geom.bcmodifications.Size(); k++)
+      {
+	delete geom.bcmodifications[k].bcname;
+	geom.bcmodifications[k].bcname = NULL;
+      }
+
+    //!!
+
+
+    for (int j = 0; j < geom.singfaces.Size(); j++)
+      {
+	Array<int> surfs;
+	geom.GetIndependentSurfaceIndices (geom.singfaces[j]->GetSolid(),
+					   geom.BoundingBox(), surfs);
+	for (int k = 1; k <= mesh.GetNFD(); k++)
+	  {
+	    FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+	    for (int l = 0; l < surfs.Size(); l++)
+	      if (surfs[l] == fd.SurfNr())
+		{
+		  if (geom.singfaces[j]->GetDomainNr() == fd.DomainIn())
+		    fd.SetDomainInSingular (1);
+		  if (geom.singfaces[j]->GetDomainNr() == fd.DomainOut())
+		    fd.SetDomainOutSingular (1);
+		}
+	  }
+      }
+    
+
+    // assemble edge hash-table
+    mesh.CalcSurfacesOfNode();
+
+    for (int k = 1; k <= mesh.GetNFD(); k++)
+      {
+	multithread.percent = 100.0 * k / (mesh.GetNFD()+1e-10);
+
+	if (masterface.Get(k) != k)
+	  continue;
+
+	FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+
+	(*testout) << "Surface " << k << endl;
+	(*testout) << "Face Descriptor: " << fd << endl;
+	PrintMessage (1, "Surface ", k, " / ", mesh.GetNFD());
+
+	int oldnf = mesh.GetNSE();
+      
+	const Surface * surf =
+	  geom.GetSurface((mesh.GetFaceDescriptor(k).SurfNr()));
+
+
+	Meshing2Surfaces meshing(*surf, mparam, geom.BoundingBox());
+	meshing.SetStartTime (starttime);
+
+        double eps = 1e-8 * geom.MaxSize();
+	for (PointIndex pi = PointIndex::BASE; pi < noldp+PointIndex::BASE; pi++)
+	  { 
+	    // if(surf->PointOnSurface(mesh[pi]))
+	    meshing.AddPoint (mesh[pi], pi, NULL,
+			      (surf->PointOnSurface(mesh[pi], eps) != 0));
+	  }
+
+	segments.SetSize (0);
+
+	for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+	  if (mesh[si].si == k)
+	    {
+	      segments.Append (mesh[si]);
+	      (*testout) << "appending segment " << mesh[si] << endl;
+	      //<< " from " << mesh[mesh[si][0]]
+	      //	 << " to " <<mesh[mesh[si][1]]<< endl;
+	    }
+
+	(*testout) << "num-segments " << segments.Size() << endl;
+
+	for (int i = 1; i <= geom.identifications.Size(); i++)
+	  {
+	    geom.identifications.Get(i)->
+	      BuildSurfaceElements(segments, mesh, surf);
+	  }
+
+	for (int si = 0; si < segments.Size(); si++)
+	  {
+	    PointGeomInfo gi;
+	    gi.trignum = k;
+	    meshing.AddBoundaryElement (segments[si][0] + 1 - PointIndex::BASE, 
+					segments[si][1] + 1 - PointIndex::BASE, 
+					gi, gi);
+	  }
+
+	double maxh = mparam.maxh;
+	if (fd.DomainIn() != 0)
+	  {
+	    const Solid * s1 = 
+	      geom.GetTopLevelObject(fd.DomainIn()-1) -> GetSolid();
+	    if (s1->GetMaxH() < maxh)
+	      maxh = s1->GetMaxH();
+	    maxh = min2(maxh, geom.GetTopLevelObject(fd.DomainIn()-1)->GetMaxH());
+	  }
+	if (fd.DomainOut() != 0)
+	  {
+	    const Solid * s1 = 
+	      geom.GetTopLevelObject(fd.DomainOut()-1) -> GetSolid();
+	    if (s1->GetMaxH() < maxh)
+	      maxh = s1->GetMaxH();
+	    maxh = min2(maxh, geom.GetTopLevelObject(fd.DomainOut()-1)->GetMaxH());
+	  }
+	if (fd.TLOSurface() != 0)
+	  {
+	    double hi = geom.GetTopLevelObject(fd.TLOSurface()-1) -> GetMaxH();
+	    if (hi < maxh) maxh = hi;
+	  }
+
+	(*testout) << "domin = " << fd.DomainIn() << ", domout = " << fd.DomainOut()
+		   << ", tlo-surf = " << fd.TLOSurface()
+		   << " mpram.maxh = " << mparam.maxh << ", maxh = " << maxh << endl;
+
+	mparam.checkoverlap = 0;
+
+	MESHING2_RESULT res =
+	  meshing.GenerateMesh (mesh, mparam, maxh, k);
+
+	if (res != MESHING2_OK)
+	  {
+	    PrintError ("Problem in Surface mesh generation");
+	    throw NgException ("Problem in Surface mesh generation");
+	  }
+
+	if (multithread.terminate) return;
+      
+	for (SurfaceElementIndex sei = oldnf; sei < mesh.GetNSE(); sei++)
+	  mesh[sei].SetIndex (k);
+
+
+	//      mesh.CalcSurfacesOfNode();
+
+	if (segments.Size())   
+	  { 
+	    // surface was meshed, not copied
+
+	    static int timer = NgProfiler::CreateTimer ("total surface mesh optimization");
+	    NgProfiler::RegionTimer reg (timer);
+
+
+	    PrintMessage (2, "Optimize Surface");
+	    for (int i = 1; i <= mparam.optsteps2d; i++)
+	      {
+		if (multithread.terminate) return;
+		
+		{
+		  MeshOptimize2dSurfaces meshopt(geom);
+		  meshopt.SetFaceIndex (k);
+		  meshopt.SetImproveEdges (0);
+		  meshopt.SetMetricWeight (mparam.elsizeweight);
+		  meshopt.SetWriteStatus (0);
+		  
+		  meshopt.EdgeSwapping (mesh, (i > mparam.optsteps2d/2));
+		}
+		
+		if (multithread.terminate) return;
+		{
+		  //		mesh.CalcSurfacesOfNode();
+		
+		  MeshOptimize2dSurfaces meshopt(geom);
+		  meshopt.SetFaceIndex (k);
+		  meshopt.SetImproveEdges (0);
+		  meshopt.SetMetricWeight (mparam.elsizeweight);
+		  meshopt.SetWriteStatus (0);
+
+		  meshopt.ImproveMesh (mesh, mparam);
+		}
+		
+		{
+		  MeshOptimize2dSurfaces meshopt(geom);
+		  meshopt.SetFaceIndex (k);
+		  meshopt.SetImproveEdges (0);
+		  meshopt.SetMetricWeight (mparam.elsizeweight);
+		  meshopt.SetWriteStatus (0);
+
+		  meshopt.CombineImprove (mesh);
+		  //		mesh.CalcSurfacesOfNode();
+		}
+		
+		if (multithread.terminate) return;
+		{
+		  MeshOptimize2dSurfaces meshopt(geom);
+		  meshopt.SetFaceIndex (k);
+		  meshopt.SetImproveEdges (0);
+		  meshopt.SetMetricWeight (mparam.elsizeweight);
+		  meshopt.SetWriteStatus (0);
+
+		  meshopt.ImproveMesh (mesh, mparam);
+		}
+	      }
+	  }
+
+
+	PrintMessage (3, (mesh.GetNSE() - oldnf), " elements, ", mesh.GetNP(), " points");
+
+	extern void Render();
+	Render();
+      }
+    
+    mesh.Compress();
+
+    do
+      {
+	changed = 0;
+	for (int k = 1; k <= mesh.GetNFD(); k++)
+	  {
+	    multithread.percent = 100.0 * k / (mesh.GetNFD()+1e-10);
+	  
+	    if (masterface.Get(k) == k)
+	      continue;
+
+	    FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+
+	    (*testout) << "Surface " << k << endl;
+	    (*testout) << "Face Descriptor: " << fd << endl;
+	    PrintMessage (2, "Surface ", k);
+
+	    int oldnf = mesh.GetNSE();
+      
+	    const Surface * surf =
+	      geom.GetSurface((mesh.GetFaceDescriptor(k).SurfNr()));
+
+	    /*
+	      if (surf -> GetBCProperty() != -1)
+	      fd.SetBCProperty (surf->GetBCProperty());
+	      else
+	      {
+	      bccnt++;
+	      fd.SetBCProperty (bccnt);
+	      }
+	    */
+  
+	    segments.SetSize (0);
+	    for (int i = 1; i <= mesh.GetNSeg(); i++)
+	      {
+		Segment * seg = &mesh.LineSegment(i);
+		if (seg->si == k)
+		  segments.Append (*seg);
+	      }
+
+	    for (int i = 1; i <= geom.identifications.Size(); i++)
+	      {
+		geom.identifications.Elem(i)->GetIdentifiedFaces (fpairs);
+		int found = 0;
+		for (int j = 1; j <= fpairs.Size(); j++)
+		  if (fpairs.Get(j).I1() == k || fpairs.Get(j).I2() == k)
+		    found = 1;
+
+		if (!found)
+		  continue;
+
+		geom.identifications.Get(i)->
+		  BuildSurfaceElements(segments, mesh, surf);
+		if (!segments.Size())
+		  break;
+	      }
+
+	  
+	    if (multithread.terminate) return;
+
+	    for (SurfaceElementIndex  sei = oldnf; sei < mesh.GetNSE(); sei++)
+	      mesh[sei].SetIndex (k);
+
+
+	    if (!segments.Size())
+	      {
+		masterface.Elem(k) = k;
+		changed = 1; 
+	      }
+
+	    PrintMessage (3, (mesh.GetNSE() - oldnf), " elements, ", mesh.GetNP(), " points");
+	  }
+      
+	extern void Render();
+	Render();
+      }
+    while (changed);
+
+    
+    mesh.SplitSeparatedFaces();
+    mesh.CalcSurfacesOfNode();
+
+    multithread.task = savetask;
+  }
+
+
+
+  int CSGGenerateMesh (CSGeometry & geom, 
+		       Mesh *& mesh, MeshingParameters & mparam,
+		       int perfstepsstart, int perfstepsend)
+  {
+    if (mesh && mesh->GetNSE() &&
+	!geom.GetNSolids())
+      {
+	if (perfstepsstart < MESHCONST_MESHVOLUME)
+	  perfstepsstart = MESHCONST_MESHVOLUME;
+      }
+
+    if (perfstepsstart <= MESHCONST_ANALYSE)
+      {
+        if (mesh)
+          mesh -> DeleteMesh();
+        else
+          mesh = new Mesh();
+
+	mesh->SetGlobalH (mparam.maxh);
+	mesh->SetMinimalH (mparam.minh);
+
+	Array<double> maxhdom(geom.GetNTopLevelObjects());
+	for (int i = 0; i < maxhdom.Size(); i++)
+	  maxhdom[i] = geom.GetTopLevelObject(i)->GetMaxH();
+
+	mesh->SetMaxHDomain (maxhdom);
+
+	if (mparam.uselocalh)
+	  {
+	    double maxsize = geom.MaxSize(); 
+	    mesh->SetLocalH (Point<3>(-maxsize, -maxsize, -maxsize),
+			     Point<3>(maxsize, maxsize, maxsize),
+			     mparam.grading);
+
+	    mesh -> LoadLocalMeshSize (mparam.meshsizefilename);
+	  }
+
+	spoints.SetSize(0);
+	FindPoints (geom, *mesh);
+      
+	PrintMessage (5, "find points done");
+
+#ifdef LOG_STREAM
+	(*logout) << "Special points found" << endl
+		  << "time = " << GetTime() << " sec" << endl
+		  << "points: " << mesh->GetNP() << endl << endl;
+#endif
+      }
+
+
+    if (multithread.terminate || perfstepsend <= MESHCONST_ANALYSE) 
+      return TCL_OK;
+
+
+    if (perfstepsstart <= MESHCONST_MESHEDGES)
+      {
+	FindEdges (geom, *mesh, true);
+	if (multithread.terminate) return TCL_OK;
+#ifdef LOG_STREAM      
+	(*logout) << "Edges meshed" << endl
+		  << "time = " << GetTime() << " sec" << endl
+		  << "points: " << mesh->GetNP() << endl;
+#endif
+      
+      
+	if (multithread.terminate)
+	  return TCL_OK;
+  
+	if (mparam.uselocalh)
+	  {
+	    mesh->CalcLocalH(mparam.grading);
+	    mesh->DeleteMesh();
+	    
+	    FindPoints (geom, *mesh);
+	    if (multithread.terminate) return TCL_OK;
+	    FindEdges (geom, *mesh, true);
+	    if (multithread.terminate) return TCL_OK;
+	    
+	    mesh->DeleteMesh();
+	  
+	    FindPoints (geom, *mesh);
+	    if (multithread.terminate) return TCL_OK;
+	    FindEdges (geom, *mesh);
+	    if (multithread.terminate) return TCL_OK;
+	  }
+      }
+  
+    if (multithread.terminate || perfstepsend <= MESHCONST_MESHEDGES)
+      return TCL_OK;
+
+
+    if (perfstepsstart <= MESHCONST_MESHSURFACE)
+      {
+	MeshSurface (geom, *mesh);  
+	if (multithread.terminate) return TCL_OK;
+      
+#ifdef LOG_STREAM
+	(*logout) << "Surfaces meshed" << endl
+		  << "time = " << GetTime() << " sec" << endl
+		  << "points: " << mesh->GetNP() << endl;
+#endif      
+      
+	if (mparam.uselocalh && 0)
+	  {
+	    mesh->CalcLocalH(mparam.grading);      
+	    mesh->DeleteMesh();
+
+	    FindPoints (geom, *mesh);
+	    if (multithread.terminate) return TCL_OK;
+	    FindEdges (geom, *mesh);
+	    if (multithread.terminate) return TCL_OK;
+
+	    MeshSurface (geom, *mesh);  
+	    if (multithread.terminate) return TCL_OK;
+	  }
+
+#ifdef LOG_STREAM      
+	(*logout) << "Surfaces remeshed" << endl
+		  << "time = " << GetTime() << " sec" << endl
+		  << "points: " << mesh->GetNP() << endl;
+#endif      
+      
+#ifdef STAT_STREAM
+	(*statout) << mesh->GetNSeg() << " & "
+		   << mesh->GetNSE() << " & - &" 
+		   << GetTime() << " & " << endl;
+#endif  
+
+	MeshQuality2d (*mesh);
+	mesh->CalcSurfacesOfNode();
+      }
+  
+    if (multithread.terminate || perfstepsend <= MESHCONST_OPTSURFACE)
+      return TCL_OK;
+
+
+    if (perfstepsstart <= MESHCONST_MESHVOLUME)
+      {
+	multithread.task = "Volume meshing";
+
+	MESHING3_RESULT res =
+	  MeshVolume (mparam, *mesh);
+
+	if (res != MESHING3_OK) return TCL_ERROR;
+      
+	if (multithread.terminate) return TCL_OK;
+      
+	RemoveIllegalElements (*mesh);
+	if (multithread.terminate) return TCL_OK;
+
+	MeshQuality3d (*mesh);
+      
+	for (int i = 0; i < geom.GetNTopLevelObjects(); i++)
+	  mesh->SetMaterial (i+1, geom.GetTopLevelObject(i)->GetMaterial().c_str());
+      
+
+#ifdef STAT_STREAM
+	(*statout) << GetTime() << " & ";
+#endif      
+      
+#ifdef LOG_STREAM
+	(*logout) << "Volume meshed" << endl
+		  << "time = " << GetTime() << " sec" << endl
+		  << "points: " << mesh->GetNP() << endl;
+#endif
+      }
+
+    if (multithread.terminate || perfstepsend <= MESHCONST_MESHVOLUME)
+      return TCL_OK;
+
+
+    if (perfstepsstart <= MESHCONST_OPTVOLUME)
+      {
+	multithread.task = "Volume optimization";
+      
+	OptimizeVolume (mparam, *mesh);
+	if (multithread.terminate) return TCL_OK;
+      
+#ifdef STAT_STREAM
+	(*statout) << GetTime() << " & "
+		   << mesh->GetNE() << " & "
+		   << mesh->GetNP() << " " << '\\' << '\\' << " \\" << "hline" << endl;
+#endif      
+
+#ifdef LOG_STREAM      
+	(*logout) << "Volume optimized" << endl
+		  << "time = " << GetTime() << " sec" << endl
+		  << "points: " << mesh->GetNP() << endl;
+#endif
+      }
+
+    return TCL_OK;
+  }
+}
diff --git a/contrib/Netgen/libsrc/csg/geoml.hpp b/contrib/Netgen/libsrc/csg/geoml.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e7974ad2063427561cb6c46ce3295321ce09fc17
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/geoml.hpp
@@ -0,0 +1,16 @@
+#ifndef FILE_GEOML
+#define FILE_GEOML
+
+/* *************************************************************************/
+/* File:   geoml.hh                                                        */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   21. Jun. 98                                                     */
+/* *************************************************************************/
+
+#include <geom/geom.hh>
+
+#include <geom/solid.hh>
+#include <geom/algprim.hh>
+#include <geom/adtree.hh>
+#include <geom/csgeom.hh>
+#endif
diff --git a/contrib/Netgen/libsrc/csg/identify.cpp b/contrib/Netgen/libsrc/csg/identify.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e3086a3bad5980ebabb38dc7667e6f55b46dd0c1
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/identify.cpp
@@ -0,0 +1,1662 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+
+namespace netgen
+{
+Identification :: Identification (int anr, const CSGeometry & ageom)
+  : geom(ageom), identfaces(10)
+{
+  nr = anr;
+}
+
+Identification :: ~Identification ()
+{
+  ;
+}
+
+
+ostream & operator<< (ostream & ost, Identification & ident)
+{
+  ident.Print (ost);
+  return ost;
+}
+
+
+/*
+void Identification :: IdentifySpecialPoints (Array<class SpecialPoint> & points)
+{
+  ;
+}
+*/
+
+
+int Identification :: 
+Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2,
+	      const TABLE<int> & specpoint2solid,
+	      const TABLE<int> & specpoint2surface) const
+{
+  cout << "Identification::Identifyable called for base-class" << endl;
+  return 0;
+}
+
+int Identification :: 
+Identifyable (const Point<3> & p1, const Point<3> & sp2) const
+{
+  cout << "Identification::Identifyable called for base-class" << endl;
+  return 0;
+}
+
+
+int Identification :: 
+IdentifyableCandidate (const SpecialPoint & sp1) const
+{
+  return 1;
+}
+
+
+int Identification :: 
+ShortEdge (const SpecialPoint & sp1, const SpecialPoint & sp2) const
+{
+  return 0;
+}
+
+int Identification :: GetIdentifiedPoint (class Mesh & mesh, int pi)
+{
+  cout << "Identification::GetIdentifiedPoint called for base-class" << endl;
+  return -1;
+}
+
+void Identification :: IdentifyPoints (Mesh & mesh)
+{
+  cout << "Identification::IdentifyPoints called for base-class" << endl;
+  ;
+}
+
+void Identification :: IdentifyFaces (class Mesh & mesh)
+{
+  cout << "Identification::IdentifyFaces called for base-class" << endl;
+  ;
+}
+
+void Identification :: 
+BuildSurfaceElements (Array<Segment> & segs,
+		      Mesh & mesh, const Surface * surf)
+{
+  cout << "Identification::BuildSurfaceElements called for base-class" << endl;
+  ;
+}
+
+
+void Identification :: 
+BuildVolumeElements (Array<class Element2d> & surfels,
+			  class Mesh & mesh)
+{
+  ;
+}
+
+void Identification :: 
+GetIdentifiedFaces (Array<INDEX_2> & idfaces) const
+{
+  idfaces.SetSize(0);
+  for (int i = 1; i <= identfaces.GetNBags(); i++)
+    for (int j = 1; j <= identfaces.GetBagSize(i); j++)
+      {
+	INDEX_2 i2;
+	int val;
+	identfaces.GetData (i, j, i2, val);
+	idfaces.Append (i2);
+      }
+}
+
+
+
+
+PeriodicIdentification ::
+PeriodicIdentification (int anr,
+			const CSGeometry & ageom,
+			const Surface * as1,
+			const Surface * as2)
+  : Identification(anr, ageom)
+{
+  s1 = as1;
+  s2 = as2;
+}
+
+PeriodicIdentification :: ~PeriodicIdentification ()
+{
+  ;
+}
+
+/*
+void PeriodicIdentification :: IdentifySpecialPoints 
+(Array<class SpecialPoint> & points)
+{
+  int i, j;
+  int bestj;
+  double bestval, val;
+
+  for (i = 1; i <= points.Size(); i++)
+    {
+      Point<3> p1 = points.Get(i).p;
+      Point<3> hp1 = p1;
+      s1->Project (hp1);
+      if (Dist (p1, hp1) > 1e-6) continue;
+
+      Vec<3> n1;
+      s1->GetNormalVector (p1, n1);
+      n1 /= n1.Length();
+      if ( fabs(n1 * points.Get(i).v) > 1e-3)
+	continue;
+
+      bestval = 1e8;
+      bestj = 1;
+      for (j = 1; j <= points.Size(); j++)
+	{
+	  Point<3> p2= points.Get(j).p;
+	  Point<3> hp2 = p2;
+	  s2->Project (hp2);
+	  if (Dist (p2, hp2) > 1e-6) continue;
+	  
+	  Vec<3> n2;
+	  s2->GetNormalVector (p2, n2);
+	  n2 /= n2.Length();
+	  if ( fabs(n2 * points.Get(j).v) > 1e-3)
+	    continue;
+
+
+	  Vec<3> v(p1, p2);
+	  double vl = v.Length();
+	  double cl = fabs (v*n1);
+
+	  val = 1 - cl*cl/(vl*vl);
+
+	  val += (points.Get(i).v - points.Get(j).v).Length();
+
+	  if (val < bestval)
+	    {
+	      bestj = j;
+	      bestval = val;
+	    }
+	}
+
+      (*testout) << "Identify Periodic special points: pi = " 
+		 << points.Get(i).p << ", vi = " << points.Get(i).v 
+		 << " pj = " << points.Get(bestj).p 
+		 << ", vj = " << points.Get(bestj).v 
+		 << " bestval = " << bestval << endl;
+    }
+}
+*/
+
+int PeriodicIdentification :: 
+Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2,
+	      const TABLE<int> & specpoint2solid,
+	      const TABLE<int> & specpoint2surface) const
+{
+  int i;
+  double val;
+  
+  SpecialPoint hsp1 = sp1;
+  SpecialPoint hsp2 = sp2;
+
+  for (i = 1; i <= 1; i++)
+    {
+      //      Swap (hsp1, hsp2);
+
+      if (!s1->PointOnSurface (hsp1.p))
+	continue;
+
+      Vec<3> n1;
+      n1 = s1->GetNormalVector (hsp1.p);
+      n1 /= n1.Length();
+      if ( fabs(n1 * hsp1.v) > 1e-3)
+	continue;
+
+
+      if (!s2->PointOnSurface(hsp2.p))
+	continue;
+
+      Vec<3> n2;
+      n2 = s2->GetNormalVector (hsp2.p);
+      n2 /= n2.Length();
+      if ( fabs(n2 * hsp2.v) > 1e-3)
+	continue;
+
+
+      Vec<3> v = hsp2.p - hsp1.p;
+      double vl = v.Length();
+      double cl = fabs (v*n1);
+      
+
+      val = 1 - cl*cl/(vl*vl);
+      val += (hsp1.v - hsp2.v).Length();
+    
+      if (val < 1e-6)
+        return 1;
+    }
+
+  return 0;
+}
+
+int PeriodicIdentification :: 
+Identifyable (const Point<3> & p1, const Point<3> & p2) const
+{
+  return (s1->PointOnSurface (p1) &&
+	  s2->PointOnSurface (p2));
+}
+  
+
+
+
+int PeriodicIdentification :: 
+GetIdentifiedPoint (class Mesh & mesh,  int pi)
+{
+  const Surface *snew;
+  const Point<3> & p = mesh.Point (pi);
+
+  if (s1->PointOnSurface (p))
+    {
+      snew = s2;
+    }
+  else
+    {
+      if (s2->PointOnSurface (p))
+	{
+	  snew = s1;
+	}
+      else
+	{
+	  cerr << "GetIdenfifiedPoint: Not possible" << endl;
+	  exit (1);
+	}    
+    }
+  
+  // project to other surface
+  Point<3> hp = p;
+  snew->Project (hp);
+
+  int newpi = 0;
+  for (int i = 1; i <= mesh.GetNP(); i++)
+    if (Dist2 (mesh.Point(i), hp) < 1e-12)
+      {
+	newpi = i;
+	break;
+      }
+  if (!newpi)
+    newpi = mesh.AddPoint (hp);
+
+  if (snew == s2)
+    mesh.GetIdentifications().Add (pi, newpi, nr);
+  else
+    mesh.GetIdentifications().Add (newpi, pi, nr);
+
+  mesh.GetIdentifications().SetType(nr,Identifications::PERIODIC);
+	   
+  /* 
+  (*testout) << "Identify points(periodic), nr = " << nr << ": " << mesh.Point(pi)
+	     << " and " << mesh.Point(newpi) 
+	     << ((snew == s2) ? "" : " inverse")
+	     << endl;
+  */
+  return newpi;
+}
+
+
+void PeriodicIdentification :: IdentifyPoints (class Mesh & mesh)
+{
+  int i, j;
+  for (i = 1; i <= mesh.GetNP(); i++)
+    {
+      Point<3> p = mesh.Point(i);
+      if (s1->PointOnSurface (p))
+	{
+	  Point<3> pp = p;
+	  s2->Project (pp);
+	  for (j = 1; j <= mesh.GetNP(); j++)
+	    if (Dist2(mesh.Point(j), pp) < 1e-6)
+	      {
+		mesh.GetIdentifications().Add (i, j, nr);
+		/*
+		(*testout) << "Identify points(periodic:), nr = " << nr << ": "
+			   << mesh.Point(i) << " - " << mesh.Point(j) << endl;
+		*/
+	      }
+	}
+    }
+
+  mesh.GetIdentifications().SetType(nr,Identifications::PERIODIC);
+}
+
+
+void PeriodicIdentification :: IdentifyFaces (class Mesh & mesh)
+{
+  int i, j, k, l;
+  int fi1, fi2, side;
+  for (i = 1; i <= mesh.GetNFD(); i++)
+    for (j = 1; j <= mesh.GetNFD(); j++)
+      {
+	int surfi = mesh.GetFaceDescriptor(i).SurfNr();
+	int surfj = mesh.GetFaceDescriptor(j).SurfNr();
+	if (surfi == surfj)
+	  continue;
+	
+	if (geom.GetSurface (surfi) != s1 ||
+	    geom.GetSurface (surfj) != s2)
+	  continue;
+	    
+	int idok = 1;
+
+
+	//	(*testout) << "check faces " << i << " and " << j << endl;
+	for (side = 1; side <= 2 && idok; side++)
+	  {
+	    if (side == 1)
+	      {
+		fi1 = i; 
+		fi2 = j;
+	      }
+	    else
+	      {
+		fi1 = j;
+		fi2 = i;
+	      }
+
+	    for (k = 1; k <= mesh.GetNSeg(); k++)
+	      {
+		const Segment & seg1 = mesh.LineSegment(k);
+		if (seg1.si != fi1)
+		  continue;
+
+		int foundother = 0;
+		for (l = 1; l <= mesh.GetNSeg(); l++)
+		  {
+		    const Segment & seg2 = mesh.LineSegment(l);
+		    if (seg2.si != fi2)
+		      continue;
+		    
+		    //		    (*testout) << "seg1 = " << seg1[0] << "-" << seg1[1] << ", seg2 = " << seg2[0] << "-" << seg2[1];
+
+		    if (side == 1)
+		      {
+			if (mesh.GetIdentifications().Get (seg1[0], seg2[0]) &&
+			    mesh.GetIdentifications().Get (seg1[1], seg2[1]))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+			
+			if (mesh.GetIdentifications().Get (seg1[0], seg2[1]) &&
+			    mesh.GetIdentifications().Get (seg1[1], seg2[0]))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+		      }
+		    else
+		      {
+			if (mesh.GetIdentifications().Get (seg2[0], seg1[0]) &&
+			    mesh.GetIdentifications().Get (seg2[1], seg1[1]))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+			
+			if (mesh.GetIdentifications().Get (seg2[0], seg1[1]) &&
+			    mesh.GetIdentifications().Get (seg2[1], seg1[0]))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+		      }
+		  }
+
+		if (!foundother)
+		  {
+		    idok = 0;
+		    break;
+		  }
+	      }
+	  }
+
+
+	if (idok)
+	  {
+	    // (*testout) << "Identify faces " << i << " and " << j << endl;
+	    INDEX_2 fpair(i,j);
+	    fpair.Sort();
+	    identfaces.Set (fpair, 1);
+	  }
+      }
+}
+
+
+
+void PeriodicIdentification :: 
+BuildSurfaceElements (Array<Segment> & segs,
+		      Mesh & mesh, const Surface * surf)
+{
+  int found = 0;
+  int fother = -1;
+
+  int facei = segs.Get(1).si;
+  int surfnr = mesh.GetFaceDescriptor(facei).SurfNr();
+
+  if (geom.GetSurface(surfnr) == s1 ||
+      geom.GetSurface(surfnr) == s2)
+    {
+      Array<int> copy_points;
+
+      for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++)
+	{
+	  const Element2d & sel = mesh[sei];
+	  INDEX_2 fpair (facei, sel.GetIndex());
+	  fpair.Sort();
+	  if (identfaces.Used (fpair))
+            {
+	      for (int k = 0; k < sel.GetNP(); k++)
+                if (!copy_points.Contains (sel[k]))
+                  copy_points.Append (sel[k]);
+            }      
+        }
+      BubbleSort (copy_points);
+      for (int k = 0; k < copy_points.Size(); k++)
+        GetIdentifiedPoint (mesh, copy_points[k]);
+
+
+
+      for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++)
+	{
+	  const Element2d & sel = mesh[sei];
+	  INDEX_2 fpair (facei, sel.GetIndex());
+	  fpair.Sort();
+	  if (identfaces.Used (fpair))
+	    {
+	      found = 1;
+	      fother = sel.GetIndex();
+
+	      // copy element
+	      Element2d newel(sel.GetType());
+	      newel.SetIndex (facei);
+	      for (int k = 0; k < sel.GetNP(); k++)
+                newel[k] = GetIdentifiedPoint (mesh, sel[k]);
+
+	      Vec<3> nt = Cross (Point<3> (mesh[newel[1]])- Point<3> (mesh[newel[0]]),
+				 Point<3> (mesh[newel[2]])- Point<3> (mesh[newel[0]]));
+	      
+	      Vec<3> nsurf = geom.GetSurface (surfnr)->GetNormalVector (mesh[newel[0]]);
+	      if (nsurf * nt < 0)
+                Swap (newel[0], newel[2]);
+				
+	      mesh.AddSurfaceElement (newel);
+	    }
+	}
+    }
+  
+  if (found)
+    {
+      // (*mycout) << " copy face " << facei << " from face " << fother;
+      PrintMessage (4, " copy face ", facei, " from face ", fother);
+      
+      segs.SetSize(0);
+    }
+}
+
+
+
+
+
+
+
+
+void PeriodicIdentification :: Print (ostream & ost) const
+{
+  ost << "Periodic Identifiaction, surfaces: " 
+      << s1->Name() << " - " << s2->Name() << endl;
+  s1->Print (ost);
+  ost << " - ";
+  s2->Print (ost);
+  ost << endl;
+}
+
+
+void PeriodicIdentification :: GetData (ostream & ost) const
+{
+  ost << "periodic " << s1->Name() << " " << s2->Name();
+}
+
+
+
+
+
+
+
+CloseSurfaceIdentification ::
+CloseSurfaceIdentification (int anr,
+			    const CSGeometry & ageom,
+			    const Surface * as1,
+			    const Surface * as2,
+			    const TopLevelObject * adomain,
+			    const Flags & flags)
+  : Identification(anr, ageom)
+{
+  s1 = as1;
+  s2 = as2;
+  domain = adomain;
+  ref_levels = int (flags.GetNumFlag ("reflevels", 2));
+  ref_levels_s1 = int (flags.GetNumFlag ("reflevels1", 0));
+  ref_levels_s2 = int (flags.GetNumFlag ("reflevels2", 0));
+  slices = flags.GetNumListFlag ("slices");
+  for(int i=0; i<slices.Size(); i++)
+    if((i==0 && slices[i] <= 0) ||
+       (i>0 && slices[i] <= slices[i-1]) ||
+       (slices[i] >= 1))
+      throw NgException ("slices have to be in ascending order, between 0 and 1");
+
+  // eps_n = 1e-3;
+  eps_n = 1e-6;
+
+  dom_surf_valid = 0;
+
+  if (domain)
+    for (int i = 0; i < geom.GetNTopLevelObjects(); i++)
+      if (domain == geom.GetTopLevelObject(i))
+	dom_nr = i;
+
+  usedirection = flags.NumListFlagDefined("direction");
+  if(usedirection)
+    {
+      for(int i=0; i<3; i++)
+	direction(i) = flags.GetNumListFlag("direction")[i];
+
+      direction.Normalize();
+    }
+}
+
+CloseSurfaceIdentification :: ~CloseSurfaceIdentification ()
+{
+  ;
+}
+
+void CloseSurfaceIdentification :: Print (ostream & ost) const
+{
+  ost << "CloseSurface Identifiaction, surfaces: " 
+      << s1->Name() << " - " << s2->Name() << endl;
+  s1->Print (ost);
+  s2->Print (ost);
+  ost << endl;
+}
+
+
+void CloseSurfaceIdentification :: GetData (ostream & ost) const
+{
+  ost << "close surface " << s1->Name() << " " << s2->Name();
+}
+
+
+/*
+void CloseSurfaceIdentification :: IdentifySpecialPoints 
+(Array<class SpecialPoint> & points)
+{
+  int i, j;
+  int bestj;
+  double bestval, val;
+
+  for (i = 1; i <= points.Size(); i++)
+    {
+      Point<3> p1 = points.Get(i).p;
+      Vec<3> n1;
+
+      if (!s1->PointOnSurface (p1))
+	continue;
+
+	s1->GetNormalVector (p1, n1);
+      n1 /= n1.Length();
+      if ( fabs(n1 * points.Get(i).v) > 1e-3)
+	continue;
+
+      bestval = 1e8;
+      bestj = 1;
+      for (j = 1; j <= points.Size(); j++)
+	{
+	  Point<3> p2= points.Get(j).p;
+	  if (!s2->PointOnSurface (p2))
+	    continue;
+	  
+	  Vec<3> n2;
+	  s2->GetNormalVector (p2, n2);
+	  n2 /= n2.Length();
+	  if ( fabs(n2 * points.Get(j).v) > 1e-3)
+	    continue;
+
+
+	  Vec<3> v(p1, p2);
+	  double vl = v.Length();
+	  double cl = fabs (v*n1);
+
+	  val = 1 - cl*cl/(vl*vl);
+
+	  val += (points.Get(i).v - points.Get(j).v).Length();
+
+	  if (val < bestval)
+	    {
+	      bestj = j;
+	      bestval = val;
+	    }
+	}
+
+      (*testout) << "Identify close surfaces special points: pi = " 
+		 << points.Get(i).p << ", vi = " << points.Get(i).v 
+		 << " pj = " << points.Get(bestj).p 
+		 << ", vj = " << points.Get(bestj).v 
+		 << " bestval = " << bestval << endl;
+    }
+}
+*/
+
+int CloseSurfaceIdentification :: 
+Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2,
+	      const TABLE<int> & specpoint2solid,
+	      const TABLE<int> & specpoint2surface) const
+{
+  //(*testout) << "identcheck: " << sp1.p << "; " << sp2.p << endl;
+
+  if (!dom_surf_valid)
+    {
+      const_cast<bool&> (dom_surf_valid) = 1;
+      Array<int> & hsurf = const_cast<Array<int>&> (domain_surfaces);
+
+      if (domain)
+	{
+	  BoxSphere<3> hbox (geom.BoundingBox());
+	  geom.GetIndependentSurfaceIndices (domain->GetSolid(), hbox, hsurf);
+	  //(*testout) << "surfs of identification " << nr << ": " << endl << hsurf << endl;
+	}
+      else
+	{
+	  hsurf.SetSize (geom.GetNSurf());
+	  for (int j = 0; j < hsurf.Size(); j++)
+	    hsurf[j] = j;
+	}
+    }
+
+  if (domain)
+    {
+      bool has1 = 0, has2 = 0;
+      for (int i = 0; i < specpoint2solid[sp1.nr].Size(); i++)
+	if (specpoint2solid[sp1.nr][i] == dom_nr)
+	  { has1 = 1; break; }
+      for (int i = 0; i < specpoint2solid[sp2.nr].Size(); i++)
+	if (specpoint2solid[sp2.nr][i] == dom_nr)
+	  { has2 = 1; break; }
+
+      if (!has1 || !has2) 
+	{
+	  //(*testout) << "failed at pos1" << endl;
+	  return 0;
+	}
+    }
+
+  if (!s1->PointOnSurface (sp1.p))
+    {
+      //(*testout) << "failed at pos2" << endl;
+      return 0;
+    }
+
+//   (*testout) << "sp1 " << sp1.p << " sp2 " << sp2.p << endl
+// 	     << "specpoint2solid[sp1.nr] " << specpoint2solid[sp1.nr] << endl
+// 	     << "specpoint2solid[sp2.nr] " << specpoint2solid[sp2.nr] << endl;
+
+
+  Vec<3> n1 = s1->GetNormalVector (sp1.p);
+  n1.Normalize();
+  if ( fabs(n1 * sp1.v) > eps_n)
+    {
+      //(*testout) << "failed at pos3" << endl;
+      return 0;
+    }
+
+  if (!s2->PointOnSurface(sp2.p))
+    {
+      //(*testout) << "failed at pos4" << endl;
+      return 0;
+    }
+
+
+  Vec<3> n2 = s2->GetNormalVector (sp2.p);
+  n2.Normalize();
+  if ( fabs(n2 * sp2.v) > eps_n)
+    {
+      //(*testout) << "failed at pos5" << endl;
+      return 0;
+    }
+  
+  // must have joint surface 
+  bool joint = 0;
+
+  int j = 0, k = 0;
+  while (1)
+    {
+      int snr1 = specpoint2surface[sp1.nr][j];
+      int snr2 = specpoint2surface[sp2.nr][k];
+      if (snr1 < snr2) 
+	{
+	  j++;
+	  if (j == specpoint2surface[sp1.nr].Size()) break;
+	}
+      else if (snr2 < snr1) 
+	{
+	  k++;
+	  if (k == specpoint2surface[sp2.nr].Size()) break;
+	}
+      else
+	{
+	  bool dom_surf = 0;
+	  for (int l = 0; l < domain_surfaces.Size(); l++)
+	    if (domain_surfaces[l] == snr1)
+	      dom_surf = 1;
+
+	  if (dom_surf)
+	    {
+	      Vec<3> hn1 = geom.GetSurface(snr1)->GetNormalVector (sp1.p);
+	      Vec<3> hn2 = geom.GetSurface(snr1)->GetNormalVector (sp2.p);
+	      
+	      if (hn1 * hn2 > 0)
+		{
+		  joint = 1;
+		  break;
+		}
+	    }
+
+	  j++;
+	  if (j == specpoint2surface[sp1.nr].Size()) break;
+	  k++;
+	  if (k == specpoint2surface[sp2.nr].Size()) break;
+	}
+    }
+
+  if (!joint)
+    {
+      //(*testout) << "failed at pos6" << endl;
+      return 0;
+    }
+
+  Vec<3> v = sp2.p - sp1.p;
+  double vl = v.Length();
+  double cl = (usedirection) ? fabs(v*direction) : fabs (v*n1);
+
+
+  if(cl <= (1-eps_n*eps_n) * vl)
+    {
+      //(*testout) << "failed at pos7" << endl;
+      return 0;
+    }
+  
+  double dl;
+
+  if(usedirection)
+    {
+      Vec<3> v1 = sp1.v - (sp1.v*direction)*direction; v1.Normalize();
+      Vec<3> v2 = sp2.v - (sp2.v*direction)*direction; v2.Normalize();
+      
+      dl = (v1 - v2).Length();
+    }
+  else
+    dl = (sp1.v - sp2.v).Length();
+
+  if (dl < 0.1)
+    return 1;
+   
+
+  //(*testout) << "failed at pos8" << endl;
+  return 0;
+}
+
+int CloseSurfaceIdentification :: 
+Identifyable (const Point<3> & p1, const Point<3> & p2) const
+{  
+//   if (domain)
+//     if (!domain->GetSolid()->IsIn (p1) || !domain->GetSolid()->IsIn (p2))
+//       return 0;
+  return (s1->PointOnSurface (p1) && s2->PointOnSurface (p2));
+}
+  
+
+
+
+int CloseSurfaceIdentification :: 
+IdentifyableCandidate (const SpecialPoint & sp1) const
+{
+  if (domain)
+    if (!domain->GetSolid()->IsIn (sp1.p))
+      return 0;
+
+  if (s1->PointOnSurface (sp1.p))
+    {
+      Vec<3> n1;
+      n1 = s1->GetNormalVector (sp1.p);
+      n1.Normalize();
+      if ( fabs(n1 * sp1.v) > eps_n)
+	return 0;
+      return 1;
+    }
+
+  if (s2->PointOnSurface (sp1.p))
+    {
+      Vec<3> n1;
+      n1 = s2->GetNormalVector (sp1.p);
+      n1.Normalize();
+      if ( fabs(n1 * sp1.v) > eps_n)
+	return 0;
+      return 1;
+    }
+  return 0;
+}
+
+
+
+int CloseSurfaceIdentification :: 
+ShortEdge (const SpecialPoint & sp1, const SpecialPoint & sp2) const
+{  
+  if ( (s1->PointOnSurface (sp1.p) && s2->PointOnSurface (sp2.p)) ||
+       (s1->PointOnSurface (sp2.p) && s2->PointOnSurface (sp1.p)) )
+    {
+      return 1;
+    }
+  return 0;
+}
+
+
+
+int CloseSurfaceIdentification :: 
+GetIdentifiedPoint (class Mesh & mesh,  int pi)
+{
+  const Surface *snew;
+  const Point<3> & p = mesh.Point (pi);
+
+  Array<int,PointIndex::BASE> identmap(mesh.GetNP());
+  mesh.GetIdentifications().GetMap (nr, identmap);
+  if (identmap.Get(pi))
+    return identmap.Get(pi);
+
+  
+  if (s1->PointOnSurface (p))
+    snew = s2;
+  else if (s2->PointOnSurface (p))
+    snew = s1;
+  else
+    {
+      (*testout)  << "GetIdenfifiedPoint: Not possible" << endl;
+      (*testout) << "p = " << p << endl;
+      (*testout) << "surf1: " << (*s1) << endl
+		 << "surf2: " << (*s2) << endl;
+      
+      cerr << "GetIdenfifiedPoint: Not possible" << endl;
+      throw NgException ("GetIdenfifiedPoint: Not possible");
+    }    
+
+  // project to other surface
+  Point<3> hp = p;
+  if(usedirection)
+    snew->SkewProject(hp,direction);
+  else
+    snew->Project (hp);
+
+  //(*testout) << "projecting " << p << " to " << hp << endl;
+
+  int newpi = 0;
+  for (int i = 1; i <= mesh.GetNP(); i++)
+    if (Dist2 (mesh.Point(i), hp) < 1e-12)
+      //    if (Dist2 (mesh.Point(i), hp) < 1 * Dist2 (hp, p))
+      {
+	newpi = i;
+	break;
+      }
+  if (!newpi)
+    newpi = mesh.AddPoint (hp);
+
+  if (snew == s2)
+    {
+      mesh.GetIdentifications().Add (pi, newpi, nr);
+      //(*testout) << "add identification(1) " << pi << " - " << newpi << ", " << nr << endl;
+    }
+  else
+    {
+      mesh.GetIdentifications().Add (newpi, pi, nr);
+      //(*testout) << "add identification(2) " << newpi << " - " << pi << ", " << nr << endl;
+    }
+  mesh.GetIdentifications().SetType(nr,Identifications::CLOSESURFACES);
+	   
+
+  /*
+  (*testout) << "Identify points(closesurface), nr = " << nr << ": " << mesh.Point(pi)
+	     << " and " << mesh.Point(newpi) 
+	     << ((snew == s2) ? "" : " inverse")
+	     << endl;
+  */
+  return newpi;
+}
+
+
+
+
+
+void CloseSurfaceIdentification :: IdentifyPoints (Mesh & mesh)
+{
+  int np = mesh.GetNP();
+
+  Array<int> points_on_surf2;
+
+  for (int i2 = 1; i2 <= np; i2++)
+    if (s2->PointOnSurface (mesh.Point(i2)))
+      points_on_surf2.Append (i2);
+    
+  Array<int> surfs_of_p1;
+
+  for (int i1 = 1; i1 <= np; i1++)
+    {
+      Point<3> p1 = mesh.Point(i1);
+      //      (*testout) << "p1 = " << i1 << " = " << p1 << endl;
+      if (domain && !domain->GetSolid()->IsIn (p1))
+	continue;
+      
+      //if(domain) (*testout) << "p1 is in " << domain->GetSolid()->Name() << endl;
+
+      if (s1->PointOnSurface (p1))
+	{
+	  int candi2 = 0;
+	  double mindist = 1e10;
+
+	  Vec<3> n1;
+	  n1 = s1->GetNormalVector (p1);
+	  n1.Normalize();
+	   
+	  surfs_of_p1.SetSize(0);
+	  for (int jj = 0; jj < domain_surfaces.Size(); jj++)
+	    {
+	      int j = domain_surfaces[jj];
+	      if (geom.GetSurface(j) -> PointOnSurface(p1))
+		surfs_of_p1.Append (j);
+	    }
+	  //(*testout) << " surfs of p1 = " << endl << surfs_of_p1 << endl;
+
+	  for (int ii2 = 0; ii2 < points_on_surf2.Size(); ii2++)
+	    {
+	      int i2 = points_on_surf2[ii2];
+	      if (i2 == i1) continue;
+	      const Point<3> p2 = mesh.Point(i2);
+	      
+	      Vec<3> n = p2 - p1;
+	      n.Normalize();
+	      
+	      bool joint = 0;
+	      for (int jj = 0; jj < surfs_of_p1.Size(); jj++)
+		{
+		  int j = surfs_of_p1[jj];
+		  if (geom.GetSurface(j) -> PointOnSurface(p2))
+		    {
+		      Vec<3> hn1 = geom.GetSurface(j)->GetNormalVector (p1);
+		      Vec<3> hn2 = geom.GetSurface(j)->GetNormalVector (p2);
+		      
+		      if (hn1 * hn2 > 0)
+			{
+			  joint = 1;
+			  break;
+			}
+		    }
+		}
+
+	      if (!joint) continue;
+	      
+	      if(usedirection)
+		{
+		  if (fabs (n*direction) > 0.9)
+		    {
+		      Vec<3> p1p2 = p2-p1;
+		      double ndist = p1p2.Length2() - pow(p1p2*direction,2);
+		      if(ndist < mindist)
+			{
+			  candi2 = i2;
+			  mindist = ndist;
+			}
+		    }
+		      
+		}
+	      else
+		{
+		  if (fabs (n * n1) > 0.9 &&
+		      Dist (p1, p2) < mindist)
+		    {
+		      candi2 = i2;
+		      mindist = Dist (p1, p2);
+		    }
+		}
+	    
+	    }
+
+	  if (candi2)
+	    {
+	      //(*testout) << "identify points " << p1 << " - " << mesh.Point(candi2) << endl;
+
+	      /*
+	      (*testout) << "Add Identification from CSI2, nr = " << nr << ", p1 = " 
+			 << i1 << " = " 
+			 << mesh[PointIndex(i1)] << ", p2 = " << candi2 << " = " 
+			 << mesh[PointIndex(candi2)] << endl;
+	      */
+	      mesh.GetIdentifications().Add (i1, candi2, nr);
+	      mesh.GetIdentifications().SetType(nr,Identifications::CLOSESURFACES);
+	      //(*testout) << "add identification " << i1 << " - " << candi2 << ", " << nr << endl;
+	    }
+	}
+    }
+}
+
+
+
+void CloseSurfaceIdentification :: IdentifyFaces (class Mesh & mesh)
+{
+  int fi1, fi2, side;
+  int s1rep = -1, s2rep = -1;
+
+  for (int i = 0; i < geom.GetNSurf(); i++)
+    {
+      if (geom.GetSurface (i) == s1) 
+	s1rep = geom.GetSurfaceClassRepresentant(i);
+      if (geom.GetSurface (i) == s2) 
+	s2rep = geom.GetSurfaceClassRepresentant(i);
+    }
+
+  Array<int> segs_on_face1, segs_on_face2;
+
+  identfaces.DeleteData();
+
+  //(*testout) << "identify faces, nr = " << nr << endl;
+  
+  for (int i = 1; i <= mesh.GetNFD(); i++)
+    {
+      int surfi = mesh.GetFaceDescriptor(i).SurfNr();
+      if (s1rep != surfi) continue;
+
+
+      if (domain &&
+	  domain != geom.GetTopLevelObject (mesh.GetFaceDescriptor(i).DomainIn()-1) &&
+	  domain != geom.GetTopLevelObject (mesh.GetFaceDescriptor(i).DomainOut()-1))
+	continue;
+
+      for (int j = 1; j <= mesh.GetNFD(); j++)
+	{
+	  int surfj = mesh.GetFaceDescriptor(j).SurfNr();
+
+	  if (surfi == surfj) continue;
+	  if (s2rep != surfj) continue;
+
+  
+	  int idok = 1;
+	  
+	  for (side = 1; side <= 2 && idok; side++)
+	    {
+	      if (side == 1)
+		{
+		  fi1 = i; 
+		  fi2 = j;
+		}
+	      else
+		{
+		  fi1 = j;
+		  fi2 = i;
+		}
+	      
+
+	      segs_on_face1.SetSize(0);
+	      segs_on_face2.SetSize(0);
+
+	      for (int k = 1; k <= mesh.GetNSeg(); k++)
+		{
+		  if (mesh.LineSegment(k).si == fi1)
+		    segs_on_face1.Append (k);
+		  if (mesh.LineSegment(k).si == fi2)
+		    segs_on_face2.Append (k);
+		}
+
+
+	      for (int k = 1; k <= mesh.GetNSeg(); k++)
+		{
+		  const Segment & seg1 = mesh.LineSegment(k);
+		  if (seg1.si != fi1)
+		    continue;
+		  
+		  int foundother = 0;
+		  /*
+		  for (int l = 1; l <= mesh.GetNSeg(); l++)
+		    {
+		      const Segment & seg2 = mesh.LineSegment(l);
+		      if (seg2.si != fi2)
+			continue;
+		  */
+		  for (int ll = 0; ll < segs_on_face2.Size(); ll++)
+		    {
+		      int l = segs_on_face2[ll];
+		      const Segment & seg2 = mesh.LineSegment(l);
+		      
+		      if (side == 1)
+			{
+			  if (mesh.GetIdentifications().Get (seg1[0], seg2[0]) &&
+			      mesh.GetIdentifications().Get (seg1[1], seg2[1]))
+			    {
+			      foundother = 1;
+			      break;
+			    }
+			  
+			  if (mesh.GetIdentifications().Get (seg1[0], seg2[1]) &&
+			      mesh.GetIdentifications().Get (seg1[1], seg2[0]))
+			    {
+			      foundother = 1;
+			      break;
+			    }
+			}
+		      else
+			{
+			  if (mesh.GetIdentifications().Get (seg2[0], seg1[0]) &&
+			      mesh.GetIdentifications().Get (seg2[1], seg1[1]))
+			    {
+			      foundother = 1;
+			      break;
+			    }
+			  
+			  if (mesh.GetIdentifications().Get (seg2[0], seg1[1]) &&
+			      mesh.GetIdentifications().Get (seg2[1], seg1[0]))
+			    {
+			      foundother = 1;
+			      break;
+			    }
+			}
+		    }
+		  
+		  if (!foundother)
+		    {
+		      idok = 0;
+		      break;
+		    }
+		}
+	    }
+	  
+	  
+	  if (idok)
+	    {
+	      //(*testout) << "Identification " << nr << ", identify faces " << i << " and " << j << endl;
+	      INDEX_2 fpair(i,j);
+	      fpair.Sort();
+	      identfaces.Set (fpair, 1);
+	    }
+	}
+    }
+}
+
+
+
+void CloseSurfaceIdentification :: 
+BuildSurfaceElements (Array<Segment> & segs,
+		      Mesh & mesh, const Surface * surf)
+{
+  bool found = 0;
+  int cntquads = 0;
+
+  Array<int,PointIndex::BASE>  identmap;
+  identmap = 0;
+
+  mesh.GetIdentifications().GetMap (nr, identmap);
+  
+  for (int i = PointIndex::BASE; i < identmap.Size()+PointIndex::BASE; i++)
+    if (identmap[i])  identmap[identmap[i]] = i;
+
+    
+  //(*testout) << "identification nr = " << nr << endl;
+  //(*testout) << "surf = " << (*surf) << endl;
+  //(*testout) << "domain = " << domain->GetSolid()->Name() << endl;
+  //(*testout) << "segs = " << endl << segs << endl;
+  //(*testout) << "identmap = " << endl << identmap << endl;
+  
+  //Array<bool> foundseg(segs.Size());
+  //foundseg = false;
+
+  // insert quad layer:
+  for (int i1 = 0; i1 < segs.Size(); i1++)
+    {
+      const Segment & s1 = segs[i1];
+      if (identmap[s1[0]] && identmap[s1[1]])
+	for (int i2 = 0; i2 < i1; i2++)
+	  {
+	    const Segment & s2 = segs[i2];
+	    //(*testout) << "checking " << s1 << " and " << s2 << " for ident." << endl;
+
+	    if(domain && !((s1.domin == dom_nr ||
+			    s1.domout == dom_nr) &&
+			   (s2.domin == dom_nr ||
+			    s2.domout == dom_nr)))
+	      continue;
+	 
+	    if ((mesh.GetIdentifications().Get (s1[0], s2[1], nr) && 
+		 mesh.GetIdentifications().Get (s1[1], s2[0], nr))    || 
+		(mesh.GetIdentifications().Get (s2[0], s1[1], nr) && 
+		 mesh.GetIdentifications().Get (s2[1], s1[0], nr)))
+	      {
+		Element2d el(s1[0], s1[1], s2[0], s2[1]);
+
+		Vec<3> n = Cross (mesh[el[1]] - mesh[el[0]],
+				  mesh[el[3]] - mesh[el[0]]);
+
+		Vec<3> ns = surf->GetNormalVector (mesh[el[0]]);
+
+		if (n * ns < 0)
+		  {
+		    Swap (el.PNum(1), el.PNum(2));
+		    Swap (el.PNum(3), el.PNum(4));
+		  }
+			     
+		mesh.AddSurfaceElement (el);
+//  		(*testout) << "(id nr "<< nr <<") add rect element: "
+//  			   << mesh.Point (el.PNum(1)) << " - "
+//  			   << mesh.Point (el.PNum(2)) << " - "
+//  			   << mesh.Point (el.PNum(3)) << " - "
+//  			   << mesh.Point (el.PNum(4)) << endl;
+		found = true;
+		//foundseg[i1]=foundseg[i2] = true;
+		cntquads++;
+	      }
+	  }
+    }
+  if (found)
+    {
+      PrintMessage(3, "insert quad layer of ", cntquads,
+		   " elements at face ", segs.Get(1).si);
+      //Array<Segment> aux;
+      //for(int i=0; i<segs.Size();i++)
+      //	if(!foundseg[i])
+      //	  aux.Append(segs[i]);
+      segs.SetSize(0);
+    }
+  else
+    {
+      BuildSurfaceElements2 (segs, mesh, surf);
+    }
+}
+
+
+
+
+
+
+void CloseSurfaceIdentification :: 
+BuildSurfaceElements2 (Array<Segment> & segs,
+		       Mesh & mesh, const Surface * surf)
+{
+  // copy mesh
+
+
+  //  (*testout) << "copy trig face, identnr = " << nr << endl;
+  //  (*testout) << "identfaces = " << endl << identfaces << endl;
+
+  if (!segs.Size()) return;
+
+  bool found = 0;
+  int fother = -1;
+
+  int facei = segs[0].si;
+  int surfnr = mesh.GetFaceDescriptor(facei).SurfNr();
+
+  
+  bool foundid = 0;
+  for (INDEX_2_HASHTABLE<int>::Iterator it = identfaces.Begin();
+       it != identfaces.End(); it++)
+    {
+      INDEX_2 i2;
+      int data;
+      identfaces.GetData (it, i2, data);
+      if (i2.I1() == facei || i2.I2() == facei)
+	foundid = 1;
+    }
+
+  /*
+  for (int i = 1; i <= identfaces.GetNBags(); i++)
+    for (int j = 1; j <= identfaces.GetBagSize(i); j++)
+      {
+	INDEX_2 i2;
+	int data;
+	identfaces.GetData (i, j, i2, data);
+	if (i2.I1() == facei || i2.I2() == facei)
+	  foundid = 1;
+
+	(*testout) << "identface = " << i2 << endl;
+	(*testout) << "face " << i2.I1() << " = " << mesh.GetFaceDescriptor(i2.I1()) << endl;
+	(*testout) << "face " << i2.I2() << " = " << mesh.GetFaceDescriptor(i2.I2()) << endl;
+      }
+  */
+
+  if (foundid)
+    {
+      //	  (*testout) << "surfaces found" << endl;
+      // copy surface
+      for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++)
+	{
+	  const Element2d & sel = mesh[sei];
+	  INDEX_2 fpair (facei, sel.GetIndex());
+	  fpair.Sort();
+	  if (identfaces.Used (fpair))
+	    {
+	      found = 1;
+	      fother = sel.GetIndex();
+	      
+	      // copy element
+	      Element2d newel(sel.GetType());
+	      newel.SetIndex (facei);
+	      for (int k = 1; k <= sel.GetNP(); k++)
+                newel.PNum(k) = GetIdentifiedPoint (mesh, sel.PNum(k));
+	      
+	      Vec<3> nt = Cross (Point<3> (mesh.Point (newel.PNum(2)))- 
+				 Point<3> (mesh.Point (newel.PNum(1))),
+				 Point<3> (mesh.Point (newel.PNum(3)))- 
+				 Point<3> (mesh.Point (newel.PNum(1))));
+	      Vec<3> nsurf;
+	      nsurf = geom.GetSurface (surfnr)->GetNormalVector (mesh.Point(newel.PNum(1)));
+	      if (nsurf * nt < 0)
+		Swap (newel.PNum(2), newel.PNum(3));
+	      
+	      mesh.AddSurfaceElement (newel);
+	    }
+	}
+    }
+  
+  if (found)
+    {
+      // (*mycout) << " copy face " << facei << " from face " << fother;
+      PrintMessage (4, " copy face ", facei, " from face ", fother);
+      segs.SetSize(0);
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void CloseSurfaceIdentification :: 
+BuildVolumeElements (Array<class Element2d> & surfels,
+		     class Mesh & mesh)
+{
+  ;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*   ***************** Close Edges Identification ********** */
+
+
+
+CloseEdgesIdentification ::
+CloseEdgesIdentification (int anr,
+			  const CSGeometry & ageom,
+			  const Surface * afacet,
+			  const Surface * as1,
+			  const Surface * as2)
+  : Identification(anr, ageom)
+{
+  facet = afacet;
+  s1 = as1;
+  s2 = as2;
+}
+
+CloseEdgesIdentification :: ~CloseEdgesIdentification ()
+{
+  ;
+}
+
+void CloseEdgesIdentification :: Print (ostream & ost) const
+{
+  ost << "CloseEdges Identifiaction, facet = " 
+      << facet->Name() << ", surfaces: " 
+      << s1->Name() << " - " << s2->Name() << endl;
+  facet->Print (ost);
+  s1->Print (ost);
+  s2->Print (ost);
+  ost << endl;
+}
+
+
+void CloseEdgesIdentification :: GetData (ostream & ost) const
+{
+  ost << "closeedges " << facet->Name() << " " 
+      << s1->Name() << " " << s2->Name();
+}
+
+
+/*
+void CloseEdgesIdentification :: IdentifySpecialPoints 
+(Array<class SpecialPoint> & points)
+{
+  int i, j;
+  int bestj;
+  double bestval, val;
+
+  for (i = 1; i <= points.Size(); i++)
+    {
+      Point<3> p1 = points.Get(i).p;
+      Vec<3> n1;
+
+      if (!s1->PointOnSurface (p1))
+	continue;
+
+	s1->GetNormalVector (p1, n1);
+      n1 /= n1.Length();
+      if ( fabs(n1 * points.Get(i).v) > 1e-3)
+	continue;
+
+      bestval = 1e8;
+      bestj = 1;
+      for (j = 1; j <= points.Size(); j++)
+	{
+	  Point<3> p2= points.Get(j).p;
+	  if (!s2->PointOnSurface (p2))
+	    continue;
+	  
+	  Vec<3> n2;
+	  s2->GetNormalVector (p2, n2);
+	  n2 /= n2.Length();
+	  if ( fabs(n2 * points.Get(j).v) > 1e-3)
+	    continue;
+
+
+	  Vec<3> v(p1, p2);
+	  double vl = v.Length();
+	  double cl = fabs (v*n1);
+
+	  val = 1 - cl*cl/(vl*vl);
+
+	  val += (points.Get(i).v - points.Get(j).v).Length();
+
+	  if (val < bestval)
+	    {
+	      bestj = j;
+	      bestval = val;
+	    }
+	}
+
+      (*testout) << "Identify close surfaces special points: pi = " 
+		 << points.Get(i).p << ", vi = " << points.Get(i).v 
+		 << " pj = " << points.Get(bestj).p 
+		 << ", vj = " << points.Get(bestj).v 
+		 << " bestval = " << bestval << endl;
+    }
+}
+*/
+
+int CloseEdgesIdentification :: 
+Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2,
+	      const TABLE<int> & specpoint2solid,
+	      const TABLE<int> & specpoint2surface) const
+{
+  int i;
+  double val;
+  
+  SpecialPoint hsp1 = sp1;
+  SpecialPoint hsp2 = sp2;
+
+  for (i = 1; i <= 1; i++)
+    {
+      if (!s1->PointOnSurface (hsp1.p))
+	continue;
+
+      Vec<3> n1;
+      n1 = s1->GetNormalVector (hsp1.p);
+      n1 /= n1.Length();
+      if ( fabs(n1 * hsp1.v) > 1e-3)
+	continue;
+
+
+      if (!s2->PointOnSurface(hsp2.p))
+	continue;
+
+      Vec<3> n2;
+      n2 = s2->GetNormalVector (hsp2.p);
+      n2 /= n2.Length();
+      if ( fabs(n2 * hsp2.v) > 1e-3)
+	continue;
+
+
+      Vec<3> v = hsp2.p - hsp1.p;
+      double vl = v.Length();
+      double cl = fabs (v*n1);
+      
+
+      val = 1 - cl*cl/(vl*vl);
+      val += (hsp1.v - hsp2.v).Length();
+    
+      if (val < 1e-3)
+	{
+	  return 1;
+	}
+    }
+
+  return 0;
+}
+
+
+
+
+void CloseEdgesIdentification :: IdentifyPoints (Mesh & mesh)
+{
+  int np = mesh.GetNP();
+  for (int i1 = 1; i1 <= np; i1++)
+    for (int i2 = 1; i2 <= np; i2++)
+      {
+	if (i2 == i1)
+	  continue;
+	
+	const Point<3> p1 = mesh.Point(i1);
+	const Point<3> p2 = mesh.Point(i2);
+	Point<3> pp1 = p1;
+	Point<3> pp2 = p2;
+	
+	s1->Project (pp1);
+	facet->Project (pp1);
+	s2->Project (pp2);
+	facet->Project (pp2);
+
+	if (Dist (p1, pp1) > 1e-6 || Dist (p2, pp2) > 1e-6)
+	  continue;
+
+	Vec<3> n1, nf, t;
+	Vec<3> n = p2 - p1;
+	n.Normalize();
+
+	n1 = s1->GetNormalVector (p1);
+	nf = facet->GetNormalVector (p1);
+	t = Cross (n1, nf);
+	t /= t.Length();
+
+	if (fabs (n * t) < 0.5)
+	  {
+	    (*testout) << "close edges identify points " << p1 << " - " << p2 << endl;
+	    mesh.GetIdentifications().Add (i1, i2, nr);
+	    mesh.GetIdentifications().SetType(nr,Identifications::CLOSEEDGES);
+	  }
+      }
+}
+
+void CloseEdgesIdentification :: 
+BuildSurfaceElements (Array<Segment> & segs,
+		      Mesh & mesh, const Surface * surf)
+{
+  int found = 0;
+
+  if (surf != facet)
+    return;
+
+  for (int i1 = 1; i1 <= segs.Size(); i1++)
+    for (int i2 = 1; i2 < i1; i2++)
+      {
+	const Segment & s1 = segs.Get(i1);
+	const Segment & s2 = segs.Get(i2);
+	if (mesh.GetIdentifications().Get (s1[0], s2[1]) &&
+	    mesh.GetIdentifications().Get (s1[1], s2[0]))
+	  {
+	    Element2d el(QUAD);
+	    el.PNum(1) = s1[0];
+	    el.PNum(2) = s1[1];
+	    el.PNum(3) = s2[1];
+	    el.PNum(4) = s2[0];
+
+	    Vec<3> n = Cross (Point<3> (mesh.Point(el.PNum(2)))-
+			      Point<3> (mesh.Point(el.PNum(1))),
+			      Point<3> (mesh.Point(el.PNum(3)))-
+			      Point<3> (mesh.Point(el.PNum(1))));
+	    Vec<3> ns;
+	    ns = surf->GetNormalVector (mesh.Point(el.PNum(1)));
+	    //(*testout) << "n = " << n << " ns = " << ns << endl;
+	    if (n * ns < 0)
+	      {
+		//(*testout) << "Swap the quad" << endl;
+		Swap (el.PNum(1), el.PNum(2));
+		Swap (el.PNum(3), el.PNum(4));
+	      }
+			     
+	    
+	    Swap (el.PNum(3), el.PNum(4));
+	    mesh.AddSurfaceElement (el);
+//  	    (*testout) << "add rect element: "
+//  		       << mesh.Point (el.PNum(1)) << " - "
+//  		       << mesh.Point (el.PNum(2)) << " - "
+//  		       << mesh.Point (el.PNum(3)) << " - "
+//  		       << mesh.Point (el.PNum(4)) << endl;
+	    found = 1;
+	  }
+      }
+
+  if (found)
+    segs.SetSize(0);
+}
+
+}
+
diff --git a/contrib/Netgen/libsrc/csg/identify.hpp b/contrib/Netgen/libsrc/csg/identify.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef3f75564c1391c92e29f002aea9b8ac56dce73a
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/identify.hpp
@@ -0,0 +1,210 @@
+
+#ifndef FILE_IDENTIFY
+#define FILE_IDENTIFY
+
+/**************************************************************************/
+/* File:   identify.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   1. Aug. 99                                                    */
+/**************************************************************************/
+
+
+namespace netgen
+{
+
+  /**
+     Identify surfaces for periodic b.c. or
+     thin domains
+  */
+
+
+  class SpecialPoint;
+  class Identification
+  {
+  protected:
+    const CSGeometry & geom;
+    // identified faces, index sorted
+    INDEX_2_HASHTABLE<int> identfaces;
+    int nr;
+
+  public:
+    DLL_HEADER Identification (int anr, const CSGeometry & ageom);
+    DLL_HEADER virtual ~Identification ();
+    DLL_HEADER virtual void Print (ostream & ost) const = 0;
+    DLL_HEADER virtual void GetData (ostream & ost) const = 0;
+
+    /// obsolete
+    //  virtual void IdentifySpecialPoints (Array<class SpecialPoint> & points);
+
+    /// can identify both special points (fixed direction)
+    /// (identified points, same tangent)
+    virtual int Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2,
+			      const TABLE<int> & specpoint2solid,			  
+			      const TABLE<int> & specpoint2surface) const;
+    ///
+    virtual int Identifyable (const Point<3> & p1, const Point<3> & sp2) const;
+    /// is it possible to identify sp1 with some other ?
+    virtual int IdentifyableCandidate (const SpecialPoint & sp1) const;
+  
+    /// are points (if connected) by a short edge (direction anyhow) ?
+    virtual int ShortEdge (const SpecialPoint & sp1, const SpecialPoint & sp2) const;
+
+    /// add entries in mesh identification tables
+    virtual void IdentifyPoints (class Mesh & mesh);
+
+    /// add entries to identified faces (based on segment infos)
+    virtual void IdentifyFaces (class Mesh & mesh);
+
+    /// get point on other surface, add entry in mesh identifications
+    virtual int GetIdentifiedPoint (class Mesh & mesh, int pi1);
+
+    /// copy surfaces, or fill rectangles
+    virtual void BuildSurfaceElements (Array<class Segment> & segs,
+				       class Mesh & mesh,
+				       const Surface * surf);
+
+    /// insert volume elements in thin layers
+    virtual void BuildVolumeElements (Array<class Element2d> & surfels,
+				      class Mesh & mesh);
+
+    /// get list of identified faces
+    virtual void GetIdentifiedFaces (Array<INDEX_2> & idfaces) const;
+
+    friend ostream & operator<< (ostream & ost, Identification & ident);
+  };
+
+
+  class PeriodicIdentification : public Identification
+  {
+    const Surface * s1;
+    const Surface * s2;
+  public:
+    PeriodicIdentification (int anr,
+			    const CSGeometry & ageom,
+			    const Surface * as1,
+			    const Surface * as2);
+    virtual ~PeriodicIdentification ();
+    virtual void Print (ostream & ost) const;
+    virtual void GetData (ostream & ost) const;
+
+
+    //  virtual void IdentifySpecialPoints (Array<class SpecialPoint> & points);
+    virtual int Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2,
+			      const TABLE<int> & specpoint2solid,
+			      const TABLE<int> & specpoint2surface) const;
+
+    virtual int Identifyable (const Point<3> & p1, const Point<3> & sp2) const;
+    virtual int GetIdentifiedPoint (class Mesh & mesh, int pi1);
+    virtual void IdentifyPoints (class Mesh & mesh);
+    virtual void IdentifyFaces (class Mesh & mesh);
+    virtual void BuildSurfaceElements (Array<class Segment> & segs,
+				       class Mesh & mesh,
+				       const Surface * surf);
+  };
+
+
+  ///
+  class TopLevelObject;
+  class CloseSurfaceIdentification : public Identification
+  {
+    const Surface * s1;
+    const Surface * s2;
+    const TopLevelObject * domain;
+    ///
+    int dom_nr;
+    /// number of refinement levels (in Z-refinement)
+    int ref_levels;
+    /// number of refinement levels for layer next to s1 (in Z-refinement)
+    int ref_levels_s1;
+    /// number of refinement levels for layer next to s2 (in Z-refinement)
+    int ref_levels_s2;
+    ///
+    double eps_n;
+    Array<double> slices;
+    /// used only for domain-local identification:
+    Array<int> domain_surfaces;
+    ///
+    bool dom_surf_valid;
+
+    ///
+    Vec<3> direction;
+    ///
+    bool usedirection;
+  public:
+    CloseSurfaceIdentification (int anr, 
+				const CSGeometry & ageom,
+				const Surface * as1,
+				const Surface * as2,
+				const TopLevelObject * adomain,
+				const Flags & flags);
+    virtual ~CloseSurfaceIdentification ();
+
+    virtual void Print (ostream & ost) const;
+    virtual void GetData (ostream & ost) const;
+
+
+    //  virtual void IdentifySpecialPoints (Array<class SpecialPoint> & points);
+    virtual int Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2,
+			      const TABLE<int> & specpoint2solid,
+			      const TABLE<int> & specpoint2surface) const;
+    virtual int Identifyable (const Point<3> & p1, const Point<3> & sp2) const;
+    virtual int IdentifyableCandidate (const SpecialPoint & sp1) const;
+    virtual int ShortEdge (const SpecialPoint & sp1, const SpecialPoint & sp2) const;
+    virtual int GetIdentifiedPoint (class Mesh & mesh, int pi1);
+    const Array<double> & GetSlices () const { return slices; }
+    virtual void IdentifyPoints (class Mesh & mesh);
+    virtual void IdentifyFaces (class Mesh & mesh);
+    virtual void BuildSurfaceElements (Array<class Segment> & segs,
+				       class Mesh & mesh,
+				       const Surface * surf);
+    void BuildSurfaceElements2 (Array<class Segment> & segs,
+				class Mesh & mesh,
+				const Surface * surf);
+
+    virtual void BuildVolumeElements (Array<class Element2d> & surfels,
+				      class Mesh & mesh);
+
+    int RefLevels () const { return ref_levels; }
+    int RefLevels1 () const { return ref_levels_s1; }
+    int RefLevels2 () const { return ref_levels_s2; }
+
+    bool IsSkewIdentification(void) const {return usedirection;}
+    const Vec<3> & GetDirection(void) const {return direction;}
+
+    const Surface & GetSurface1(void) const
+    { return *s1;}
+    const Surface & GetSurface2(void) const
+    { return *s2;}
+  };
+
+
+  class CloseEdgesIdentification : public Identification
+  {
+    const Surface * facet;
+    const Surface * s1;
+    const Surface * s2;
+  public:
+    CloseEdgesIdentification (int anr,
+			      const CSGeometry & ageom,
+			      const Surface * afacet,
+			      const Surface * as1,
+			      const Surface * as2);
+    virtual ~CloseEdgesIdentification ();
+    virtual void Print (ostream & ost) const;
+    virtual void GetData (ostream & ost) const;
+
+    //  virtual void IdentifySpecialPoints (Array<class SpecialPoint> & points);
+    virtual int Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2,
+			      const TABLE<int> & specpoint2solid,
+			      const TABLE<int> & specpoint2surface) const;
+
+
+    virtual void IdentifyPoints (class Mesh & mesh);
+    virtual void BuildSurfaceElements (Array<class Segment> & segs,
+				       class Mesh & mesh,
+				       const Surface * surf);
+  };
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/manifold.cpp b/contrib/Netgen/libsrc/csg/manifold.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9733389d670b4b764beac4586d3d37d800d4bb83
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/manifold.cpp
@@ -0,0 +1,14 @@
+#include <csg.hpp>
+
+namespace netgen
+{
+Manifold :: Manifold () 
+{
+  ;
+}
+
+Manifold :: ~Manifold () 
+{
+  ;
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/manifold.hpp b/contrib/Netgen/libsrc/csg/manifold.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cc60e955e06106b7c3843392db8c4a290eef4a1e
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/manifold.hpp
@@ -0,0 +1,29 @@
+#ifndef FILE_MANIFOLD
+#define FILE_MANIFOLD
+
+/**************************************************************************/
+/* File:   manifold.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   7. Aug. 96                                                     */
+/**************************************************************************/
+
+namespace netgen
+{
+
+
+  /**
+     Basis class for manifolds in 2d and 3d
+  */
+  class Manifold
+  {
+  public:
+    ///
+    Manifold ();
+    ///
+    virtual ~Manifold ();
+  };
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/meshsurf.cpp b/contrib/Netgen/libsrc/csg/meshsurf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..19e12eb2266758cad64766287ed516385fa63699
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/meshsurf.cpp
@@ -0,0 +1,211 @@
+#include <mystdlib.h>
+
+#include <csg.hpp>
+#include <meshing.hpp>
+
+
+
+namespace netgen
+{
+  /*
+Meshing2Surfaces :: Meshing2Surfaces (const Surface & asurface)
+  : surface(asurface)
+{
+  ;
+}
+  */
+Meshing2Surfaces :: Meshing2Surfaces (const Surface & asurf,
+				      const MeshingParameters & mp,
+				      const Box<3> & abb)
+  : Meshing2(mp, abb), surface(asurf)
+{
+  ;
+}
+
+
+void Meshing2Surfaces :: DefineTransformation (const Point3d & p1, const Point3d & p2,
+					       const PointGeomInfo * geominfo1,
+					       const PointGeomInfo * geominfo2)
+{
+  ((Surface&)surface).DefineTangentialPlane (p1, p2);
+}
+
+void Meshing2Surfaces :: TransformToPlain (const Point3d & locpoint, 
+					   const MultiPointGeomInfo & geominfo,
+					   Point2d & planepoint, 
+					   double h, int & zone)
+{
+  Point<2> hp;
+  surface.ToPlane (locpoint, hp, h, zone);
+  planepoint.X() = hp(0);
+  planepoint.Y() = hp(1);
+}
+
+int Meshing2Surfaces :: TransformFromPlain (Point2d & planepoint,
+					    Point3d & locpoint, 
+					    PointGeomInfo & gi,
+					    double h)
+{
+  Point<3> hp;
+  Point<2> hp2 (planepoint.X(), planepoint.Y());
+  surface.FromPlane (hp2, hp, h);
+  locpoint = hp;
+  gi.trignum = 1;
+  return 0;
+}
+
+
+
+double Meshing2Surfaces :: CalcLocalH (const Point3d & p, double gh) const
+{
+  return surface.LocH (p, 3, 1, gh);
+  /*
+    double loch = mesh.lochfunc->GetH(p);
+    if (gh < loch) loch = gh;
+    return loch;
+    */
+}
+
+
+
+
+
+
+MeshOptimize2dSurfaces :: MeshOptimize2dSurfaces (const CSGeometry & ageometry)
+  : MeshOptimize2d(), geometry(ageometry)
+{
+  ;
+}
+
+
+void MeshOptimize2dSurfaces :: ProjectPoint (INDEX surfind, Point<3> & p) const
+{
+  Point<3> hp = p;
+  geometry.GetSurface(surfind)->Project (hp);
+  p = hp;
+}
+
+void MeshOptimize2dSurfaces :: ProjectPoint2 (INDEX surfind, INDEX surfind2, 
+					      Point<3> & p) const
+{
+  Point<3> hp = p;
+  ProjectToEdge ( geometry.GetSurface(surfind), 
+		  geometry.GetSurface(surfind2), hp);
+  p = hp;
+}
+
+
+void MeshOptimize2dSurfaces :: 
+GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const
+{
+  Vec<3> hn = n;
+  geometry.GetSurface(surfind)->CalcGradient (p, hn);
+  hn.Normalize();
+  n = hn;
+
+  /*
+  if (geometry.GetSurface(surfind)->Inverse())
+    n *= -1;
+  */
+}
+  
+
+
+
+
+
+
+RefinementSurfaces :: RefinementSurfaces (const CSGeometry & ageometry)
+  : Refinement(), geometry(ageometry)
+{
+  if(geometry.GetNSurf() == 0)
+    *testout << endl 
+             << "WARNING: Intializing 2D refinement with 0-surface geometry" << endl
+             << "==========================================================" << endl
+             << endl << endl;
+}
+
+RefinementSurfaces :: ~RefinementSurfaces ()
+{
+  ;
+}
+  
+void RefinementSurfaces :: 
+PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+	      int surfi, 
+	      const PointGeomInfo & gi1, 
+	      const PointGeomInfo & gi2,
+	      Point<3> & newp, PointGeomInfo & newgi) const
+{
+  Point<3> hnewp;
+  hnewp = p1+secpoint*(p2-p1);
+  if (surfi != -1)
+    {
+      geometry.GetSurface (surfi) -> Project (hnewp);
+      newgi.trignum = 1;
+    }
+
+  newp = hnewp;
+}
+
+void RefinementSurfaces :: 
+PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+	      int surfi1, int surfi2, 
+	      const EdgePointGeomInfo & ap1, 
+	      const EdgePointGeomInfo & ap2,
+	      Point<3> & newp, EdgePointGeomInfo & newgi) const
+{
+  Point<3> hnewp = p1+secpoint*(p2-p1);
+  //(*testout) << "hnewp " << hnewp << " s1 " << surfi1 << " s2 " << surfi2 << endl;
+  if (surfi1 != -1 && surfi2 != -1 && surfi1 != surfi2)
+    {
+      netgen::ProjectToEdge (geometry.GetSurface(surfi1), 
+                             geometry.GetSurface(surfi2), 
+                             hnewp);
+      // (*testout) << "Pointbetween, newp = " << hnewp << endl
+      // << ", err = " << sqrt (sqr (hnewp(0))+ sqr(hnewp(1)) + sqr (hnewp(2))) - 1 << endl;
+      newgi.edgenr = 1;
+      //(*testout) << "hnewp (a1) " << hnewp << endl;
+    }
+  else if (surfi1 != -1)
+    {
+      geometry.GetSurface (surfi1) -> Project (hnewp);
+      //(*testout) << "hnewp (a2) " << hnewp << endl;
+    }
+
+  newp = hnewp;
+};
+
+Vec<3> RefinementSurfaces :: GetTangent (const Point<3> & p, int surfi1, int surfi2,
+                                         const EdgePointGeomInfo & ap1) const
+{
+  Vec<3> n1 = geometry.GetSurface (surfi1)->GetNormalVector (p);
+  Vec<3> n2 = geometry.GetSurface (surfi2)->GetNormalVector (p);
+  Vec<3> tau = Cross (n1, n2).Normalize();
+  return tau;
+}
+
+Vec<3> RefinementSurfaces :: GetNormal (const Point<3> & p, int surfi1, 
+                                        const PointGeomInfo & gi) const
+{
+  return geometry.GetSurface (surfi1)->GetNormalVector (p);
+}
+
+
+
+void RefinementSurfaces :: ProjectToSurface (Point<3> & p, int surfi) const
+{
+  if (surfi != -1)
+    geometry.GetSurface (surfi) -> Project (p);
+};
+
+void RefinementSurfaces :: ProjectToEdge (Point<3> & p, int surfi1, int surfi2, const EdgePointGeomInfo & egi) const
+{
+  netgen::ProjectToEdge (geometry.GetSurface(surfi1), 
+                         geometry.GetSurface(surfi2), 
+                         p);
+  
+}
+
+
+}
diff --git a/contrib/Netgen/libsrc/csg/meshsurf.hpp b/contrib/Netgen/libsrc/csg/meshsurf.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e6c69225557af202cc4d9a4066751395da0ba43e
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/meshsurf.hpp
@@ -0,0 +1,97 @@
+#ifndef FILE_MESHSURF
+#define FILE_MESHSURF
+
+namespace netgen
+{
+
+  ///
+  class Meshing2Surfaces : public Meshing2
+  {
+    ///
+    const Surface & surface;
+  
+  public:
+    ///
+    //  Meshing2Surfaces (const Surface & asurf);
+    ///
+    Meshing2Surfaces (const Surface & asurf, const MeshingParameters & mp, 
+		      const Box<3> & aboundingbox);
+
+  protected:
+    ///
+    virtual void DefineTransformation (const Point3d & p1, const Point3d & p2,
+				       const PointGeomInfo * geominfo1,
+				       const PointGeomInfo * geominfo2);
+    ///
+    virtual void TransformToPlain (const Point3d & locpoint, 
+				   const MultiPointGeomInfo & geominfo,
+				   Point2d & plainpoint, 
+				   double h, int & zone);
+    ///
+    virtual int TransformFromPlain (Point2d & plainpoint,
+				    Point3d & locpoint, 
+				    PointGeomInfo & gi,
+				    double h);
+    ///
+    virtual double CalcLocalH (const Point3d & p, double gh) const;
+  };
+
+
+
+  ///
+  class MeshOptimize2dSurfaces : public MeshOptimize2d
+  {
+    ///
+    const CSGeometry & geometry;
+
+  public:
+    ///
+    MeshOptimize2dSurfaces (const CSGeometry & ageometry); 
+   
+    ///
+    virtual void ProjectPoint (INDEX surfind, Point<3> & p) const;
+    ///
+    virtual void ProjectPoint2 (INDEX surfind, INDEX surfind2, Point<3> & p) const;
+    ///
+    virtual void GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const;
+  };
+
+
+
+  class RefinementSurfaces : public Refinement
+  {
+    const CSGeometry & geometry;
+
+  public:
+    RefinementSurfaces (const CSGeometry & ageometry);
+    virtual ~RefinementSurfaces ();
+  
+    virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+			       int surfi, 
+			       const PointGeomInfo & gi1, 
+			       const PointGeomInfo & gi2,
+			       Point<3> & newp, PointGeomInfo & newgi) const;
+
+    virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+			       int surfi1, int surfi2, 
+			       const EdgePointGeomInfo & ap1, 
+			       const EdgePointGeomInfo & ap2,
+			       Point<3> & newp, EdgePointGeomInfo & newgi) const;
+
+    virtual Vec<3> GetTangent (const Point<3> & p, int surfi1, int surfi2,
+			       const EdgePointGeomInfo & ap1) const;
+
+    virtual Vec<3> GetNormal (const Point<3> & p, int surfi1, 
+			      const PointGeomInfo & gi) const;
+
+
+    virtual void ProjectToSurface (Point<3> & p, int surfi) const;
+
+    virtual void ProjectToEdge (Point<3> & p, int surfi1, int surfi2, const EdgePointGeomInfo & egi) const;
+
+  };
+
+}
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/csg/polyhedra.cpp b/contrib/Netgen/libsrc/csg/polyhedra.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d96d7b437eed634a8e2b12cea022ab05e2823a9
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/polyhedra.cpp
@@ -0,0 +1,738 @@
+#include <mystdlib.h>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+namespace netgen
+{
+
+Polyhedra::Face::Face (int pi1, int pi2, int pi3,
+		       const Array<Point<3> > & points,
+		       int ainputnr)
+{
+  inputnr = ainputnr;
+
+  pnums[0] = pi1;
+  pnums[1] = pi2;
+  pnums[2] = pi3;
+
+
+  bbox.Set (points[pi1]);
+  bbox.Add (points[pi2]);
+  bbox.Add (points[pi3]);
+
+  v1 = points[pi2] - points[pi1];
+  v2 = points[pi3] - points[pi1];
+
+  n = Cross (v1, v2);
+
+  nn = n;
+  nn.Normalize();
+  //  PseudoInverse (v1, v2, w1, w2);
+  
+  Mat<2,3> mat;
+  Mat<3,2> inv;
+  for (int i = 0; i < 3; i++)
+    {
+      mat(0,i) = v1(i);
+      mat(1,i) = v2(i);
+    }
+  CalcInverse (mat, inv);
+  for (int i = 0; i < 3; i++)
+    {
+      w1(i) = inv(i,0);
+      w2(i) = inv(i,1);
+    }
+}
+
+
+Polyhedra :: Polyhedra ()
+{
+  surfaceactive.SetSize(0);
+  surfaceids.SetSize(0);
+  eps_base1 = 1e-8;
+}
+
+Polyhedra :: ~Polyhedra ()
+{
+  ;
+}
+
+Primitive * Polyhedra :: CreateDefault ()
+{
+  return new Polyhedra();
+}
+
+INSOLID_TYPE Polyhedra :: BoxInSolid (const BoxSphere<3> & box) const
+{
+  /*
+  for (i = 1; i <= faces.Size(); i++)
+    if (FaceBoxIntersection (i, box))
+      return DOES_INTERSECT;
+  */
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      if (!faces[i].bbox.Intersect (box))
+	continue;
+      //(*testout) << "face " << i << endl;
+
+      const Point<3> & p1 = points[faces[i].pnums[0]];
+      const Point<3> & p2 = points[faces[i].pnums[1]];
+      const Point<3> & p3 = points[faces[i].pnums[2]];
+
+      if (fabs (faces[i].nn * (p1 - box.Center())) > box.Diam()/2)
+	continue;
+
+      //(*testout) << "still in loop" << endl;
+
+      double dist2 = MinDistTP2 (p1, p2, p3, box.Center());
+      //(*testout) << "p1 " << p1 << " p2 " << p2 << " p3 " << p3 << endl
+      //		 << " box.Center " << box.Center() << " box.Diam() " << box.Diam() << endl
+      //	 << " dist2 " << dist2 << " sqr(box.Diam()/2) " << sqr(box.Diam()/2) << endl;
+      if (dist2 < sqr (box.Diam()/2))
+	{
+	  //(*testout) << "DOES_INTERSECT" << endl;
+	  return DOES_INTERSECT;
+	}
+    };
+
+  return PointInSolid (box.Center(), 1e-3 * box.Diam());
+}
+
+
+INSOLID_TYPE Polyhedra :: PointInSolid (const Point<3> & p,
+					double eps) const
+{
+  //(*testout) << "PointInSolid p " << p << " eps " << eps << endl;
+  //(*testout) << "bbox " << poly_bbox << endl;
+
+  if((p(0) > poly_bbox.PMax()(0) + eps) || (p(0) < poly_bbox.PMin()(0) - eps) ||
+     (p(1) > poly_bbox.PMax()(1) + eps) || (p(1) < poly_bbox.PMin()(1) - eps) ||
+     (p(2) > poly_bbox.PMax()(2) + eps) || (p(2) < poly_bbox.PMin()(2) - eps))
+    {
+      //(*testout) << "returning IS_OUTSIDE" << endl;
+      return IS_OUTSIDE;
+    }
+
+  Vec<3> n, v1, v2;
+
+  // random (?) numbers:
+  n(0) = -0.424621;
+  n(1) = 0.15432;
+  n(2) = 0.89212238;
+
+  int cnt = 0;
+
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      const Point<3> & p1 = points[faces[i].pnums[0]];
+      
+      Vec<3> v0 = p - p1;
+
+      double lam3 = faces[i].nn * v0;
+
+      if(fabs(lam3) < eps)
+	{
+	  double lam1 = (faces[i].w1 * v0);
+	  double lam2 = (faces[i].w2 * v0);
+	  if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1)
+	    {
+	      //(*testout) << "returning DOES_INTERSECT" << endl;
+	      return DOES_INTERSECT;
+	    }
+	}
+      else
+	{
+	  lam3 = -(faces[i].n * v0) / (faces[i].n * n);
+
+	  if (lam3 < 0) continue;
+
+	  Vec<3> rs = v0 + lam3 * n;
+	  
+	  double lam1 = (faces[i].w1 * rs);
+	  double lam2 = (faces[i].w2 * rs);
+	  if (lam1 >= 0 && lam2 >= 0 && lam1+lam2 <= 1)
+	    cnt++;
+	}
+
+    }
+
+  //(*testout) << " cnt = " << cnt%2 << endl;
+  return (cnt % 2) ? IS_INSIDE : IS_OUTSIDE;
+}
+
+
+
+
+void Polyhedra :: GetTangentialSurfaceIndices (const Point<3> & p, 
+					       Array<int> & surfind, double eps) const
+{
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      const Point<3> & p1 = points[faces[i].pnums[0]];
+      
+      Vec<3> v0 = p - p1;
+      double lam3 = -(faces[i].nn * v0); // n->nn
+
+      if (fabs (lam3) > eps) continue;
+
+      double lam1 = (faces[i].w1 * v0);
+      double lam2 = (faces[i].w2 * v0);
+
+      if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1)
+	if (!surfind.Contains (GetSurfaceId(i)))
+	  surfind.Append (GetSurfaceId(i));
+    }
+
+}
+
+
+
+INSOLID_TYPE Polyhedra :: VecInSolid (const Point<3> & p,
+				      const Vec<3> & v,
+				      double eps) const
+{
+  Array<int> point_on_faces;
+  INSOLID_TYPE res(DOES_INTERSECT);
+
+  Vec<3> vn = v;
+  vn.Normalize();
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      const Point<3> & p1 = points[faces[i].pnums[0]];
+      
+      Vec<3> v0 = p - p1;
+      double lam3 = -(faces[i].nn * v0); // n->nn 
+
+
+      if (fabs (lam3) > eps) continue;
+      //(*testout) << "lam3 <= eps" << endl;
+
+      double lam1 = (faces[i].w1 * v0);
+      double lam2 = (faces[i].w2 * v0);
+
+      if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1)
+	{
+	  point_on_faces.Append(i);
+
+	  double scal = vn * faces[i].nn; // n->nn
+	
+	  res = DOES_INTERSECT;
+	  if (scal > eps_base1) res = IS_OUTSIDE;
+	  if (scal < -eps_base1) res = IS_INSIDE;
+	}
+    }
+  
+  //(*testout) << "point_on_faces.Size() " << point_on_faces.Size() 
+  //	     << " res " << res << endl;
+
+  if (point_on_faces.Size() == 0)
+    return PointInSolid (p, 0);
+  if (point_on_faces.Size() == 1)
+    return res;
+
+
+
+  
+  double mindist(0);
+  bool first = true;
+
+  for(int i=0; i<point_on_faces.Size(); i++)
+    {
+      for(int j=0; j<3; j++)
+	{
+	  double dist = Dist(p,points[faces[point_on_faces[i]].pnums[j]]);
+	  if(dist > eps && (first || dist < mindist))
+	    {
+	      mindist = dist;
+	      first = false;
+	    }
+	}
+    }
+  
+  Point<3> p2 = p + (1e-2*mindist) * vn;
+  res = PointInSolid (p2, eps);
+
+  //  (*testout) << "mindist " << mindist << " res " << res << endl;
+
+  return res;
+  
+ 
+}
+
+
+/*
+INSOLID_TYPE Polyhedra :: VecInSolid2 (const Point<3> & p,
+				       const Vec<3> & v1,
+				       const Vec<3> & v2,
+				       double eps) const
+{
+  INSOLID_TYPE res;
+
+  res = VecInSolid(p,v1,eps);
+  if(res != DOES_INTERSECT)
+    return res;
+
+  int point_on_n_faces = 0;
+
+  Vec<3> v1n = v1;
+  v1n.Normalize();
+  Vec<3> v2n = v2;
+  v2n.Normalize();
+
+
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      const Point<3> & p1 = points[faces[i].pnums[0]];
+      
+      Vec<3> v0 = p - p1;
+      double lam3 = -(faces[i].n * v0);
+
+      if (fabs (lam3) > eps) continue;
+
+      double lam1 = (faces[i].w1 * v0);
+      double lam2 = (faces[i].w2 * v0);
+
+      if (lam1 >= -eps && lam2 >= -eps && lam1+lam2 <= 1+eps)
+	{
+	  double scal1 = v1n * faces[i].n;
+	  if (fabs (scal1) > eps) continue;
+
+
+	  point_on_n_faces++;
+
+	  double scal2 = v2n * faces[i].n;
+	  res = DOES_INTERSECT;
+	  if (scal2 > eps) res = IS_OUTSIDE;
+	  if (scal2 < -eps) res = IS_INSIDE;
+	}
+    }
+
+  if (point_on_n_faces == 1)
+    return res;
+
+  cerr << "primitive::vecinsolid2 makes nonsense for polyhedra" << endl;
+
+  return Primitive :: VecInSolid2 (p, v1, v2, eps);
+}
+*/
+
+
+
+INSOLID_TYPE Polyhedra :: VecInSolid2 (const Point<3> & p,
+				       const Vec<3> & v1,
+				       const Vec<3> & v2,
+				       double eps) const
+{
+  //(*testout) << "VecInSolid2 eps " << eps << endl;
+  INSOLID_TYPE res = VecInSolid(p,v1,eps);
+  //(*testout) << "VecInSolid = " <<res <<endl;
+
+  if(res != DOES_INTERSECT)
+    return res;
+
+  int point_on_n_faces = 0;
+
+  Vec<3> v1n = v1;
+  v1n.Normalize();
+  Vec<3> v2n = v2 - (v2 * v1n) * v1n;
+  v2n.Normalize();
+
+  double cosv2, cosv2max = -1;
+
+  
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      const Point<3> & p1 = points[faces[i].pnums[0]];
+      
+      Vec<3> v0 = p - p1;
+      if (fabs (faces[i].nn * v0) > eps) continue; // n->nn
+      if (fabs (v1n * faces[i].nn) > eps_base1) continue; // n->nn
+
+      double lam1 = (faces[i].w1 * v0);
+      double lam2 = (faces[i].w2 * v0);
+
+      if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1)
+	{
+	  // v1 is in face
+
+	  Point<3> fc = Center (points[faces[i].pnums[0]],
+				points[faces[i].pnums[1]],
+				points[faces[i].pnums[2]]);
+
+	  Vec<3> vpfc = fc - p;
+	  cosv2 = (v2n * vpfc) / vpfc.Length();
+	  if (cosv2 > cosv2max)
+	    {
+	      cosv2max = cosv2;
+	      point_on_n_faces++;
+
+	      double scal2 = v2n * faces[i].nn; // n->nn
+	      res = DOES_INTERSECT;
+	      if (scal2 > eps_base1) res = IS_OUTSIDE;
+	      if (scal2 < -eps_base1) res = IS_INSIDE;
+
+	    }
+	}
+    }
+
+  if (point_on_n_faces >= 1)
+    return res;
+
+  (*testout) << "primitive::vecinsolid2 makes nonsense for polyhedra" << endl;
+  cerr << "primitive::vecinsolid2 makes nonsense for polyhedra" << endl;
+
+  return Primitive :: VecInSolid2 (p, v1, v2, eps);
+}
+
+
+
+void Polyhedra :: GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2,
+						   Array<int> & surfind, double eps) const
+{
+  Vec<3> v1n = v1;
+  v1n.Normalize();
+  Vec<3> v2n = v2; //  - (v2 * v1n) * v1n;
+  v2n.Normalize();
+
+
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      const Point<3> & p1 = points[faces[i].pnums[0]];
+      
+      Vec<3> v0 = p - p1;
+      if (fabs (v0 * faces[i].nn) > eps) continue; // n->nn
+      if (fabs (v1n * faces[i].nn) > eps_base1) continue; // n->nn
+      if (fabs (v2n * faces[i].nn) > eps_base1) continue; // n->nn
+
+      double lam01 = (faces[i].w1 * v0);
+      double lam02 = (faces[i].w2 * v0);
+      double lam03 = 1-lam01-lam02;
+      double lam11 = (faces[i].w1 * v1);
+      double lam12 = (faces[i].w2 * v1);
+      double lam13 = -lam11-lam12;
+      double lam21 = (faces[i].w1 * v2);
+      double lam22 = (faces[i].w2 * v2);
+      double lam23 = -lam21-lam22;
+
+      bool ok1 = lam01 > eps_base1 ||
+	(lam01 > -eps_base1 && lam11 > eps_base1) ||
+	(lam01 > -eps_base1 && lam11 > -eps_base1 && lam21 > eps_base1);
+
+      bool ok2 = lam02 > eps_base1 ||
+	(lam02 > -eps_base1 && lam12 > eps_base1) ||
+	(lam02 > -eps_base1 && lam12 > -eps_base1 && lam22 > eps_base1);
+      
+      bool ok3 = lam03 > eps_base1 ||
+	(lam03 > -eps_base1 && lam13 > eps_base1) ||
+	(lam03 > -eps_base1 && lam13 > -eps_base1 && lam23 > eps_base1);
+
+      if (ok1 && ok2 && ok3)
+	{
+	  if (!surfind.Contains (GetSurfaceId(faces[i].planenr)))
+	    surfind.Append (GetSurfaceId(faces[i].planenr));
+	}
+    }  
+}
+
+
+
+
+
+
+
+
+
+
+
+
+void Polyhedra :: GetPrimitiveData (const char *& classname, 
+				    Array<double> & coeffs) const
+{
+  classname = "Polyhedra";
+  coeffs.SetSize(0);
+  coeffs.Append (points.Size());
+  coeffs.Append (faces.Size());
+  coeffs.Append (planes.Size());
+
+  /*
+  int i, j;
+  for (i = 1; i <= planes.Size(); i++)
+    {
+      planes.Elem(i)->Print (*testout);
+    }
+  for (i = 1; i <= faces.Size(); i++)
+    {
+      (*testout) << "face " << i << " has plane " << faces.Get(i).planenr << endl;
+      for (j = 1; j <= 3; j++)
+	(*testout) << points.Get(faces.Get(i).pnums[j-1]);
+      (*testout) << endl;
+    }
+  */
+}
+
+void Polyhedra :: SetPrimitiveData (Array<double> & /* coeffs */)
+{
+  ;
+}
+
+void Polyhedra :: Reduce (const BoxSphere<3> & box)
+{
+  for (int i = 0; i < planes.Size(); i++)
+    surfaceactive[i] = 0;
+
+  for (int i = 0; i < faces.Size(); i++)
+    if (FaceBoxIntersection (i, box))
+      surfaceactive[faces[i].planenr] = 1;
+}
+
+void Polyhedra :: UnReduce ()
+{
+  for (int i = 0; i < planes.Size(); i++)
+    surfaceactive[i] = 1;
+}
+
+int Polyhedra :: AddPoint (const Point<3> & p)
+{
+  if(points.Size() == 0)
+    poly_bbox.Set(p);
+  else
+    poly_bbox.Add(p);
+
+  return points.Append (p);
+}
+
+int Polyhedra :: AddFace (int pi1, int pi2, int pi3, int inputnum)
+{
+  (*testout) << "polyhedra, add face " << pi1 << ", " << pi2 << ", " << pi3 << endl;
+
+  if(pi1 == pi2 || pi2 == pi3 || pi3 == pi1)
+    {
+      ostringstream msg;
+      msg << "Illegal point numbers for polyhedron face: " << pi1+1 << ", " << pi2+1 << ", " << pi3+1;
+      throw NgException(msg.str());
+    }
+
+  faces.Append (Face (pi1, pi2, pi3, points, inputnum));
+  
+  Point<3> p1 = points[pi1];
+  Point<3> p2 = points[pi2];
+  Point<3> p3 = points[pi3];
+
+  Vec<3> v1 = p2 - p1;
+  Vec<3> v2 = p3 - p1;
+
+  Vec<3> n = Cross (v1, v2); 
+  n.Normalize();
+
+  Plane pl (p1, n);
+//   int inverse;
+//   int identicto = -1;
+//   for (int i = 0; i < planes.Size(); i++)
+//     if (pl.IsIdentic (*planes[i], inverse, 1e-9*max3(v1.Length(),v2.Length(),Dist(p2,p3))))
+//       {
+// 	if (!inverse)
+// 	  identicto = i;
+//       }
+//   //  cout << "is identic = " << identicto << endl;
+//   identicto = -1;    // changed April 10, JS
+
+//   if (identicto != -1)
+//     faces.Last().planenr = identicto;
+//   else
+    {
+      planes.Append (new Plane (p1, n));
+      surfaceactive.Append (1);
+      surfaceids.Append (0);
+      faces.Last().planenr = planes.Size()-1;
+    }
+
+//  (*testout) << "is plane nr " << faces.Last().planenr << endl;
+
+  return faces.Size();
+}
+
+
+
+int Polyhedra :: FaceBoxIntersection (int fnr, const BoxSphere<3> & box) const
+{
+  /*
+  (*testout) << "check face box intersection, fnr = " << fnr << endl;
+  (*testout) << "box = " << box << endl;
+  (*testout) << "face-box = " << faces[fnr].bbox << endl;
+  */
+
+  if (!faces[fnr].bbox.Intersect (box))
+    return 0;
+
+  const Point<3> & p1 = points[faces[fnr].pnums[0]];
+  const Point<3> & p2 = points[faces[fnr].pnums[1]];
+  const Point<3> & p3 = points[faces[fnr].pnums[2]];
+
+  double dist2 = MinDistTP2 (p1, p2, p3, box.Center());
+  /*
+  (*testout) << "p1 = " << p1 << endl;
+  (*testout) << "p2 = " << p2 << endl;
+  (*testout) << "p3 = " << p3 << endl;
+
+  (*testout) << "box.Center() = " << box.Center() << endl;
+  (*testout) << "center = " << box.Center() << endl;
+  (*testout) << "dist2 = " << dist2 << endl;
+  (*testout) << "diam = " << box.Diam() << endl;
+  */
+  if (dist2 < sqr (box.Diam()/2))
+    {
+      //      (*testout) << "intersect" << endl;
+      return 1;
+    }
+  return 0;
+}
+
+
+void Polyhedra :: GetPolySurfs(Array < Array<int> * > & polysurfs)
+{
+  int maxnum = -1;
+  
+  for(int i = 0; i<faces.Size(); i++)
+    {
+      if(faces[i].inputnr > maxnum)
+	maxnum = faces[i].inputnr;
+    }
+  
+  polysurfs.SetSize(maxnum+1);
+  for(int i=0; i<polysurfs.Size(); i++)
+    polysurfs[i] = new Array<int>;
+
+  for(int i = 0; i<faces.Size(); i++)
+    polysurfs[faces[i].inputnr]->Append(faces[i].planenr);
+}
+
+
+void Polyhedra::CalcSpecialPoints (Array<Point<3> > & pts) const
+{
+  for (int i = 0; i < points.Size(); i++)
+    pts.Append (points[i]);
+}
+
+
+void Polyhedra :: AnalyzeSpecialPoint (const Point<3> & /* pt */, 
+				       Array<Point<3> > & /* specpts */) const
+{
+  ;
+}
+
+Vec<3> Polyhedra :: SpecialPointTangentialVector (const Point<3> & p, int s1, int s2) const
+{
+  const double eps = 1e-10*poly_bbox.Diam();
+
+  for (int fi1 = 0; fi1 < faces.Size(); fi1++)
+    for (int fi2 = 0; fi2 < faces.Size(); fi2++)
+      {
+	int si1 = faces[fi1].planenr;
+	int si2 = faces[fi2].planenr;
+
+	if (surfaceids[si1] != s1 || surfaceids[si2] != s2) continue;
+
+	//(*testout) << "check pair fi1/fi2 " << fi1 << "/" << fi2 << endl;
+	
+	Vec<3> n1 = GetSurface(si1) . GetNormalVector (p);
+	Vec<3> n2 = GetSurface(si2) . GetNormalVector (p);
+	Vec<3> t = Cross (n1, n2);
+
+	//(*testout) << "t = " << t << endl;
+
+
+	/*
+	int samepts = 0;
+	for (int j = 0; j < 3; j++)
+	  for (int k = 0; k < 3; k++)
+	    if (Dist(points[faces[fi1].pnums[j]],
+		     points[faces[fi2].pnums[k]]) < eps)
+	      samepts++;
+	if (samepts < 2) continue;
+	*/
+
+	bool shareedge = false;
+	for(int j = 0; !shareedge && j < 3; j++)
+	  {
+	    Vec<3> v1 = points[faces[fi1].pnums[(j+1)%3]] - points[faces[fi1].pnums[j]];
+	    double smax = v1.Length();
+	    v1 *= 1./smax;
+	    
+	    int pospos;
+	    if(fabs(v1(0)) > 0.5)
+	      pospos = 0;
+	    else if(fabs(v1(1)) > 0.5)
+	      pospos = 1;
+	    else
+	      pospos = 2;
+
+	    double sp = (p(pospos) - points[faces[fi1].pnums[j]](pospos)) / v1(pospos);
+	    if(sp < -eps || sp > smax+eps)
+	      continue;
+
+	    for (int k = 0; !shareedge && k < 3; k ++)
+	      {
+		 Vec<3> v2 = points[faces[fi2].pnums[(k+1)%3]] - points[faces[fi2].pnums[k]];
+		 v2.Normalize();
+		 if(v2 * v1 > 0)
+		   v2 -= v1;
+		 else
+		   v2 += v1;
+		 
+		 //(*testout) << "v2.Length2() " << v2.Length2() << endl;
+
+		 if(v2.Length2() > 1e-18)
+		   continue;
+
+		 double sa,sb;
+
+		 sa = (points[faces[fi2].pnums[k]](pospos) - points[faces[fi1].pnums[j]](pospos)) / v1(pospos);
+		 sb = (points[faces[fi2].pnums[(k+1)%3]](pospos) - points[faces[fi1].pnums[j]](pospos)) / v1(pospos);
+		 
+
+		 if(Dist(points[faces[fi1].pnums[j]] + sa*v1, points[faces[fi2].pnums[k]]) > eps)
+		   continue;
+
+		 if(sa > sb)
+		   {
+		     double aux = sa; sa = sb; sb = aux;
+		   }
+
+		 //testout->precision(20);
+		 //(*testout) << "sa " << sa << " sb " << sb << " smax " << smax << " sp " << sp  << " v1 " << v1 << endl;
+		 //testout->precision(8);
+
+
+		 shareedge = (sa < -eps && sb > eps) ||
+		   (sa < smax-eps && sb > smax+eps) ||
+		   (sa > -eps && sb < smax+eps);
+
+		 if(!shareedge)
+		   continue;
+
+		 sa = max2(sa,0.);
+		 sb = min2(sb,smax);
+
+		 if(sp < sa+eps)
+		   shareedge = (t * v1 > 0);
+		 else if (sp > sb-eps)
+		   shareedge = (t * v1 < 0);
+		   
+	      }
+	  }
+	if (!shareedge) continue;
+
+	t.Normalize();
+	  
+	
+	return t;
+      }
+
+  return Vec<3> (0,0,0);
+}
+
+
+}
+
+
diff --git a/contrib/Netgen/libsrc/csg/polyhedra.hpp b/contrib/Netgen/libsrc/csg/polyhedra.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0a7850425611c91812033c43613f3d72888ad41a
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/polyhedra.hpp
@@ -0,0 +1,104 @@
+#ifndef FILE_POLYHEDRA
+#define FILE_POLYHEDRA
+
+
+/**************************************************************************/
+/* File:   polyhedra.hh                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   19. Mar. 2000                                                  */
+/**************************************************************************/
+
+namespace netgen
+{
+
+  /*
+
+  Polyhedral primitive
+  
+  */
+
+  class Polyhedra : public Primitive
+  {
+    class Face {
+    public:
+      int pnums[3];
+      int planenr;
+
+      int inputnr;
+
+      Box<3> bbox;
+      //    Point<3> center;
+      Vec<3> v1, v2;   // edges
+      Vec<3> w1, w2;   // pseudo-inverse
+      Vec<3> n;        // normal to face
+      Vec<3> nn;       // normed normal
+
+      Face () { ; }
+      Face (int pi1, int pi2, int pi3, 
+	    const Array<Point<3> > & points,
+	    int ainputnr);
+    };
+
+    Array<Point<3> > points;
+    Array<Face> faces;
+    Array<Plane*> planes;
+    Box<3> poly_bbox;
+
+    double eps_base1;
+
+  public:
+    Polyhedra ();
+    virtual ~Polyhedra ();
+    static Primitive * CreateDefault ();
+
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    virtual INSOLID_TYPE PointInSolid (const Point<3> & p,
+				       double eps) const;
+    virtual INSOLID_TYPE VecInSolid (const Point<3> & p,
+				     const Vec<3> & v,
+				     double eps) const;
+
+    // checks if lim s->0 lim t->0  p + t(v1 + s v2) in solid
+    virtual INSOLID_TYPE VecInSolid2 (const Point<3> & p,
+				      const Vec<3> & v1,
+				      const Vec<3> & v2,
+				      double eps) const;
+
+    virtual void GetTangentialSurfaceIndices (const Point<3> & p, 
+					      Array<int> & surfind, double eps) const;
+
+
+    virtual void GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2,
+						  Array<int> & surfind, double eps) const;
+
+    virtual void CalcSpecialPoints (Array<Point<3> > & pts) const;
+    virtual void AnalyzeSpecialPoint (const Point<3> & pt, 
+				      Array<Point<3> > & specpts) const;
+    virtual Vec<3> SpecialPointTangentialVector (const Point<3> & p, int s1, int s2) const;
+
+    virtual int GetNSurfaces() const 
+    { return planes.Size(); }
+    virtual Surface & GetSurface (int i) 
+    { return *planes[i]; }
+    virtual const Surface & GetSurface (int i) const
+    { return *planes[i]; }
+
+    virtual void GetPrimitiveData (const char *& classname, Array<double> & coeffs) const;
+    virtual void SetPrimitiveData (Array<double> & coeffs);
+
+    virtual void Reduce (const BoxSphere<3> & box);
+    virtual void UnReduce ();
+
+    int AddPoint (const Point<3> & p);
+    int AddFace (int pi1, int pi2, int pi3, int inputnum);
+
+    void GetPolySurfs(Array < Array<int> * > & polysurfs);
+  
+  protected:
+    int FaceBoxIntersection (int fnr, const BoxSphere<3> & box) const;
+    //  void CalcData();
+  };
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/revolution.cpp b/contrib/Netgen/libsrc/csg/revolution.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..89863b6dd0855bc0631d9347011c8e9edf651334
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/revolution.cpp
@@ -0,0 +1,900 @@
+#include <mystdlib.h>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+namespace netgen
+{
+  void RevolutionFace :: Init(void)
+  {
+    const LineSeg<2> * line = dynamic_cast<const LineSeg<2>*>(spline);
+    const SplineSeg3<2> * spline3 = dynamic_cast<const SplineSeg3<2>*>(spline);
+
+    if(line)
+      {
+	checklines_start.Append(new Point<2>(line->StartPI()));
+	checklines_vec.Append(new Vec<2>(line->EndPI() - line->StartPI()));
+	(*checklines_vec.Last()) *= 1./pow(checklines_vec.Last()->Length(),2); //!!
+      }
+    else if (spline3)
+      {
+	checklines_start.Append(new Point<2>(spline3->EndPI()));
+	checklines_start.Append(new Point<2>(spline3->TangentPoint()));
+	checklines_start.Append(new Point<2>(spline3->StartPI()));
+	checklines_vec.Append(new Vec<2>(spline3->StartPI() - spline3->EndPI()));
+	(*checklines_vec.Last()) *= 1./pow(checklines_vec.Last()->Length(),2); //!!
+	checklines_vec.Append(new Vec<2>(spline3->EndPI() - spline3->TangentPoint()));
+	(*checklines_vec.Last()) *= 1./pow(checklines_vec.Last()->Length(),2); //!!
+	checklines_vec.Append(new Vec<2>(spline3->TangentPoint() - spline3->StartPI()));
+	(*checklines_vec.Last()) *= 1./pow(checklines_vec.Last()->Length(),2); //!!
+	
+      }
+    
+    for(int i=0; i<checklines_vec.Size(); i++)
+      {
+	checklines_normal.Append(new Vec<2>);
+	(*checklines_normal.Last())(0) = - (*checklines_vec[i])(1);
+	(*checklines_normal.Last())(1) = (*checklines_vec[i])(0);
+	checklines_normal.Last()->Normalize();
+      }
+  }
+
+  RevolutionFace :: RevolutionFace(const SplineSeg<2> & spline_in,
+				   const Point<3> & p,
+				   const Vec<3> & vec,
+				   bool first,
+				   bool last,
+				   const int id_in) :
+    isfirst(first), islast(last), spline(&spline_in), p0(p), v_axis(vec),  id(id_in)
+  {    
+    deletable = false;
+    Init();
+  }
+
+
+  RevolutionFace :: RevolutionFace(const Array<double> & raw_data)
+  {
+    deletable = true;
+    
+    int pos = 0;
+
+    Array< Point<2> > p(3);
+
+    int stype = int(raw_data[pos]); pos++;
+
+    for(int i=0; i<stype; i++)
+      {
+	p[i](0) = raw_data[pos]; pos++;
+	p[i](1) = raw_data[pos]; pos++;
+      }
+
+    if(stype == 2)
+      {
+	spline = new LineSeg<2>(GeomPoint<2>(p[0],1),
+				GeomPoint<2>(p[1],1));
+	//(*testout) << "appending LineSeg<2> " << p[0] 
+	//	   << " to " << p[1] << endl;
+      }
+    else if(stype == 3)
+      {
+	spline = new SplineSeg3<2>(GeomPoint<2>(p[0],1),
+				   GeomPoint<2>(p[1],1),
+				   GeomPoint<2>(p[2],1));
+	//(*testout) << "appending SplineSeg<3> "
+	//	   << p[0] << " -- " << p[1] << " -- " << p[2] << endl;
+      }
+
+    for(int i=0; i<3; i++)
+      {
+	p0(i) = raw_data[pos];
+	pos++;
+      }
+    for(int i=0; i<3; i++)
+      {
+	v_axis(i) = raw_data[pos];
+	pos++;
+      }
+    isfirst = (raw_data[pos] > 0.9);
+    pos++;
+    islast = (raw_data[pos] < 0.1);
+    pos++;
+    
+
+  }
+
+  
+  RevolutionFace :: ~RevolutionFace()
+  {
+    for(int i=0; i<checklines_start.Size(); i++)
+      {
+	delete checklines_start[i];
+	delete checklines_vec[i];
+	delete checklines_normal[i];
+      }
+
+    if(deletable)
+      delete spline;
+  }
+  
+  void RevolutionFace :: CalcProj(const Point<3> & point3d, Point<2> & point2d,
+				  const Vec<3> & vector3d, Vec<2> & vector2d) const
+  {
+    Vec<3> pmp0 = point3d-p0;
+    CalcProj0(pmp0,point2d);
+    Vec<3> y=pmp0-point2d(0)*v_axis; y.Normalize();
+    vector2d(0) = vector3d*v_axis;
+    vector2d(1) = vector3d*y;
+  }
+  
+
+  void RevolutionFace :: CalcProj(const Point<3> & point3d, Point<2> & point2d) const
+  {
+    Vec<3> pmp0 = point3d-p0;
+    CalcProj0(pmp0,point2d);
+  }
+  
+  void RevolutionFace :: CalcProj0(const Vec<3> & point3d_minus_p0, Point<2> & point2d) const
+  {
+    point2d(0) = point3d_minus_p0 * v_axis;
+    point2d(1) = sqrt( point3d_minus_p0 * point3d_minus_p0 - point2d(0)*point2d(0) );
+  }
+  
+
+  int  RevolutionFace ::IsIdentic (const Surface & s2, int & inv, double eps) const
+  {
+    const RevolutionFace * rev2 = dynamic_cast<const RevolutionFace*>(&s2);
+    
+    if(!rev2) return 0;
+    
+    if(rev2 == this)
+      return 1;
+        
+    return 0;
+  }
+
+  double RevolutionFace :: CalcFunctionValue (const Point<3> & point) const
+  {
+    if(spline_coefficient.Size() == 0)
+      spline->GetCoeff(spline_coefficient);
+
+    Point<2> p;
+    CalcProj(point,p);
+
+    return spline_coefficient(0)*p(0)*p(0) + spline_coefficient(1)*p(1)*p(1)
+      + spline_coefficient(2)*p(0)*p(1) + spline_coefficient(3)*p(0)
+      + spline_coefficient(4)*p(1) + spline_coefficient(5);
+  }
+
+  void RevolutionFace :: CalcGradient (const Point<3> & point, Vec<3> & grad) const
+  {
+    if(spline_coefficient.Size() == 0)
+      spline->GetCoeff(spline_coefficient);
+
+    Vec<3> point_minus_p0 = point-p0;
+
+    Point<2> p;
+    CalcProj0(point_minus_p0,p);
+
+    const double dFdxbar = 2.*spline_coefficient(0)*p(0) + spline_coefficient(2)*p(1) + spline_coefficient(3);
+
+    if(fabs(p(1)) > 1e-10)
+      {
+	const double dFdybar = 2.*spline_coefficient(1)*p(1) + spline_coefficient(2)*p(0) + spline_coefficient(4);
+
+	grad(0) = dFdxbar*v_axis(0) + dFdybar * ( point_minus_p0(0)-v_axis(0)*p(0) )/p(1);
+	grad(1) = dFdxbar*v_axis(1) + dFdybar * ( point_minus_p0(1)-v_axis(1)*p(0) )/p(1);
+	grad(2) = dFdxbar*v_axis(2) + dFdybar * ( point_minus_p0(2)-v_axis(2)*p(0) )/p(1);
+	//(*testout) << "grad1("<<point<<") = " << grad << endl;
+      }
+    else
+      {
+	grad(0) = dFdxbar*v_axis(0);
+	grad(1) = dFdxbar*v_axis(1);
+	grad(2) = dFdxbar*v_axis(2);
+	//(*testout) << "grad2("<<point<<") = " << grad << endl;
+      }
+  }
+
+  
+  void RevolutionFace :: CalcHesse (const Point<3> & point, Mat<3> & hesse) const
+  {
+    if(spline_coefficient.Size() == 0)
+      spline->GetCoeff(spline_coefficient);
+
+    Vec<3> point_minus_p0 = point-p0;
+
+    Point<2> p;
+    CalcProj0(point_minus_p0,p);
+
+
+    if(fabs(p(1)) > 1e-10)
+      {
+	const double dFdybar = 2.*spline_coefficient(1)*p(1) + spline_coefficient(2)*p(0) + spline_coefficient(4);
+	
+	const double aux = -pow(p(1),-3);
+	const double aux0 = point_minus_p0(0) - v_axis(0)*p(0);
+	const double aux1 = point_minus_p0(1) - v_axis(1)*p(0);
+	const double aux2 = point_minus_p0(2) - v_axis(2)*p(0);
+	
+
+	const double dybardx = aux0/p(1);
+	const double dybardy = aux1/p(1);
+	const double dybardz = aux2/p(1);
+    
+	const double dybardxx = aux*aux0*aux0 + (1.-v_axis(0)*v_axis(0))/p(1);
+	const double dybardyy = aux*aux1*aux1 + (1.-v_axis(1)*v_axis(1))/p(1);
+	const double dybardzz = aux*aux2*aux2 + (1.-v_axis(2)*v_axis(2))/p(1);
+	const double dybardxy = aux*aux0*aux1 - v_axis(0)*v_axis(1)/p(1);
+	const double dybardxz = aux*aux0*aux2 - v_axis(0)*v_axis(2)/p(1);
+	const double dybardyz = aux*aux1*aux2 - v_axis(1)*v_axis(2)/p(1);
+	
+	hesse(0,0) = 2.*spline_coefficient(0)*v_axis(0)*v_axis(0) + 2.*spline_coefficient(2)*v_axis(0)*dybardx + 2.*spline_coefficient(1)*dybardx*dybardx
+	  + dFdybar*dybardxx;
+	hesse(1,1) = 2.*spline_coefficient(0)*v_axis(1)*v_axis(1) + 2.*spline_coefficient(2)*v_axis(1)*dybardy + 2.*spline_coefficient(1)*dybardy*dybardy
+	  + dFdybar*dybardyy;
+	hesse(2,2) = 2.*spline_coefficient(0)*v_axis(2)*v_axis(2) + 2.*spline_coefficient(2)*v_axis(2)*dybardz + 2.*spline_coefficient(1)*dybardz*dybardz
+	  + dFdybar*dybardzz;
+	
+	hesse(0,1) = hesse(1,0) = 2.*spline_coefficient(0)*v_axis(0)*v_axis(1) + spline_coefficient(2)*v_axis(0)*dybardy + spline_coefficient(2)*dybardx*v_axis(1)
+	  + 2.*spline_coefficient(2)*dybardx*dybardy + dFdybar*dybardxy;
+	hesse(0,2) = hesse(2,0) = 2.*spline_coefficient(0)*v_axis(0)*v_axis(2) + spline_coefficient(2)*v_axis(0)*dybardz + spline_coefficient(2)*dybardx*v_axis(2)
+	  + 2.*spline_coefficient(2)*dybardx*dybardz + dFdybar*dybardxz;
+	hesse(1,2) = hesse(2,1) = 2.*spline_coefficient(0)*v_axis(1)*v_axis(2) + spline_coefficient(2)*v_axis(1)*dybardz + spline_coefficient(2)*dybardy*v_axis(2)
+	  + 2.*spline_coefficient(2)*dybardy*dybardz + dFdybar*dybardyz;
+
+	//(*testout) << "hesse1: " << hesse <<endl;
+      }
+    else if (fabs(spline_coefficient(2)) + fabs(spline_coefficient(4)) < 1.e-9 &&
+	     fabs(spline_coefficient(0)) > 1e-10)
+      {
+	double aux = spline_coefficient(0)-spline_coefficient(1);
+	
+	hesse(0,0) = aux*v_axis(0)*v_axis(0) + spline_coefficient(1);
+	hesse(0,0) = aux*v_axis(1)*v_axis(1) + spline_coefficient(1);
+	hesse(0,0) = aux*v_axis(2)*v_axis(2) + spline_coefficient(1);
+
+	hesse(0,1) = hesse(1,0) = aux*v_axis(0)*v_axis(1);
+	hesse(0,2) = hesse(2,0) = aux*v_axis(0)*v_axis(2);
+	hesse(1,2) = hesse(2,1) = aux*v_axis(1)*v_axis(2);
+	//(*testout) << "hesse2: " << hesse <<endl;
+	
+      }
+    else if (fabs(spline_coefficient(1)) + fabs(spline_coefficient(3)) + fabs(spline_coefficient(4)) + fabs(spline_coefficient(5)) < 1.e-9) // line
+      {
+	hesse = 0;
+	//(*testout) << "hesse3: " << hesse <<endl;
+      }
+    else
+      {
+	(*testout) << "hesse4: " << hesse <<endl;
+      }
+  }
+
+  
+
+  double RevolutionFace ::HesseNorm () const
+  {
+    if (fabs(spline_coefficient(1)) + fabs(spline_coefficient(3)) + fabs(spline_coefficient(4)) + fabs(spline_coefficient(5)) < 1.e-9) // line
+      return 0;
+      
+    if (fabs(spline_coefficient(2)) + fabs(spline_coefficient(4)) < 1.e-9 &&
+	fabs(spline_coefficient(0)) > 1e-10)
+      return 2.*max2(fabs(spline_coefficient(0)),fabs(spline_coefficient(1)));
+
+
+    double alpha = fabs(spline_coefficient(2)*(spline->StartPI()(0)-spline->EndPI()(0))) /
+      max2(fabs(spline->StartPI()(1)),fabs(spline->EndPI()(1)));
+
+    return max2(2.*fabs(spline_coefficient(0))+sqrt(2.)*fabs(spline_coefficient(2)),
+		2.*fabs(spline_coefficient(1))+spline_coefficient(2)+1.5*alpha);
+  }
+
+  double  RevolutionFace :: MaxCurvature() const
+  {
+    double retval = spline->MaxCurvature();
+
+    Array < Point<2> > checkpoints;
+
+    const SplineSeg3<2> * ss3 = dynamic_cast<const SplineSeg3<2> *>(spline);
+    const LineSeg<2> * ls = dynamic_cast<const LineSeg<2> *>(spline);
+    
+    if(ss3)
+      {
+	checkpoints.Append(ss3->StartPI());
+	checkpoints.Append(ss3->TangentPoint());
+	checkpoints.Append(ss3->TangentPoint());
+	checkpoints.Append(ss3->EndPI());
+      }
+    else if(ls)
+      {
+	checkpoints.Append(ls->StartPI());
+	checkpoints.Append(ls->EndPI());
+      }
+
+    for(int i=0; i<checkpoints.Size(); i+=2)
+      {
+	Vec<2> v = checkpoints[i+1]-checkpoints[i];
+	Vec<2> n(v(1),-v(0)); n.Normalize();
+
+	//if(ss3)
+	//  (*testout) << "n " << n << endl;
+
+	if(fabs(n(1)) < 1e-15)
+	  continue;
+
+	double t1 = -checkpoints[i](1)/n(1);
+	double t2 = -checkpoints[i+1](1)/n(1);
+	
+	double c1 = (t1 > 0) ? (1./t1) : -1;
+	double c2 = (t2 > 0) ? (1./t2) : -1;
+	
+	//if(ss3)
+	//  (*testout) << "t1 " << t1 << " t2 " << t2 << " c1 " << c1 << " c2 " << c2 << endl;
+
+	if(c1 > retval)
+	  retval = c1;
+	if(c2 > retval)
+	  retval = c2;
+      }
+	
+    //if(ss3)
+    //  (*testout) << "curvature " << retval << endl;
+
+    return retval;
+
+    /*
+
+
+    // find smallest y value of spline:
+    Array<double> testt;
+
+    if(!isfirst)
+      testt.Append(0);
+    if(!islast)
+      testt.Append(1);
+    
+    const SplineSegment3 * s3 = dynamic_cast<const SplineSegment3 *>(&spline);
+
+    if(s3)
+      {
+	double denom = (2.-sqrt(2.))*(s3->EndPI()(1) - s3->StartPI()(1));
+	
+	if(fabs(denom) < 1e-20)
+	  testt.Append(0.5);
+	else
+	  {
+	    double sD = sqrt(pow(s3->TangentPoint()(1) - s3->StartPI()(1),2)+
+			     pow(s3->TangentPoint()(1) - s3->EndPI()(1),2));
+	    testt.Append((s3->StartPI()(1)*(sqrt(2.)-1.) - sqrt(2.)*s3->TangentPoint()(1) + s3->EndPI()(1) + sD)/denom);
+	    testt.Append((s3->StartPI()(1)*(sqrt(2.)-1.) - sqrt(2.)*s3->TangentPoint()(1) + s3->EndPI()(1) - sD)/denom);
+	  }	
+      }
+
+    double miny = fabs(spline.GetPoint(testt[0])(1));
+    for(int i=1; i<testt.Size(); i++)
+      {
+	double thisy = fabs(spline.GetPoint(testt[i])(1));
+	if(thisy < miny)
+	  miny = thisy;
+      }
+
+    return max2(splinecurvature,1./miny);
+    */
+  }
+
+  void RevolutionFace :: Project (Point<3> & p) const
+  {
+    Point<2> p2d;
+
+    CalcProj(p,p2d);
+
+    const Vec<3> y = (p-p0)-p2d(0)*v_axis;
+    const double yl = y.Length();
+
+    double dummy;
+
+    spline->Project(p2d,p2d,dummy);
+
+    p = p0 + p2d(0)*v_axis;
+
+    if(yl > 1e-20*Dist(spline->StartPI(),spline->EndPI()))
+      p+= (p2d(1)/yl)*y;
+  }
+
+
+
+  
+  Point<3>  RevolutionFace :: GetSurfacePoint () const
+  {
+    Vec<3> random_vec(0.760320,-0.241175,0.60311534);
+
+    Vec<3> n = Cross(v_axis,random_vec); n.Normalize();
+
+    Point<2> sp = spline->GetPoint(0.5);
+
+    Point<3> retval = p0 + sp(0)*v_axis + sp(1)*n;
+
+    return retval;
+  }
+
+
+  void RevolutionFace :: Print (ostream & str) const
+  {
+    if(spline_coefficient.Size() == 0)
+      spline->GetCoeff(spline_coefficient);
+
+    str << p0(0) << " " << p0(1) << " " << p0(2) << " "
+	<< v_axis(0) << " " << v_axis(1) << " " << v_axis(2) << " ";
+    for(int i=0; i<6; i++) str << spline_coefficient(i) << " ";
+    str << endl;
+  }
+
+
+  void RevolutionFace :: GetTriangleApproximation (TriangleApproximation & tas, 
+						   const Box<3> & boundingbox, 
+						   double facets) const
+  {
+    Vec<3> random_vec(0.760320,-0.241175,0.60311534);
+
+    Vec<3> v1 = Cross(v_axis,random_vec); v1.Normalize();
+    Vec<3> v2 = Cross(v1,v_axis); v2.Normalize();
+
+    int n = int(2.*facets) + 1;
+
+    for(int i=0; i<=n; i++)
+      {
+	Point<2> sp = spline->GetPoint(double(i)/double(n));
+	for(int j=0; j<=n; j++)
+	  {
+	    double phi = 2.*M_PI*double(j)/double(n);
+	    
+	    Point<3> p = p0 + sp(0)*v_axis + sp(1)*cos(phi)*v1 + sp(1)*sin(phi)*v2;
+	    tas.AddPoint(p);   
+	  }
+      }
+    
+    for(int i=0; i<n; i++)
+      for(int j=0; j<n; j++)
+	{
+	  int pi = (n+1)*i+j;
+
+	  tas.AddTriangle( TATriangle (id, pi,pi+1,pi+n+1));
+	  tas.AddTriangle( TATriangle (id, pi+1,pi+n+1,pi+n+2));
+	}
+  }
+  
+
+  bool RevolutionFace :: BoxIntersectsFace(const Box<3> & box) const
+  {
+    Point<3> center = box.Center();
+
+    Project(center);
+
+    return (Dist(box.Center(),center) < 0.5*box.Diam());
+  }
+
+
+  /*
+  bool RevolutionFace :: BoxIntersectsFace (const BoxSphere<3> & box, bool & uncertain) const
+  {
+    Point<2> c,pmin,pmax;
+    CalcProj(box.Center(),c);
+    double aux = box.Diam()/sqrt(8.);
+    pmin(0) = c(0)-aux; pmin(1) = c(1)-aux;
+    pmax(0) = c(0)+aux; pmax(1) = c(1)+aux;
+    
+    BoxSphere<2> box2d(pmin,pmax);
+    return BoxIntersectsFace(box2d, uncertain);
+  }
+
+  bool RevolutionFace :: BoxIntersectsFace (const BoxSphere<2> & box, bool & uncertain) const
+  {
+    const LineSegment * line = dynamic_cast<const LineSegment*>(&spline);
+    const SplineSegment3 * spline3 = dynamic_cast<const SplineSegment3*>(&spline);
+
+    bool always_right = true, always_left = true;
+
+    bool retval = false;
+    bool thisint;
+    bool intdirect = false;
+    bool inttangent = false;
+    uncertain = false;
+
+    if(line) inttangent = true;
+  
+    for(int i=0; i<checklines_start.Size(); i++)
+      {
+	Vec<2> b = box.Center()- (*checklines_start[i]);
+
+	double d;
+
+	double checkdist = b * (*checklines_vec[i]);
+	double ncomp = b * (*checklines_normal[i]);
+
+	if(checkdist < 0)
+	  d = b.Length();
+	else if (checkdist > 1)
+	  {
+	    if(spline3)
+	      d = Dist(box.Center(),*checklines_start[(i+1)%3]);
+	    else
+	      d = Dist(box.Center(),(*checklines_start[i]) 
+		       + pow(checklines_vec[i]->Length(),2)*(*checklines_vec[i]));
+	  }
+	else 
+	  d = fabs(ncomp);
+	  
+	thisint = (box.Diam() >= 2.*d);
+	retval = retval || thisint;
+	if(thisint)
+	  {
+	    if(i==0)
+	      intdirect = true;
+	    else
+	      inttangent = true;
+	  }
+
+	if(ncomp > 0) always_right = false;
+	else if(ncomp < 0) always_left = false;
+      }
+
+    if(retval && !(intdirect && inttangent))
+      uncertain = true;
+
+    if(!retval && spline3 && (always_right || always_left))
+      {
+	retval = true;
+	uncertain = true;
+      }
+    
+    return retval;	
+  }  
+  */
+  
+
+  INSOLID_TYPE RevolutionFace :: PointInFace (const Point<3> & p, const double eps) const
+  {
+    Point<2> p2d;
+    CalcProj(p,p2d);
+
+    double val = spline_coefficient(0)*p2d(0)*p2d(0) + spline_coefficient(1)*p2d(1)*p2d(1) + spline_coefficient(2)*p2d(0)*p2d(1) +
+      spline_coefficient(3)*p2d(0) + spline_coefficient(4)*p2d(1) + spline_coefficient(5);
+
+    if(val > eps)
+      return IS_OUTSIDE;
+    if(val < -eps)
+      return IS_INSIDE;
+     
+    return DOES_INTERSECT;
+  }
+
+  
+
+  void RevolutionFace :: GetRawData(Array<double> & data) const
+  {
+    data.DeleteAll();
+    spline->GetRawData(data);
+    for(int i=0; i<3; i++)
+      data.Append(p0(i));
+    for(int i=0; i<3; i++)
+      data.Append(v_axis(i));
+    data.Append((isfirst) ? 1. : 0.);
+    data.Append((islast) ? 1. : 0.);
+  }
+
+
+
+  Revolution :: Revolution(const Point<3> & p0_in,
+			   const Point<3> & p1_in,
+			   const SplineGeometry<2> & spline_in) :
+    p0(p0_in), p1(p1_in), splinecurve(spline_in),
+    nsplines(spline_in.GetNSplines())
+  {
+    surfaceactive.SetSize(0);
+    surfaceids.SetSize(0);
+
+    v_axis = p1-p0;
+
+    v_axis.Normalize();
+
+    if(splinecurve.GetSpline(0).StartPI()(1) <= 0. &&
+       splinecurve.GetSpline(nsplines-1).EndPI()(1) <= 0.)
+      type = 2;
+    else if (Dist(splinecurve.GetSpline(0).StartPI(),
+		  splinecurve.GetSpline(nsplines-1).EndPI()) < 1e-7)
+      type = 1;
+    else
+      cerr << "Surface of revolution cannot be constructed" << endl;
+
+    for(int i=0; i<splinecurve.GetNSplines(); i++)
+      {
+	RevolutionFace * face = new RevolutionFace(splinecurve.GetSpline(i),
+						   p0,v_axis,
+						   type==2 && i==0,
+						   type==2 && i==splinecurve.GetNSplines()-1);
+	faces.Append(face);
+	surfaceactive.Append(1);
+	surfaceids.Append(0);
+      }
+  }
+  
+  Revolution::~Revolution()
+  {
+    for(int i=0; i<faces.Size(); i++)
+      delete faces[i];
+  }
+
+
+  INSOLID_TYPE Revolution :: BoxInSolid (const BoxSphere<3> & box) const
+  {
+    for(int i=0; i<faces.Size(); i++)
+      if(faces[i]->BoxIntersectsFace(box))
+	return DOES_INTERSECT;
+    
+    
+    return PointInSolid(box.Center(),0);
+	 
+
+    /*
+    Point<2> c,pmin,pmax;
+    faces[0]->CalcProj(box.Center(),c);
+    double aux = box.Diam()/sqrt(8.);
+    pmin(0) = c(0)-aux; pmin(1) = c(1)-aux;
+    pmax(0) = c(0)+aux; pmax(1) = c(1)+aux;
+    
+
+    BoxSphere<2> box2d(pmin,pmax);
+
+    bool intersection = false;
+    bool uncertain = true;
+
+    for(int i=0; !(intersection && !uncertain) && i<faces.Size(); i++)
+      {
+	bool thisintersects;
+	bool thisuncertain;
+	thisintersects = faces[i]->BoxIntersectsFace(box2d,thisuncertain);
+	intersection = intersection || thisintersects;
+	if(thisintersects && !thisuncertain)
+	  uncertain = false;
+      }
+
+    if(intersection)
+      {
+	if(!uncertain)
+	  return DOES_INTERSECT;
+	else
+	  {
+	    Array < Point<3> > pext(2);
+	    Point<3> p;
+
+	    pext[0] = box.PMin();
+	    pext[1] = box.PMax();
+
+	    INSOLID_TYPE position;
+	    bool firsttime = true;
+
+	    for(int i=0; i<2; i++)
+	      for(int j=0; j<2; j++)
+		for(int k=0; k<2; k++)
+		  {
+		    p(0) = pext[i](0);
+		    p(1) = pext[j](1);
+		    p(2) = pext[k](2);
+		    INSOLID_TYPE ppos = PointInSolid(p,0);
+		    if(ppos == DOES_INTERSECT)
+		      return DOES_INTERSECT;
+		    
+		    if(firsttime)
+		      {
+			firsttime = false;
+			position = ppos;
+		      }
+		    if(position != ppos)
+		      return DOES_INTERSECT;	    
+		  }
+	    return position;
+
+	  }
+      }
+
+    return PointInSolid(box.Center(),0);
+    */ 
+  }
+
+  INSOLID_TYPE Revolution :: PointInSolid (const Point<3> & p,
+					   double eps) const
+  {
+    Point<2> p2d;
+    faces[0]->CalcProj(p,p2d);
+
+    int intersections_before(0), intersections_after(0);
+    double randomx = 7.42357;
+    double randomy = 1.814756;
+    randomx *= 1./sqrt(randomx*randomx+randomy*randomy);
+    randomy *= 1./sqrt(randomx*randomx+randomy*randomy);
+    
+
+    const double a = randomy;
+    const double b = -randomx;
+    const double c = -a*p2d(0)-b*p2d(1);
+
+    Array < Point<2> > points;
+
+    //(*testout) << "face intersections at: " << endl;
+    for(int i=0; i<faces.Size(); i++)
+      {
+	faces[i]->GetSpline().LineIntersections(a,b,c,points,eps);
+	
+	for(int j=0; j<points.Size(); j++)
+	  {
+	    double t = (points[j](0)-p2d(0))/randomx;
+
+	    //(*testout) << t << endl;
+	    if ( t < -eps )
+	      intersections_before++;
+	    else if ( t > eps )
+	      intersections_after++;
+	    else
+	      {
+		intersecting_face = i;
+		return DOES_INTERSECT;
+	      }
+	  }
+      }
+
+    if(intersections_before % 2 == 0)
+      return IS_OUTSIDE;
+    else
+      return IS_INSIDE;
+  }
+
+  INSOLID_TYPE Revolution :: VecInSolid (const Point<3> & p,
+					 const Vec<3> & v,
+					 double eps) const
+  {
+    INSOLID_TYPE pInSolid = PointInSolid(p,eps);
+
+    if(pInSolid != DOES_INTERSECT)
+      {
+	//(*testout) << "pInSolid" << endl;
+	return pInSolid;
+      }
+
+    Array<int> intersecting_faces;
+
+    for(int i=0; i<faces.Size(); i++)
+      if(faces[i]->PointInFace(p,eps) == DOES_INTERSECT)
+	intersecting_faces.Append(i);
+
+     Vec<3> hv;
+
+    if(intersecting_faces.Size() == 1)
+      {
+	faces[intersecting_faces[0]]->CalcGradient(p,hv);
+
+	double hv1;
+	hv1 = v * hv;
+	
+	if (hv1 <= -eps)
+	  return IS_INSIDE;
+	if (hv1 >= eps)
+	  return IS_OUTSIDE;
+	
+	return DOES_INTERSECT; 
+      }
+    else if(intersecting_faces.Size() == 2)
+      {
+	Point<2> p2d;
+	Vec<2> v2d;
+	faces[intersecting_faces[0]]->CalcProj(p,p2d,v,v2d);
+
+	if(Dist(faces[intersecting_faces[0]]->GetSpline().StartPI(),p2d) <
+	   Dist(faces[intersecting_faces[0]]->GetSpline().EndPI(),p2d))
+	  {
+	    int aux = intersecting_faces[0];
+	    intersecting_faces[0] = intersecting_faces[1];
+	    intersecting_faces[1] = aux;
+	  }
+	
+	const SplineSeg3<2> * splinesegment3 = 
+	  dynamic_cast<const SplineSeg3<2> *>(&faces[intersecting_faces[0]]->GetSpline());
+	const LineSeg<2> * linesegment = 
+	  dynamic_cast<const LineSeg<2> *>(&faces[intersecting_faces[0]]->GetSpline());
+		
+	Vec<2> t1,t2;
+
+	if(linesegment)
+	  t1 = linesegment->StartPI() - linesegment->EndPI();
+	else if(splinesegment3)
+	  t1 = splinesegment3->TangentPoint() - splinesegment3->EndPI();
+
+	linesegment = 
+	  dynamic_cast<const LineSeg<2> *>(&faces[intersecting_faces[1]]->GetSpline());
+	splinesegment3 = 
+	  dynamic_cast<const SplineSeg3<2> *>(&faces[intersecting_faces[1]]->GetSpline());
+	
+	if(linesegment)
+	  t2 = linesegment->EndPI() - linesegment->StartPI();
+	else if(splinesegment3)
+	  t2 = splinesegment3->TangentPoint() - splinesegment3->StartPI();
+
+	t1.Normalize();
+	t2.Normalize();
+
+	double d1 = v2d*t1;
+	double d2 = v2d*t2;
+
+	Vec<2> n;
+
+	if(d1 > d2)
+	  {
+	    n(0) = t1(1);
+	    n(1) = -t1(0);
+	  }
+	else
+	  {
+	    n(0) = -t2(1);
+	    n(1) = t2(0);
+	  }
+
+	double d = v2d*n;
+
+	if(d > eps)
+	  return IS_OUTSIDE;
+	else if (d < -eps)
+	  return IS_INSIDE;
+	else
+	  return DOES_INTERSECT;
+
+
+      }
+    else
+      {
+	cerr << "Jo gibt's denn des?" << endl;
+      }
+
+    return DOES_INTERSECT;    
+  }
+
+  INSOLID_TYPE Revolution :: VecInSolid2 (const Point<3> & p,
+					  const Vec<3> & v1,
+					  const Vec<3> & v2,
+					  double eps) const
+  {
+    INSOLID_TYPE ret1 = VecInSolid(p,v1,eps);
+    if(ret1 != DOES_INTERSECT)
+      return ret1;
+
+    return VecInSolid(p,v1+0.01*v2,eps);
+  }
+  
+  int Revolution :: GetNSurfaces() const
+  {
+    return faces.Size();
+  }
+
+  Surface & Revolution :: GetSurface (int i)
+  {
+    return *faces[i];
+  }
+
+  const Surface & Revolution :: GetSurface (int i) const
+  {
+    return *faces[i];
+  }
+
+
+  void Revolution :: Reduce (const BoxSphere<3> & box)
+  { 
+    //bool dummy;
+    for(int i=0; i<faces.Size(); i++)
+      surfaceactive[i] = (faces[i]->BoxIntersectsFace(box));
+    //surfaceactive[i] = (faces[i]->BoxIntersectsFace(box,dummy));
+  }
+
+  void Revolution :: UnReduce ()
+  {
+    for(int i=0; i<faces.Size(); i++)
+      surfaceactive[i] = true;
+  }
+}
diff --git a/contrib/Netgen/libsrc/csg/revolution.hpp b/contrib/Netgen/libsrc/csg/revolution.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f1c719febb49b4bda82d286b6b79ec801e4fa39d
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/revolution.hpp
@@ -0,0 +1,153 @@
+#ifndef _REVOLUTION_HPP
+#define _REVOLUTION_HPP
+
+namespace netgen
+{
+
+  class Revolution;
+
+  class RevolutionFace : public Surface
+  {
+  private:
+    bool isfirst, islast;
+    const SplineSeg<2> * spline;
+    bool deletable;
+
+    Point<3> p0;
+    Vec<3> v_axis;
+
+    int id;
+  
+    mutable Vector spline_coefficient;
+
+
+    Array < Vec<2>* > checklines_vec;
+    Array < Point<2>* > checklines_start;
+    Array < Vec<2>* > checklines_normal;
+  
+  private:
+    void Init (void);
+
+  public:
+    void CalcProj(const Point<3> & point3d, Point<2> & point2d) const;
+    void CalcProj(const Point<3> & point3d, Point<2> & point2d,
+		  const Vec<3> & vector3d, Vec<2> & vector2d) const;
+    void CalcProj0(const Vec<3> & point3d_minus_p0, Point<2> & point2d) const;
+
+  public:
+    RevolutionFace(const SplineSeg<2> & spline_in,
+		   const Point<3> & p,
+		   const Vec<3> & vec,
+		   bool first = false,
+		   bool last = false,
+		   const int id_in = 0);
+
+    RevolutionFace(const Array<double> & raw_data);
+
+    ~RevolutionFace();
+
+    virtual int IsIdentic (const Surface & s2, int & inv, double eps) const; 
+  
+    virtual double CalcFunctionValue (const Point<3> & point) const;
+    virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const;
+    virtual void CalcHesse (const Point<3> & point, Mat<3> & hesse) const;
+    virtual double HesseNorm () const;
+
+    virtual double MaxCurvature () const;
+    //virtual double MaxCurvatureLoc (const Point<3> & /* c */ , 
+    //				  double /* rad */) const;
+
+    virtual void Project (Point<3> & p) const;
+
+    virtual Point<3> GetSurfacePoint () const;
+    virtual void Print (ostream & str) const;
+  
+    virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					   const Box<3> & boundingbox, 
+					   double facets) const;
+
+    bool BoxIntersectsFace (const Box<3> & box) const;
+    /*
+      bool BoxIntersectsFace (const BoxSphere<2> & box, bool & uncertain) const;
+      bool BoxIntersectsFace (const BoxSphere<3> & box, bool & uncertain) const;
+    */
+
+    const SplineSeg<2> & GetSpline(void) const {return *spline;}
+
+    INSOLID_TYPE PointInFace (const Point<3> & p, const double eps) const;
+
+    void GetRawData(Array<double> & data) const;
+
+  };
+
+
+
+  /*
+
+  Primitive of revolution
+  
+  */
+
+
+  class Revolution : public Primitive
+  {
+  private:
+    Point<3> p0,p1;
+    Vec<3> v_axis;
+    const SplineGeometry<2> & splinecurve;
+    const int nsplines;
+
+    // 1 ... torus-like
+    // 2 ... sphere-like
+    int type;
+  
+
+    Array<RevolutionFace*> faces;
+
+    mutable int intersecting_face;
+
+  public:
+    Revolution(const Point<3> & p0_in,
+	       const Point<3> & p1_in,
+	       const SplineGeometry<2> & spline_in);
+
+    ~Revolution();
+  
+  
+    /*
+      Check, whether box intersects solid defined by surface.
+
+      return values:
+      0 .. box outside solid \\
+      1 .. box in solid \\
+      2 .. can't decide (allowed, iff box is close to solid)
+    */
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const;
+    virtual INSOLID_TYPE PointInSolid (const Point<3> & p,
+				       double eps) const;
+    virtual INSOLID_TYPE VecInSolid (const Point<3> & p,
+				     const Vec<3> & v,
+				     double eps) const;
+
+    // checks if lim s->0 lim t->0  p + t(v1 + s v2) in solid
+    virtual INSOLID_TYPE VecInSolid2 (const Point<3> & p,
+				      const Vec<3> & v1,
+				      const Vec<3> & v2,
+				      double eps) const;
+
+  
+    virtual int GetNSurfaces() const;
+    virtual Surface & GetSurface (int i = 0);
+    virtual const Surface & GetSurface (int i = 0) const;
+
+
+    virtual void Reduce (const BoxSphere<3> & box);
+    virtual void UnReduce ();
+  
+	     
+  };
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/singularref.cpp b/contrib/Netgen/libsrc/csg/singularref.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0ad13db9cf5e759847b3fdeb4b19e5e4c962d428
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/singularref.cpp
@@ -0,0 +1,217 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+
+  SingularEdge :: SingularEdge (double abeta, int adomnr, 
+                                const CSGeometry & ageom,
+                                const Solid * asol1, 
+                                const Solid * asol2, double sf,
+                                const double maxh_at_initialization)
+    : domnr(adomnr), geom(ageom)
+  {
+  beta = abeta;
+  maxhinit = maxh_at_initialization;
+
+  if (beta > 1) 
+    {
+      beta = 1;
+      cout << "Warning: beta set to 1" << endl;
+    }
+  if (beta <= 1e-3)
+    {
+      beta = 1e-3;
+      cout << "Warning: beta set to minimal value 0.001" << endl;
+    }
+
+  sol1 = asol1;
+  sol2 = asol2;
+  factor = sf; 
+}
+
+void SingularEdge :: FindPointsOnEdge (class Mesh & mesh)
+{
+  (*testout) << "find points on edge" << endl;
+  points.SetSize(0);
+  segms.SetSize(0);
+
+
+  Array<int> si1, si2;
+  sol1->GetSurfaceIndices (si1);
+  sol2->GetSurfaceIndices (si2);
+
+  for (int i = 0; i < si1.Size(); i++)
+    si1[i] = geom.GetSurfaceClassRepresentant(si1[i]);
+  for (int i = 0; i < si2.Size(); i++)
+    si2[i] = geom.GetSurfaceClassRepresentant(si2[i]);
+
+
+  for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+    {
+      INDEX_2 i2 (mesh[si][0], mesh[si][1]);
+      /*
+      
+      bool onedge = 1;
+      for (j = 1; j <= 2; j++)
+	{
+	  const Point<3> p = mesh[ PointIndex (i2.I(j)) ];
+	  if (sol1->IsIn (p, 1e-3) && sol2->IsIn(p, 1e-3) &&
+	      !sol1->IsStrictIn (p, 1e-3) && !sol2->IsStrictIn(p, 1e-3))
+	    {
+	      ;
+	    }
+	  else
+	    onedge = 0;
+	}
+      */
+
+      if (domnr != -1 && domnr != mesh[si].domin && domnr != mesh[si].domout)
+	continue;
+
+      /*
+      bool onedge = 1;
+      for (int j = 0; j < 2; j++)
+	{
+	  int surfi = (j == 0) ? mesh[si].surfnr1 : mesh[si].surfnr2;
+	  surfi = geom.GetSurfaceClassRepresentant(surfi);
+	  if (!si1.Contains(surfi) && !si2.Contains(surfi))
+	    onedge = 0;
+	}
+      */
+      int surfi1 = geom.GetSurfaceClassRepresentant(mesh[si].surfnr1);
+      int surfi2 = geom.GetSurfaceClassRepresentant(mesh[si].surfnr2);
+
+      if ( (si1.Contains(surfi1) && si2.Contains(surfi2)) ||
+           (si1.Contains(surfi2) && si2.Contains(surfi1)) )
+
+	// if (onedge)
+	{
+	  segms.Append (i2);
+	  //	  PrintMessage (5, "sing segment ", i2.I1(), " - ", i2.I2());
+	  points.Append (mesh[ PointIndex (i2.I1())]);
+	  points.Append (mesh[ PointIndex (i2.I2())]);
+	  mesh[si].singedge_left = factor;
+	  mesh[si].singedge_right = factor;
+	}	    
+    }
+  
+  /*
+  (*testout) << "Singular edge points:" << endl;
+  for (int i = 0; i < points.Size(); i++)
+    (*testout) << points[i] << endl;
+  */
+ 
+}
+
+void SingularEdge :: SetMeshSize (class Mesh & mesh, double globalh)
+{
+  double hloc = pow (globalh, 1/beta);
+  if(maxhinit > 0 && maxhinit < hloc)
+    {
+      hloc = maxhinit;
+      if(points.Size() > 1)
+	{
+	  for (int i = 0; i < points.Size()-1; i++)
+	    mesh.RestrictLocalHLine(points[i],points[i+1],hloc);
+	}
+      else
+	{
+	  for (int i = 0; i < points.Size(); i++)
+	    mesh.RestrictLocalH (points[i], hloc);
+	}
+    }
+  else
+    {
+      for (int i = 0; i < points.Size(); i++)
+	mesh.RestrictLocalH (points[i], hloc);
+    }
+}
+
+
+
+SingularPoint :: SingularPoint (double abeta, 
+				const Solid * asol1, 
+				const Solid * asol2,
+				const Solid * asol3, double sf)
+{
+  beta = abeta;
+  sol1 = asol1;
+  sol2 = asol2;
+  sol3 = asol3;
+  factor = sf; 
+}
+
+
+void SingularPoint :: FindPoints (class Mesh & mesh)
+{
+  points.SetSize(0);
+  Array<int> surfk, surf;
+
+
+  for (PointIndex pi = PointIndex::BASE; 
+       pi < mesh.GetNP()+PointIndex::BASE; pi++)
+    {
+      if (mesh[pi].Type() != FIXEDPOINT) continue;
+      const Point<3> p = mesh[pi];
+
+      (*testout) << "check singular point" << p << endl;
+
+      if (sol1->IsIn (p) && sol2->IsIn(p) && sol3->IsIn(p) &&
+	  !sol1->IsStrictIn (p) && !sol2->IsStrictIn(p) && !sol3->IsStrictIn(p))
+	{
+	  surf.SetSize (0);
+	  for (int k = 1; k <= 3; k++)
+	    {
+	      const Solid * solk(NULL);
+	      Solid *tansol;
+	      switch (k)
+		{
+		case 1:  solk = sol1; break;
+		case 2:  solk = sol2; break;
+		case 3:  solk = sol3; break;
+		}
+
+	      solk -> TangentialSolid (p, tansol, surfk, 1e-3);
+	      (*testout) << "Tansol = " << *tansol << endl;
+
+	      if (!tansol) continue;
+
+	      ReducePrimitiveIterator rpi(Box<3> (p-Vec<3> (1e-3,1e-3,1e-3),
+						  p+Vec<3> (1e-3,1e-3,1e-3)));
+	      UnReducePrimitiveIterator urpi;
+	      
+	      tansol -> IterateSolid (rpi);
+	      tansol->GetSurfaceIndices (surfk);
+	      tansol -> IterateSolid (urpi);
+
+	      (*testout) << "surfinds = " << surfk << endl;
+
+	      for (int i = 0; i < surfk.Size(); i++)
+		if (!surf.Contains (surfk[i]))
+		  surf.Append (surfk[i]);
+	      
+	      delete tansol;
+	    }
+
+	  if (surf.Size() < 3) continue;
+
+	  points.Append (p);
+	  PrintMessage (5, "Point (", p(0), ", ", p(1), ", ", p(2), ") is singular");
+	  mesh[pi].Singularity(factor);
+	}
+    }  
+}
+
+
+void SingularPoint :: SetMeshSize (class Mesh & mesh, double globalh)
+{
+  double hloc = pow (globalh, 1/beta);
+  for (int i = 1; i <= points.Size(); i++)
+    mesh.RestrictLocalH (points.Get(i), hloc);  
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/singularref.hpp b/contrib/Netgen/libsrc/csg/singularref.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c029fbef595c6138d709ce6bda7ce52f87ae7dc2
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/singularref.hpp
@@ -0,0 +1,84 @@
+#ifndef FILE_SINGULARREF
+#define FILE_SINGULARREF
+
+/**************************************************************************/
+/* File:   singularref.hh                                                 */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   25. Sep. 99                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+
+  /**
+     Control for local refinement
+  */
+
+
+
+  /**
+     Singular Face.
+     Causes a bounday layer mesh refinement.
+     All elements in subdomain domnr will get a boundary layer
+     on faces sharing the solid sol
+  */
+  class SingularFace 
+  {
+  public:
+    int domnr;
+    const Solid *sol;
+    double factor; 
+    // Array<Point<3> > points;
+    // Array<INDEX_2> segms;
+  public:
+    SingularFace (int adomnr, const Solid * asol, double sf)
+      : domnr(adomnr), sol(asol), factor(sf) { ; }
+    const Solid * GetSolid() const { return sol; }
+    int GetDomainNr () const { return domnr; }
+  };
+
+
+  ///
+  class SingularEdge 
+  {
+  public:
+    double beta;
+    int domnr;
+    const CSGeometry& geom;
+    const Solid *sol1, *sol2;
+    Array<Point<3> > points;
+    Array<INDEX_2> segms;
+    double factor; 
+
+    double maxhinit;
+  public:
+    SingularEdge (double abeta, int adomnr, 
+		  const CSGeometry & ageom,
+		  const Solid * asol1, const Solid * asol2, double sf,
+		  const double maxh_at_initialization = -1);
+    void FindPointsOnEdge (class Mesh & mesh);
+    void SetMeshSize (class Mesh & mesh, double globalh);
+  };
+
+
+  ///
+  class SingularPoint
+  {
+  public:
+    double beta;
+    const Solid *sol1, *sol2, *sol3;
+    Array<Point<3> > points;
+    double factor; 
+ 
+  public:
+    SingularPoint (double abeta, const Solid * asol1, const Solid * asol2,
+		   const Solid * asol3, double sf);
+    void FindPoints (class Mesh & mesh);
+    void SetMeshSize (class Mesh & mesh, double globalh);
+  };
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/solid.cpp b/contrib/Netgen/libsrc/csg/solid.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aba39d8a57dca68d8b75b171671881c416605834
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/solid.cpp
@@ -0,0 +1,1732 @@
+#include <mystdlib.h>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+  //using namespace netgen;
+
+
+  /*
+  SolidIterator :: SolidIterator ()
+  {
+    ;
+  }
+
+  SolidIterator :: ~SolidIterator ()
+  {
+    ;
+  }
+  */
+
+
+
+  // int Solid :: cntnames = 0;
+  
+  Solid :: Solid (Primitive * aprim)
+  {
+    op = TERM;
+    prim = aprim;
+    s1 = s2 = NULL;
+    maxh = 1e10;
+    name = NULL;   
+  }
+
+  Solid :: Solid (optyp aop, Solid * as1, Solid * as2)
+  {
+    op = aop;
+    s1 = as1;
+    s2 = as2;
+    prim = NULL;
+    name = NULL;
+    maxh = 1e10;
+  }
+
+  Solid :: ~Solid ()
+  {
+    // cout << "delete solid, op = " << int(op) << endl;
+    delete [] name;
+
+    switch (op)
+      {
+      case UNION:
+      case SECTION:
+	{
+	  if (s1->op != ROOT) delete s1;
+	  if (s2->op != ROOT) delete s2;
+	  break;
+	}
+      case SUB:
+	// case ROOT:
+	{
+	  if (s1->op != ROOT) delete s1;
+	  break;
+	}
+      case TERM:
+	{
+	  // cout << "has term" << endl;
+	  delete prim;
+	  break;
+	}
+      default:
+	break;
+      }
+  }
+
+  void Solid :: SetName (const char * aname)
+  {
+    delete [] name;
+    name = new char[strlen (aname)+1];
+    strcpy (name, aname);
+  }
+
+
+  Solid * Solid :: Copy (CSGeometry & geom) const
+  {
+    Solid * nsol(NULL);
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  Primitive * nprim = prim->Copy();
+	  geom.AddSurfaces (nprim);
+	  nsol = new Solid (nprim);
+	  break;
+	}
+
+      case SECTION:
+      case UNION:
+	{
+	  nsol = new Solid (op, s1->Copy(geom), s2->Copy(geom));
+	  break;
+	}
+
+      case SUB:
+	{
+	  nsol = new Solid (SUB, s1 -> Copy (geom));
+	  break;
+	}
+      
+      case ROOT:
+	{
+	  nsol = s1->Copy(geom);
+	  break;
+	}
+      }
+
+    return nsol;
+  }
+
+ 
+  void Solid :: Transform (Transformation<3> & trans)
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  prim -> Transform (trans);
+	  break;
+	}
+      case SECTION:
+      case UNION:
+	{
+	  s1 -> Transform (trans);
+	  s2 -> Transform (trans);
+	  break;
+	}
+
+      case SUB:
+      case ROOT:
+	{
+	  s1 -> Transform (trans);
+	  break;
+	}
+      }  
+  }
+
+
+
+  void Solid :: IterateSolid (SolidIterator & it,
+			      bool only_once)
+  {
+    if (only_once)
+      {
+	if (visited)
+	  return;
+
+	visited = 1; 
+      }
+
+    it.Do (this);
+
+    switch (op)
+      {
+      case SECTION:
+	{
+	  s1->IterateSolid (it, only_once);
+	  s2->IterateSolid (it, only_once);
+	  break;
+	}
+      case UNION:
+	{
+	  s1->IterateSolid (it, only_once);
+	  s2->IterateSolid (it, only_once);
+	  break;
+	}
+      case SUB:
+      case ROOT:
+	{
+	  s1->IterateSolid (it, only_once);
+	  break;
+	}
+      case TERM:
+      case TERM_REF:
+	break;   // do nothing
+      } 
+  }
+
+
+
+
+  bool Solid :: IsIn (const Point<3> & p, double eps) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  INSOLID_TYPE ist = prim->PointInSolid (p, eps);
+	  return ( (ist == IS_INSIDE) || (ist == DOES_INTERSECT) ) ? 1 : 0;
+	}
+      case SECTION:
+	return s1->IsIn (p, eps) && s2->IsIn (p, eps);
+      case UNION:
+	return s1->IsIn (p, eps) || s2->IsIn (p, eps);
+      case SUB:
+	return !s1->IsStrictIn (p, eps);
+      case ROOT:
+	return s1->IsIn (p, eps);
+      }
+    return 0;
+  }
+
+  bool Solid :: IsStrictIn (const Point<3> & p, double eps) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  INSOLID_TYPE ist = prim->PointInSolid (p, eps);
+	  return (ist == IS_INSIDE) ? 1 : 0;
+	}
+      case SECTION:
+	return s1->IsStrictIn(p, eps) && s2->IsStrictIn(p, eps);
+      case UNION:
+	return s1->IsStrictIn(p, eps) || s2->IsStrictIn(p, eps);
+      case SUB:
+	return !s1->IsIn (p, eps);
+      case ROOT:
+	return s1->IsStrictIn (p, eps);
+      }
+    return 0;
+  }
+
+  bool Solid :: VectorIn (const Point<3> & p, const Vec<3> & v, 
+			 double eps) const
+  {
+    Vec<3> hv;
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  INSOLID_TYPE ist = prim->VecInSolid (p, v, eps);
+	  return (ist == IS_INSIDE || ist == DOES_INTERSECT) ? 1 : 0;
+	}
+      case SECTION:
+	return s1 -> VectorIn (p, v, eps) && s2 -> VectorIn (p, v, eps);
+      case UNION:
+	return s1 -> VectorIn (p, v, eps) || s2 -> VectorIn (p, v, eps);
+      case SUB:
+	return !s1->VectorStrictIn(p, v, eps);
+      case ROOT:
+	return s1->VectorIn(p, v, eps);
+      }
+    return 0;
+  }
+
+  bool Solid :: VectorStrictIn (const Point<3> & p, const Vec<3> & v,
+			       double eps) const
+  {
+    Vec<3> hv;
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  INSOLID_TYPE ist = prim->VecInSolid (p, v, eps);
+	  return (ist == IS_INSIDE) ? true : false;
+	}
+      case SECTION:
+	return s1 -> VectorStrictIn (p, v, eps) && 
+	  s2 -> VectorStrictIn (p, v, eps);
+      case UNION:
+	return s1 -> VectorStrictIn (p, v, eps) || 
+	  s2 -> VectorStrictIn (p, v, eps);
+      case SUB:
+	return !s1->VectorIn(p, v, eps);
+      case ROOT:
+	return s1->VectorStrictIn(p, v, eps);
+      }
+    return 0;
+  }
+
+
+  bool Solid::VectorIn2 (const Point<3> & p, const Vec<3> & v1, 
+			const Vec<3> & v2, double eps) const
+  {
+    if (VectorStrictIn (p, v1, eps))
+      return 1;
+    if (!VectorIn (p, v1, eps))
+      return 0;
+  
+    bool res = VectorIn2Rec (p, v1, v2, eps);
+    return res;
+  }
+
+  bool Solid::VectorIn2Rec (const Point<3> & p, const Vec<3> & v1, 
+			   const Vec<3> & v2, double eps) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	return (prim->VecInSolid2 (p, v1, v2, eps) != IS_OUTSIDE); // Is this correct????
+      case SECTION:
+	return s1->VectorIn2Rec (p, v1, v2, eps) && 
+	  s2->VectorIn2Rec (p, v1, v2, eps);
+      case UNION:
+	return s1->VectorIn2Rec (p, v1, v2, eps) ||
+	  s2->VectorIn2Rec (p, v1, v2, eps);
+      case SUB:
+	return !s1->VectorIn2Rec (p, v1, v2, eps);
+      case ROOT:
+	return s1->VectorIn2Rec (p, v1, v2, eps);
+      }
+    return 0;  
+  }
+
+
+
+
+
+
+  void Solid :: Print (ostream & str) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  str << prim->GetSurfaceId(0);
+	  for (int i = 1; i < prim->GetNSurfaces(); i++)
+	    str << "," << prim->GetSurfaceId(i);
+	  break;
+	}
+      case SECTION:
+	{
+	  str << "(";
+	  s1 -> Print (str);
+	  str << " AND ";
+	  s2 -> Print (str);
+	  str << ")";
+	  break;
+	}
+      case UNION:
+	{
+	  str << "(";
+	  s1 -> Print (str);
+	  str << " OR ";
+	  s2 -> Print (str);
+	  str << ")";
+	  break;
+	}
+      case SUB:
+	{
+	  str << " NOT ";
+	  s1 -> Print (str);
+	  break;
+	}
+      case ROOT:
+	{
+	  str << " [" << name << "=";
+	  s1 -> Print (str);
+	  str << "] ";
+	  break;
+	}
+      }
+  }
+
+
+
+  void Solid :: GetSolidData (ostream & ost, int first) const
+  {
+    switch (op)
+      {
+      case SECTION:
+	{
+	  ost << "(";
+	  s1 -> GetSolidData (ost, 0);
+	  ost << " AND ";
+	  s2 -> GetSolidData (ost, 0);
+	  ost << ")";
+	  break;
+	}
+      case UNION:
+	{
+	  ost << "(";
+	  s1 -> GetSolidData (ost, 0);
+	  ost << " OR ";
+	  s2 -> GetSolidData (ost, 0);
+	  ost << ")";
+	  break;
+	}
+      case SUB:
+	{
+	  ost << "NOT ";
+	  s1 -> GetSolidData (ost, 0);
+	  break;
+	}
+      case TERM: case TERM_REF:
+	{
+	  if (name)
+	    ost << name;
+	  else
+	    ost << "(noname)";
+	  break;
+	}
+      case ROOT:
+	{
+	  if (first)
+	    s1 -> GetSolidData (ost, 0);
+	  else
+	    ost << name;
+	  break;
+	}
+      }
+  }
+
+
+
+  static Solid * CreateSolidExpr (istream & ist, const SYMBOLTABLE<Solid*> & solids);
+  static Solid * CreateSolidTerm (istream & ist, const SYMBOLTABLE<Solid*> & solids);
+  static Solid * CreateSolidPrim (istream & ist, const SYMBOLTABLE<Solid*> & solids);
+
+  static void ReadString (istream & ist, char * str)
+  {
+    //char * hstr = str;
+    char ch;
+
+    while (1)
+      {
+	ist.get(ch);
+	if (!ist.good()) break;
+
+	if (!isspace (ch))
+	  {
+	    ist.putback (ch);
+	    break;
+	  }
+      }
+
+    while (1)
+      {
+	ist.get(ch);
+	if (!ist.good()) break;
+	if (isalpha(ch) || isdigit(ch))
+	  {
+	    *str = ch;
+	    str++;
+	  }
+	else
+	  {
+	    ist.putback (ch);
+	    break;
+	  }
+      }
+    *str = 0;
+    //  cout << "Read string (" << hstr << ")" 
+    //       << "put back: " << ch << endl;
+  }
+
+
+  Solid * CreateSolidExpr (istream & ist, const SYMBOLTABLE<Solid*> & solids)
+  {
+    //  cout << "create expr" << endl;
+
+    Solid *s1, *s2;
+    char str[100];
+
+    s1 = CreateSolidTerm (ist, solids);
+    ReadString (ist, str);
+    if (strcmp (str, "OR") == 0)
+      {
+	//      cout << " OR ";
+	s2 = CreateSolidExpr (ist, solids);
+	return new Solid (Solid::UNION, s1, s2);
+      }
+
+    //  cout << "no OR found, put back string: " << str << endl;
+    for (int i = int(strlen(str))-1; i >= 0; i--)
+      ist.putback (str[i]);
+
+    return s1;
+  }
+
+  Solid * CreateSolidTerm (istream & ist, const SYMBOLTABLE<Solid*> & solids)
+  {
+    //  cout << "create term" << endl;
+
+    Solid *s1, *s2;
+    char str[100];
+
+    s1 = CreateSolidPrim (ist, solids);
+    ReadString (ist, str);
+    if (strcmp (str, "AND") == 0)
+      {
+	//      cout << " AND ";
+	s2 = CreateSolidTerm (ist, solids);
+	return new Solid (Solid::SECTION, s1, s2);
+      }
+
+
+    //  cout << "no AND found, put back string: " << str << endl;
+    for (int i = int(strlen(str))-1; i >= 0; i--)
+      ist.putback (str[i]);
+
+    return s1;
+  }
+
+  Solid * CreateSolidPrim (istream & ist, const SYMBOLTABLE<Solid*> & solids)
+  {
+    Solid * s1;
+    char ch;
+    char str[100];
+
+    ist >> ch;
+    if (ch == '(')
+      {
+	s1 = CreateSolidExpr (ist, solids);
+	ist >> ch;  // ')'
+	//      cout << "close back " << ch << endl;
+	return s1;
+      }
+    ist.putback (ch);
+  
+    ReadString (ist, str);
+    if (strcmp (str, "NOT") == 0)
+      {
+	//      cout << " NOT ";
+	s1 = CreateSolidPrim (ist, solids);
+	return new Solid (Solid::SUB, s1);
+      }
+
+    (*testout) << "get terminal " << str << endl;
+    s1 = solids.Get(str);
+    if (s1)
+      {
+	//      cout << "primitive: " << str << endl;
+	return s1;
+      }
+    cerr << "syntax error" << endl;
+
+    return NULL;
+  }
+
+
+  Solid * Solid :: CreateSolid (istream & ist, const SYMBOLTABLE<Solid*> & solids)
+  {
+    Solid * nsol =  CreateSolidExpr (ist, solids);
+    nsol = new Solid (ROOT, nsol);
+    (*testout) << "Print new sol: ";
+    nsol -> Print (*testout);
+    (*testout) << endl;
+    return nsol;
+  }
+
+
+
+  void Solid :: Boundaries (const Point<3> & p, Array<int> & bounds) const
+  {
+    int in, strin;
+    bounds.SetSize (0);
+    RecBoundaries (p, bounds, in, strin);
+  }
+
+  void Solid :: RecBoundaries (const Point<3> & p, Array<int> & bounds,
+			       int & in, int & strin) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  /*
+	    double val;
+	    val = surf->CalcFunctionValue (p);
+	    in = (val < 1e-6);
+	    strin = (val < -1e-6);
+	    if (in && !strin) bounds.Append (id);
+	  */
+	  if (prim->PointInSolid (p, 1e-6) == DOES_INTERSECT)
+	    bounds.Append (prim->GetSurfaceId (1));
+	  break;
+	}
+      case SECTION:
+	{
+	  int i, in1, in2, strin1, strin2;
+	  Array<int> bounds1, bounds2;
+
+	  s1 -> RecBoundaries (p, bounds1, in1, strin1);
+	  s2 -> RecBoundaries (p, bounds2, in2, strin2);
+
+	  if (in1 && in2)
+	    {
+	      for (i = 1; i <= bounds1.Size(); i++)
+		bounds.Append (bounds1.Get(i));
+	      for (i = 1; i <= bounds2.Size(); i++)
+		bounds.Append (bounds2.Get(i));
+	    }
+	  in = (in1 && in2);
+	  strin = (strin1 && strin2);
+	  break;
+	}
+      case UNION:
+	{
+	  int i, in1, in2, strin1, strin2;
+	  Array<int> bounds1, bounds2;
+
+	  s1 -> RecBoundaries (p, bounds1, in1, strin1);
+	  s2 -> RecBoundaries (p, bounds2, in2, strin2);
+
+	  if (!strin1 && !strin2)
+	    {
+	      for (i = 1; i <= bounds1.Size(); i++)
+		bounds.Append (bounds1.Get(i));
+	      for (i = 1; i <= bounds2.Size(); i++)
+		bounds.Append (bounds2.Get(i));
+	    }
+	  in = (in1 || in2);
+	  strin = (strin1 || strin2);
+	  break;
+	}
+      case SUB:
+	{
+	  int hin, hstrin;
+	  s1 -> RecBoundaries (p, bounds, hin, hstrin);
+	  in = !hstrin;
+	  strin = !hin;
+	  break;
+	}
+
+      case ROOT:
+	{
+	  s1 -> RecBoundaries (p, bounds, in, strin);
+	  break;
+	}
+      }
+  }
+
+
+  void Solid :: TangentialSolid (const Point<3> & p, Solid *& tansol, Array<int> & surfids, double eps) const
+  {
+    int in, strin;
+    RecTangentialSolid (p, tansol, surfids, in, strin, eps);
+    surfids.SetSize (0);
+    if (tansol)
+      tansol -> GetTangentialSurfaceIndices (p, surfids, eps);
+  }
+
+
+  void Solid :: RecTangentialSolid (const Point<3> & p, Solid *& tansol, Array<int> & surfids,
+				    int & in, int & strin, double eps) const
+  {
+    tansol = NULL;
+
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  INSOLID_TYPE ist = prim->PointInSolid(p, eps);
+
+	  in = (ist == IS_INSIDE || ist == DOES_INTERSECT);
+	  strin = (ist == IS_INSIDE);
+
+	  if (ist == DOES_INTERSECT)
+	    {
+	      tansol = new Solid (prim);
+	      tansol -> op = TERM_REF;
+	    }
+	  break;
+	}
+      case SECTION:
+	{
+	  int in1, in2, strin1, strin2;
+	  Solid * tansol1, * tansol2;
+
+	  s1 -> RecTangentialSolid (p, tansol1, surfids, in1, strin1, eps);
+	  s2 -> RecTangentialSolid (p, tansol2, surfids, in2, strin2, eps);
+
+	  if (in1 && in2)
+	    {
+	      if (tansol1 && tansol2)
+		tansol = new Solid (SECTION, tansol1, tansol2);
+	      else if (tansol1)
+		tansol = tansol1;
+	      else if (tansol2)
+		tansol = tansol2;
+	    }
+	  in = (in1 && in2);
+	  strin = (strin1 && strin2);
+	  break;
+	}
+      case UNION:
+	{
+	  int in1, in2, strin1, strin2;
+	  Solid * tansol1 = 0, * tansol2 = 0;
+
+	  s1 -> RecTangentialSolid (p, tansol1, surfids, in1, strin1, eps);
+	  s2 -> RecTangentialSolid (p, tansol2, surfids, in2, strin2, eps);
+
+	  if (!strin1 && !strin2)
+	    {
+	      if (tansol1 && tansol2)
+		tansol = new Solid (UNION, tansol1, tansol2);
+	      else if (tansol1)
+		tansol = tansol1;
+	      else if (tansol2)
+		tansol = tansol2;
+	    }
+	  else
+	    {
+	      delete tansol1;
+	      delete tansol2;
+	    }
+	  in = (in1 || in2);
+	  strin = (strin1 || strin2);
+	  break;
+	}
+      case SUB:
+	{
+	  int hin, hstrin;
+	  Solid * tansol1;
+
+	  s1 -> RecTangentialSolid (p, tansol1, surfids, hin, hstrin, eps);
+
+	  if (tansol1)
+	    tansol = new Solid (SUB, tansol1);
+	  in = !hstrin;
+	  strin = !hin;
+	  break;
+	}
+      case ROOT:
+	{
+	  s1 -> RecTangentialSolid (p, tansol, surfids, in, strin, eps);
+	  break;
+	}
+      }
+  }
+
+
+
+
+  void Solid :: TangentialSolid2 (const Point<3> & p, 
+				  const Vec<3> & t,
+				  Solid *& tansol, Array<int> & surfids, double eps) const
+  {
+    int in, strin;
+    surfids.SetSize (0);
+    RecTangentialSolid2 (p, t, tansol, surfids, in, strin, eps);
+    if (tansol)
+      tansol -> GetTangentialSurfaceIndices2 (p, t, surfids, eps);
+  }
+
+  void Solid :: RecTangentialSolid2 (const Point<3> & p, const Vec<3> & t,
+				     Solid *& tansol, Array<int> & surfids, 
+				     int & in, int & strin, double eps) const
+  {
+    tansol = NULL;
+
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  /*
+	    double val;
+	    val = surf->CalcFunctionValue (p);
+	    in = (val < 1e-6);
+	    strin = (val < -1e-6);
+	    if (in && !strin)
+	    tansol = new Solid (surf, id);
+	  */
+
+	  INSOLID_TYPE ist = prim->PointInSolid(p, eps);
+	  if (ist == DOES_INTERSECT)
+	    ist = prim->VecInSolid (p, t, eps);
+
+	  in = (ist == IS_INSIDE || ist == DOES_INTERSECT);
+	  strin = (ist == IS_INSIDE);
+
+	  if (ist == DOES_INTERSECT)
+	    {
+	      tansol = new Solid (prim);
+	      tansol -> op = TERM_REF;
+	    }
+	  break;
+	}
+      case SECTION:
+	{
+	  int in1, in2, strin1, strin2;
+	  Solid * tansol1, * tansol2;
+
+	  s1 -> RecTangentialSolid2 (p, t, tansol1, surfids, in1, strin1, eps);
+	  s2 -> RecTangentialSolid2 (p, t, tansol2, surfids, in2, strin2, eps);
+
+	  if (in1 && in2)
+	    {
+	      if (tansol1 && tansol2)
+		tansol = new Solid (SECTION, tansol1, tansol2);
+	      else if (tansol1)
+		tansol = tansol1;
+	      else if (tansol2)
+		tansol = tansol2;
+	    }
+	  in = (in1 && in2);
+	  strin = (strin1 && strin2);
+	  break;
+	}
+      case UNION:
+	{
+	  int in1, in2, strin1, strin2;
+	  Solid * tansol1, * tansol2;
+
+	  s1 -> RecTangentialSolid2 (p, t, tansol1, surfids, in1, strin1, eps);
+	  s2 -> RecTangentialSolid2 (p, t, tansol2, surfids, in2, strin2, eps);
+
+	  if (!strin1 && !strin2)
+	    {
+	      if (tansol1 && tansol2)
+		tansol = new Solid (UNION, tansol1, tansol2);
+	      else if (tansol1)
+		tansol = tansol1;
+	      else if (tansol2)
+		tansol = tansol2;
+	    }
+	  in = (in1 || in2);
+	  strin = (strin1 || strin2);
+	  break;
+	}
+      case SUB:
+	{
+	  int hin, hstrin;
+	  Solid * tansol1;
+
+	  s1 -> RecTangentialSolid2 (p, t, tansol1, surfids, hin, hstrin, eps);
+
+	  if (tansol1)
+	    tansol = new Solid (SUB, tansol1);
+	  in = !hstrin;
+	  strin = !hin;
+	  break;
+	}
+      case ROOT:
+	{
+	  s1 -> RecTangentialSolid2 (p, t, tansol, surfids, in, strin, eps);
+	  break;
+	}
+      }
+  }
+
+
+
+
+
+
+
+
+  void Solid :: TangentialSolid3 (const Point<3> & p, 
+				  const Vec<3> & t, const Vec<3> & t2,
+				  Solid *& tansol, Array<int> & surfids, 
+				  double eps) const
+  {
+    int in, strin;
+    surfids.SetSize (0);
+    RecTangentialSolid3 (p, t, t2, tansol, surfids, in, strin, eps);
+
+    if (tansol)
+      tansol -> GetTangentialSurfaceIndices3 (p, t, t2, surfids, eps);
+  }
+
+  void Solid :: RecTangentialSolid3 (const Point<3> & p, 
+				     const Vec<3> & t, const Vec<3> & t2,
+				     Solid *& tansol, Array<int> & surfids, 
+				     int & in, int & strin, double eps) const
+  {
+    tansol = NULL;
+
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  INSOLID_TYPE ist = prim->PointInSolid(p, eps);
+
+	  if (ist == DOES_INTERSECT)
+	    ist = prim->VecInSolid3 (p, t, t2, eps);
+	  in = (ist == IS_INSIDE || ist == DOES_INTERSECT);
+	  strin = (ist == IS_INSIDE);
+
+	  if (ist == DOES_INTERSECT)
+	    {
+	      tansol = new Solid (prim);
+	      tansol -> op = TERM_REF;
+	    }
+	  break;
+	}
+      case SECTION:
+	{
+	  int in1, in2, strin1, strin2;
+	  Solid * tansol1, * tansol2;
+
+	  s1 -> RecTangentialSolid3 (p, t, t2, tansol1, surfids, in1, strin1, eps);
+	  s2 -> RecTangentialSolid3 (p, t, t2, tansol2, surfids, in2, strin2, eps);
+
+	  if (in1 && in2)
+	    {
+	      if (tansol1 && tansol2)
+		tansol = new Solid (SECTION, tansol1, tansol2);
+	      else if (tansol1)
+		tansol = tansol1;
+	      else if (tansol2)
+		tansol = tansol2;
+	    }
+	  in = (in1 && in2);
+	  strin = (strin1 && strin2);
+	  break;
+	}
+      case UNION:
+	{
+	  int in1, in2, strin1, strin2;
+	  Solid * tansol1, * tansol2;
+
+	  s1 -> RecTangentialSolid3 (p, t, t2, tansol1, surfids, in1, strin1, eps);
+	  s2 -> RecTangentialSolid3 (p, t, t2, tansol2, surfids, in2, strin2, eps);
+
+	  if (!strin1 && !strin2)
+	    {
+	      if (tansol1 && tansol2)
+		tansol = new Solid (UNION, tansol1, tansol2);
+	      else if (tansol1)
+		tansol = tansol1;
+	      else if (tansol2)
+		tansol = tansol2;
+	    }
+	  in = (in1 || in2);
+	  strin = (strin1 || strin2);
+	  break;
+	}
+      case SUB:
+	{
+	  int hin, hstrin;
+	  Solid * tansol1;
+
+	  s1 -> RecTangentialSolid3 (p, t, t2, tansol1, surfids, hin, hstrin, eps);
+
+	  if (tansol1)
+	    tansol = new Solid (SUB, tansol1);
+	  in = !hstrin;
+	  strin = !hin;
+	  break;
+	}
+      case ROOT:
+	{
+	  s1 -> RecTangentialSolid3 (p, t, t2, tansol, surfids, in, strin, eps);
+	  break;
+	}
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+
+  void Solid :: TangentialEdgeSolid (const Point<3> & p, 
+				     const Vec<3> & t, const Vec<3> & t2, const Vec<3> & m, 
+				     Solid *& tansol, Array<int> & surfids, 
+				     double eps) const
+  {
+    int in, strin;
+    surfids.SetSize (0);
+
+    // *testout << "tangentialedgesolid,sol = " << (*this) << endl;
+    RecTangentialEdgeSolid (p, t, t2, m, tansol, surfids, in, strin, eps);
+
+    if (tansol)
+      tansol -> RecGetTangentialEdgeSurfaceIndices (p, t, t2, m, surfids, eps);
+  }
+
+  void Solid :: RecTangentialEdgeSolid (const Point<3> & p, 
+					const Vec<3> & t, const Vec<3> & t2, const Vec<3> & m,
+					Solid *& tansol, Array<int> & surfids, 
+					int & in, int & strin, double eps) const
+  {
+    tansol = NULL;
+
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  INSOLID_TYPE ist = prim->PointInSolid(p, eps);
+
+	  /*
+	  (*testout) << "tangedgesolid, p = " << p << ", t = " << t 
+		     << " for prim " << typeid (*prim).name() 
+		     << " with surf " << prim->GetSurface() << endl;
+	  (*testout) << "ist = " << ist << endl;
+	  */
+
+	  if (ist == DOES_INTERSECT)
+	    ist = prim->VecInSolid4 (p, t, t2, m, eps);
+
+	  // (*testout) << "ist2 = " << ist << endl;
+
+	  in = (ist == IS_INSIDE || ist == DOES_INTERSECT);
+	  strin = (ist == IS_INSIDE);
+
+	  if (ist == DOES_INTERSECT)
+	    {
+	      tansol = new Solid (prim);
+	      tansol -> op = TERM_REF;
+	    }
+	  break;
+	}
+      case SECTION:
+	{
+	  int in1, in2, strin1, strin2;
+	  Solid * tansol1, * tansol2;
+
+	  s1 -> RecTangentialEdgeSolid (p, t, t2, m, tansol1, surfids, in1, strin1, eps);
+	  s2 -> RecTangentialEdgeSolid (p, t, t2, m, tansol2, surfids, in2, strin2, eps);
+
+	  if (in1 && in2)
+	    {
+	      if (tansol1 && tansol2)
+		tansol = new Solid (SECTION, tansol1, tansol2);
+	      else if (tansol1)
+		tansol = tansol1;
+	      else if (tansol2)
+		tansol = tansol2;
+	    }
+	  in = (in1 && in2);
+	  strin = (strin1 && strin2);
+	  break;
+	}
+      case UNION:
+	{
+	  int in1, in2, strin1, strin2;
+	  Solid * tansol1, * tansol2;
+
+	  s1 -> RecTangentialEdgeSolid (p, t, t2, m, tansol1, surfids, in1, strin1, eps);
+	  s2 -> RecTangentialEdgeSolid (p, t, t2, m, tansol2, surfids, in2, strin2, eps);
+
+	  if (!strin1 && !strin2)
+	    {
+	      if (tansol1 && tansol2)
+		tansol = new Solid (UNION, tansol1, tansol2);
+	      else if (tansol1)
+		tansol = tansol1;
+	      else if (tansol2)
+		tansol = tansol2;
+	    }
+	  in = (in1 || in2);
+	  strin = (strin1 || strin2);
+	  break;
+	}
+      case SUB:
+	{
+	  int hin, hstrin;
+	  Solid * tansol1;
+
+	  s1 -> RecTangentialEdgeSolid (p, t, t2, m, tansol1, surfids, hin, hstrin, eps);
+
+	  if (tansol1)
+	    tansol = new Solid (SUB, tansol1);
+	  in = !hstrin;
+	  strin = !hin;
+	  break;
+	}
+      case ROOT:
+	{
+	  s1 -> RecTangentialEdgeSolid (p, t, t2, m, tansol, surfids, in, strin, eps);
+	  break;
+	}
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  int Solid :: Edge (const Point<3> & p, const Vec<3> & v, double eps) const
+  {
+    int in, strin, faces;
+    RecEdge (p, v, in, strin, faces, eps);
+    return faces >= 2;
+  }
+
+  int Solid :: OnFace (const Point<3> & p, const Vec<3> & v, double eps) const
+  {
+    int in, strin, faces;
+    RecEdge (p, v, in, strin, faces, eps);
+    return faces >= 1;
+  }
+
+
+  void Solid :: RecEdge (const Point<3> & p, const Vec<3> & v,
+			 int & in, int & strin, int & faces, double eps) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  INSOLID_TYPE ist = prim->VecInSolid (p, v, eps);
+	  in = (ist == IS_INSIDE || ist == DOES_INTERSECT);
+	  strin = (ist == IS_INSIDE);
+	  /*
+	    in = VectorIn (p, v);
+	    strin = VectorStrictIn (p, v);
+	  */
+	  faces = 0;
+
+	  if (in && ! strin)
+	    {
+	      //	    faces = 1;
+	      int i; 
+	      Vec<3> grad;
+	      for (i = 0; i < prim->GetNSurfaces(); i++)
+		{
+		  double val = prim->GetSurface(i).CalcFunctionValue(p);
+		  prim->GetSurface(i).CalcGradient (p, grad);
+		  if (fabs (val) < eps && fabs (v * grad) < 1e-6)
+		    faces++;
+		}
+	    }
+	  //	else
+	  //	  faces = 0;
+	  break;
+	}
+      case SECTION:
+	{
+	  int in1, in2, strin1, strin2, faces1, faces2;
+
+	  s1 -> RecEdge (p, v, in1, strin1, faces1, eps);
+	  s2 -> RecEdge (p, v, in2, strin2, faces2, eps);
+
+	  faces = 0;
+	  if (in1 && in2)
+	    faces = faces1 + faces2;
+	  in = in1 && in2;
+	  strin = strin1 && strin2;
+	  break;
+	}
+      case UNION:
+	{
+	  int in1, in2, strin1, strin2, faces1, faces2;
+
+	  s1 -> RecEdge (p, v, in1, strin1, faces1, eps);
+	  s2 -> RecEdge (p, v, in2, strin2, faces2, eps);
+
+	  faces = 0;
+	  if (!strin1 && !strin2)
+	    faces = faces1 + faces2;
+	  in = in1 || in2;
+	  strin = strin1 || strin2;
+	  break;
+	}
+      case SUB:
+	{
+	  int in1, strin1;
+	  s1 -> RecEdge (p, v, in1, strin1, faces, eps);
+	  in = !strin1;
+	  strin = !in1;
+	  break;
+	}
+      case ROOT:
+	{
+	  s1 -> RecEdge (p, v, in, strin, faces, eps);
+	  break;
+	}
+      }
+  }
+
+
+  void Solid :: CalcSurfaceInverse ()
+  {
+    CalcSurfaceInverseRec (0);
+  }
+
+  void Solid :: CalcSurfaceInverseRec (int inv)
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  bool priminv;
+	  for (int i = 0; i < prim->GetNSurfaces(); i++)
+	    {
+	      priminv = (prim->SurfaceInverted(i) != 0);
+	      if (inv) priminv = !priminv;
+	      prim->GetSurface(i).SetInverse (priminv);
+	    }
+	  break;
+	}
+      case UNION:
+      case SECTION:
+	{
+	  s1 -> CalcSurfaceInverseRec (inv);
+	  s2 -> CalcSurfaceInverseRec (inv);
+	  break;
+	}
+      case SUB:
+	{
+	  s1 -> CalcSurfaceInverseRec (1 - inv);
+	  break;
+	}
+      case ROOT:
+	{
+	  s1 -> CalcSurfaceInverseRec (inv);
+	  break;
+	}
+      }
+  }
+
+
+  Solid * Solid :: GetReducedSolid (const BoxSphere<3> & box) const
+  {
+    INSOLID_TYPE in;
+    return RecGetReducedSolid (box, in);
+  }
+
+  Solid * Solid :: RecGetReducedSolid (const BoxSphere<3> & box, INSOLID_TYPE & in) const
+  {
+    Solid * redsol = NULL;
+
+    switch (op)
+      {
+      case TERM: 
+      case TERM_REF:
+	{
+	  in = prim -> BoxInSolid (box);
+	  if (in == DOES_INTERSECT)
+	    {
+	      redsol = new Solid (prim);
+	      redsol -> op = TERM_REF;
+	    }
+	  break;
+	}
+      case SECTION:
+	{
+	  INSOLID_TYPE in1, in2;
+	  Solid * redsol1, * redsol2;
+
+	  redsol1 = s1 -> RecGetReducedSolid (box, in1);
+	  redsol2 = s2 -> RecGetReducedSolid (box, in2);
+
+	  if (in1 == IS_OUTSIDE || in2 == IS_OUTSIDE)
+	    {
+	      if (in1 == DOES_INTERSECT) delete redsol1;
+	      if (in2 == DOES_INTERSECT) delete redsol2;
+	      in = IS_OUTSIDE;
+	    }
+	  else
+	    {
+	      if (in1 == DOES_INTERSECT || in2 == DOES_INTERSECT) 
+		in = DOES_INTERSECT;
+	      else 
+		in = IS_INSIDE;
+
+	      if (in1 == DOES_INTERSECT && in2 == DOES_INTERSECT)
+		redsol = new Solid (SECTION, redsol1, redsol2);
+	      else if (in1 == DOES_INTERSECT)
+		redsol = redsol1;
+	      else if (in2 == DOES_INTERSECT)
+		redsol = redsol2;
+	    }
+	  break;
+	}
+
+      case UNION:
+	{
+	  INSOLID_TYPE in1, in2;
+	  Solid * redsol1, * redsol2;
+
+	  redsol1 = s1 -> RecGetReducedSolid (box, in1);
+	  redsol2 = s2 -> RecGetReducedSolid (box, in2);
+
+	  if (in1 == IS_INSIDE || in2 == IS_INSIDE)
+	    {
+	      if (in1 == DOES_INTERSECT) delete redsol1;
+	      if (in2 == DOES_INTERSECT) delete redsol2;
+	      in = IS_INSIDE;
+	    }
+	  else
+	    {
+	      if (in1 == DOES_INTERSECT || in2 == DOES_INTERSECT) in = DOES_INTERSECT;
+	      else in = IS_OUTSIDE;
+
+	      if (in1 == DOES_INTERSECT && in2 == DOES_INTERSECT)
+		redsol = new Solid (UNION, redsol1, redsol2);
+	      else if (in1 == DOES_INTERSECT)
+		redsol = redsol1;
+	      else if (in2 == DOES_INTERSECT)
+		redsol = redsol2;
+	    }
+	  break;
+	}
+
+      case SUB:
+	{
+	  INSOLID_TYPE in1;
+	  Solid * redsol1 = s1 -> RecGetReducedSolid (box, in1);
+
+	  switch (in1)
+	    {
+	    case IS_OUTSIDE: in = IS_INSIDE; break;
+	    case IS_INSIDE:  in = IS_OUTSIDE; break;
+	    case DOES_INTERSECT: in = DOES_INTERSECT; break;
+	    }
+
+	  if (redsol1)
+	    redsol = new Solid (SUB, redsol1);
+	  break;
+	}
+      
+      case ROOT:
+	{
+	  INSOLID_TYPE in1;
+	  redsol = s1 -> RecGetReducedSolid (box, in1);
+	  in = in1;
+	  break;
+	}
+      }
+
+    /*
+    if (redsol)
+      (*testout) << "getrecsolid, redsol = " << endl << (*redsol) << endl;
+    else
+      (*testout) << "redsol is null" << endl;
+    */
+
+    return redsol;
+  }
+
+
+  int Solid :: NumPrimitives () const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  return 1;
+	}
+      case UNION:
+      case SECTION:
+	{
+	  return s1->NumPrimitives () + s2 -> NumPrimitives();
+	}
+      case SUB:
+      case ROOT:
+	{
+	  return s1->NumPrimitives ();
+	}
+      }
+    return 0;
+  }
+
+  void Solid :: GetSurfaceIndices (Array<int> & surfind) const
+  {
+    surfind.SetSize (0);
+    RecGetSurfaceIndices (surfind);
+  }
+
+  void Solid :: RecGetSurfaceIndices (Array<int> & surfind) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  /*
+	    int i;
+	    for (i = 1; i <= surfind.Size(); i++)
+	    if (surfind.Get(i) == prim->GetSurfaceId())
+	    return;
+	    surfind.Append (prim->GetSurfaceId());
+	    break;
+	  */
+	  for (int j = 0; j < prim->GetNSurfaces(); j++)
+	    if (prim->SurfaceActive (j))
+	      {
+		bool found = 0;
+		int siprim = prim->GetSurfaceId(j);
+
+		for (int i = 0; i < surfind.Size(); i++)
+		  if (surfind[i] == siprim)
+		    {
+		      found = 1;
+		      break;
+		    }
+		if (!found) surfind.Append (siprim);
+	      }
+	  break;
+	}
+      case UNION:
+      case SECTION:
+	{
+	  s1 -> RecGetSurfaceIndices (surfind);
+	  s2 -> RecGetSurfaceIndices (surfind);
+	  break;
+	}
+      case SUB:
+      case ROOT:
+	{
+	  s1 -> RecGetSurfaceIndices (surfind);
+	  break;
+	}
+      }
+  }
+
+
+
+  void Solid :: GetTangentialSurfaceIndices (const Point<3> & p, Array<int> & surfind, double eps) const
+  {
+    surfind.SetSize (0);
+    RecGetTangentialSurfaceIndices (p, surfind, eps);
+  }
+
+  void Solid :: RecGetTangentialSurfaceIndices (const Point<3> & p, Array<int> & surfind, double eps) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  /*
+	  for (int j = 0; j < prim->GetNSurfaces(); j++)
+	    if (fabs (prim->GetSurface(j).CalcFunctionValue (p)) < eps)
+	      if (!surfind.Contains (prim->GetSurfaceId(j)))
+		surfind.Append (prim->GetSurfaceId(j));
+	  */
+	  prim->GetTangentialSurfaceIndices (p, surfind, eps);
+	  break;
+	}
+      case UNION:
+      case SECTION:
+	{
+	  s1 -> RecGetTangentialSurfaceIndices (p, surfind, eps);
+	  s2 -> RecGetTangentialSurfaceIndices (p, surfind, eps);
+	  break;
+	}
+      case SUB:
+      case ROOT:
+	{
+	  s1 -> RecGetTangentialSurfaceIndices (p, surfind, eps);
+	  break;
+	}
+      }
+  }
+
+
+
+
+
+
+  void Solid :: GetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v,
+					     Array<int> & surfind, double eps) const
+  {
+    surfind.SetSize (0);
+    RecGetTangentialSurfaceIndices2 (p, v, surfind, eps);
+  }
+
+  void Solid :: RecGetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v,
+						 Array<int> & surfind, double eps) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  for (int j = 0; j < prim->GetNSurfaces(); j++)
+	    {
+	      if (fabs (prim->GetSurface(j).CalcFunctionValue (p)) < eps)
+		{
+		  Vec<3> grad;
+		  prim->GetSurface(j).CalcGradient (p, grad);
+		  if (sqr (grad * v) < 1e-6 * v.Length2() * grad.Length2())
+		    {
+		      if (!surfind.Contains (prim->GetSurfaceId(j)))
+			surfind.Append (prim->GetSurfaceId(j));
+		    }
+		}
+	    }
+	  break;
+	}
+      case UNION:
+      case SECTION:
+	{
+	  s1 -> RecGetTangentialSurfaceIndices2 (p, v, surfind, eps);
+	  s2 -> RecGetTangentialSurfaceIndices2 (p, v, surfind, eps);
+	  break;
+	}
+      case SUB:
+      case ROOT:
+	{
+	  s1 -> RecGetTangentialSurfaceIndices2 (p, v, surfind, eps);
+	  break;
+	}
+      }
+  }
+
+
+
+
+
+
+
+
+  void Solid :: GetTangentialSurfaceIndices3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, 
+					     Array<int> & surfind, double eps) const
+  {
+    surfind.SetSize (0);
+    RecGetTangentialSurfaceIndices3 (p, v, v2, surfind, eps);
+  }
+
+  void Solid :: RecGetTangentialSurfaceIndices3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, 
+						 Array<int> & surfind, double eps) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  for (int j = 0; j < prim->GetNSurfaces(); j++)
+	    {
+	      if (fabs (prim->GetSurface(j).CalcFunctionValue (p)) < eps)
+		{
+		  Vec<3> grad;
+		  prim->GetSurface(j).CalcGradient (p, grad);
+		  if (sqr (grad * v) < 1e-6 * v.Length2() * grad.Length2())
+		    {
+		      Mat<3> hesse;
+		      prim->GetSurface(j).CalcHesse (p, hesse);
+		      double hv2 = v2 * grad + v * (hesse * v);
+		      
+		      if (fabs (hv2) < 1e-6) 
+			{
+			  if (!surfind.Contains (prim->GetSurfaceId(j)))
+			    surfind.Append (prim->GetSurfaceId(j));
+			}
+		      /*
+		      else
+			{
+			  *testout << "QUAD NOT OK" << endl;
+			  *testout << "v = " << v << ", v2 = " << v2 << endl;
+			  *testout << "v * grad = " << v*grad << endl;
+			  *testout << "v2 * grad = " << v2*grad << endl;
+			  *testout << "v H v = " << v*(hesse*v) << endl;
+			  *testout << "grad = " << grad << endl;
+			  *testout << "hesse = " << hesse << endl;
+			  *testout << "hv2 = " << v2 * grad + v * (hesse * v) << endl;
+			}
+		      */
+		    }
+		}
+	    }
+	  break;
+	}
+      case UNION:
+      case SECTION:
+	{
+	  s1 -> RecGetTangentialSurfaceIndices3 (p, v, v2, surfind, eps);
+	  s2 -> RecGetTangentialSurfaceIndices3 (p, v, v2, surfind, eps);
+	  break;
+	}
+      case SUB:
+      case ROOT:
+	{
+	  s1 -> RecGetTangentialSurfaceIndices3 (p, v, v2, surfind, eps);
+	  break;
+	}
+      }
+  }
+
+
+
+
+
+  void Solid :: RecGetTangentialEdgeSurfaceIndices (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, const Vec<3> & m,
+						    Array<int> & surfind, double eps) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  // *testout << "check vecinsolid4, p = " << p << ", v = " << v << "; m = " << m << endl;
+	  if (prim->VecInSolid4 (p, v, v2, m, eps) == DOES_INTERSECT)
+	    {
+	      prim->GetTangentialVecSurfaceIndices2 (p, v, m, surfind, eps);
+	      /*
+	      for (int j = 0; j < prim->GetNSurfaces(); j++)
+		{
+		  if (fabs (prim->GetSurface(j).CalcFunctionValue (p)) < eps)
+		    {
+		      Vec<3> grad;
+		      prim->GetSurface(j).CalcGradient (p, grad);
+		      *testout << "grad = " << grad << endl;
+		      if (sqr (grad * v) < 1e-6 * v.Length2() * grad.Length2()  && 
+			  sqr (grad * m) < 1e-6 * m.Length2() * grad.Length2() )   // new, 18032006 JS
+			  
+			{
+			  *testout << "add surf " << prim->GetSurfaceId(j) << endl;
+			  if (!surfind.Contains (prim->GetSurfaceId(j)))
+			    surfind.Append (prim->GetSurfaceId(j));
+			}
+		    }
+		}
+	      */
+	    }
+	  break;
+	}
+      case UNION:
+      case SECTION:
+	{
+	  s1 -> RecGetTangentialEdgeSurfaceIndices (p, v, v2, m, surfind, eps);
+	  s2 -> RecGetTangentialEdgeSurfaceIndices (p, v, v2, m, surfind, eps);
+	  break;
+	}
+      case SUB:
+      case ROOT:
+	{
+	  s1 -> RecGetTangentialEdgeSurfaceIndices (p, v, v2, m, surfind, eps);
+	  break;
+	}
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+  void Solid :: GetSurfaceIndices (IndexSet & iset) const
+  {
+    iset.Clear();
+    RecGetSurfaceIndices (iset);
+  }
+
+  void Solid :: RecGetSurfaceIndices (IndexSet & iset) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  /*
+	    int i;
+	    for (i = 1; i <= surfind.Size(); i++)
+	    if (surfind.Get(i) == prim->GetSurfaceId())
+	    return;
+	    surfind.Append (prim->GetSurfaceId());
+	    break;
+	  */
+	  for (int j = 0; j < prim->GetNSurfaces(); j++)
+	    if (prim->SurfaceActive (j))
+	      {
+		int siprim = prim->GetSurfaceId(j);
+		iset.Add (siprim);
+	      }
+	  break;
+	}
+      case UNION:
+      case SECTION:
+	{
+	  s1 -> RecGetSurfaceIndices (iset);
+	  s2 -> RecGetSurfaceIndices (iset);
+	  break;
+	}
+      case SUB:
+      case ROOT:
+	{
+	  s1 -> RecGetSurfaceIndices (iset);
+	  break;
+	}
+      }
+  }
+
+
+  void Solid :: CalcOnePrimitiveSpecialPoints (const Box<3> & box, Array<Point<3> > & pts) const
+  {
+    double eps = 1e-8 * box.Diam ();
+
+    pts.SetSize (0);
+    this -> RecCalcOnePrimitiveSpecialPoints (pts);
+    for (int i = pts.Size()-1; i >= 0; i--)
+      {
+	if (!IsIn (pts[i],eps) || IsStrictIn (pts[i],eps))
+	  pts.Delete (i);
+      }
+  }
+
+  void Solid :: RecCalcOnePrimitiveSpecialPoints (Array<Point<3> > & pts) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  prim -> CalcSpecialPoints (pts);
+	  break;
+	}
+      case UNION:
+      case SECTION:
+	{
+	  s1 -> RecCalcOnePrimitiveSpecialPoints (pts);
+	  s2 -> RecCalcOnePrimitiveSpecialPoints (pts);
+	  break;
+	}
+      case SUB:
+      case ROOT:
+	{
+	  s1 -> RecCalcOnePrimitiveSpecialPoints (pts);
+	  break;
+	}
+      } 
+  }
+
+
+
+
+  BlockAllocator Solid :: ball(sizeof (Solid));
+}
diff --git a/contrib/Netgen/libsrc/csg/solid.hpp b/contrib/Netgen/libsrc/csg/solid.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ebd7c88c984e939fb20b100f9bdd86fed05fed0b
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/solid.hpp
@@ -0,0 +1,246 @@
+#ifndef FILE_SOLID
+#define FILE_SOLID
+
+/**************************************************************************/
+/* File:   solid.hh                                                       */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   1. Dez. 95                                                     */
+/**************************************************************************/
+
+namespace netgen 
+{
+
+  
+  /*
+    
+  Constructive Solid Model (csg)
+  
+  */
+  
+
+
+
+  class Solid;
+  
+  class SolidIterator
+  {
+  public:
+    SolidIterator () { ; }
+    virtual ~SolidIterator () { ; }
+    virtual void Do (Solid * sol) = 0;
+  };
+
+
+
+  class Solid
+  {
+  public:
+  
+    typedef enum optyp1 { TERM, TERM_REF, SECTION, UNION, SUB, ROOT /*, DUMMY */ } optyp;
+  
+  private:
+    char * name;
+    Primitive * prim;
+    Solid * s1, * s2;
+  
+    optyp op;
+    bool visited;
+    double maxh;
+
+    // static int cntnames;
+
+  public:
+    Solid (Primitive * aprim);
+    Solid (optyp aop, Solid * as1, Solid * as2 = NULL);
+    ~Solid ();
+
+    const char * Name () const { return name; }
+    void SetName (const char * aname);
+
+    Solid * Copy (class CSGeometry & geom) const;
+    void Transform (Transformation<3> & trans);
+
+  
+    void IterateSolid (SolidIterator & it, bool only_once = 0);
+
+  
+    void Boundaries (const Point<3> & p, Array<int> & bounds) const;
+    int NumPrimitives () const;
+    void GetSurfaceIndices (Array<int> & surfind) const;
+    void GetSurfaceIndices (IndexSet & iset) const;
+
+    void GetTangentialSurfaceIndices (const Point<3> & p, Array<int> & surfids, double eps) const;
+    void GetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v, Array<int> & surfids, double eps) const;
+    void GetTangentialSurfaceIndices3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, Array<int> & surfids, double eps) const;
+
+
+    Primitive * GetPrimitive ()
+    { return (op == TERM || op == TERM_REF) ? prim : NULL; }
+    const Primitive * GetPrimitive () const
+    { return (op == TERM || op == TERM_REF) ? prim : NULL; }
+
+    Solid * S1() { return s1; }
+    Solid * S2() { return s2; }
+
+    // geometric tests
+
+    bool IsIn (const Point<3> & p, double eps = 1e-6) const;
+    bool IsStrictIn (const Point<3> & p, double eps = 1e-6) const;
+    bool VectorIn (const Point<3> & p, const Vec<3> & v, double eps = 1e-6) const;
+    bool VectorStrictIn (const Point<3> & p, const Vec<3> & v, double eps = 1e-6) const;
+  
+    bool VectorIn2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2,
+		    double eps) const;
+    bool VectorIn2Rec (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2,
+		       double eps) const;
+
+
+    /// compute localization in point p
+    void TangentialSolid (const Point<3> & p, Solid *& tansol, Array<int> & surfids, double eps) const;
+
+    /// compute localization in point p tangential to vector t
+    void TangentialSolid2 (const Point<3> & p, const Vec<3> & t,
+			   Solid *& tansol, Array<int> & surfids, double eps) const;
+
+    /** compute localization in point p, with second order approximation to edge
+	p + s t + s*s/2 t2 **/
+    void TangentialSolid3 (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, 
+			   Solid *& tansol, Array<int> & surfids, double eps) const;
+
+
+
+    /** tangential solid, which follows the edge
+	p + s t + s*s/2 t2
+	with second order, and the neighbouring face
+	p + s t + s*s/2 t2 + r m
+	with first order
+    **/
+    void TangentialEdgeSolid (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, 
+			      const Vec<3> & m, 
+			      Solid *& tansol, Array<int> & surfids, double eps) const;
+
+
+    void CalcOnePrimitiveSpecialPoints (const Box<3> & box, Array<Point<3> > & pts) const;
+
+    ///
+    int Edge (const Point<3> & p, const Vec<3> & v, double eps) const;
+    ///
+    int OnFace (const Point<3> & p, const Vec<3> & v, double eps) const;
+    ///
+    void Print (ostream & str) const;
+    ///
+    void CalcSurfaceInverse ();
+    ///
+    Solid * GetReducedSolid (const BoxSphere<3> & box) const;
+  
+
+    void SetMaxH (double amaxh)
+    { maxh = amaxh; }
+    double GetMaxH () const
+    { return maxh; }
+
+    void GetSolidData (ostream & ost, int first = 1) const;
+    static Solid * CreateSolid (istream & ist, const SYMBOLTABLE<Solid*> & solids);
+
+
+    static BlockAllocator ball;
+    void * operator new(size_t /* s */) 
+    {
+      return ball.Alloc();
+    }
+
+    void operator delete (void * p)
+    {
+      ball.Free (p);
+    }
+
+
+  protected:
+    ///
+
+    void RecBoundaries (const Point<3> & p, Array<int> & bounds, 
+			int & in, int & strin) const;
+    ///
+    void RecTangentialSolid (const Point<3> & p, Solid *& tansol, Array<int> & surfids, 
+			     int & in, int & strin, double eps) const;
+
+    void RecTangentialSolid2 (const Point<3> & p, const Vec<3> & vec, 
+			      Solid *& tansol, Array<int> & surfids, 
+			      int & in, int & strin, double eps) const;
+    ///
+    void RecTangentialSolid3 (const Point<3> & p, const Vec<3> & vec,const Vec<3> & vec2, 
+			      Solid *& tansol, Array<int> & surfids, 
+			      int & in, int & strin, double eps) const;
+    ///
+    void RecTangentialEdgeSolid (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, 
+				 const Vec<3> & m, 
+				 Solid *& tansol, Array<int> & surfids, 
+				 int & in, int & strin, double eps) const;
+
+    ///
+    void RecEdge (const Point<3> & p, const Vec<3> & v,
+		  int & in, int & strin, int & faces, double eps) const;
+    ///
+    void CalcSurfaceInverseRec (int inv);
+    ///
+    Solid * RecGetReducedSolid (const BoxSphere<3> & box, INSOLID_TYPE & in) const;
+    ///
+    void RecGetSurfaceIndices (Array<int> & surfind) const;
+    void RecGetTangentialSurfaceIndices (const Point<3> & p, Array<int> & surfids, double eps) const;
+    void RecGetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v, Array<int> & surfids, double eps) const;
+    void RecGetTangentialSurfaceIndices3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, 
+					  Array<int> & surfids, double eps) const;
+    void RecGetTangentialEdgeSurfaceIndices (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, const Vec<3> & m,
+					     Array<int> & surfids, double eps) const;
+    void RecGetSurfaceIndices (IndexSet & iset) const;
+
+    void RecCalcOnePrimitiveSpecialPoints (Array<Point<3> > & pts) const;
+
+    friend class SolidIterator;
+    friend class ClearVisitedIt;
+    friend class RemoveDummyIterator;
+    friend class CSGeometry;
+  };
+
+
+  inline ostream & operator<< (ostream & ost, const Solid & sol)
+  {
+    sol.Print (ost);
+    return ost;
+  }
+
+
+
+
+
+
+  class ReducePrimitiveIterator : public SolidIterator
+  {
+    const BoxSphere<3> & box;
+  public:
+    ReducePrimitiveIterator (const BoxSphere<3> & abox)
+      : SolidIterator(), box(abox) { ; }
+    virtual ~ReducePrimitiveIterator () { ; }
+    virtual void Do (Solid * sol)
+    {
+      if (sol -> GetPrimitive())
+	sol -> GetPrimitive() -> Reduce (box);
+    }
+  };
+
+
+  class UnReducePrimitiveIterator : public SolidIterator
+  {
+  public:
+    UnReducePrimitiveIterator () { ; }
+    virtual ~UnReducePrimitiveIterator () { ; }
+    virtual void Do (Solid * sol)
+    {
+      if (sol -> GetPrimitive())
+	sol -> GetPrimitive() -> UnReduce ();
+    }
+  };
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/specpoin.cpp b/contrib/Netgen/libsrc/csg/specpoin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f4c5f946a97eb76a63233f1cf0e2881cd92988d4
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/specpoin.cpp
@@ -0,0 +1,2099 @@
+#include <mystdlib.h>
+#include <meshing.hpp>
+#include <csg.hpp>
+
+
+/*
+  Special Point calculation uses the global Flags:
+
+  relydegtest       when to rely on degeneration ?
+  calccp            calculate points of intersection ?
+  cpeps1            eps for degenerated poi
+  calcep            calculate points of extreme coordinates ?
+  epeps1            eps for degenerated edge
+  epeps2            eps for axis parallel pec
+  epspointdist      eps for distance of special points 
+*/
+
+
+// #define DEVELOP
+
+
+namespace netgen
+{
+  Array<Box<3> > boxes;
+
+
+  void ProjectToEdge (const Surface * f1, const Surface * f2, Point<3> & hp);
+
+  enum { check_crosspoint = 5 };
+
+  SpecialPoint :: SpecialPoint (const SpecialPoint & sp)
+  {
+    p = sp.p;
+    v = sp.v;
+    s1 = sp.s1;
+    s2 = sp.s2;
+    s1_orig = sp.s1_orig;
+    s2_orig = sp.s2_orig;
+    layer = sp.layer;
+    unconditional = sp.unconditional;
+  }
+  
+  SpecialPoint & SpecialPoint :: operator= (const SpecialPoint & sp)
+  {
+    p = sp.p;
+    v = sp.v;
+    s1 = sp.s1;
+    s2 = sp.s2;
+    s1_orig = sp.s1_orig;
+    s2_orig = sp.s2_orig;
+    layer = sp.layer;
+    unconditional = sp.unconditional;
+    return *this;
+  }
+
+
+  void SpecialPoint :: Print (ostream & str) const
+  {
+    str << "p = " << p << "   v = " << v 
+	<< " s1/s2 = " << s1 << "/" << s2;
+    str << " layer = " << layer
+	<< " unconditional = " << unconditional
+	<< endl;
+  }
+
+
+  static Array<int> numprim_hist;
+
+  SpecialPointCalculation :: SpecialPointCalculation ()
+  {
+    ideps = 1e-9;
+  }
+
+  void SpecialPointCalculation :: 
+  CalcSpecialPoints (const CSGeometry & ageometry, 
+		     Array<MeshPoint> & apoints)
+  {
+    static int timer = NgProfiler::CreateTimer ("CSG: find special points");
+    NgProfiler::RegionTimer reg (timer);
+
+
+    geometry = &ageometry;
+    points = &apoints;
+
+    size = geometry->MaxSize();
+    (*testout) << "Find Special Points" << endl;
+    (*testout) << "maxsize = " << size << endl;
+
+    cpeps1 = 1e-6; 
+    epeps1 = 1e-3; 
+    epeps2 = 1e-6; 
+
+    epspointdist2 = sqr (size * 1e-8); 
+    relydegtest = size * 1e-4; 
+
+
+    BoxSphere<3> box (Point<3> (-size, -size, -size),
+		      Point<3> ( size,  size,  size));
+
+    box.CalcDiamCenter();
+    PrintMessage (3, "main-solids: ", geometry->GetNTopLevelObjects());
+
+    numprim_hist.SetSize (geometry->GetNSurf()+1);
+    numprim_hist = 0;
+
+    for (int i = 0; i < geometry->GetNTopLevelObjects(); i++)
+      {
+	const TopLevelObject * tlo = geometry->GetTopLevelObject(i);
+
+	(*testout) << "tlo " << i << ":" << endl
+		   << *tlo->GetSolid() << endl;
+
+	if (tlo->GetSolid())
+	  {
+	    Array<Point<3> > hpts;
+	    tlo->GetSolid()->CalcOnePrimitiveSpecialPoints (box, hpts);
+            // if (hpts.Size())
+            //  cout << "oneprimitivespecialpoints = " << hpts << endl;
+	    for (int j = 0; j < hpts.Size(); j++)
+	      AddPoint (hpts[j], tlo->GetLayer());
+	  }
+
+	CalcSpecialPointsRec (tlo->GetSolid(), tlo->GetLayer(),
+			      box, 1, 1, 1);
+      }
+ 
+  
+    geometry->DeleteIdentPoints();
+    for (int i = 0; i < geometry->GetNIdentifications(); i++)
+      {
+	CloseSurfaceIdentification * ident =
+	  dynamic_cast<CloseSurfaceIdentification * >(geometry->identifications[i]);
+	
+	if(!ident || !ident->IsSkewIdentification())
+	  continue;
+
+	for(int j=0; j<points->Size(); j++)
+	  {
+	    if(fabs(ident->GetSurface1().CalcFunctionValue((*points)[j])) < 1e-15)
+	      {
+		Point<3> auxpoint = (*points)[j];
+		ident->GetSurface2().SkewProject(auxpoint,ident->GetDirection());
+		geometry->AddIdentPoint(auxpoint);
+		geometry->AddIdentPoint((*points)[j]);
+		AddPoint (auxpoint,1);
+
+#ifdef DEVELOP
+		(*testout) << "added identpoint " << auxpoint << "; proj. of "
+			   <<  (*points)[j] << endl;
+#endif
+		break;
+	      }
+	  }
+      }
+    
+
+    // add user point:
+    for (int i = 0; i < geometry->GetNUserPoints(); i++)
+      AddPoint (geometry->GetUserPoint(i), 1);
+	
+
+    PrintMessage (3, "Found points ", apoints.Size());
+
+    for (int i = 0; i < boxesinlevel.Size(); i++)
+      (*testout) << "level " << i << " has " 
+		 << boxesinlevel[i] << " boxes" << endl;
+    (*testout) << "numprim_histogramm = " << endl << numprim_hist << endl;
+  }
+  
+
+
+  void SpecialPointCalculation :: 
+  CalcSpecialPointsRec (const Solid * sol, int layer,
+			const BoxSphere<3> & box, 
+			int level, bool calccp, bool calcep)
+  {
+    // boxes.Append (box);
+
+#ifdef DEVELOP
+    *testout << "lev " << level << ", box = " << box << endl;
+    *testout << "calccp = " << calccp << ", calcep = " << calcep << endl;
+    *testout << "locsol = " << *sol << endl;
+#endif
+
+    if (multithread.terminate)
+      {
+	*testout << "boxes = " << boxes << endl;
+	*testout << "boxesinlevel = " << boxesinlevel << endl;
+	throw NgException ("Meshing stopped");
+      }
+
+
+    if (!sol) return;
+
+    if (level >= 100)
+      {
+	MyStr err =
+	  MyStr("Problems in CalcSpecialPoints\nPoint: ") + MyStr (box.Center());
+	throw NgException (err.c_str());
+      }
+
+
+    bool decision;
+    bool possiblecrossp, possibleexp;  // possible cross or extremalpoint
+    bool surecrossp = 0, sureexp = 0;          // sure ...
+  
+    static Array<int> locsurf;  // attention: array is static
+
+    static int cntbox = 0;
+    cntbox++;
+
+    if (level <= boxesinlevel.Size())
+      boxesinlevel.Elem(level)++;
+    else
+      boxesinlevel.Append (1);
+
+    /*
+      numprim = sol -> NumPrimitives();
+      sol -> GetSurfaceIndices (locsurf);
+    */
+
+    geometry -> GetIndependentSurfaceIndices (sol, box, locsurf);
+    int numprim = locsurf.Size();
+
+#ifdef DEVELOP
+    (*testout) << "numprim = " << numprim << endl;
+#endif
+
+    numprim_hist[numprim]++;
+
+    Point<3> p = box.Center();
+
+
+    // explicit solution for planes only and at most one quadratic
+    if (numprim <= check_crosspoint)
+      {
+	int nplane = 0, nquad = 0, quadi = -1, nsphere = 0;
+	const QuadraticSurface *qsurf = 0, *qsurfi;
+
+	for (int i = 0; i < numprim; i++)
+	  {
+	    qsurfi = dynamic_cast<const QuadraticSurface*> 
+	      (geometry->GetSurface(locsurf[i]));
+
+	    if (qsurfi) nquad++;
+	    if (dynamic_cast<const Plane*> (qsurfi))
+	      nplane++;
+	    else
+	      {
+		quadi = i;
+		qsurf = qsurfi;
+	      }
+
+	    if (dynamic_cast<const Sphere*> (qsurfi))
+	      nsphere++;
+	  }
+
+	/*
+	if (nquad == numprim && nplane == numprim-2)
+	  return;
+	*/
+
+#ifdef DEVELOP
+	(*testout) << "nquad " << nquad << " nplane " << nplane << endl;
+#endif
+
+	if (nquad == numprim && nplane >= numprim-1)
+	  {
+	    Array<Point<3> > pts;
+	    Array<int> surfids;
+
+	    for (int k1 = 0; k1 < numprim - 2; k1++)
+	      for (int k2 = k1 + 1; k2 < numprim - 1; k2++)
+		for (int k3 = k2 + 1; k3 < numprim; k3++)
+		  if (k1 != quadi && k2 != quadi && k3 != quadi)
+		    {
+		      ComputeCrossPoints (dynamic_cast<const Plane*> (geometry->GetSurface(locsurf[k1])),
+					  dynamic_cast<const Plane*> (geometry->GetSurface(locsurf[k2])),
+					  dynamic_cast<const Plane*> (geometry->GetSurface(locsurf[k3])),
+					  pts);
+
+		      for (int j = 0; j < pts.Size(); j++)
+			if (Dist (pts[j], box.Center()) < box.Diam()/2)
+			  {
+			    Solid * tansol;
+			    sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size);
+
+			    if(!tansol)
+			      continue;
+
+			    bool ok1 = false, ok2 = false, ok3 = false;
+			    int rep1 = geometry->GetSurfaceClassRepresentant(locsurf[k1]);
+			    int rep2 = geometry->GetSurfaceClassRepresentant(locsurf[k2]);
+			    int rep3 = geometry->GetSurfaceClassRepresentant(locsurf[k3]);
+			    for(int jj=0; jj<surfids.Size(); jj++)
+			      {
+				int actrep = geometry->GetSurfaceClassRepresentant(surfids[jj]);
+				if(actrep == rep1) ok1 = true;
+				if(actrep == rep2) ok2 = true;
+				if(actrep == rep3) ok3 = true;				  
+			      }
+
+			    
+			    if (tansol && ok1 && ok2 && ok3)
+			    // if (sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size))
+			      {
+				if (AddPoint (pts[j], layer))
+				  (*testout) << "cross point found, 1: " << pts[j] << endl;
+			      }  
+			    delete tansol;
+			  }
+		    }
+
+
+	    if (qsurf)
+	      {
+		for (int k1 = 0; k1 < numprim - 1; k1++)
+		  for (int k2 = k1 + 1; k2 < numprim; k2++)
+		    if (k1 != quadi && k2 != quadi)
+		      {
+			ComputeCrossPoints (dynamic_cast<const Plane*> (geometry->GetSurface(locsurf[k1])),
+					    dynamic_cast<const Plane*> (geometry->GetSurface(locsurf[k2])),
+					    qsurf, pts);
+			//(*testout) << "checking pot. crosspoints: " << pts << endl;
+
+			for (int j = 0; j < pts.Size(); j++)
+			  if (Dist (pts[j], box.Center()) < box.Diam()/2)
+			    {
+			      Solid * tansol;
+			      sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size);
+
+			      if(!tansol)
+				continue;
+			      			      
+			      bool ok1 = false, ok2 = false, ok3 = true;//false;
+			      int rep1 = geometry->GetSurfaceClassRepresentant(locsurf[k1]);
+			      int rep2 = geometry->GetSurfaceClassRepresentant(locsurf[k2]);
+			      //int rep3 = geometry->GetSurfaceClassRepresentant(quadi);
+			      for(int jj=0; jj<surfids.Size(); jj++)
+				{
+				  int actrep = geometry->GetSurfaceClassRepresentant(surfids[jj]);
+				  if(actrep == rep1) ok1 = true;
+				  if(actrep == rep2) ok2 = true;
+				  //if(actrep == rep3) ok3 = true;				  
+				}
+
+
+			      if (tansol && ok1 && ok2 && ok3)
+				//if (sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size) )
+				{
+				  if (AddPoint (pts[j], layer))
+				    (*testout) << "cross point found, 2: " << pts[j] << endl;
+				}  
+			      delete tansol;
+			    }
+		      }
+
+
+		for (int k1 = 0; k1 < numprim; k1++)
+		  if (k1 != quadi)
+		    {
+		      ComputeExtremalPoints (dynamic_cast<const Plane*> (geometry->GetSurface(locsurf[k1])),
+					     qsurf, pts);
+		      
+		      for (int j = 0; j < pts.Size(); j++)
+			if (Dist (pts[j], box.Center()) < box.Diam()/2)
+			  {
+			    Solid * tansol;
+			    sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size);
+			    if (tansol)
+			      // sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size) )
+			      {
+				if (AddPoint (pts[j], layer))
+				  (*testout) << "extremal point found, 1: " << pts[j] << endl;
+			      }  
+			    delete tansol;
+			  }
+		    }
+	      }
+	    
+	    return;
+	  }
+
+
+
+	if (nsphere == numprim) //  && calccp == false)
+	  {
+	    Array<Point<3> > pts;
+	    Array<int> surfids;
+
+
+	    
+	    for (int k1 = 0; k1 < numprim; k1++)
+	      for (int k2 = 0; k2 < k1; k2++)
+		for (int k3 = 0; k3 < k2; k3++)
+		  {
+		    ComputeCrossPoints (dynamic_cast<const Sphere*> (geometry->GetSurface(locsurf[k1])),
+					dynamic_cast<const Sphere*> (geometry->GetSurface(locsurf[k2])),
+					dynamic_cast<const Sphere*> (geometry->GetSurface(locsurf[k3])),
+					pts);
+		    
+		    for (int j = 0; j < pts.Size(); j++)
+		      if (Dist (pts[j], box.Center()) < box.Diam()/2)
+			{
+			  Solid * tansol;
+			  sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size);
+			  
+			  if(!tansol)
+			    continue;
+			  
+			  bool ok1 = false, ok2 = false, ok3 = false;
+			  int rep1 = geometry->GetSurfaceClassRepresentant(locsurf[k1]);
+			  int rep2 = geometry->GetSurfaceClassRepresentant(locsurf[k2]);
+			  int rep3 = geometry->GetSurfaceClassRepresentant(locsurf[k3]);
+			  for(int jj=0; jj<surfids.Size(); jj++)
+			    {
+			      int actrep = geometry->GetSurfaceClassRepresentant(surfids[jj]);
+			      if(actrep == rep1) ok1 = true;
+			      if(actrep == rep2) ok2 = true;
+			      if(actrep == rep3) ok3 = true;				  
+			    }
+			  
+			  
+			  if (tansol && ok1 && ok2 && ok3)
+			    // if (sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size))
+			    {
+			      if (AddPoint (pts[j], layer))
+				(*testout) << "cross point found, 1: " << pts[j] << endl;
+			    }  
+			  delete tansol;
+			}
+		  }
+	    
+
+	    for (int k1 = 0; k1 < numprim; k1++)
+	      for (int k2 = 0; k2 < k1; k2++)
+		{
+		  ComputeExtremalPoints (dynamic_cast<const Sphere*> (geometry->GetSurface(locsurf[k1])),
+					 dynamic_cast<const Sphere*> (geometry->GetSurface(locsurf[k2])),
+					 pts);
+		  
+		  for (int j = 0; j < pts.Size(); j++)
+		    if (Dist (pts[j], box.Center()) < box.Diam()/2)
+		      {
+			Solid * tansol;
+			sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size);
+			if (tansol)
+			  // sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size) )
+			  {
+			    if (AddPoint (pts[j], layer))
+			      (*testout) << "extremal point found, spheres: " << pts[j] << endl;
+			  }  
+			delete tansol;
+		      }
+		}
+	    
+	    return;
+	  }
+      }
+
+
+    
+    possiblecrossp = (numprim >= 3) && calccp;
+    surecrossp = 0;
+
+    if (possiblecrossp && (locsurf.Size() <= check_crosspoint || level > 50))
+      {
+	decision = 1;
+	surecrossp = 0;
+
+	for (int k1 = 1; k1 <= locsurf.Size() - 2; k1++)
+	  for (int k2 = k1 + 1; k2 <= locsurf.Size() - 1; k2++)
+	    for (int k3 = k2 + 1; k3 <= locsurf.Size(); k3++)
+	      {
+		int nc, deg;
+		nc = CrossPointNewtonConvergence 
+		  (geometry->GetSurface(locsurf.Get(k1)), 
+		   geometry->GetSurface(locsurf.Get(k2)), 
+		   geometry->GetSurface(locsurf.Get(k3)), box );
+	      
+		deg = CrossPointDegenerated 
+		  (geometry->GetSurface(locsurf.Get(k1)), 
+		   geometry->GetSurface(locsurf.Get(k2)), 
+		   geometry->GetSurface(locsurf.Get(k3)), box );
+	      
+#ifdef DEVELOP
+		(*testout) << "k1,2,3 = " << k1 << "," << k2 << "," << k3 << ", nc = " << nc << ", deg = " << deg << endl;
+#endif
+
+
+		if (!nc && !deg) decision = 0;
+		if (nc) surecrossp = 1;
+	      }
+
+#ifdef DEVELOP
+        (*testout) << "dec = " << decision << ", surcp = " << surecrossp << endl;
+#endif
+
+	if (decision && surecrossp)
+	  {
+	    for (int k1 = 1; k1 <= locsurf.Size() - 2; k1++)
+	      for (int k2 = k1 + 1; k2 <= locsurf.Size() - 1; k2++)
+		for (int k3 = k2 + 1; k3 <= locsurf.Size(); k3++)
+		  {
+		    if (CrossPointNewtonConvergence 
+			(geometry->GetSurface(locsurf.Get(k1)), 
+			 geometry->GetSurface(locsurf.Get(k2)), 
+			 geometry->GetSurface(locsurf.Get(k3)), box ) )
+		      {
+                        
+			Point<3> pp = p;
+			CrossPointNewton 
+			  (geometry->GetSurface(locsurf.Get(k1)), 
+			   geometry->GetSurface(locsurf.Get(k2)), 
+			   geometry->GetSurface(locsurf.Get(k3)), pp);
+              
+			BoxSphere<3> hbox (pp, pp);
+			hbox.Increase (1e-8*size);
+
+			if (pp(0) > box.PMin()(0) - 1e-5*size && 
+			    pp(0) < box.PMax()(0) + 1e-5*size &&
+			    pp(1) > box.PMin()(1) - 1e-5*size && 
+			    pp(1) < box.PMax()(1) + 1e-5*size &&
+			    pp(2) > box.PMin()(2) - 1e-5*size && 
+			    pp(2) < box.PMax()(2) + 1e-5*size &&
+			    sol -> IsIn (pp, 1e-6*size) && !sol->IsStrictIn (pp, 1e-6*size) &&
+			    !CrossPointDegenerated
+			    (geometry->GetSurface(locsurf.Get(k1)), 
+			     geometry->GetSurface(locsurf.Get(k2)), 
+			     geometry->GetSurface(locsurf.Get(k3)), hbox ))
+
+			  { 
+			    //                AddCrossPoint (locsurf, sol, p);
+			    BoxSphere<3> boxp (pp, pp);
+			    boxp.Increase (1e-3*size);
+			    boxp.CalcDiamCenter();
+			    Array<int> locsurf2;
+
+			    geometry -> GetIndependentSurfaceIndices (sol, boxp, locsurf2);
+			  
+			    bool found1 = false, found2 = false, found3 = false;
+			    for (int i = 0; i < locsurf2.Size(); i++)
+			      {
+				if (locsurf2[i] == locsurf.Get(k1)) found1 = true;
+				if (locsurf2[i] == locsurf.Get(k2)) found2 = true;
+				if (locsurf2[i] == locsurf.Get(k3)) found3 = true;
+			      }
+
+			    if (found1 && found2 && found3)
+			      if (AddPoint (pp, layer))
+				{
+				  (*testout) << "Crosspoint found: " << pp 
+					     << " diam = " << box.Diam()
+					     << ",  surfs: " 
+					     << locsurf.Get(k1) << "," 
+					     << locsurf.Get(k2) << "," 
+					     << locsurf.Get(k3) << endl;
+				}
+			  }
+		      }
+		  }
+	  }
+      
+	if (decision)
+	  possiblecrossp = 0;
+      }
+
+
+    possibleexp = (numprim >= 2) && calcep;
+
+    // (*testout) << "l = " << level << "locsize = " << locsurf.Size() << " possexp = " << possibleexp << "\n";
+    if (possibleexp && (numprim <= check_crosspoint || level >= 50))
+      {
+	decision = 1;
+	sureexp = 0;
+
+	/*
+	(*testout) << "extremal surfs = ";
+	for (int k5 = 0; k5 < locsurf.Size(); k5++)
+	  (*testout) << typeid(*geometry->GetSurface(locsurf[k5])).name() << " ";
+	(*testout) << "\n";
+	*/
+
+	for (int k1 = 0; k1 < locsurf.Size() - 1; k1++)
+	  for (int k2 = k1+1; k2 < locsurf.Size(); k2++)
+	    {
+	      const Surface * surf1 = geometry->GetSurface(locsurf[k1]);
+	      const Surface * surf2 = geometry->GetSurface(locsurf[k2]);
+	      /*
+	      (*testout) << "edgecheck, types = " << typeid(*surf1).name() << ", " << typeid(*surf2).name()
+			 << "edge-newton-conv = " << EdgeNewtonConvergence (surf1, surf2, p)
+			 << "edge-deg = " << EdgeDegenerated (surf1, surf2, box)
+			 << "\n";
+	      */
+
+	      if (EdgeNewtonConvergence (surf1, surf2, p) ) 
+		sureexp = 1;
+	      else
+		{
+		  if (!EdgeDegenerated (surf1, surf2, box)) 
+		    decision = 0;
+		}
+	    }
+
+	// (*testout) << "l = " << level << " dec/sureexp = " << decision << sureexp << endl;
+
+	if (decision && sureexp)
+	  {
+	    for (int k1 = 0; k1 < locsurf.Size() - 1; k1++)
+	      for (int k2 = k1+1; k2 < locsurf.Size(); k2++)
+		{
+		  const Surface * surf1 = geometry->GetSurface(locsurf[k1]);
+		  const Surface * surf2 = geometry->GetSurface(locsurf[k2]);
+
+		  if (EdgeNewtonConvergence (surf1, surf2, p))
+		    {
+		      EdgeNewton (surf1, surf2, p);
+		    
+		      Point<3> pp;
+		      if (IsEdgeExtremalPoint (surf1, surf2, p, pp, box.Diam()/2))
+			{
+			  (*testout) << "extremalpoint (nearly) found:" << pp << endl;
+
+			  if (Dist (pp, box.Center()) < box.Diam()/2 &&
+			      sol -> IsIn (pp, 1e-6*size) && !sol->IsStrictIn (pp, 1e-6*size) )
+			    {
+			      if (AddPoint (pp, layer))
+				(*testout) << "Extremal point found: " << pp << endl;//"(eps="<<1e-9*size<<")"<< endl;
+			    }  
+			}            
+		    }
+		}
+	  }
+	if (decision)
+	  possibleexp = 0;
+      }
+ 
+
+    // (*testout) << "l = " << level << " poss cp/ep sure exp = " << possiblecrossp << " " << possibleexp << " " << sureexp << "\n";
+    if (possiblecrossp || possibleexp)
+      {
+	BoxSphere<3> sbox;
+	for (int i = 0; i < 8; i++)
+	  {
+	    box.GetSubBox (i, sbox);
+	    sbox.Increase (1e-4 * sbox.Diam());
+	    sbox.CalcDiamCenter();
+	    Solid * redsol = sol -> GetReducedSolid (sbox);
+
+	    if (redsol)
+	      {
+		CalcSpecialPointsRec (redsol, layer, sbox, level+1, calccp, calcep);
+		delete redsol;
+	      }
+	  }
+      }
+  }
+
+
+
+
+
+  /******* Tests for Point of intersection **********************/
+
+
+
+  bool SpecialPointCalculation :: 
+  CrossPointNewtonConvergence (const Surface * f1, 
+			       const Surface * f2, 
+			       const Surface * f3,
+			       const BoxSphere<3> & box)
+  {
+    Vec<3> grad, rs, x;
+    Mat<3> jacobi, inv;
+    Point<3> p = box.Center();
+
+    f1->CalcGradient (p, grad);
+    jacobi(0,0) = grad(0);
+    jacobi(0,1) = grad(1);
+    jacobi(0,2) = grad(2);
+
+    f2->CalcGradient (p, grad);
+    jacobi(1,0) = grad(0);
+    jacobi(1,1) = grad(1);
+    jacobi(1,2) = grad(2);
+
+    f3->CalcGradient (p, grad);
+    jacobi(2,0) = grad(0);
+    jacobi(2,1) = grad(1);
+    jacobi(2,2) = grad(2);
+
+    if (fabs (Det (jacobi)) > 1e-8)
+      {
+	double gamma = f1 -> HesseNorm() + f2 -> HesseNorm() + f3 -> HesseNorm();
+	if (gamma == 0.0) return 1;
+
+	CalcInverse (jacobi, inv);
+
+	rs(0) = f1->CalcFunctionValue (p);
+	rs(1) = f2->CalcFunctionValue (p);
+	rs(2) = f3->CalcFunctionValue (p);
+
+	x = inv * rs;
+
+	double beta = 0;
+	for (int i = 0; i < 3; i++)
+	  {
+	    double sum = 0;
+	    for (int j = 0; j < 3; j++)
+	      sum += fabs (inv(i,j));
+	    if (sum > beta)  beta = sum;
+	  }
+	double eta = Abs (x);
+
+
+#ifdef DEVELOP
+        *testout << "check Newton: " << "beta = " << beta << ", gamma = " << gamma << ", eta = " << eta << endl;
+        double rad = 1.0 / (beta * gamma);
+        *testout << "rad = " << rad << endl;
+	*testout << "rs = " << rs << endl;
+#endif
+        
+	return (beta * gamma * eta < 0.1) && (2 > box.Diam()*beta*gamma);
+      }
+    return 0;
+
+  }
+
+
+
+
+  bool SpecialPointCalculation :: 
+  CrossPointDegenerated (const Surface * f1,
+			 const Surface * f2, 
+			 const Surface * f3, 
+			 const BoxSphere<3> & box) const
+  {
+    Mat<3> mat;
+    Vec<3> g1, g2, g3;
+    double normprod;
+
+    if (box.Diam() > relydegtest) return 0;
+
+    f1->CalcGradient (box.Center(), g1);
+    normprod = Abs2 (g1);
+
+    f2->CalcGradient (box.Center(), g2);
+    normprod *= Abs2 (g2);
+ 
+    f3->CalcGradient (box.Center(), g3);
+    normprod *= Abs2 (g3);
+
+    for (int i = 0; i < 3; i++)
+      {
+	mat(i,0) = g1(i);
+	mat(i,1) = g2(i);
+	mat(i,2) = g3(i);
+      }
+
+    return sqr (Det (mat)) < sqr(cpeps1) * normprod;
+  }
+ 
+
+
+
+
+  void SpecialPointCalculation :: CrossPointNewton (const Surface * f1, 
+						    const Surface * f2, 
+						    const Surface * f3, Point<3> & p)
+  {
+    Vec<3> g1, g2, g3;
+    Vec<3> rs, sol;
+    Mat<3> mat;
+
+    int i = 10;
+    while (i > 0)
+      {
+	i--;
+	rs(0) = f1->CalcFunctionValue (p);
+	rs(1) = f2->CalcFunctionValue (p);
+	rs(2) = f3->CalcFunctionValue (p);
+
+	f1->CalcGradient (p, g1);
+	f2->CalcGradient (p, g2);
+	f3->CalcGradient (p, g3);
+
+	for (int j = 0; j < 3; j++)
+	  {
+	    mat(0, j) = g1(j);
+	    mat(1, j) = g2(j);
+	    mat(2, j) = g3(j);
+	  }
+	mat.Solve (rs, sol);
+	if (sol.Length2() < 1e-24 && i > 1) i = 1;
+
+#ifdef DEVELOP
+        *testout << "CrossPointNewton, err = " << sol.Length2() << endl;
+#endif
+	p -= sol;
+      }
+  }
+
+
+
+
+  /******* Tests for Point on edges **********************/
+
+
+
+
+  bool SpecialPointCalculation :: 
+  EdgeNewtonConvergence (const Surface * f1, const Surface * f2, 
+			 const Point<3> & p)
+  {
+    Vec<3> g1, g2, sol;
+    Vec<2> vrs;
+    Mat<2,3> mat;
+    Mat<3,2> inv;
+
+    f1->CalcGradient (p, g1);
+    f2->CalcGradient (p, g2);
+
+    if ( sqr(g1 * g2) < (1 - 1e-8) * Abs2 (g1) * Abs2 (g2))
+      {
+	double gamma = f1 -> HesseNorm() + f2 -> HesseNorm();
+	if (gamma < 1e-32) return 1;
+	gamma = sqr (gamma);
+      
+	for (int i = 0; i < 3; i++)
+	  {
+	    mat(0,i) = g1(i);
+	    mat(1,i) = g2(i);
+	  }
+
+	CalcInverse (mat, inv);
+
+	vrs(0) = f1->CalcFunctionValue (p);
+	vrs(1) = f2->CalcFunctionValue (p);
+	sol = inv * vrs;
+
+	double beta = 0;
+	for (int i = 0; i < 3; i++)
+	  for (int j = 0; j < 2; j++)
+	    beta += inv(i,j) * inv(i,j);
+	// beta = sqrt (beta);
+
+	double eta = Abs2 (sol);
+
+	// alpha = beta * gamma * eta;
+	return (beta * gamma * eta < 0.01);
+      }
+    return 0;
+  }
+
+
+
+
+  bool SpecialPointCalculation :: 
+  EdgeDegenerated (const Surface * f1,
+		   const Surface * f2, 
+		   const BoxSphere<3> & box) const
+  {
+    // perform newton steps. normals parallel ?
+    // if not decideable: return 0 
+  
+    Point<3> p = box.Center();
+    Vec<3> g1, g2, sol;
+    Vec<2> vrs;
+    Mat<2,3> mat;
+
+    int i = 20;
+    while (i > 0)
+      {
+	if (Dist2 (p, box.Center()) > sqr(box.Diam()))
+	  return 0;
+
+	i--;
+	vrs(0) = f1->CalcFunctionValue (p);
+	vrs(1) = f2->CalcFunctionValue (p);
+
+	f1->CalcGradient (p, g1);
+	f2->CalcGradient (p, g2);
+
+	if ( sqr (g1 * g2) > (1 - 1e-10) * Abs2 (g1) * Abs2 (g2))
+	  return 1;
+
+	for (int j = 0; j < 3; j++)
+	  {
+	    mat(0,j) = g1(j);
+	    mat(1,j) = g2(j);
+	  }
+	mat.Solve (vrs, sol);
+
+	if (Abs2 (sol) < 1e-24 && i > 1) i = 1;
+	p -= sol;
+      }
+
+    return 0;
+  }
+
+
+
+
+
+
+  void SpecialPointCalculation :: EdgeNewton (const Surface * f1, 
+					      const Surface * f2, Point<3> & p)
+  {
+    Vec<3> g1, g2, sol;
+    Vec<2> vrs;
+    Mat<2,3> mat;
+
+    int i = 10;
+    while (i > 0)
+      {
+	i--;
+	vrs(0) = f1->CalcFunctionValue (p);
+	vrs(1) = f2->CalcFunctionValue (p);
+
+	f1->CalcGradient (p, g1);
+	f2->CalcGradient (p, g2);
+
+	//(*testout) << "p " << p << " f1 " << vrs(0) << " f2 " << vrs(1) << " g1 " << g1 << " g2 " << g2 << endl;
+
+	for (int j = 0; j < 3; j++)
+	  {
+	    mat(0,j) = g1(j);
+	    mat(1,j) = g2(j);
+	  }
+	mat.Solve (vrs, sol);
+	
+	if (Abs2 (sol) < 1e-24 && i > 1) i = 1;
+	p -= sol;
+      }
+  }
+
+
+
+  bool SpecialPointCalculation :: 
+  IsEdgeExtremalPoint (const Surface * f1, const Surface * f2, 
+		       const Point<3> & p, Point<3> & pp, double rad)
+  {
+    Vec<3> g1, g2, t, t1, t2;
+
+    f1->CalcGradient (p, g1);
+    f2->CalcGradient (p, g2);
+  
+    t = Cross (g1, g2);
+    t.Normalize();
+
+    Point<3> p1 = p + rad * t;
+    Point<3> p2 = p - rad * t;
+
+    EdgeNewton (f1, f2, p1);
+    EdgeNewton (f1, f2, p2);
+  
+    f1->CalcGradient (p1, g1);
+    f2->CalcGradient (p1, g2);
+    t1 = Cross (g1, g2);
+    t1.Normalize();
+
+    f1->CalcGradient (p2, g1);
+    f2->CalcGradient (p2, g2);
+    t2 = Cross (g1, g2);
+    t2.Normalize();
+
+    double val = 1e-8 * rad * rad;
+    for (int j = 0; j < 3; j++)
+      if ( (t1(j) * t2(j) < -val) )
+	{
+	  pp = p;
+	  ExtremalPointNewton (f1, f2, j+1, pp);
+	  return 1;
+	}
+
+    return 0;
+  }
+
+
+
+
+
+
+
+
+
+  /********** Tests of Points of extremal coordinates  ****************/
+
+
+  void SpecialPointCalculation :: ExtremalPointNewton (const Surface * f1, 
+						       const Surface * f2, 
+						       int dir, Point<3> & p)
+  {
+    Vec<3> g1, g2, v, curv;
+    Vec<3> rs, x, y1, y2, y;
+    Mat<3> h1, h2;
+    Mat<3> jacobi;
+
+    int i = 50;
+    while (i > 0)
+      {
+	i--;
+	rs(0) = f1->CalcFunctionValue (p);
+	rs(1) = f2->CalcFunctionValue (p);
+
+	f1 -> CalcGradient (p, g1);
+	f2 -> CalcGradient (p, g2);
+
+	f1 -> CalcHesse (p, h1);
+	f2 -> CalcHesse (p, h2);
+
+	v = Cross (g1, g2);
+
+	rs(2) = v(dir-1);
+
+	jacobi(0,0) = g1(0);
+	jacobi(0,1) = g1(1);
+	jacobi(0,2) = g1(2);
+
+	jacobi(1,0) = g2(0);
+	jacobi(1,1) = g2(1);
+	jacobi(1,2) = g2(2);
+
+
+	switch (dir)
+	  {
+	  case 1:
+	    {
+	      y1(0) = 0;
+	      y1(1) = g2(2);
+	      y1(2) = -g2(1);
+	      y2(0) = 0;
+	      y2(1) = -g1(2);
+	      y2(2) = g1(1);
+	      break;
+	    }
+	  case 2:
+	    {
+	      y1(0) = -g2(2);
+	      y1(1) = 0;
+	      y1(2) = g2(0);
+	      y2(0) = g1(2);
+	      y2(1) = 0;
+	      y2(2) = -g1(0);
+	      break;
+	    }
+	  case 3:
+	    {
+	      y1(0) = g2(1);
+	      y1(1) = -g2(0);
+	      y1(2) = 0;
+	      y2(0) = -g1(1);
+	      y2(1) = g1(0);
+	      y2(2) = 0;
+	      break;
+	    }
+	  }
+
+	y = h1 * y1 + h2 * y2;
+
+	jacobi(2,0) = y(0);
+	jacobi(2,1) = y(1);
+	jacobi(2,2) = y(2);
+
+	/*
+	(*testout) << "p " << p << " f1 " << rs(0) << " f2 " << rs(1) << endl
+		   << " jacobi " << jacobi << endl
+		   << " rhs " << rs << endl;
+	*/	
+
+	jacobi.Solve (rs, x);
+
+	if (Abs2 (x) < 1e-24 && i > 1)
+	  {
+	    i = 1;
+	  }
+
+	
+	double minval(Abs2(rs)),minfac(1);
+	double startval(minval);
+	for(double fac = 1; fac > 1e-7; fac *= 0.6)
+	  {
+	    Point<3> testpoint = p-fac*x;
+
+	    rs(0) = f1->CalcFunctionValue (testpoint);
+	    rs(1) = f2->CalcFunctionValue (testpoint);
+
+	    f1 -> CalcGradient (testpoint, g1);
+	    f2 -> CalcGradient (testpoint, g2);
+
+	    v = Cross (g1, g2);
+
+	    rs(2) = v(dir-1);
+
+	    double val = Abs2(rs);
+
+	    if(val < minval)
+	      {
+		minfac = fac;
+		if(val < 0.5 * startval)
+		  break;
+		minval = val;
+	      }
+
+	  }
+	p -= minfac*x;
+	
+
+	//p -= x;
+      }
+
+
+    if (Abs2 (x) > 1e-20)
+      {
+	(*testout) << "Error: extremum Newton not convergent" << endl;
+	(*testout) << "dir = " << dir << endl;
+	(*testout) << "p = " << p << endl;
+	(*testout) << "x = " << x << endl;
+      }
+  }
+
+  void SpecialPointCalculation :: 
+  ComputeCrossPoints (const Plane * plane1, 
+		      const Plane * plane2, 
+		      const Plane * plane3, 
+		      Array<Point<3> > & pts)
+  {
+    Mat<3> mat;
+    Vec<3> rhs, sol;
+    Point<3> p0(0,0,0);
+
+    pts.SetSize (0);
+    for (int i = 0; i < 3; i++)
+      {
+	const Plane * pi(NULL);
+	switch (i)
+	  {
+	  case 0: pi = plane1; break;
+	  case 1: pi = plane2; break;
+	  case 2: pi = plane3; break;
+	  }
+
+	double val;
+	Vec<3> hvec;
+	val = pi -> CalcFunctionValue(p0);
+	pi -> CalcGradient (p0, hvec);
+
+	for (int j = 0; j < 3; j++)
+	  mat(i,j) = hvec(j);
+	rhs(i) = -val;
+      }
+
+    if (fabs (Det (mat)) > 1e-8)
+      {
+	mat.Solve (rhs, sol);
+	pts.Append (Point<3> (sol));
+      }
+  }
+
+
+
+
+
+  void SpecialPointCalculation :: 
+  ComputeCrossPoints (const Plane * plane1, 
+		      const Plane * plane2, 
+		      const QuadraticSurface * quadric, 
+		      Array<Point<3> > & pts)
+  {
+    Mat<2,3> mat;
+    Mat<3,2> inv;
+    Vec<2> rhs;
+    Vec<3> sol, t;
+    Point<3> p0(0,0,0);
+
+    pts.SetSize (0);
+    for (int i = 0; i < 2; i++)
+      {
+	const Plane * pi(NULL);
+	switch (i)
+	  {
+	  case 0: pi = plane1; break;
+	  case 1: pi = plane2; break;
+	  }
+
+	double val;
+	Vec<3> hvec;
+	val = pi -> CalcFunctionValue(p0);
+	pi -> CalcGradient (p0, hvec);
+
+	for (int j = 0; j < 3; j++)
+	  mat(i,j) = hvec(j);
+	rhs(i) = -val;
+      }
+    CalcInverse (mat, inv);
+    sol = inv * rhs;
+    t = Cross (mat.Row(0), mat.Row(1));
+
+    if (t.Length() > 1e-8)
+      {
+	Point<3> p (sol);
+	// quadratic on  p + s t = 0
+	double quad_a;
+	Vec<3> quad_b;
+	Mat<3> quad_c;
+	
+	quad_a = quadric -> CalcFunctionValue(p);
+	quadric -> CalcGradient (p, quad_b);
+	quadric -> CalcHesse (p, quad_c);
+	
+	double a, b, c;
+	a = quad_a;
+	b = quad_b * t;
+	c = 0.5 * t * (quad_c * t);
+
+	// a  + s b + s^2 c = 0;
+	double disc = b*b-4*a*c;
+	if (disc > 1e-10 * fabs (b))
+	  {
+	    disc = sqrt (disc);
+	    double s1 = (-b-disc) / (2*c);
+	    double s2 = (-b+disc) / (2*c);
+
+	    pts.Append (p + s1 * t);
+	    pts.Append (p + s2 * t);
+	  }
+      }
+  }
+
+
+
+
+
+  void SpecialPointCalculation :: 
+  ComputeCrossPoints (const Sphere * sphere1, 
+		      const Sphere * sphere2, 
+		      const Sphere * sphere3, 
+		      Array<Point<3> > & pts)
+  {
+    Mat<2,3> mat;
+    Mat<3,2> inv;
+    Vec<2> rhs;
+    Vec<3> sol, t;
+    Point<3> p0(0,0,0);
+
+    pts.SetSize (0);
+
+
+    Point<3> c1 = sphere1 -> Center();
+    Point<3> c2 = sphere2 -> Center();
+    Point<3> c3 = sphere3 -> Center();
+    double r1 = sphere1 -> Radius();
+    double r2 = sphere2 -> Radius();
+    double r3 = sphere3 -> Radius();
+
+
+    Vec<3> a1 = c2-c1;
+    double b1 = 0.5 * (sqr(r1) - sqr(r2) - Abs2(Vec<3> (c1)) + Abs2(Vec<3> (c2)) );
+
+    Vec<3> a2 = c3-c1;
+    double b2 = 0.5 * (sqr(r1) - sqr(r3) - Abs2(Vec<3> (c1)) + Abs2(Vec<3> (c3)) );
+
+
+    for (int j = 0; j < 3; j++)
+      {
+	mat(0,j) = a1(j);
+	mat(1,j) = a2(j);
+      }
+    
+    rhs(0) = b1;
+    rhs(1) = b2;
+
+
+    CalcInverse (mat, inv);
+    sol = inv * rhs;
+    t = Cross (mat.Row(0), mat.Row(1));
+
+    if (t.Length() > 1e-8)
+      {
+	Point<3> p (sol);
+	// quadratic on  p + s t = 0
+	double quad_a;
+	Vec<3> quad_b;
+	Mat<3> quad_c;
+	
+	quad_a = sphere1 -> CalcFunctionValue(p);
+	sphere1 -> CalcGradient (p, quad_b);
+	sphere1 -> CalcHesse (p, quad_c);
+	
+	double a, b, c;
+	a = quad_a;
+	b = quad_b * t;
+	c = 0.5 * t * (quad_c * t);
+
+	// a  + s b + s^2 c = 0;
+	double disc = b*b-4*a*c;
+	if (disc > 1e-10 * fabs (b))
+	  {
+	    disc = sqrt (disc);
+	    double s1 = (-b-disc) / (2*c);
+	    double s2 = (-b+disc) / (2*c);
+
+	    pts.Append (p + s1 * t);
+	    pts.Append (p + s2 * t);
+	  }
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+  void SpecialPointCalculation :: 
+  ComputeExtremalPoints (const Plane * plane, 
+			 const QuadraticSurface * quadric, 
+			 Array<Point<3> > & pts)
+  {
+    // 3 equations:
+    // surf1 = 0  <===> plane_a + plane_b x = 0;
+    // surf2 = 0  <===> quad_a + quad_b x + x^T quad_c x = 0
+    // (grad 1 x grad 2)(i) = 0  <====> (grad 1 x e_i) . grad_2 = 0
+
+    pts.SetSize (0);
+
+    Point<3> p0(0,0,0);
+    double plane_a, quad_a;
+    Vec<3> plane_b, quad_b, ei;
+    Mat<3> quad_c;
+
+    plane_a = plane -> CalcFunctionValue(p0);
+    plane -> CalcGradient (p0, plane_b);
+
+    quad_a = quadric -> CalcFunctionValue(p0);
+    quadric -> CalcGradient (p0, quad_b);
+    quadric -> CalcHesse (p0, quad_c);
+    for (int i = 0; i < 3; i++)
+      for (int j = 0; j < 3; j++)
+	quad_c(i,j) *= 0.5;
+
+    for (int dir = 0; dir <= 2; dir++)
+      {
+	ei = 0.0; ei(dir) = 1;
+	Vec<3> v1 = Cross (plane_b, ei);
+	
+	// grad_2 . v1 ... linear:
+	double g2v1_c = v1 * quad_b;
+	Vec<3> g2v1_l = 2.0 * (quad_c * v1);
+
+	// find line of two linear equations:
+	
+	Vec<2> rhs;
+	Vec<3> sol;
+	Mat<2,3> mat;
+
+	for (int j = 0; j < 3; j++)
+	  {
+	    mat(0,j) = plane_b(j);
+	    mat(1,j) = g2v1_l(j);
+	  }
+	rhs(0) = -plane_a;
+	rhs(1) = -g2v1_c;
+
+	Vec<3> t = Cross (plane_b, g2v1_l);
+	if (Abs2(t) > 0)
+	  {
+	    mat.Solve (rhs, sol);
+	    
+	    // solve quadratic equation along line  sol + alpha t ....
+	    double a = quad_a + quad_b * sol + sol * (quad_c * sol);
+	    double b = quad_b * t + 2 * (sol * (quad_c * t));
+	    double c = t * (quad_c * t);
+
+	    // solve a + b alpha + c alpha^2:
+
+	    if (fabs (c) > 1e-32)
+	      {
+		double disc = sqr (0.5*b/c) - a/c;
+		if (disc > 0)
+		  {
+		    disc = sqrt (disc);
+		    double alpha1 = -0.5*b/c + disc;
+		    double alpha2 = -0.5*b/c - disc;
+
+		    pts.Append (Point<3> (sol+alpha1*t));
+		    pts.Append (Point<3> (sol+alpha2*t));
+		    /*
+		    cout << "sol1 = " << sol + alpha1 * t
+			 << ", sol2 = " << sol + alpha2 * t << endl;
+		    */
+		  }
+	      }
+	  }
+      }
+  }
+
+
+
+
+
+
+
+  void SpecialPointCalculation :: 
+  ComputeExtremalPoints (const Sphere * sphere1,
+			 const Sphere * sphere2,
+			 Array<Point<3> > & pts)
+  {
+    // 3 equations:
+    // surf1 = 0  <===> |x-c1|^2 - r1^2 = 0;
+    // surf2 = 0  <===> |x-c2|^2 - r2^2 = 0;
+    // (grad 1 x grad 2)(i) = 0  <====> (x-p1) x (p1-p2) . e_i = 0;
+
+    pts.SetSize (0);
+
+    Point<3> c1 = sphere1 -> Center();
+    Point<3> c2 = sphere2 -> Center();
+    double r1 = sphere1 -> Radius();
+    double r2 = sphere2 -> Radius();
+
+    /*
+    *testout << "\n\ncompute extremalpoint, sphere-sphere" << endl;
+    *testout << "c1 = " << c1 << ", r1 = " << r1 << endl;
+    *testout << "c2 = " << c2 << ", r2 = " << r2 << endl;
+    *testout << "dist = " << Abs (c2-c1) << ", r1+r2 = " << r1+r2 << endl;
+    */
+
+    Vec<3> v12 = c2 - c1;
+
+    Vec<3> a1, a2;
+    double b1, b2;
+
+    // eqn: ai . x = bi
+
+    a1 = v12;
+    b1 = 0.5 * (sqr(r1) - sqr(r2) - Abs2(Vec<3> (c1)) + Abs2(Vec<3> (c2)) );
+
+    int dir = 0;
+    for (int j = 1; j < 3; j++)
+      if (fabs (v12(j)) > v12(dir))
+	dir = j;
+    
+    // *testout << "dir = " << dir << endl;
+
+    Vec<3> ei = 0.0;
+    ei(dir) = 1;
+    a2 = Cross (v12, ei);
+    b2 = Vec<3>(c1) * a2;
+    
+    
+    Point<3> p0 (0,0,0);
+    double quad_a;
+    Vec<3> quad_b;
+    Mat<3> quad_c;
+
+    quad_a = sphere1 -> CalcFunctionValue(p0);
+    sphere1 -> CalcGradient (p0, quad_b);
+    sphere1 -> CalcHesse (p0, quad_c);
+    for (int i = 0; i < 3; i++)
+      for (int j = 0; j < 3; j++)
+	quad_c(i,j) *= 0.5;
+
+    
+    // find line of two linear equations:
+    
+    Vec<2> rhs;
+    Vec<3> sol;
+    Mat<2,3> mat;
+    
+    for (int j = 0; j < 3; j++)
+      {
+	mat(0,j) = a1(j);
+	mat(1,j) = a2(j);
+      }
+    rhs(0) = b1;
+    rhs(1) = b2;
+
+
+    // *testout << "mat = " << endl << mat << endl;
+    // *testout << "rhs = " << endl << rhs << endl;
+
+    Vec<3> t = Cross (a1, a2);
+    if (Abs2(t) > 0)
+      {
+	mat.Solve (rhs, sol);
+	
+	/*
+	*testout << "sol = " << endl << sol << endl;
+
+	*testout << "a * sol = " << mat * sol << endl;
+
+	*testout << "c1-sol = " << Abs (Vec<3>(c1)-sol) << endl;
+	*testout << "c2-sol = " << Abs (Vec<3>(c2)-sol) << endl;
+	*/
+
+	// solve quadratic equation along line  sol + alpha t ....
+	double a = quad_a + quad_b * sol + sol * (quad_c * sol);
+	double b = quad_b * t + 2 * (sol * (quad_c * t));
+	double c = t * (quad_c * t);
+
+	// solve a + b alpha + c alpha^2:
+	
+	if (fabs (c) > 1e-32)
+	  {
+	    double disc = sqr (0.5*b/c) - a/c;
+	    if (disc > 0)
+	      {
+		disc = sqrt (disc);
+		double alpha1 = -0.5*b/c + disc;
+		double alpha2 = -0.5*b/c - disc;
+		
+		pts.Append (Point<3> (sol+alpha1*t));
+		pts.Append (Point<3> (sol+alpha2*t));
+
+		// *testout << "pts = " << endl << pts << endl;
+
+		/*
+		  cout << "sol1 = " << sol + alpha1 * t
+		  << ", sol2 = " << sol + alpha2 * t << endl;
+		*/
+	      }
+	  }
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+  /*
+    bool SpecialPointCalculation :: ExtremalPointPossible (const Surface * f1, 
+    const Surface * f2, 
+    int dir, 
+    const BoxSphere<3> & box)
+    {
+    double hn1, hn2, gn1, gn2;
+    Point<3> p;
+    Vec<3> g1, g2, v;
+    double f3;
+    double r = box.Diam()/2;
+
+    p = box.Center();
+
+    f1 -> CalcGradient (p, g1);
+    f2 -> CalcGradient (p, g2);
+
+    gn1 = g1.Length();
+    gn2 = g2.Length();
+
+    hn1 = f1 -> HesseNorm ();
+    hn2 = f2 -> HesseNorm ();
+
+    v = Cross (g1, g2);
+    f3 = fabs (v(dir-1));
+
+    //  (*testout) << "f3 = " << f3 << "  r = " << r 
+    //             << "normbound = " 
+    //             << (hn1 * (gn2 + r * hn2) + hn2 * (gn1 + r * hn1)) << endl;
+ 
+    return (f3 <= 3 * r * (hn1 * (gn2 + r * hn2) + hn2 * (gn1 + r * hn1)));
+    }
+
+
+
+    bool SpecialPointCalculation :: 
+    ExtremalPointNewtonConvergence (const Surface * f1, const Surface * f2, 
+    int dir, 
+    const BoxSphere<3> & box)
+    {
+    return box.Diam() < 1e-8;
+    }
+
+
+    bool SpecialPointCalculation :: 
+    ExtremalPointDegenerated (const Surface * f1, const Surface * f2, 
+    int dir, const BoxSphere<3> & box)
+    {
+    double gn1, gn2;
+    Point<3> p;
+    Vec<3> g1, g2, v;
+    double maxderiv;
+    double minv;
+    Vec<3> curv, t;
+    Vec<2> rs, x;
+    Mat<3> h1, h2;
+    Mat<2> a, inv;
+    double leftside;
+
+    if (box.Diam() > relydegtest) return 0;
+
+    p = box.Center();
+
+    f1 -> CalcGradient (p, g1);
+    f2 -> CalcGradient (p, g2);
+    gn1 = g1.Length();
+    gn2 = g2.Length();
+
+    v = Cross (g1, g2);
+    if (Abs (v) < epeps1 * gn1 * gn2) return 1;       // irregular edge
+
+    f1 -> CalcHesse (p, h1);
+    f2 -> CalcHesse (p, h2);
+
+    //  hn1 = f1 -> HesseNorm ();
+    //  hn2 = f2 -> HesseNorm ();
+
+    t = v;
+    a(0, 0) = g1 * g1;
+    a(0, 1) = 
+    a(1, 0) = g1 * g2;
+    a(1, 1) = g2 * g2;
+  
+    rs(0) = g1(dir-1);
+    rs(1) = g2(dir-1);
+
+    a.Solve (rs, x);
+
+    //  (*testout) << "g1 = " << g1 << " g2 = " << g2 << endl;
+    //  (*testout) << "lam = " << x << endl;
+    //  (*testout) << "h2 = " << h2 << endl;
+
+    leftside = fabs (x(0) * ( t * (h1 * t)) + 
+    x(1) * ( t * (h2 * t)));
+
+    //  (*testout) << "leftside = " << leftside << endl;
+
+    if (leftside < epeps2 * Abs2 (v)) return 1;  
+
+    return 0;
+    }
+  */
+ 
+
+  bool SpecialPointCalculation :: AddPoint (const Point<3> & p, int layer)
+  {
+    for (int i = 0; i < points->Size(); i++)
+      if (Dist2 ( (*points)[i], p) < epspointdist2 &&
+	  (*points)[i].GetLayer() == layer)
+	return false;
+
+    points->Append (MeshPoint(p, layer));
+    PrintMessageCR (3, "Found points ", points->Size());
+    return true;
+  }
+
+
+
+
+
+
+
+  void SpecialPointCalculation :: 
+  AnalyzeSpecialPoints (const CSGeometry & ageometry,
+			Array<MeshPoint> & apoints, 
+			Array<SpecialPoint> & specpoints)
+  {
+    static int timer = NgProfiler::CreateTimer ("CSG: analyze special points");
+    NgProfiler::RegionTimer reg (timer);
+
+
+    Array<int> surfind, rep_surfind, surfind2, rep_surfind2, surfind3;
+
+    Array<Vec<3> > normalvecs;
+    Vec<3> nsurf = 0.0;
+
+    Array<int> specpoint2point;
+    specpoints.SetSize (0);
+
+    geometry = &ageometry;
+
+    double geomsize = ageometry.MaxSize();
+ 
+    (*testout) << "AnalyzeSpecialPoints\n";
+
+    if (!apoints.Size()) return;
+
+
+    {
+      /*
+	sort points in the (arbitrary) direction dir
+	important for periodic boundaries: 
+	corner points on the left and the right boundary come in the same ordering
+      */
+      Vec<3> dir(1.2, 1.7, 0.9);
+      
+      Array<double> coord(apoints.Size());
+      for (int i = 0; i < apoints.Size(); i++)
+	coord[i] = dir * Vec<3> (apoints[i]);
+      
+      QuickSort (coord, apoints);
+    }
+
+
+
+
+
+    Box<3> bbox (apoints[0], apoints[0]);
+    for (int i = 1; i < apoints.Size(); i++)
+      bbox.Add (apoints[i]);
+    bbox.Increase (0.1 * bbox.Diam());
+
+    (*testout) << "points = " << apoints << endl;
+
+    Point3dTree searchtree (bbox.PMin(), bbox.PMax());
+    Array<int> locsearch;
+
+    for (int si = 0; si < ageometry.GetNTopLevelObjects(); si++)
+      {
+	const TopLevelObject * tlo = ageometry.GetTopLevelObject(si);
+
+	const Solid * sol = tlo->GetSolid();
+	const Surface * surf = tlo->GetSurface();
+
+
+	for (int i = 0; i < apoints.Size(); i++)
+	  {
+	    Point<3> p = apoints[i];
+	    
+#ifdef DEVELOP
+	    *testout << "                               test point " << p << endl;
+#endif	    
+
+	    if (tlo->GetLayer() != apoints[i].GetLayer())
+	      continue;
+	    
+
+	    Solid * locsol;
+	    sol -> TangentialSolid (p, locsol, surfind, ideps*geomsize);
+
+
+	    rep_surfind.SetSize (surfind.Size());
+	    int num_indep_surfs = 0;
+	    
+	    for (int j = 0; j < surfind.Size(); j++)
+	      {
+		rep_surfind[j] = ageometry.GetSurfaceClassRepresentant (surfind[j]);
+		bool found = false;
+		for (int k = 0; !found && k < j; k++)
+		  found = (rep_surfind[k] == rep_surfind[j]);
+		if(!found)
+		  num_indep_surfs++;
+	      }
+	    
+
+#ifdef DEVELOP
+	    *testout << "surfs = " << surfind << endl;
+	    *testout << "rep_surfs = " << rep_surfind << endl;
+#endif
+
+	    if (!locsol) continue;
+
+	  
+	    // get all surface indices, 
+	    if (surf)
+	      {
+		// locsol -> GetSurfaceIndices (surfind);
+		bool hassurf = 0;
+		for (int m = 0; m < surfind.Size(); m++)
+		  if (ageometry.GetSurface(surfind[m]) == surf)
+		    hassurf = 1;
+
+		if (!hassurf)
+		  continue;
+
+		nsurf = surf->GetNormalVector (p);
+	      }
+
+	    /*
+	    // get independent surfaces of tangential solid
+	    BoxSphere<3> box(p,p);
+	    box.Increase (1e-6*geomsize);
+	    box.CalcDiamCenter();
+	    ageometry.GetIndependentSurfaceIndices (locsol, box, surfind);
+	    */
+
+	    // ageometry.GetIndependentSurfaceIndices (surfind);
+
+
+	    normalvecs.SetSize(surfind.Size());
+	    for (int j = 0; j < surfind.Size(); j++)
+	      normalvecs[j] = 
+		ageometry.GetSurface(surfind[j]) -> GetNormalVector(apoints[i]);
+
+
+	    for (int j = 0; j < normalvecs.Size(); j++)
+	      for (int k = 0; k < normalvecs.Size(); k++)
+		{
+		  if (rep_surfind[j] == rep_surfind[k]) continue;
+		  //if (j == k) continue;
+
+		  Vec<3> t;
+
+		  if (dynamic_cast<const Polyhedra*> (ageometry.surf2prim[surfind[j]]) && 
+		      ageometry.surf2prim[surfind[j]] == 
+		      ageometry.surf2prim[surfind[k]])
+		    {
+		      t = ageometry.surf2prim[surfind[j]] -> 
+			SpecialPointTangentialVector (p, surfind[j], surfind[k]);
+		    }
+		  else
+		    {
+		      t = Cross (normalvecs[j], normalvecs[k]);
+		    }
+
+
+		  if (Abs2 (t) < 1e-8)
+		    continue;
+
+#ifdef DEVELOP
+		  *testout << "           tangential vector " << t << endl;
+#endif
+
+		  t.Normalize();
+
+		  
+		  // try tangential direction t
+		  if (surf && fabs (nsurf * t) > 1e-6)
+		    continue;
+
+		
+#ifdef DEVELOP
+		  *testout << "           j " << j << " k " << k << endl;
+#endif  
+
+		  if (!surf)
+		    {
+		      // compute second order approximation
+		      // c(s) = p + s t + s*s/2 t2
+		      Vec<3> gradj, gradk;
+		      Mat<3> hessej, hessek;
+		      ageometry.GetSurface (surfind[j]) -> CalcGradient (p, gradj);
+		      ageometry.GetSurface (surfind[k]) -> CalcGradient (p, gradk);
+		      ageometry.GetSurface (surfind[j]) -> CalcHesse (p, hessej);
+		      ageometry.GetSurface (surfind[k]) -> CalcHesse (p, hessek);
+		      
+		      Vec<2> rhs;
+		      Vec<3> t2;
+		      Mat<2,3> mat;
+		      Mat<3,2> inv;
+		      for (int l = 0; l < 3; l++)
+			{
+			  mat(0,l) = gradj(l);
+			  mat(1,l) = gradk(l);
+			}
+		      rhs(0) = -t * (hessej * t);
+		      rhs(1) = -t * (hessek * t);
+
+		      CalcInverse (mat, inv);
+		      t2 = inv * rhs;
+
+		      
+		      /*
+		      ageometry.GetIndependentSurfaceIndices 
+			(locsol, p, t, surfind2);
+		      */
+
+		      Solid * locsol2;
+		      locsol -> TangentialSolid3 (p, t, t2, locsol2, surfind2, ideps*geomsize); 
+		      if (!locsol2) continue;
+		      
+		      // locsol2 -> GetTangentialSurfaceIndices3 (p, t, t2, surfind2, 1e-9*geomsize);
+
+		      rep_surfind2.SetSize (surfind2.Size());
+		      for (int j2 = 0; j2 < surfind2.Size(); j2++)
+			rep_surfind2[j2] = ageometry.GetSurfaceClassRepresentant (surfind2[j2]);
+
+#ifdef DEVELOP
+		      (*testout) << "surfind2 = " << endl << surfind2 << endl;
+#endif
+		      Array<int> surfind2_aux(surfind2);
+		      ageometry.GetIndependentSurfaceIndices (surfind2_aux);
+#ifdef DEVELOP
+		      (*testout) << "surfind2,rep = " << endl << surfind2_aux << endl;
+#endif
+
+		      bool ok = true;
+
+		      // intersecting surfaces must be in second order tangential solid
+		      /*
+		      if (!surfind2.Contains(surfind[j]) ||
+			  !surfind2.Contains(surfind[k]))
+			ok = false;
+		      */
+		      if (!surfind2_aux.Contains(rep_surfind[j]) ||
+			  !surfind2_aux.Contains(rep_surfind[k]))
+			ok = false;
+
+#ifdef DEVELOP
+		      (*testout) << "ok,1 = " << ok << endl;
+#endif
+
+		      // there must be 2 different tangential faces to the edge
+		      int cnt_tang_faces = 0;
+		      for (int l = 0; l < surfind2.Size(); l++)
+			{
+			  Vec<3> nv =
+			    ageometry.GetSurface(surfind2[l]) -> GetNormalVector(p);
+
+			 
+			  Vec<3> m1 = Cross (t, nv);
+			  Vec<3> m2 = -m1;
+			  bool isface1 = 0, isface2 = 0;
+			  
+			  Solid * locsol3;
+
+			  // locsol2 -> TangentialSolid2 (p, m1, locsol3, surfind3, 1e-9*geomsize);
+			  locsol -> TangentialEdgeSolid (p, t, t2, m1, locsol3, surfind3, ideps*geomsize);
+
+			  //ageometry.GetIndependentSurfaceIndices (surfind3);
+
+			  if (surfind3.Contains(surfind2[l]))
+			    isface1 = 1;
+			  delete locsol3;
+			  
+			  // locsol2 -> TangentialSolid2 (p, m2, locsol3, surfind3, 1e-9*geomsize);
+			  locsol -> TangentialEdgeSolid (p, t, t2, m2, locsol3, surfind3, ideps*geomsize); 
+
+			  // ageometry.GetIndependentSurfaceIndices (surfind3);
+
+			  
+			  if (surfind3.Contains(surfind2[l]))
+			    isface2 = 1;
+			  delete locsol3;
+
+			  if (isface1 != isface2)
+			    cnt_tang_faces++;
+			}
+
+#ifdef DEVELOP
+		      (*testout) << "cnt_tang = " << cnt_tang_faces << endl;
+#endif
+
+		      if (cnt_tang_faces < 1)
+			ok = false;
+
+		      delete locsol2;
+		      if (!ok) continue;
+		    }
+
+		  
+		  // edge must be on tangential surface
+		  bool isedge = 
+		    locsol->VectorIn (p, t) &&
+		    !locsol->VectorStrictIn (p, t);
+		  
+#ifdef DEVELOP
+		  (*testout) << "isedge,1 = " << isedge << "\n";
+#endif		
+  
+		  // there must exist at least two different faces on edge
+		  if (isedge)
+		    {
+		      // *testout << "succ 1" << endl;
+		      int cnts = 0;
+		      for (int m = 0; m < surfind.Size(); m++)
+			{
+			  if (fabs (normalvecs[m] * t) > 1e-6)
+			    continue;
+			  
+			  Vec<3> s = Cross (normalvecs[m], t);
+			  Vec<3> t2a = t + 0.01 *s;
+			  Vec<3> t2b = t - 0.01 *s;
+			  
+			  bool isface =
+			    (locsol->VectorIn (p, t2a, 1e-6*geomsize) &&
+			     !locsol->VectorStrictIn (p, t2a, 1e-6*geomsize))
+			    ||
+			    (locsol->VectorIn (p, t2b, 1e-6*geomsize) &&
+			     !locsol->VectorStrictIn (p, t2b, 1e-6*geomsize));
+			  
+			  /*
+			  bool isface =
+			    (locsol->VectorIn (p, t2a) &&
+			     !locsol->VectorStrictIn (p, t2a))
+			    ||
+			    (locsol->VectorIn (p, t2b) &&
+			     !locsol->VectorStrictIn (p, t2b));
+			  */
+
+			  if (isface)
+			    {
+			      cnts++;
+			    }
+			}
+		      if (cnts < 2) isedge = 0;
+		    }
+		  
+		  if (isedge)
+		    {
+#ifdef DEVELOP
+		      *testout << "success" << endl;
+#endif
+		      int spi = -1;
+		      
+		      const double searchradius = 1e-4*geomsize;//1e-5*geomsize;
+		      searchtree.GetIntersecting (apoints[i]-Vec3d(searchradius,searchradius,searchradius), 
+						  apoints[i]+Vec3d(searchradius,searchradius,searchradius), 
+						  locsearch);
+		      
+		      for (int m = 0; m < locsearch.Size(); m++)
+			{
+			  if (Dist2 (specpoints[locsearch[m]].p, apoints[i]) < 1e-10*geomsize
+			      && Abs2(specpoints[locsearch[m]].v - t) < 1e-8)
+			    {
+			      spi = locsearch[m];
+			      break;
+			    }
+			}
+		      
+		      
+		      if (spi == -1)
+			{
+			  spi = specpoints.Append (SpecialPoint()) - 1;
+			  specpoint2point.Append (i);
+			  specpoints.Last().unconditional = 0;
+			  searchtree.Insert (apoints[i], spi);
+			}
+
+		      if(!specpoints[spi].unconditional)
+			{
+			  specpoints[spi].p = apoints[i];
+			  specpoints[spi].v = t;
+			  //if (surfind.Size() >= 3)
+			  if (num_indep_surfs >= 3)
+			    specpoints[spi].unconditional = 1;
+			  specpoints[spi].s1 = rep_surfind[j];
+			  specpoints[spi].s2 = rep_surfind[k];
+			  specpoints[spi].s1_orig = surfind[j];
+			  specpoints[spi].s2_orig = surfind[k];
+			  specpoints[spi].layer = apoints[i].GetLayer();
+			  for (int up = 0; up < geometry->GetNUserPoints(); up++)
+			    if (Dist (geometry->GetUserPoint(up), apoints[i]) < 1e-8*geomsize)
+			      specpoints[spi].unconditional = 1;
+			  for (int ip = 0; ip < geometry->GetNIdentPoints(); ip++)
+			    if (Dist (geometry->GetIdentPoint(ip), apoints[i]) < 1e-8*geomsize)
+			      specpoints[spi].unconditional = 1;
+			}
+		    }
+		  
+		}
+
+	    delete locsol;
+	  }
+      }
+
+    /*
+    BitArray testuncond (specpoints.Size());
+    testuncond.Clear();
+    for(int i = 0; i<specpoints.Size(); i++)
+      {
+	if(testuncond.Test(i))
+	  continue;
+	
+	Array<int> same;
+	same.Append(i);
+	
+	for(int j = i+1; j<specpoints.Size(); j++)
+	  {
+	    if(Dist(specpoints[i].p,specpoints[j].p) < 1e-20)
+	      {
+		same.Append(j);
+		testuncond.Set(j);
+	      }
+	  }
+	
+	if(same.Size() < 3)
+	  for(int j=0; j<same.Size(); j++)
+	    {
+	      (*testout) << "setting " << specpoints[same[j]].p << "; " << specpoints[same[j]].v << "; " 
+			 <<specpoints[same[j]].unconditional << " to conditional" << endl;
+	      specpoints[same[j]].unconditional=0;
+	    }
+      }
+    */
+
+
+    // if special point is unconditional on some solid,
+    // it must be unconditional everywhere:
+
+    BitArray uncond (apoints.Size());
+    uncond.Clear();
+
+    for (int i = 0; i < specpoints.Size(); i++)
+      if (specpoints[i].unconditional)
+	uncond.Set (specpoint2point[i]);
+  
+    for (int i = 0; i < specpoints.Size(); i++)
+      specpoints[i].unconditional = 
+	uncond.Test (specpoint2point[i]) ? 1 : 0;
+  }
+}
diff --git a/contrib/Netgen/libsrc/csg/specpoin.hpp b/contrib/Netgen/libsrc/csg/specpoin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a6d850c932e278290fcb8076e9dbb1d5fee986cd
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/specpoin.hpp
@@ -0,0 +1,191 @@
+#ifndef FILE_SPECPOIN
+#define FILE_SPECPOIN
+
+
+/**************************************************************************/
+/* File:   specpoin.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+  extern DLL_HEADER MeshingParameters mparam;
+
+  /*
+
+  Special Point Calculation
+  
+  */
+
+  class Surface;
+  class Solid;
+
+  /// Special point.
+  class SpecialPoint
+  {
+  public:
+    /// coordinates
+    Point<3> p;
+    /// tangential to edge
+    Vec<3> v;
+    ///
+    int layer;
+    /// point must be used in mesh 
+    bool unconditional; 
+
+    /// surfaces defining edge 
+    int s1, s2;
+    /// if s1 and s2 are only representatives, then these are the original indices
+    int s1_orig, s2_orig;
+    int nr;
+    ///
+    SpecialPoint () : p(0,0,0), v(0,0,0), layer(0), unconditional(0), s1(0), s2(0), s1_orig(0), s2_orig(0)
+    { ; }
+
+    ///
+    SpecialPoint (const SpecialPoint & sp2);
+
+    ///
+    SpecialPoint & operator= (const SpecialPoint & sp2);
+  
+    ///
+    void Print (ostream & str) const;
+
+
+    int GetLayer() const { return layer; }
+
+    ///
+    bool HasSurfaces (int as1, int as2) const
+    {
+      return ( (s1 == as1 && s2 == as2) || (s1 == as2 && s2 == as1) );
+    }
+  };
+
+  inline ostream & operator<< (ostream & ost, const SpecialPoint & sp)
+  {
+    sp.Print (ost);
+    return ost;
+  }
+
+
+
+
+  ///
+  class SpecialPointCalculation
+  {
+  private:
+    ///
+    const CSGeometry * geometry;
+    ///
+    Array<MeshPoint> * points;
+    ///
+    Array<long int> boxesinlevel;
+
+    ///
+    double size;
+    ///
+    double relydegtest;   // maximal dimension of bisection intervall for
+    /// test of degeneration parameters
+    double cpeps1, epeps1, epeps2, epspointdist2;
+
+    double ideps;
+
+  public: 
+
+    ///
+    SpecialPointCalculation (); 
+  
+    ///
+    void SetIdEps(const double epsin) {ideps = epsin;}
+
+    ///
+    void CalcSpecialPoints (const CSGeometry & ageometry, 
+			    Array<MeshPoint> & points);
+    ///
+    void AnalyzeSpecialPoints (const CSGeometry & geometry, 
+			       Array<MeshPoint> & points, 
+			       Array<SpecialPoint> & specpoints);
+
+  protected:
+    ///
+    void CalcSpecialPointsRec (const Solid * sol, int layer,
+			       const BoxSphere<3> & box, 
+			       int level, 
+			       bool calccp, bool calcep);
+
+
+    ///
+    bool CrossPointNewtonConvergence (const Surface * f1, const Surface * f2, 
+				      const Surface * f3, const BoxSphere<3> & box);  
+    ///
+    bool CrossPointDegenerated (const Surface * f1, const Surface * f2,
+				const Surface * f3, const BoxSphere<3> & box) const;
+    ///
+    void CrossPointNewton (const Surface * f1, const Surface * f2, 
+			   const Surface * f3, Point<3> & p);
+  
+    bool EdgeNewtonConvergence (const Surface * f1, const Surface * f2, 
+				const Point<3> & p);  
+    ///
+    bool EdgeDegenerated (const Surface * f1, const Surface * f2,
+			  const BoxSphere<3> & box) const;
+    ///
+    void EdgeNewton (const Surface * f1, const Surface * f2, 
+		     Point<3> & p);
+    ///
+    bool IsEdgeExtremalPoint (const Surface * f1, const Surface * f2, 
+			      const Point<3> & p, Point<3> & pp, double rad);
+
+
+
+    /*
+   ///
+   bool ExtremalPointPossible (const Surface * f1, const Surface * f2, 
+   int dir, const BoxSphere<3> & box);
+   ///
+   bool ExtremalPointDegenerated (const Surface * f1, const Surface * f2, 
+   int dir, const BoxSphere<3> & box);
+   ///
+   bool ExtremalPointNewtonConvergence (const Surface * f1, const Surface * f2, 
+   int dir, const BoxSphere<3> & box);
+    */
+    ///
+    void ExtremalPointNewton (const Surface * f1, const Surface * f2, 
+			      int dir, Point<3> & p);
+
+
+    ///
+    bool AddPoint (const Point<3> & p, int layer);
+
+    void ComputeExtremalPoints (const Plane * plane, 
+				const QuadraticSurface * quadric, 
+				Array<Point<3> > & pts);
+
+    void ComputeExtremalPoints (const Sphere * sphere1, 
+				const Sphere * sphere2, 
+				Array<Point<3> > & pts);
+
+
+    void ComputeCrossPoints (const Plane * plane1, 
+			     const Plane * plane2, 
+			     const Plane * plane3, 
+			     Array<Point<3> > & pts);
+
+    void ComputeCrossPoints (const Plane * plane1, 
+			     const Plane * plane2, 
+			     const QuadraticSurface * quadratic, 
+			     Array<Point<3> > & pts);
+
+    void ComputeCrossPoints (const Sphere * sphere1, 
+			     const Sphere * sphere2, 
+			     const Sphere * sphere3, 
+			     Array<Point<3> > & pts);
+  };
+
+}
+
+#endif
+
+
diff --git a/contrib/Netgen/libsrc/csg/spline3d.cpp b/contrib/Netgen/libsrc/csg/spline3d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b89e7f70935014cf1d5b0d539ee514bec8c29d82
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/spline3d.cpp
@@ -0,0 +1,355 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+splinesegment3d :: splinesegment3d (const Point<3> & ap1, const Point<3> & ap2, 
+				    const Point<3> & ap3)
+{
+  p1 = ap1;
+  p2 = ap2;
+  p3 = ap3;
+}
+
+
+/*
+  todo
+  Tip von Joerg Stiller:
+  setzt Du in 
+  void splinesegment3d :: Evaluate
+  Zeilen 54 und 56
+  b2 = 2 * t * (1-t);
+  b2 /= sqrt(2);
+  Das heisst, Du wichtest das zweite Bersteinpolynom mit 
+  w2 = 1 / sqrt(2);
+  Das ist aber nur fuer 45-Grad-Segmente korrekt. Fuer den
+  allgemeinen Fall funktioniert
+  w2 = ( e(p3 - p1), e(p2 - p1) );  // also cos(winkel(p3-p1, p2-p1))
+  bzw. schoen symmetrisch
+  w2 = ( e(p3 - p1), e(p2 - p1) )/2 + ( e(p1 - p3), e(p2 - p3) )/2;
+  Das ist natuerlich kein C++ Code sondern symbolisch, wobei
+  e(p3 - p1)    ist der von p1 zu p3 zeigende Einheitsvektor und
+  (a, b)        steht fuer das Skalarprodukt zweier Vektoren etc.
+
+  Eine vergleichbare Information steht auch irgendwo im Hoscheck & Lasser.
+  Ich habe das Buch aber eben nicht zur Hand.
+*/
+
+void splinesegment3d :: Evaluate (double t, Point<3> & p) const
+{
+  double x, y, z, w;
+  double b1, b2, b3;
+
+  b1 = (1-t)*(1-t);
+  b2 = 2 * t * (1-t);
+  b3 = t * t;
+
+  b2 /= sqrt(double(2));
+
+  x = p1(0) * b1 + p2(0) * b2 + p3(0) * b3;
+  y = p1(1) * b1 + p2(1) * b2 + p3(1) * b3;
+  z = p1(2) * b1 + p2(2) * b2 + p3(2) * b3;
+  w = b1 + b2 + b3;
+
+  p(0) = x / w;
+  p(1) = y / w;
+  p(2) = z / w;
+}
+
+void splinesegment3d :: EvaluateTangent (double t, Vec<3> & tang) const
+{
+  double x, y, z, w, xprime, yprime, zprime, wprime;
+  double b1, b2, b3, b1prime, b2prime, b3prime;
+
+  b1 = (1-t)*(1-t);
+  b2 = 2 * t * (1-t);
+  b3 = t * t;
+  b2 /= sqrt(double(2));
+
+  b1prime = 2 * t - 2;
+  b2prime = - 4 * t + 2;
+  b3prime = 2 * t;
+  b2prime /= sqrt(double(2));
+
+ 
+  x = p1(0) * b1 + p2(0) * b2 + p3(0) * b3;
+  y = p1(1) * b1 + p2(1) * b2 + p3(1) * b3;
+  z = p1(2) * b1 + p2(2) * b2 + p3(2) * b3;
+  w = b1 + b2 + b3;
+
+  xprime = p1(0) * b1prime + p2(0) * b2prime + p3(0) * b3prime;
+  yprime = p1(1) * b1prime + p2(1) * b2prime + p3(1) * b3prime;
+  zprime = p1(2) * b1prime + p2(2) * b2prime + p3(2) * b3prime;
+  wprime = b1prime + b2prime + b3prime;
+
+  tang(0) = (w * xprime - x * wprime) / (w * w);
+  tang(1) = (w * yprime - y * wprime) / (w * w);
+  tang(2) = (w * zprime - z * wprime) / (w * w);
+}
+ 
+
+void spline3d :: AddSegment (const Point<3> & ap1, const Point<3> & ap2, 
+			     const Point<3> & ap3)
+{
+  segments.Append (new splinesegment3d (ap1, ap2, ap3));
+}
+
+void spline3d :: Evaluate (double t, Point<3> & p) const
+{
+  int nr;
+  double loct;
+  static int cnt = 0;
+  
+  cnt++;
+  if (cnt % 10000 == 0) (*mycout) << "Evaluate calls: " << cnt << endl;
+
+  while (t < 0) t += GetNumSegments();
+  while (t >= GetNumSegments()) t -= GetNumSegments();
+  nr = 1 + int (t);
+  loct = t - nr + 1;
+  segments.Get(nr)->Evaluate (loct, p);
+}
+  
+void spline3d :: EvaluateTangent (double t, Vec<3> & tang) const
+{
+  int nr;
+  double loct;
+
+  while (t < 0) t += GetNumSegments();
+  while (t >= GetNumSegments()) t -= GetNumSegments();
+  nr = 1 + int (t);
+  loct = t - nr + 1;
+  segments.Get(nr)->EvaluateTangent (loct, tang);
+}
+
+
+double spline3d :: ProjectToSpline (Point<3> & p) const
+{
+  double t, tl, tu, dt, dist, mindist, optt(0);
+  Point<3> hp;
+  Vec<3> tanx, px;
+  
+  dt = 0.01;
+  mindist = 0;
+  for (t = 0; t <= GetNumSegments() + dt/2; t += dt)
+    {
+      Evaluate (t, hp);
+      dist = Dist (hp, p);
+      if (t == 0 || dist < mindist)
+	{
+	  optt = t;
+	  mindist = dist;
+	} 
+    }
+
+  
+  tu = optt + dt;
+  tl = optt - dt;
+  while (tu - tl > 1e-2)
+    {
+      optt = 0.5 * (tu + tl);
+      Evaluate (optt, hp);
+      EvaluateTangent (optt, tanx);
+      if (tanx * (hp - p) > 0)
+	tu = optt;
+      else
+	tl = optt;
+    } 
+
+  optt = 0.5 * (tu + tl);
+
+  optt = ProjectToSpline (p, optt);
+  return optt;
+}
+ 
+ 
+double spline3d :: ProjectToSpline (Point<3> & p, double optt) const
+{ 
+  double tl, tu, dt, val, dval, valu, vall;
+  Point<3> hp;
+  Vec<3> tanx, px;
+  int its = 0;
+  int cnt = 1000;
+  do
+    {
+      dt = 1e-8;
+      tl = optt - dt;
+      tu = optt + dt;
+    
+      EvaluateTangent (optt, tanx); 
+      Evaluate (optt, hp);
+      px = hp - p;
+      val =  px * tanx;
+    
+      EvaluateTangent (tl, tanx); 
+      Evaluate (tl, hp);
+      px = hp - p;
+      vall =  px * tanx;
+    
+      EvaluateTangent (tu, tanx); 
+      Evaluate (tu, hp);
+      px = hp - p;
+      valu =  px * tanx;
+    
+      dval = (valu - vall) / (2 * dt);
+
+      if (its % 100 == 99)    
+	(*testout) << "optt = " << optt 
+		   << " val = " << val 
+		   << " dval = " << dval << endl;
+      optt -= val / dval;
+      its++;
+      if (fabs(val) < 1e-8 && cnt > 5) cnt = 5;
+      cnt--;
+    }
+  while (cnt > 0);
+        
+  Evaluate (optt, p);
+  return optt;
+}
+  
+  
+splinetube :: splinetube (const spline3d & amiddlecurve, double ar)
+  : Surface(), middlecurve (amiddlecurve), r(ar)
+{
+  (*mycout) << "Splinetube Allocated, r = " << r << endl;
+
+}
+  
+void splinetube :: DefineTangentialPlane (const Point<3> & ap1, 
+					  const Point<3> & ap2)
+{
+  double t;
+  double phi, z;
+  
+  p1 = ap1;
+  p2 = ap2;
+  cp = p1;
+  t = middlecurve.ProjectToSpline (cp);
+  ex = p1 - cp;
+  middlecurve.EvaluateTangent (t, ez); 
+  ex.Normalize();
+  ez.Normalize();
+  ey = Cross (ez, ex);
+  
+  phi = r * atan2 (ey * (p2-cp), ex * (p2-cp));
+  z = ez * (p2 - cp); 
+  e2x(0) = phi;
+  e2x(1) = z;
+  e2x.Normalize();
+  e2y(1) = e2x(0);
+  e2y(0) = -e2x(1);
+  
+  //  (*testout) << "Defineplane: " << endl
+  //  	<< "p1 = " << p1 << "   p2 = " << p2 << endl
+  //  	<< "pc = " << cp << endl
+  //  	<< "ex = " << ex << " ey = " << ey << " ez = " << ez << endl
+  //  	<< "phi = " << phi << "  z = " << z << endl
+  //  	<< "e2x = " << e2x << " e2y = " << e2y << endl;
+}
+  
+void splinetube :: ToPlane (const Point<3> & p3d, Point<2> & pplain, double h, 
+			    int & zone) const
+{
+  Vec<2> v;
+  v(0) = r * atan2 (ey * (p3d-cp), ex * (p3d-cp));
+  v(1) = ez * (p3d - cp); 
+  zone = 0;
+  if (v(0) > r * 2) zone = 1;
+  if (v(0) < r * 2) zone = 2;
+  
+  pplain(0) = (v * e2x) / h;
+  pplain(1) = (v * e2y) / h;
+}
+  
+void splinetube :: FromPlane (const Point<2> & pplain, Point<3> & p3d, double h) const
+{
+  Vec<2> v;
+  
+  v(0) = pplain(0) * h * e2x(0) + pplain(1) * h * e2y(0);
+  v(1) = pplain(0) * h * e2x(1) + pplain(1) * h * e2y(1);
+  
+  p3d = p1 + v(0) * ey + v(1) * ez;
+
+  Project (p3d);
+}
+  
+void splinetube :: Project (Point<3> & p3d) const
+{
+  Point<3> hp;
+  
+  hp = p3d;
+  middlecurve.ProjectToSpline (hp);
+  
+  p3d = hp + (r / Dist(p3d, hp)) * (p3d - hp); 
+}
+
+
+
+double splinetube :: CalcFunctionValue (const Point<3> & point) const
+{
+  Point<3> hcp;
+  double rad;
+
+  hcp = point;
+  middlecurve.ProjectToSpline (hcp);
+  rad = Dist (hcp, point);
+  return 0.5 * (rad * rad / r - r);
+}
+  
+void splinetube :: CalcGradient (const Point<3> & point, Vec<3> & grad) const
+{
+  Point<3> hcp;
+
+  hcp = point;
+  middlecurve.ProjectToSpline (hcp);
+
+  grad = point - hcp;
+  grad /= r;
+}
+  
+  
+
+
+Point<3> splinetube :: GetSurfacePoint () const
+{
+  Point<3> p;
+  Vec<3> t, n;
+  
+  middlecurve.Evaluate (0, p);
+  middlecurve.EvaluateTangent (0, t);
+  n = t.GetNormal ();
+  n *= r;
+  (*mycout) << "p = " << p << " t = " << t << "  n = " << n << endl;
+  return p + n;
+}
+
+void splinetube :: Print (ostream & str) const
+{
+  int i;
+  str << "SplineTube, " 
+      << middlecurve.GetNumSegments () << " segments, r = " << r << endl;
+  for (i = 1; i <= middlecurve.GetNumSegments(); i++)
+    str << middlecurve.P1(i) << " - " 
+	<< middlecurve.P2(i) << " - " 
+	<< middlecurve.P3(i) << endl;
+}
+
+
+int splinetube :: BoxInSolid (const BoxSphere<3> & box) const
+  // 0 .. no, 1 .. yes, 2 .. maybe
+{
+  Point<3> pc = box.Center();
+  middlecurve.ProjectToSpline (pc);
+  double d = Dist (pc, box.Center());
+  
+  if (d < r - box.Diam()/2) return 1;
+  if (d > r + box.Diam()/2) return 0;
+  return 2;
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/spline3d.hpp b/contrib/Netgen/libsrc/csg/spline3d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..db3a40c56d98b3b6c3d3dbefab83d59ab810f720
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/spline3d.hpp
@@ -0,0 +1,99 @@
+namespace netgen
+{
+
+  ///
+  class splinesegment3d
+  {
+    ///
+    Point<3> p1, p2, p3;
+  
+  public:
+    ///
+    splinesegment3d (const Point<3> & ap1, const Point<3> & ap2, 
+		     const Point<3> & ap3);
+    ///
+    void Evaluate (double t, Point<3> & p) const;
+    ///
+    void EvaluateTangent (double t, Vec<3> & tang) const;
+    ///
+    const Point<3> & P1() const { return p1; }
+    ///
+    const Point<3> & P2() const { return p2; }
+    ///
+    const Point<3> & P3() const { return p3; }
+  };
+
+  ///
+  class spline3d
+  {
+    ///
+    Array<splinesegment3d *> segments;
+  
+  public:
+    ///
+    spline3d () { };
+    ///
+    void AddSegment (const Point<3> & ap1, const Point<3> & ap2, const Point<3> & ap3);
+    ///
+    int GetNumSegments () const { return segments.Size(); }
+    ///
+    double ProjectToSpline (Point<3> & p) const;
+    ///
+    double ProjectToSpline (Point<3> & p, double t) const;
+    ///
+    void Evaluate (double t, Point<3> & p) const;
+    ///
+    void EvaluateTangent (double t, Vec<3> & tang) const;
+    ///
+    const Point<3> & P1(int i) const { return segments.Get(i)->P1(); }
+    ///
+    const Point<3> & P2(int i) const { return segments.Get(i)->P2(); }
+    ///
+    const Point<3> & P3(int i) const { return segments.Get(i)->P3(); }
+  };
+  
+  ///
+  class splinetube : public Surface
+  {
+    ///
+    const spline3d & middlecurve;
+    ///
+    double r;
+    ///  Vec<3> ex, ey, ez;
+    Vec<2> e2x, e2y;
+    ///
+    Point<3> cp;
+  
+  public:
+    ///
+    splinetube (const spline3d & amiddlecurve, double ar);
+  
+    ///
+    virtual void DefineTangentialPlane (const Point<3> & ap1, const Point<3> & ap2);
+    ///
+    virtual void ToPlane (const Point<3> & p, Point<2> & pplain, double h, int & zone) const;
+    ///
+    virtual void FromPlane (const Point<2> & pplain, Point<3> & p, double h) const;
+    ///
+    virtual void Project (Point<3> & p) const;
+
+    //  virtual int RootInBox (const box3d & box) const { return 0; }
+    /// 0 .. no, 1 .. yes, 2 .. maybe
+
+    virtual int BoxInSolid (const BoxSphere<3> & box) const;
+    /// 0 .. no, 1 .. yes, 2 .. maybe
+
+    virtual double CalcFunctionValue (const Point<3> & point) const;
+    ///
+    virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const;
+    ///
+    virtual double HesseNorm () const { return 0.5 / r; }
+    ///
+    virtual Point<3> GetSurfacePoint () const;
+    ///
+    virtual void Print (ostream & str) const;
+  };  
+
+
+}
+
diff --git a/contrib/Netgen/libsrc/csg/surface.cpp b/contrib/Netgen/libsrc/csg/surface.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe0415ec722b47289f100f17797a3a6966ed38a1
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/surface.cpp
@@ -0,0 +1,568 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <csg.hpp>
+
+#include <linalg.hpp>
+#include <meshing.hpp>
+
+
+namespace netgen
+{
+Surface :: Surface ()
+{
+  maxh = 1e10;
+  name = new char[7];
+  strcpy (name, "noname");
+  bcprop = -1;
+  bcname = "default";
+}
+
+Surface :: ~Surface()
+{
+  delete [] name;
+}
+
+
+void Surface :: SetName (const char * aname)
+{
+  delete [] name;
+  name = new char[strlen (aname)+1];
+  strcpy (name, aname);
+}
+
+
+int Surface :: PointOnSurface (const Point<3> & p,
+			       double eps) const
+{
+  double val = CalcFunctionValue (p);
+  return fabs (val) < eps;
+}
+
+
+void Surface :: CalcHesse (const Point<3> & point, Mat<3> & hesse) const
+{
+  double dx = 1e-5;
+  Point<3> hp1, hp2;
+  Vec<3> g1, g2;
+
+  for (int i = 0; i < 3; i++)
+    {
+      hp1 = point;
+      hp2 = point;
+
+      hp1(i) += dx;
+      hp2(i) -= dx;
+
+      CalcGradient (hp1, g1);
+      CalcGradient (hp2, g2);
+      	
+      for (int j = 0; j < 3; j++)
+	hesse(i, j) = (g1(j) - g2(j)) / (2 * dx);
+    }
+}
+  
+/*
+void Surface :: GetNormalVector (const Point<3> & p, Vec<3> & n) const
+{
+  CalcGradient (p, n);
+  n.Normalize();
+}
+*/
+Vec<3> Surface :: GetNormalVector (const Point<3> & p) const
+{
+  Vec<3> n;
+  CalcGradient (p, n);
+  n.Normalize();
+  return n;
+}
+
+void Surface :: DefineTangentialPlane (const Point<3> & ap1, 
+				       const Point<3> & ap2)
+{
+  p1 = ap1;
+  p2 = ap2;
+  
+  ez = GetNormalVector (p1);
+  ex = p2 - p1;
+  ex -= (ex * ez) * ez;
+  ex.Normalize();
+  ey = Cross (ez, ex);  
+}
+
+void Surface :: ToPlane (const Point<3> & p3d, Point<2> & pplane, 
+			 double h, int & zone) const
+{
+  Vec<3> p1p, n;
+
+  n = GetNormalVector (p3d);
+  if (n * ez < 0)
+    {
+      zone = -1;
+      pplane(0) = 1e8;
+      pplane(1) = 1e9;
+      return;
+    }
+  
+  p1p = p3d - p1;
+  pplane(0) = (p1p * ex) / h;
+  pplane(1) = (p1p * ey) / h;
+  zone = 0;
+}	
+
+void Surface :: FromPlane (const Point<2> & pplane, 
+			   Point<3> & p3d, double h) const 
+{ 
+  p3d = p1 
+    + (h * pplane(0)) * ex 
+    + (h * pplane(1)) * ey;
+  
+  Project (p3d);
+}
+
+void Surface :: Project (Point<3> & p) const
+{
+  Vec<3> n;
+  double val;
+
+  for (int i = 1; i <= 10; i++)
+    {
+      val = CalcFunctionValue (p);
+      if (fabs (val) < 1e-12) return;
+	
+      CalcGradient (p, n);
+      p -= (val / Abs2 (n)) * n;
+    }
+}
+
+void Surface :: SkewProject (Point<3> & p, const Vec<3> & direction) const
+{
+  Point<3> startp(p);
+  double t_old(0),t_new(1);
+  Vec<3> grad;
+  for(int i=0; fabs(t_old-t_new) > 1e-20 && i<15; i++)
+    {
+      t_old = t_new;
+      CalcGradient(p,grad);
+      t_new = t_old - CalcFunctionValue(p)/(grad*direction);
+      p = startp + t_new*direction;
+    }
+}
+
+
+double Surface :: MaxCurvature () const
+{ 
+  return 0.5 * HesseNorm (); 
+}
+
+double Surface :: 
+MaxCurvatureLoc (const Point<3> & /* c */ , double /* rad */) const
+{ 
+  return MaxCurvature (); 
+}
+              
+
+
+double Surface :: LocH (const Point<3> & p, double x, 
+			double c, double hmax) const
+  // finds h <= hmax, s.t.  h * \kappa_x*h < c
+{
+  /*
+    double h, hmin, kappa;
+    hmin = 0;
+  
+    while (hmin < 0.9 * hmax)
+    {
+    h = 0.5 * (hmin + hmax);
+    kappa = 2 * MaxCurvatureLoc (p, x * h);
+      
+    if (kappa * h >= c)
+    hmax = h;
+    else
+    hmin = h;
+    }
+    return h;
+  */
+
+  double hret;
+  double kappa = MaxCurvatureLoc (p, x*hmax);
+
+  kappa *= c *  mparam.curvaturesafety;
+  
+  if (hmax * kappa < 1)
+    hret = hmax;
+  else
+    hret = 1 / kappa;
+
+  if (maxh < hret)
+    hret = maxh;
+
+  return hret;
+}
+
+
+
+
+Primitive :: Primitive ()
+{
+  surfaceids.SetSize (1);
+  surfaceactive.SetSize (1);
+  surfaceactive[0] = 1;
+}
+
+Primitive :: ~Primitive()
+{
+  ;
+}
+
+int Primitive :: GetSurfaceId (int i) const
+{
+  return surfaceids[i];
+}
+
+void Primitive :: SetSurfaceId (int i, int id) 
+{
+  surfaceids[i] = id;
+}
+
+
+
+
+void Primitive :: GetPrimitiveData (const char *& classname, 
+				    Array<double> & coeffs) const
+{
+  classname = "undef";
+  coeffs.SetSize (0);
+}
+
+void Primitive :: SetPrimitiveData (Array<double> & coeffs)
+{
+  ;
+}
+
+Primitive * Primitive :: CreatePrimitive (const char * classname)
+{
+  if (strcmp (classname, "sphere") == 0)
+    return Sphere::CreateDefault();
+  if (strcmp (classname, "plane") == 0)
+    return Plane::CreateDefault();
+  if (strcmp (classname, "cylinder") == 0)
+    return Cylinder::CreateDefault();
+  if (strcmp (classname, "cone") == 0)
+    return Cone::CreateDefault();
+  if (strcmp (classname, "brick") == 0)
+    return Brick::CreateDefault();
+
+
+  stringstream ost;
+  ost << "Primitve::CreatePrimitive not implemented for " << classname << endl;
+  throw NgException (ost.str());
+}
+
+
+Primitive * Primitive :: Copy () const
+{
+  stringstream ost;
+  ost << "Primitve::Copy not implemented for " << typeid(*this).name() << endl;
+  throw NgException (ost.str());
+}
+
+
+void Primitive :: Transform (Transformation<3> & trans)
+{
+  stringstream ost;
+  ost << "Primitve::Transform not implemented for " << typeid(*this).name() << endl;
+  throw NgException (ost.str());
+}
+
+void Primitive :: GetTangentialSurfaceIndices (const Point<3> & p, 
+					       Array<int> & surfind, double eps) const
+{
+  for (int j = 0; j < GetNSurfaces(); j++)
+    if (fabs (GetSurface(j).CalcFunctionValue (p)) < eps)
+      if (!surfind.Contains (GetSurfaceId(j)))
+	surfind.Append (GetSurfaceId(j));
+}
+
+
+void Primitive :: 
+GetTangentialVecSurfaceIndices (const Point<3> & p, const Vec<3> & v,
+				Array<int> & surfind, double eps) const
+{
+  cout << "get tangvecsurfind not implemented" << endl;
+  surfind.SetSize (0);
+}
+
+void Primitive :: 
+GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2,
+				 Array<int> & surfind, double eps) const
+{
+  for (int j = 0; j < GetNSurfaces(); j++)
+    {
+      if (fabs (GetSurface(j).CalcFunctionValue (p)) < eps)
+	{
+	  Vec<3> grad;
+	  GetSurface(j).CalcGradient (p, grad);
+	  if (sqr (grad * v1) < 1e-6 * v1.Length2() * grad.Length2()  && 
+	      sqr (grad * v2) < 1e-6 * v2.Length2() * grad.Length2() )   // new, 18032006 JS
+	    {
+	      if (!surfind.Contains (GetSurfaceId(j)))
+		surfind.Append (GetSurfaceId(j));
+	    }
+	}
+    }
+}
+
+
+
+
+INSOLID_TYPE Primitive :: 
+VecInSolid2 (const Point<3> & p,
+	     const Vec<3> & v1,
+	     const Vec<3> & v2,
+	     double eps) const
+{
+  //(*testout) << "Primitive::VecInSolid2" << endl;
+  Point<3> hp = p + 1e-3 * v1 + 1e-5 * v2;
+
+  INSOLID_TYPE res = PointInSolid (hp, eps);
+  //  (*testout) << "vectorin2, type = " << typeid(*this).name() << ", res = " << res << endl;
+
+  return res;
+}
+
+INSOLID_TYPE Primitive :: 
+VecInSolid3 (const Point<3> & p,
+	     const Vec<3> & v1,
+	     const Vec<3> & v2,
+	     double eps) const
+{
+  //(*testout) << "Primitive::VecInSolid3" << endl;
+  return VecInSolid (p, v1, eps);
+}
+
+INSOLID_TYPE Primitive :: 
+VecInSolid4 (const Point<3> & p,
+	     const Vec<3> & v,
+	     const Vec<3> & v2,
+	     const Vec<3> & m,
+	     double eps) const
+{
+  return VecInSolid2 (p, v, m, eps);
+}
+
+
+
+
+
+OneSurfacePrimitive :: OneSurfacePrimitive()
+{
+  ;
+}
+
+OneSurfacePrimitive :: ~OneSurfacePrimitive()
+{
+  ;
+}
+
+
+INSOLID_TYPE OneSurfacePrimitive :: 
+PointInSolid (const Point<3> & p,
+	      double eps) const
+{
+  double hv1 = (GetSurface(0).CalcFunctionValue(p));
+  if (hv1 <= -eps)
+    return IS_INSIDE;
+  if (hv1 >= eps)
+    return IS_OUTSIDE;
+  return DOES_INTERSECT;
+}
+ 
+
+INSOLID_TYPE OneSurfacePrimitive :: 
+VecInSolid (const Point<3> & p, const Vec<3> & v,
+	    double eps) const
+{
+  double hv1 = (GetSurface(0).CalcFunctionValue(p));
+  if (hv1 <= -eps)
+    return IS_INSIDE;
+  if (hv1 >= eps)
+    return IS_OUTSIDE;
+
+
+  Vec<3> hv;
+  GetSurface(0).CalcGradient (p, hv);
+
+  hv1 = v * hv;
+
+  if (hv1 <= -eps)
+    return IS_INSIDE;
+  if (hv1 >= eps)
+    return IS_OUTSIDE;
+
+  return DOES_INTERSECT;
+}
+
+
+INSOLID_TYPE OneSurfacePrimitive :: 
+VecInSolid2 (const Point<3> & p,
+	     const Vec<3> & v1,
+	     const Vec<3> & v2,
+	     double eps) const
+{
+  double hv1 = (GetSurface(0).CalcFunctionValue(p));
+  if (hv1 <= -eps)
+    return IS_INSIDE;
+  if (hv1 >= eps)
+    return IS_OUTSIDE;
+
+  Vec<3> hv;
+
+  GetSurface(0).CalcGradient (p, hv);
+
+  hv1 = v1 * hv;
+  if (hv1 <= -eps)
+    return IS_INSIDE;
+  if (hv1 >= eps)
+    return IS_OUTSIDE;
+
+  double hv2 = v2 * hv;
+  if (hv2 <= 0)
+    return IS_INSIDE;
+  else
+    return IS_OUTSIDE;
+}
+  
+
+
+INSOLID_TYPE OneSurfacePrimitive :: 
+VecInSolid3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2,
+	     double eps) const
+{
+  //(*testout) << "OneSurfacePrimitive::VecInSolid3" << endl;
+  double hv1 = (GetSurface(0).CalcFunctionValue(p));
+  if (hv1 <= -eps)
+    return IS_INSIDE;
+  if (hv1 >= eps)
+    return IS_OUTSIDE;
+
+  Vec<3> grad;
+  GetSurface(0).CalcGradient (p, grad);
+
+  hv1 = v * grad;
+  if (hv1 <= -eps) return IS_INSIDE;
+  if (hv1 >= eps) return IS_OUTSIDE;
+
+  Mat<3> hesse;
+  GetSurface(0).CalcHesse (p, hesse);
+
+  double hv2 = v2 * grad + v * (hesse * v);
+
+  if (hv2 <= -eps) return IS_INSIDE;
+  if (hv2 >= eps) return IS_OUTSIDE;
+
+  return DOES_INTERSECT;
+}
+
+
+
+
+INSOLID_TYPE OneSurfacePrimitive :: 
+VecInSolid4 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2,
+	     const Vec<3> & m,
+	     double eps) const
+{
+  double hv1 = (GetSurface(0).CalcFunctionValue(p));
+  if (hv1 <= -eps)
+    return IS_INSIDE;
+  if (hv1 >= eps)
+    return IS_OUTSIDE;
+
+  Vec<3> grad;
+  GetSurface(0).CalcGradient (p, grad);
+
+  hv1 = v * grad;
+  if (hv1 <= -eps) return IS_INSIDE;
+  if (hv1 >= eps) return IS_OUTSIDE;
+
+  Mat<3> hesse;
+  GetSurface(0).CalcHesse (p, hesse);
+
+  double hv2 = v2 * grad + v * (hesse * v);
+
+  if (hv2 <= -eps) return IS_INSIDE;
+  if (hv2 >= eps) return IS_OUTSIDE;
+
+
+  double hv3 = m * grad;
+  if (hv3 <= -eps) return IS_INSIDE;
+  if (hv3 >= eps) return IS_OUTSIDE;
+
+  return DOES_INTERSECT;
+}
+
+
+
+
+
+
+
+int OneSurfacePrimitive :: GetNSurfaces() const
+{
+  return 1;
+}
+
+Surface & OneSurfacePrimitive :: GetSurface (int i)
+{
+  return *this;
+}
+
+const Surface & OneSurfacePrimitive :: GetSurface (int i) const
+{
+  return *this;
+}
+
+
+
+
+
+
+void ProjectToEdge (const Surface * f1, const Surface * f2, Point<3> & hp)
+{
+  Vec<2> rs, lam;
+  Vec<3> a1, a2;
+  Mat<2> a;
+
+  int i = 10;
+  while (i > 0)
+    {
+      i--;
+      rs(0) = f1 -> CalcFunctionValue (hp);
+      rs(1) = f2 -> CalcFunctionValue (hp);
+      f1->CalcGradient (hp, a1);
+      f2->CalcGradient (hp, a2);
+
+      double alpha = fabs(a1*a2)/sqrt(a1.Length2()*a2.Length2());
+      if(fabs(1.-alpha) < 1e-6)
+	{
+	  if(fabs(rs(0)) >= fabs(rs(1)))
+	    f1 -> Project(hp);
+	  else
+	    f2 -> Project(hp);
+	}
+      else
+	{
+
+	  a(0,0) = a1 * a1;
+	  a(0,1) = a(1,0) = a1 * a2;
+	  a(1,1) = a2 * a2;
+	  
+	  a.Solve (rs, lam);
+
+	  hp -= lam(0) * a1 + lam(1) * a2;
+	}
+
+      if (Abs2 (rs) < 1e-24 && i > 1) i = 1;
+    }
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/surface.hpp b/contrib/Netgen/libsrc/csg/surface.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..21821241eea0f1bd7d3b62c3896ab253725009b6
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/surface.hpp
@@ -0,0 +1,374 @@
+#ifndef FILE_SURFACE
+#define FILE_SURFACE
+
+/**************************************************************************/
+/* File:   surface.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   1. Dez. 95                                                     */
+/**************************************************************************/
+
+
+namespace netgen 
+{
+
+  class TriangleApproximation;
+
+
+  /**
+     Basis class for implicit surface geometry.
+     This class is used for generation of surface meshes
+     in NETGEN 
+  */
+  class Surface
+  {
+  protected:
+    /// invert normal vector
+    bool inverse;
+    /// maximal h in surface
+    double maxh;
+    /// name of surface
+    char * name;
+    /// boundary condition nr
+    int bcprop;
+    /// boundary condition label
+    string bcname;
+  
+  public:
+    Surface ();
+    /** @name Tangential plane.
+	The tangential plane is used for surface mesh generation.
+    */
+  
+    virtual ~Surface();
+
+  protected:
+    /** @name Points in the surface defining tangential plane.
+	Tangential plane is taken in p1, the local x-axis
+	is directed to p2.
+    */
+    //@{
+    ///
+    Point<3> p1;
+    ///
+    Point<3> p2;
+    //@}
+    /** @name Base-vectos for local coordinate system. */
+    //@{
+    /// in plane, directed p1->p2
+    Vec<3> ex;
+    /// in plane
+    Vec<3> ey;
+    /// outer normal direction
+    Vec<3> ez;
+    //@}
+  public:
+
+    void SetName (const char * aname);
+    const char * Name () const { return name; }
+
+    //@{
+    /**
+       Defines tangential plane in ap1.
+       The local x-coordinate axis points to the direction of ap2 */
+    virtual void DefineTangentialPlane (const Point<3> & ap1, 
+					const Point<3> & ap2);
+
+    /// Transforms 3d point p3d to local coordinates pplane
+    virtual void ToPlane (const Point<3> & p3d, Point<2> & pplane, 
+			  double h, int & zone) const;
+  
+    /// Transforms point pplane in local coordinates to 3d point
+    virtual void FromPlane (const Point<2> & pplane, 
+			    Point<3> & p3d, double h) const;
+    //@}
+
+
+    /// Project point p onto surface (closest point)
+    virtual void Project (Point<3> & p) const;
+
+    /// Project along direction
+    virtual void SkewProject(Point<3> & p, const Vec<3> & direction) const;
+
+    /// Is current surface identic to surface 2 ?
+    virtual int IsIdentic (const Surface & /* s2 */, int & /* inv */, 
+			   double /* eps */) const
+    { return 0; }
+  
+    ///
+    virtual int PointOnSurface (const Point<3> & p,
+				double eps = 1e-6) const;
+  
+
+    /** @name Implicit function.
+	Calculate function value and derivatives.
+    */
+    //@{
+    /// Calculate implicit function value in point point
+    virtual double CalcFunctionValue (const Point<3> & point) const = 0;
+
+    /**
+       Calc gradient of implicit function.
+       gradient should be O(1) at surface
+    */
+    virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const = 0;
+
+    /**
+       Calculate second derivatives of implicit function.
+    */
+    virtual void CalcHesse (const Point<3> & point, Mat<3> & hesse) const;
+
+    /**
+       Returns outer normal vector.
+    */
+    // virtual void GetNormalVector (const Point<3> & p, Vec<3> & n) const;
+    virtual Vec<3> GetNormalVector (const Point<3> & p) const;
+
+    /**
+       Upper bound for spectral norm of Hesse-matrix
+    */
+    virtual double HesseNorm () const = 0;
+
+    /**
+       Upper bound for spectral norm of Hesse-matrix in the
+       rad - environment of point c.
+    */
+    virtual double HesseNormLoc (const Point<3> & /* c */, 
+				 double /* rad */) const
+    { return HesseNorm (); }
+    //@}
+
+
+    ///
+    virtual double MaxCurvature () const;
+    ///
+    virtual double MaxCurvatureLoc (const Point<3> & /* c */ , 
+				    double /* rad */) const;
+
+    /** Returns any point in the surface.
+	Needed to start surface mesh generation e.g. on sphere */
+    virtual Point<3> GetSurfacePoint () const = 0;
+
+    ///
+    bool Inverse () const { return inverse; }
+    ///
+    void SetInverse (bool ainverse) { inverse = ainverse; }
+    /// 
+    virtual void Print (ostream & str) const = 0;
+  
+    ///
+    virtual void Reduce (const BoxSphere<3> & /* box */) { };
+    ///
+    virtual void UnReduce () { };
+
+    /// set max h in surface
+    void SetMaxH (double amaxh) { maxh = amaxh; }
+    ///
+    double GetMaxH () const { return maxh; }
+    ///
+    int GetBCProperty () const { return bcprop; }
+    ///
+    void SetBCProperty (int abc) { bcprop = abc; }
+
+    /** Determine local mesh-size.
+	Find 
+	\[ h \leq hmax, \]
+	such that
+	\[ h  \times \kappa (x) \leq c \qquad \mbox{in} B(x, h), \]
+	where kappa(x) is the curvature in x. */
+    virtual double LocH (const Point<3> & p, double x, 
+			 double c, double hmax) const;
+
+    /**
+       Gets Approximation by triangles,
+       where qual is about the number of triangles per radius
+    */
+    virtual void GetTriangleApproximation (TriangleApproximation & /* tas */, 
+					   const Box<3> & /* boundingbox */, 
+					   double /* facets */ ) const { };
+
+
+    string GetBCName() const { return bcname; }
+
+    void SetBCName( string abc ) { bcname = abc; }
+  };
+
+
+  inline ostream & operator<< (ostream & ost, const Surface & surf)
+  {
+    surf.Print(ost);
+    return ost;
+  }
+
+
+
+  typedef enum { IS_OUTSIDE = 0, IS_INSIDE = 1, DOES_INTERSECT = 2}
+    INSOLID_TYPE;
+
+
+
+
+  class DummySurface : public Surface
+  {
+    virtual double CalcFunctionValue (const Point<3> & /* point */) const
+    { return 0; }
+
+    virtual void CalcGradient (const Point<3> & /* point */, Vec<3> & grad) const
+    { grad = Vec<3> (0,0,0); }
+  
+    virtual Point<3> GetSurfacePoint () const
+    { return Point<3> (0,0,0); }
+
+    virtual double HesseNorm () const
+    { return 0; }
+
+    virtual void Project (Point<3> & /* p */) const
+    { ; }
+
+    virtual void Print (ostream & ost) const
+    { ost << "dummy surface"; }
+  };
+
+
+
+  class Primitive
+  {
+
+  public:
+
+    Primitive ();
+
+    virtual ~Primitive();
+
+  
+    /*
+      Check, whether box intersects solid defined by surface.
+
+      return values:
+      0 .. box outside solid \\
+      1 .. box in solid \\
+      2 .. can't decide (allowed, iff box is close to solid)
+    */
+    virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const = 0;
+    virtual INSOLID_TYPE PointInSolid (const Point<3> & p,
+				       double eps) const = 0;
+
+    virtual void GetTangentialSurfaceIndices (const Point<3> & p, 
+					      Array<int> & surfind, double eps) const;
+
+    virtual INSOLID_TYPE VecInSolid (const Point<3> & p,
+				     const Vec<3> & v,
+				     double eps) const = 0;
+
+    // checks if lim s->0 lim t->0  p + t(v1 + s v2) in solid
+    virtual INSOLID_TYPE VecInSolid2 (const Point<3> & p,
+				      const Vec<3> & v1,
+				      const Vec<3> & v2,
+				      double eps) const;
+
+    // checks if  p + s v1 + s*s/2 v2 is inside
+    virtual INSOLID_TYPE VecInSolid3 (const Point<3> & p,
+				      const Vec<3> & v1,
+				      const Vec<3> & v2,
+				      double eps) const;
+
+    // like VecInSolid2, but second order approximation
+    virtual INSOLID_TYPE VecInSolid4 (const Point<3> & p,
+				      const Vec<3> & v,
+				      const Vec<3> & v2,
+				      const Vec<3> & m,
+				      double eps) const;
+
+    virtual void GetTangentialVecSurfaceIndices (const Point<3> & p, const Vec<3> & v,
+						 Array<int> & surfind, double eps) const;
+
+    virtual void GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2,
+						  Array<int> & surfind, double eps) const;
+
+
+    virtual void CalcSpecialPoints (Array<Point<3> > & /* pts */) const { ; }
+    virtual void AnalyzeSpecialPoint (const Point<3> & /* pt */, 
+				      Array<Point<3> > & /* specpts */) const { ; }
+    virtual Vec<3> SpecialPointTangentialVector (const Point<3> & /* p */, 
+						 int /* s1 */, int /* s2 */) const 
+    { return Vec<3> (0,0,0); }
+
+  
+    virtual int GetNSurfaces() const = 0;
+    virtual Surface & GetSurface (int i = 0) = 0;
+    virtual const Surface & GetSurface (int i = 0) const = 0;
+
+    Array<int> surfaceids;
+    Array<int> surfaceactive;
+
+    int GetSurfaceId (int i = 0) const;
+    void SetSurfaceId (int i, int id);
+    int SurfaceActive (int i) const { return surfaceactive[i]; }
+    virtual int SurfaceInverted (int /* i */ = 0) const { return 0; }
+
+    virtual void GetPrimitiveData (const char *& classname, 
+				   Array<double> & coeffs) const;
+    virtual void SetPrimitiveData (Array<double> & coeffs);
+    static Primitive * CreatePrimitive (const char * classname);
+
+
+    virtual void Reduce (const BoxSphere<3> & /* box */) { };
+    virtual void UnReduce () { };
+
+    virtual Primitive * Copy () const;
+    virtual void Transform (Transformation<3> & trans);
+  };
+
+
+
+
+  class OneSurfacePrimitive : public Surface, public Primitive
+  {
+  public:
+    OneSurfacePrimitive();
+    ~OneSurfacePrimitive();
+
+    virtual INSOLID_TYPE PointInSolid (const Point<3> & p,
+				       double eps) const;
+    virtual INSOLID_TYPE VecInSolid (const Point<3> & p,
+				     const Vec<3> & v,
+				     double eps) const;
+    virtual INSOLID_TYPE VecInSolid2 (const Point<3> & p,
+				      const Vec<3> & v1,
+				      const Vec<3> & v2,
+				      double eps) const;
+
+    virtual INSOLID_TYPE VecInSolid3 (const Point<3> & p,
+				      const Vec<3> & v1,
+				      const Vec<3> & v2,
+				      double eps) const;
+
+    virtual INSOLID_TYPE VecInSolid4 (const Point<3> & p,
+				      const Vec<3> & v,
+				      const Vec<3> & v2,
+				      const Vec<3> & m,
+				      double eps) const;
+
+    virtual int GetNSurfaces() const;
+    virtual Surface & GetSurface (int i = 0);
+    virtual const Surface & GetSurface (int i = 0) const;
+  };
+
+
+
+
+
+
+  /**
+     Projects point to edge.
+     The point hp is projected to the edge descibed by f1 and f2.
+     It is assumed that the edge is non-degenerated, and the
+     (generalized) Newton method converges.
+  */
+  extern void ProjectToEdge (const Surface * f1, 
+			     const Surface * f2,
+			     Point<3> & hp);
+
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/triapprox.cpp b/contrib/Netgen/libsrc/csg/triapprox.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0c4f2b14de9a6d42f0b1d9675f81f32af818e2fc
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/triapprox.cpp
@@ -0,0 +1,59 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+
+  TriangleApproximation :: TriangleApproximation ()
+  {
+    ;
+  }
+
+  int TriangleApproximation :: 
+  AddTriangle (const TATriangle & tri, bool invert)
+  { 
+    trigs.Append (tri);
+    if (invert)
+      {
+	trigs.Last()[1] = tri[2];
+	trigs.Last()[2] = tri[1];
+      }
+    return trigs.Size()-1;
+  }
+
+
+  void TriangleApproximation :: RemoveUnusedPoints ()
+  {
+    BitArray used(GetNP());
+    Array<int> map (GetNP());
+    int i, j;
+    int cnt = 0;
+
+    used.Clear();
+    for (i = 0; i < GetNT(); i++)
+      for (j = 0; j < 3; j++)
+	used.Set (GetTriangle (i)[j]);
+
+    for (i = 0; i < GetNP(); i++)
+      if (used.Test(i))
+	map[i] = cnt++;
+  
+    for (i = 0; i < GetNT(); i++)
+      for (j = 0; j < 3; j++)
+	trigs[i][j] = map[trigs[i][j]];
+
+    for (i = 0; i < GetNP(); i++)
+      if (used.Test(i))
+	{
+	  points[map[i]] = points[i];
+	  normals[map[i]] = normals[i];
+	}
+
+    points.SetSize (cnt);
+    normals.SetSize (cnt);
+  }
+}
diff --git a/contrib/Netgen/libsrc/csg/triapprox.hpp b/contrib/Netgen/libsrc/csg/triapprox.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c17a3e946653f4697f4f34f2f597ffc245dbe940
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/triapprox.hpp
@@ -0,0 +1,63 @@
+#ifndef FILE_TRIAPPROX
+#define FILE_TRIAPPROX
+
+/**************************************************************************/
+/* File:   triapprox.hh                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   2. Mar. 98                                                    */
+/**************************************************************************/
+
+
+namespace netgen
+{
+
+  /**
+     Triangulated approxiamtion to true surface
+  */
+ 
+
+  class TATriangle
+  {
+    int pi[3];
+    int surfind;
+  public:
+    TATriangle () { ; }
+
+    TATriangle (int si, int pi1, int pi2, int pi3)
+    { surfind = si; pi[0] = pi1; pi[1] = pi2; pi[2] = pi3; }
+
+    int SurfaceIndex() const { return surfind; }
+    int & SurfaceIndex() { return surfind; }
+
+    int & operator[] (int i) { return pi[i]; }
+    const int & operator[] (int i) const { return pi[i]; }
+  };
+
+
+  class TriangleApproximation
+  {
+    Array<Point<3> > points;
+    Array<Vec<3> > normals;
+    Array<TATriangle> trigs;
+
+  public:
+    TriangleApproximation();
+    int GetNP () const { return points.Size(); }
+    int GetNT () const { return trigs.Size(); }
+
+    int AddPoint (const Point<3> & p) { points.Append (p); return points.Size()-1; }
+    int AddNormal (const Vec<3> & n) { normals.Append (n); return normals.Size()-1; }
+    int AddTriangle (const TATriangle & tri, bool invert = 0);
+
+    const Point<3> & GetPoint (int i) const { return points[i]; }
+    const TATriangle & GetTriangle (int i) const { return trigs[i]; }
+    const Vec<3> & GetNormal (int i) const { return normals[i]; }
+
+    void RemoveUnusedPoints ();
+
+    friend class CSGeometry;
+  };
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/vscsg.cpp b/contrib/Netgen/libsrc/csg/vscsg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a49f3a0db85367d369ece3f3aa77f242c6b11f40
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/vscsg.cpp
@@ -0,0 +1,559 @@
+#include <mystdlib.h>
+#include "incvis.hpp"
+
+#include <myadt.hpp>
+#include <meshing.hpp>
+#include <csg.hpp>
+#include <stlgeom.hpp>
+
+#include <visual.hpp>
+
+#include "vscsg.hpp"
+
+namespace netgen
+{
+
+
+
+  /* *********************** Draw Geometry **************** */
+
+  extern AutoPtr<Mesh> mesh;
+  extern Array<SpecialPoint> specpoints;
+  extern Array<Box<3> > boxes;
+
+
+  extern Array<Point<3> > project1, project2;
+
+
+  // extern AutoPtr<CSGeometry> geometry;
+
+
+  VisualSceneGeometry :: VisualSceneGeometry ()
+    : VisualScene()
+  {
+    selsurf = 0;
+  }
+
+  VisualSceneGeometry :: ~VisualSceneGeometry ()
+  {
+    ;
+  }
+
+  void VisualSceneGeometry :: SelectSurface (int aselsurf)
+  {
+    selsurf = aselsurf;
+    DrawScene();
+  }
+
+
+  void VisualSceneGeometry :: DrawScene ()
+  {
+    int i;
+
+    if (changeval != geometry->GetChangeVal())
+      BuildScene();
+    changeval = geometry->GetChangeVal();
+
+    glClearColor(backcolor, backcolor, backcolor, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  
+    SetLight();
+
+
+    glPushMatrix();
+    glMultMatrixf (transformationmat);
+
+    SetClippingPlane ();
+
+    glShadeModel (GL_SMOOTH);
+    glDisable (GL_COLOR_MATERIAL);
+    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+    glEnable (GL_BLEND);
+    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    /*
+      float mat_spec_col[] = { 1, 1, 1, 1 };
+      glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_spec_col);
+    */
+
+    double shine = vispar.shininess;
+    double transp = vispar.transp;
+
+
+    glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine);
+    glLogicOp (GL_COPY);
+  
+    glEnable (GL_NORMALIZE);
+
+    for (i = 0; i < geometry->GetNTopLevelObjects(); i++)
+      {
+	const TopLevelObject * tlo = geometry -> GetTopLevelObject (i);
+	if (tlo->GetVisible() && !tlo->GetTransparent())
+	  {
+	    float mat_col[] = { tlo->GetRed(), tlo->GetGreen(), tlo->GetBlue(), 1 };
+	    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+	  
+	    glCallList (trilists[i]);
+	  }
+      }
+
+
+    glPolygonOffset (1, 1);
+    glEnable (GL_POLYGON_OFFSET_FILL);
+
+    glLogicOp (GL_NOOP);
+    for (i = 0; i < geometry->GetNTopLevelObjects(); i++)
+      {
+	const TopLevelObject * tlo = geometry -> GetTopLevelObject (i);
+	if (tlo->GetVisible() && tlo->GetTransparent())
+	  {
+	    float mat_col[] = { tlo->GetRed(), tlo->GetGreen(), tlo->GetBlue(), transp };
+
+	    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+	  
+	    glCallList (trilists[i]);
+	  }
+      }
+
+    glDisable (GL_POLYGON_OFFSET_FILL);
+
+    /*
+      cout << "draw " << project1.Size() << " lines " << endl;
+      glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+      glLineWidth (1.0f);
+      glEnable (GL_COLOR_MATERIAL);
+
+      glColor3f (1.0f, 0.0f, 0.0f);
+
+      glBegin (GL_LINES);
+      for (int i = 0; i < project1.Size(); i++)
+      {
+      glVertex3dv (project1[i]);
+      glVertex3dv (project2[i]);
+      }
+      glEnd();
+    */
+
+
+    glPopMatrix();
+    glDisable(GL_CLIP_PLANE0);
+ 
+
+
+    /*
+      glFlush();
+  
+      int err;
+      do
+      {
+      err = glGetError();
+      // cout << "glerr,1 = " << err << endl;
+      }
+      while (err != GL_NO_ERROR);
+
+
+      // CreateTexture (0, 1, GL_DECAL);
+      CreateTexture (0, 1, GL_MODULATE);
+      glEnable (GL_TEXTURE_1D);
+
+      float mat_col[] = { 1.0, 1.0, 1.0 }; 
+      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+      glDisable (GL_BLEND);
+      glDisable (GL_COLOR_MATERIAL);
+      glEnable (GL_NORMALIZE);
+
+      if (geometry->GetNTopLevelObjects())
+      {
+      cout << "call list" << endl;
+      glCallList (trilists[0]);
+      }
+
+      glColor3d (1.0, 1.0, 1.0);
+  
+      glBegin (GL_TRIANGLES);
+      glNormal3f (0, 0, 1);
+      SetOpenGlColor  (-1.0, 0, 1, 0);
+      glVertex3f (0.0, 0.0, 0.0);
+      SetOpenGlColor  (0.5, 0, 1, 0);
+      glNormal3f (0, 0, 1);
+      glVertex3f (1.0, 0.0, 0.0);
+      SetOpenGlColor  (2.0, 0, 1, 0);
+      glNormal3f (0, 0, 1);
+      glVertex3f (0.0, 1.0, 0.0);
+
+      glEnd ();
+
+      cout << "trig drawn" << endl;
+
+      glDisable (GL_TEXTURE_1D);
+      glDisable (GL_COLOR_MATERIAL);
+      glFlush();
+
+      cout << "glerr,2 = " << glGetError() << endl;
+    */
+
+
+
+    DrawCoordinateCross ();
+    DrawNetgenLogo ();  
+
+    glFinish();  
+  }
+
+
+  void VisualSceneGeometry :: BuildScene (int zoomall)
+  {
+    Box<3> box;
+    int hasp = 0;
+    for (int i = 0; i < geometry->GetNTopLevelObjects(); i++)
+      {
+	const TriangleApproximation & ta =
+	  *geometry->GetTriApprox(i);
+	if (!&ta) continue;
+
+	for (int j = 0; j < ta.GetNP(); j++)      
+	  {
+	    if (hasp)
+	      box.Add (ta.GetPoint(j));
+	    else
+	      {
+		hasp = 1;
+		box.Set (ta.GetPoint(j));
+	      }
+	  }
+      }
+    if (hasp)
+      {
+	center = box.Center();
+	rad = box.Diam() / 2;
+      }
+    else
+      {
+	center = Point3d(0,0,0);
+	rad = 1;
+      }
+
+    CalcTransformationMatrices();
+
+    for (int i = 0; i < trilists.Size(); i++)
+      glDeleteLists (trilists[i], 1);
+    trilists.SetSize(0);
+
+    for (int i = 0; i < geometry->GetNTopLevelObjects(); i++)
+      {
+	trilists.Append (glGenLists (1));
+	glNewList (trilists.Last(), GL_COMPILE);
+
+	glEnable (GL_NORMALIZE);
+	const TriangleApproximation & ta =
+	  *geometry->GetTriApprox(i);
+	if (&ta) 
+	  {
+	    glBegin (GL_TRIANGLES);
+	    for (int j = 0; j < ta.GetNT(); j++)
+	      {
+		for (int k = 0; k < 3; k++)
+		  {
+		    int pi = ta.GetTriangle(j)[k];
+		    glNormal3dv (ta.GetNormal (pi));
+		    glVertex3dv (ta.GetPoint(pi));
+		  }
+	      }
+	    glEnd ();
+	  }
+	glEndList ();
+      }
+
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  VisualSceneSpecPoints :: VisualSceneSpecPoints ()
+    : VisualScene()
+  {
+    ;
+  }
+
+  VisualSceneSpecPoints :: ~VisualSceneSpecPoints ()
+  {
+    ;
+  }
+
+
+  void VisualSceneSpecPoints :: DrawScene ()
+  {
+    if (!mesh) 
+      {
+	VisualScene::DrawScene();
+	return;
+      }
+
+    if (changeval != specpoints.Size())
+      BuildScene();
+    changeval = specpoints.Size();
+
+
+
+    glClearColor(backcolor, backcolor, backcolor, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glEnable (GL_COLOR_MATERIAL);
+    glColor3f (1.0f, 1.0f, 1.0f);
+    glLineWidth (1.0f);
+
+    glPushMatrix();
+    glMultMatrixf (transformationmat);
+
+    //  glEnable (GL_COLOR);
+    //  glDisable (GL_COLOR_MATERIAL);
+    if (vispar.drawedtangents)
+      {
+	glColor3d (1, 0, 0);
+	glBegin (GL_LINES);
+	for (int i = 1; i <= specpoints.Size(); i++)
+	  {
+	    const Point3d p1 = specpoints.Get(i).p;
+	    const Point3d p2 = specpoints.Get(i).p + len * specpoints.Get(i).v;
+	    glVertex3d (p1.X(), p1.Y(), p1.Z());
+	    glVertex3d (p2.X(), p2.Y(), p2.Z());
+	  }
+	glEnd();
+      }
+
+    if (vispar.drawededges)
+      {
+	glColor3d (1, 0, 0);
+	glBegin (GL_LINES);
+	for (int i = 1; i <= mesh->GetNSeg(); i++)
+	  {
+	    const Segment & seg = mesh -> LineSegment (i);
+	    glVertex3dv ( (*mesh)[seg[0]] );
+            glVertex3dv ( (*mesh)[seg[1]] );
+	    // glVertex3dv ( &(*mesh)[seg[0]].X() );
+	    // glVertex3dv ( &(*mesh)[seg[1]].X() );
+	  }
+	glEnd();
+      }
+
+    glColor3d (1, 0, 0);
+    glBegin (GL_LINES);
+    int edges[12][2] = 
+      { { 0, 1 },
+	{ 2, 3 },
+	{ 4, 5 },
+	{ 6, 7 },
+	{ 0, 2 },
+	{ 1, 3 },
+	{ 4, 6 },
+	{ 5, 7 },
+	{ 0, 4 },
+	{ 1, 5 },
+	{ 2, 6 },
+	{ 3, 7 } };
+    for (int i = 0; i < boxes.Size(); i++)
+      {
+	for (int j = 0; j < 12; j++)
+	  {
+	    glVertex3dv ( boxes[i].GetPointNr(edges[j][0]) );
+	    glVertex3dv ( boxes[i].GetPointNr(edges[j][1]) );
+	  }
+	/*
+	glVertex3dv ( boxes[i].PMin() );
+	glVertex3dv ( boxes[i].PMax() );
+	*/
+      }
+    glEnd();
+
+
+
+    if (vispar.drawededgenrs)
+      {
+	glEnable (GL_COLOR_MATERIAL);
+	GLfloat textcol[3] = { 1 - backcolor,
+			       1 - backcolor,
+			       1 - backcolor };
+	glColor3fv (textcol);
+	glNormal3d (0, 0, 1);
+	glPushAttrib (GL_LIST_BIT);
+	// glListBase (fontbase);
+
+	char buf[20];
+	for (int i = 1; i <= mesh->GetNSeg(); i++)
+	  {
+	    const Segment & seg = mesh -> LineSegment (i);
+	    const Point3d p1 = mesh -> Point (seg[0]);
+	    const Point3d p2 = mesh -> Point (seg[1]);
+
+	    const Point3d p = Center (p1, p2);
+	    glRasterPos3d (p.X(), p.Y(), p.Z());
+	  
+	    sprintf (buf, "%d", seg.edgenr);
+	    // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf);
+	    MyOpenGLText (buf);
+	  }
+      
+	glPopAttrib ();
+	glDisable (GL_COLOR_MATERIAL);
+      }
+
+
+    if (vispar.drawedpoints)
+      {
+
+	glColor3d (0, 0, 1);
+	/*
+	  glPointSize( 3.0 );
+
+	float range[2];
+	glGetFloatv(GL_POINT_SIZE_RANGE, &range[0]);
+	cout << "max ptsize = " << range[0] << "-" << range[1] << endl;
+      
+
+	glBegin( GL_POINTS );
+	for (int i = 1; i <= mesh -> GetNP(); i++)
+	  {
+	    const Point3d & p = mesh -> Point(i);
+	    if (i % 2)
+	      glVertex3f( p.X(), p.Y(), p.Z());
+	  }
+	glEnd();
+	*/
+
+	static GLubyte knoedel[] = 
+	  {
+	    0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+	  };
+	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+	glDisable (GL_COLOR_MATERIAL);
+	glDisable (GL_LIGHTING);
+	glDisable (GL_CLIP_PLANE0);
+      
+	for (int i = 1; i <= mesh -> GetNP(); i++)
+	  {
+	    const Point3d & p = mesh -> Point(i);
+	    glRasterPos3d (p.X(), p.Y(), p.Z());
+	    glBitmap (7, 7, 3, 3, 0, 0, &knoedel[0]);
+	  }
+      }
+
+    if (vispar.drawedpointnrs)
+      {
+	glEnable (GL_COLOR_MATERIAL);
+	GLfloat textcol[3] = { 1 - backcolor,
+			       1 - backcolor,
+			       1 - backcolor };
+	glColor3fv (textcol);
+	glNormal3d (0, 0, 1);
+	glPushAttrib (GL_LIST_BIT);
+	// glListBase (fontbase);
+      
+	char buf[20];
+	for (int i = 1; i <= mesh->GetNP(); i++)
+	  {
+	    const Point3d & p = mesh->Point(i);
+	    glRasterPos3d (p.X(), p.Y(), p.Z());
+	  
+	    sprintf (buf, "%d", i);
+	    // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf);
+	    MyOpenGLText (buf);
+	  }
+      
+	glPopAttrib ();
+	glDisable (GL_COLOR_MATERIAL);
+      }
+
+
+    
+    
+    
+
+    glPopMatrix();
+
+    if (vispar.drawcoordinatecross)
+      DrawCoordinateCross ();
+    DrawNetgenLogo ();
+
+    glFinish();  
+  }
+
+
+  void VisualSceneSpecPoints :: BuildScene (int zoomall)
+  {
+    if (!mesh) 
+      {
+	VisualScene::BuildScene(zoomall);
+	return;
+      }
+  
+    Box3d box;
+  
+    if (mesh->GetNSeg())
+      {
+	box.SetPoint (mesh->Point (mesh->LineSegment(1)[0]));
+	for (int i = 1; i <= mesh->GetNSeg(); i++)
+	  {
+	    box.AddPoint (mesh->Point (mesh->LineSegment(i)[0]));
+	    box.AddPoint (mesh->Point (mesh->LineSegment(i)[1]));
+	  }
+      }
+    else if (specpoints.Size() >= 2)
+      {
+	box.SetPoint (specpoints.Get(1).p);
+	for (int i = 2; i <= specpoints.Size(); i++)
+	  box.AddPoint (specpoints.Get(i).p);
+      }
+    else
+      {
+	box = Box3d (Point3d (0,0,0), Point3d (1,1,1));
+      }
+  
+    if (zoomall == 2 && ((vispar.centerpoint >= 1 && vispar.centerpoint <= mesh->GetNP()) ||
+			 vispar.use_center_coords))
+      {
+	if (vispar.use_center_coords)
+	  {
+	    center.X() = vispar.centerx; center.Y() = vispar.centery; center.Z() = vispar.centerz; 
+	  }
+	else
+	  center = mesh->Point (vispar.centerpoint);
+      }
+    else
+      center = Center (box.PMin(), box.PMax());
+        
+
+    rad = 0.5 * Dist (box.PMin(), box.PMax());
+  
+  
+    CalcTransformationMatrices();
+  }
+
+
+
+
+
+
+}
+
+
diff --git a/contrib/Netgen/libsrc/csg/vscsg.hpp b/contrib/Netgen/libsrc/csg/vscsg.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c32f5da83a77f662b4f74a588783e348da5d414d
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/vscsg.hpp
@@ -0,0 +1,34 @@
+#ifndef FILE_VSCSG
+#define FILE_VSCSG
+
+/**************************************************************************/
+/* File:   vscsg.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   05. Jan. 2011                                                  */
+/**************************************************************************/
+
+namespace netgen
+{
+
+  class VisualSceneGeometry : public VisualScene
+  {
+    class CSGeometry * geometry;
+    Array<int> trilists;
+    int selsurf;
+  public:
+    VisualSceneGeometry ();
+    virtual ~VisualSceneGeometry ();
+
+    void SetGeometry (class CSGeometry * ageometry) { geometry = ageometry; }
+    virtual void SelectSurface (int aselsurf);
+    virtual void BuildScene (int zoomall = 0);
+    virtual void DrawScene ();
+  };
+
+
+
+}
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/Makefile.am b/contrib/Netgen/libsrc/general/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..9c555b023891c82c66c0b159030204de0cfd63d9
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/Makefile.am
@@ -0,0 +1,14 @@
+noinst_HEADERS = array.hpp myadt.hpp optmem.hpp sort.hpp table.hpp autodiff.hpp flags.hpp mystring.hpp spbita2d.hpp template.hpp autoptr.hpp hashtabl.hpp netgenout.hpp profiler.hpp stack.hpp bitarray.hpp seti.hpp symbolta.hpp dynamicmem.hpp  parthreads.hpp mpi_interface.hpp
+
+#  moveablemem.hpp
+
+include_HEADERS = ngexception.hpp
+
+AM_CPPFLAGS =  $(MPI_INCLUDES) -I$(top_srcdir)/libsrc/include
+METASOURCES = AUTO
+noinst_LTLIBRARIES = libgen.la
+libgen_la_SOURCES = array.cpp bitarray.cpp dynamicmem.cpp flags.cpp \
+	hashtabl.cpp mystring.cpp ngexception.cpp optmem.cpp parthreads.cpp \
+	profiler.cpp seti.cpp sort.cpp spbita2d.cpp symbolta.cpp table.cpp
+
+#  moveablemem.cpp
diff --git a/contrib/Netgen/libsrc/general/array.cpp b/contrib/Netgen/libsrc/general/array.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d3f48d36cca725eba06e0e75534f454845a9e2a4
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/array.cpp
@@ -0,0 +1,75 @@
+#ifndef FILE_NGSTD_ArrayCPP
+#define FILE_NGSTD_ArrayCPP
+// necessary for SGI ????
+
+/**************************************************************************/
+/* File:   array.cpp                                                       */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/* 
+   Abstract data type Array
+*/
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <assert.h>
+
+
+namespace netgen
+{
+  //using namespace netgen;
+
+#ifdef NONE  
+  void BASE_Array :: ReSize (int minsize, int elementsize)
+  {
+    cout << "resize, minsize = " << minsize << endl;
+
+    if (inc == -1)
+      throw Exception ("Try to resize fixed size array");
+
+    
+    void * p;
+    int nsize = (inc) ? allocsize + inc : 2 * allocsize;
+    if (nsize < minsize) nsize = minsize;
+
+    if (data)
+      {
+	p = new char [nsize * elementsize];
+	
+	int mins = (nsize < actsize) ? nsize : actsize; 
+	memcpy (p, data, mins * elementsize);
+	
+	delete [] static_cast<char*> (data);
+	data = p;
+      }
+    else
+      {
+	data = new char[nsize * elementsize];
+      }
+    
+    allocsize = nsize;
+    cout << "resize done" << endl;
+  }
+  
+  
+  
+  void BASE_Array :: RangeCheck (int i) const
+  {
+    if (i < 0 || i >= actsize)
+      throw ArrayRangeException ();
+  }
+  
+  void BASE_Array :: CheckNonEmpty () const
+  {
+    if (!actsize)
+      {
+	throw Exception ("Array should not be empty");
+	//      cerr << "Array souldn't be empty";
+      }
+  }
+#endif
+}
+#endif
+
diff --git a/contrib/Netgen/libsrc/general/array.hpp b/contrib/Netgen/libsrc/general/array.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d7eeee342a30933262faff2219d5954a36af0b04
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/array.hpp
@@ -0,0 +1,661 @@
+#ifndef FILE_Array
+#define FILE_Array
+
+/**************************************************************************/
+/* File:   array.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+
+namespace netgen
+{
+
+  // template <class T, int B1, int B2> class IndirectArray;
+
+
+
+  /**
+     A simple array container.
+     Array represented by size and data-pointer.
+     No memory allocation and deallocation, must be provided by user.
+     Helper functions for printing. 
+     Optional range check by macro RANGE_CHECK
+  */
+
+  template <class T, int BASE = 0>
+  class FlatArray
+  {
+  protected:
+    /// the size
+    int size;
+    /// the data
+    T * data;
+  public:
+
+    /// provide size and memory
+    FlatArray (int asize, T * adata) 
+      : size(asize), data(adata) { ; }
+
+    /// the size
+    int Size() const { return size; }
+
+    int Begin() const { return BASE; }
+    int End() const { return size+BASE; }
+
+    /// Access array. BASE-based
+    T & operator[] (int i) const
+    {
+#ifdef DEBUG
+      if (i-BASE < 0 || i-BASE >= size)
+	cout << "array<" << typeid(T).name() << "> out of range, i = " << i << ", s = " << size << endl;
+#endif
+
+      return data[i-BASE]; 
+    }
+
+    /// Access array, one-based  (old fashioned)
+    T & Elem (int i)
+    {
+#ifdef DEBUG
+      if (i < 1 || i > size)
+	cout << "Array<" << typeid(T).name() 
+	     << ">::Elem out of range, i = " << i
+	     << ", s = " << size << endl;
+#endif
+
+      return ((T*)data)[i-1]; 
+    }
+  
+    /// Access array, one-based  (old fashioned)
+    const T & Get (int i) const 
+    {
+#ifdef DEBUG
+      if (i < 1 || i > size)
+	cout << "Array<" << typeid(T).name() << ">::Get out of range, i = " << i
+	     << ", s = " << size << endl;
+#endif
+
+      return ((const T*)data)[i-1]; 
+    }
+
+    /// Access array, one-based  (old fashioned)
+    void Set (int i, const T & el)
+    { 
+#ifdef DEBUG
+      if (i < 1 || i > size)
+	cout << "Array<" << typeid(T).name() << ">::Set out of range, i = " << i
+	     << ", s = " << size << endl;
+#endif
+
+      ((T*)data)[i-1] = el; 
+    }
+
+    /// access first element
+    T & First () const
+    {
+      return data[0];
+    }
+
+
+    /// access last element. check by macro CHECK_RANGE
+    T & Last () const
+    {
+      return data[size-1];
+    }
+
+    /// Fill array with value val
+    FlatArray & operator= (const T & val)
+    {
+      for (int i = 0; i < size; i++)
+	data[i] = val;
+      return *this;
+    }
+
+    /// takes range starting from position start of end-start elements
+    const FlatArray<T> Range (int start, int end)
+    {
+      return FlatArray<T> (end-start, data+start);
+    }
+
+    /// first position of element elem, returns -1 if element not contained in array 
+    int Pos(const T & elem) const
+    {
+      int pos = -1;
+      for(int i=0; pos==-1 && i < this->size; i++)
+	if(elem == data[i]) pos = i;
+      return pos;
+    }
+
+    /// does the array contain element elem ?
+    bool Contains(const T & elem) const
+    {
+      return ( Pos(elem) >= 0 );
+    }
+  };
+
+
+
+  // print array
+  template <class T, int BASE>
+  inline ostream & operator<< (ostream & s, const FlatArray<T,BASE> & a)
+  {
+    for (int i = a.Begin(); i < a.End(); i++)
+      s << i << ": " << a[i] << endl;
+    return s;
+  }
+
+
+
+  /** 
+      Dynamic array container.
+   
+      Array<T> is an automatically increasing array container.
+      The allocated memory doubles on overflow. 
+      Either the container takes care of memory allocation and deallocation,
+      or the user provides one block of data.
+  */
+  template <class T, int BASE = 0> 
+  class Array : public FlatArray<T, BASE>
+  {
+  protected:
+    using FlatArray<T,BASE>::size;
+    using FlatArray<T,BASE>::data;
+
+    /// physical size of array
+    int allocsize;
+    /// memory is responsibility of container
+    bool ownmem;
+
+  public:
+
+    /// Generate array of logical and physical size asize
+    explicit Array(int asize = 0)
+      : FlatArray<T, BASE> (asize, asize ? new T[asize] : 0)
+    {
+      allocsize = asize; 
+      ownmem = 1;
+    }
+
+    /// Generate array in user data
+    Array(int asize, T* adata)
+      : FlatArray<T, BASE> (asize, adata)
+    {
+      allocsize = asize; 
+      ownmem = 0;
+    }
+
+    /// array copy 
+    explicit Array (const Array<T> & a2)
+      : FlatArray<T, BASE> (a2.Size(), a2.Size() ? new T[a2.Size()] : 0)
+    {
+      allocsize = size;
+      ownmem = 1;
+      for (int i = BASE; i < size+BASE; i++)
+	(*this)[i] = a2[i];
+    }
+
+
+
+    /// if responsible, deletes memory
+    ~Array()
+    {
+      if (ownmem)
+	delete [] data;
+    }
+
+    /// Change logical size. If necessary, do reallocation. Keeps contents.
+    void SetSize(int nsize)
+    {
+      if (nsize > allocsize) 
+	ReSize (nsize);
+      size = nsize; 
+    }
+
+    /// Change physical size. Keeps logical size. Keeps contents.
+    void SetAllocSize (int nallocsize)
+    {
+      if (nallocsize > allocsize)
+	ReSize (nallocsize);
+    }
+
+
+    /// Add element at end of array. reallocation if necessary.
+    int Append (const T & el)
+    {
+      if (size == allocsize) 
+	ReSize (size+1);
+      data[size] = el;
+      size++;
+      return size;
+    }
+
+    template <typename T2, int B2>
+    void Append (FlatArray<T2, B2> a2)
+    {
+      if (size+a2.Size() > allocsize)
+	ReSize (size+a2.Size());
+      for (int i = 0; i < a2.Size(); i++)
+	data[size+i] = a2[i+B2];
+      size += a2.Size();
+    }
+
+
+    /// Delete element i (0-based). Move last element to position i.
+    void Delete (int i)
+    {
+#ifdef CHECK_Array_RANGE
+      RangeCheck (i+1);
+#endif
+
+      data[i] = data[size-1];
+      size--;
+      //    DeleteElement (i+1);
+    }
+
+
+    /// Delete element i (1-based). Move last element to position i.
+    void DeleteElement (int i)
+    {
+#ifdef CHECK_Array_RANGE
+      RangeCheck (i);
+#endif
+
+      data[i-1] = data[size-1];
+      size--;
+    }
+
+    /// Delete last element. 
+    void DeleteLast ()
+    {
+      size--;
+    }
+
+    /// Deallocate memory
+    void DeleteAll ()
+    {
+      if (ownmem)
+	delete [] data;
+      data = 0;
+      size = allocsize = 0;
+    }
+
+    /// Fill array with val
+    Array & operator= (const T & val)
+    {
+      FlatArray<T, BASE>::operator= (val);
+      return *this;
+    }
+
+    /// array copy
+    Array & operator= (const Array & a2)
+    {
+      SetSize (a2.Size());
+      for (int i = BASE; i < size+BASE; i++)
+	(*this)[i] = a2[i];
+      return *this;
+    }
+
+    /// array copy
+    Array & operator= (const FlatArray<T> & a2)
+    {
+      SetSize (a2.Size());
+      for (int i = BASE; i < size+BASE; i++)
+	(*this)[i] = a2[i];
+      return *this;
+    }
+
+
+  private:
+
+    /// resize array, at least to size minsize. copy contents
+    void ReSize (int minsize)
+    {
+      int nsize = 2 * allocsize;
+      if (nsize < minsize) nsize = minsize;
+
+      if (data)
+	{
+	  T * p = new T[nsize];
+	
+	  int mins = (nsize < size) ? nsize : size; 
+	  memcpy (p, data, mins * sizeof(T));
+
+	  if (ownmem)
+	    delete [] data;
+	  ownmem = 1;
+	  data = p;
+	}
+      else
+	{
+	  data = new T[nsize];
+	  ownmem = 1;
+	}
+    
+      allocsize = nsize;
+    }
+  };
+
+
+
+  template <class T, int S> 
+  class ArrayMem : public Array<T>
+  {
+    using Array<T>::size;
+    using Array<T>::data;
+    using Array<T>::ownmem;
+
+    // T mem[S];     // Intel C++ calls dummy constructor
+    // char mem[S*sizeof(T)];
+    double mem[(S*sizeof(T)+7) / 8];
+  public:
+    /// Generate array of logical and physical size asize
+    explicit ArrayMem(int asize = 0)
+      : Array<T> (S, static_cast<T*> (static_cast<void*>(&mem[0])))
+    {
+      size = asize;
+      if (asize > S)
+	{
+	  data = new T[asize];
+	  ownmem = 1;
+	}
+      // SetSize (asize);
+    }
+
+    ArrayMem & operator= (const T & val)  
+    {
+      Array<T>::operator= (val);
+      return *this;
+    }
+
+    /// array copy
+    ArrayMem & operator= (const FlatArray<T> & a2)
+    {
+      this->SetSize (a2.Size());
+      for (int i = 0; i < size; i++)
+	(*this)[i] = a2[i];
+      return *this;
+    }
+
+  };
+
+
+
+
+
+  /*
+    template <class T, int B1, int B2>
+    class IndirectArray
+    {
+    const FlatArray<T, B1> & array;
+    const FlatArray<int, B2> & ia; 
+
+    public:
+    IndirectArray (const FlatArray<T,B1> & aa, const FlatArray<int, B2> & aia)
+    : array(aa), ia(aia) { ; }
+    int Size() const { return ia.Size(); }
+    const T & operator[] (int i) const { return array[ia[i]]; }
+    };
+  */
+
+
+
+
+
+
+
+
+
+  ///
+  template <class T, int BASE = 0> 
+  class MoveableArray 
+  {
+    int size;
+    int allocsize;
+    DynamicMem<T> data;
+
+  public:
+
+    MoveableArray()
+    { 
+      size = allocsize = 0; 
+      data.SetName ("MoveableArray");
+    }
+
+    MoveableArray(int asize)
+      : size(asize), allocsize(asize), data(asize)
+    { ; }
+  
+    ~MoveableArray () { ; }
+
+    int Size() const { return size; }
+
+    void SetSize(int nsize)
+    {
+      if (nsize > allocsize) 
+	{
+	  data.ReAlloc (nsize);
+	  allocsize = nsize;
+	}
+      size = nsize;
+    }
+
+    void SetAllocSize (int nallocsize)
+    {
+      data.ReAlloc (nallocsize);
+      allocsize = nallocsize;
+    }
+
+    ///
+    T & operator[] (int i)
+    { return ((T*)data)[i-BASE]; }
+
+    ///
+    const T & operator[] (int i) const
+    { return ((const T*)data)[i-BASE]; }
+
+    ///
+    T & Elem (int i)
+    { return ((T*)data)[i-1]; }
+  
+    ///
+    const T & Get (int i) const 
+    { return ((const T*)data)[i-1]; }
+
+    ///
+    void Set (int i, const T & el)
+    { ((T*)data)[i-1] = el; }
+
+    ///
+    T & Last ()
+    { return ((T*)data)[size-1]; }
+  
+    ///
+    const T & Last () const
+    { return ((const T*)data)[size-1]; }
+  
+    ///
+    int Append (const T & el)
+    {
+      if (size == allocsize) 
+	{
+	  SetAllocSize (2*allocsize+1);
+	}
+      ((T*)data)[size] = el;
+      size++;
+      return size;
+    }
+  
+    ///
+    void Delete (int i)
+    {
+      DeleteElement (i+1);
+    }
+
+    ///
+    void DeleteElement (int i)
+    {
+      ((T*)data)[i-1] = ((T*)data)[size-1];
+      size--;
+    }
+  
+    ///
+    void DeleteLast ()
+    { size--; }
+
+    ///
+    void DeleteAll ()
+    {
+      size = allocsize = 0;
+      data.Free();
+    }
+
+    ///
+    void PrintMemInfo (ostream & ost) const
+    {
+      ost << Size() << " elements of size " << sizeof(T) << " = " 
+	  << Size() * sizeof(T) << endl;
+    }
+
+    MoveableArray & operator= (const T & el)
+    {
+      for (int i = 0; i < size; i++)
+	((T*)data)[i] = el;
+      return *this;
+    }
+
+
+    MoveableArray & Copy (const MoveableArray & a2)
+    {
+      SetSize (a2.Size());
+      for (int i = 0; i < this->size; i++)
+	data[i] = a2.data[i];
+      return *this;
+    }
+
+    /// array copy
+    MoveableArray & operator= (const MoveableArray & a2)
+    {
+      return Copy(a2);
+    }
+
+
+    void SetName (const char * aname)
+    {
+      data.SetName(aname);
+    }
+  private:
+    ///
+    //MoveableArray & operator= (MoveableArray &); //???
+    ///
+    //MoveableArray (const MoveableArray &); //???
+  };
+
+
+  template <class T>
+  inline ostream & operator<< (ostream & ost, MoveableArray<T> & a)
+  {
+    for (int i = 0; i < a.Size(); i++)
+      ost << i << ": " << a[i] << endl;
+    return ost;
+  }
+
+
+
+  /// bubble sort array
+  template <class T>
+  inline void BubbleSort (const FlatArray<T> & data)
+  {
+    for (int i = 0; i < data.Size(); i++)
+      for (int j = i+1; j < data.Size(); j++)
+	if (data[i] > data[j])
+	  {
+	    T hv = data[i];
+	    data[i] = data[j];
+	    data[j] = hv;
+	  }
+  }
+
+  /// bubble sort array
+  template <class T, class S>
+  inline void BubbleSort (FlatArray<T> & data, FlatArray<S> & slave)
+  {
+    for (int i = 0; i < data.Size(); i++)
+      for (int j = i+1; j < data.Size(); j++)
+	if (data[i] > data[j])
+	  {
+	    T hv = data[i];
+	    data[i] = data[j];
+	    data[j] = hv;
+	    
+	    S hvs = slave[i];
+	    slave[i] = slave[j];
+	    slave[j] = hvs;
+	  }
+  }
+
+
+  template <class T, class S>
+  void QuickSortRec (FlatArray<T> & data,
+		     FlatArray<S> & slave,
+		     int left, int right)
+  {
+    int i = left;
+    int j = right;
+    T midval = data[(left+right)/2];
+  
+    do
+      {
+	while (data[i] < midval) i++;
+	while (midval < data[j]) j--;
+      
+	if (i <= j)
+	  {
+	    Swap (data[i], data[j]);
+	    Swap (slave[i], slave[j]);
+	    i++; j--;
+	  }
+      }
+    while (i <= j);
+    if (left < j) QuickSortRec (data, slave, left, j);
+    if (i < right) QuickSortRec (data, slave, i, right);
+  }
+
+  template <class T, class S>
+  void QuickSort (FlatArray<T> & data, FlatArray<S> & slave)
+  {
+    QuickSortRec (data, slave, 0, data.Size()-1);
+  }
+
+
+
+
+
+
+
+
+
+  template <class T> 
+  void Intersection (const FlatArray<T> & in1, const FlatArray<T> & in2, 
+		     Array<T> & out)
+  {
+    out.SetSize(0);
+    for(int i=0; i<in1.Size(); i++)
+      if(in2.Contains(in1[i]))
+	out.Append(in1[i]);
+  }
+  template <class T> 
+  void Intersection (const FlatArray<T> & in1, const FlatArray<T> & in2, const FlatArray<T> & in3,
+		     Array<T> & out)
+  {
+    out.SetSize(0);
+    for(int i=0; i<in1.Size(); i++)
+      if(in2.Contains(in1[i]) && in3.Contains(in1[i]))
+	out.Append(in1[i]);
+  }
+
+
+}
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/general/autodiff.hpp b/contrib/Netgen/libsrc/general/autodiff.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2ba63d0e7d5102521195fbc63d1b86ebc0b88d8
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/autodiff.hpp
@@ -0,0 +1,351 @@
+#ifndef FILE_AUTODIFF
+#define FILE_AUTODIFF
+
+/**************************************************************************/
+/* File:   autodiff.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   24. Oct. 02                                                    */
+/**************************************************************************/
+
+// Automatic differentiation datatype
+
+
+/**
+   Datatype for automatic differentiation.
+   Contains function value and D derivatives. Algebraic
+   operations are overloaded by using product-rule etc. etc. 
+**/
+template <int D, typename SCAL = double>
+class AutoDiff
+{
+  SCAL val;
+  SCAL dval[D];
+public:
+
+  typedef AutoDiff<D,SCAL> TELEM;
+  typedef SCAL TSCAL;
+
+
+  /// elements are undefined
+  AutoDiff  () throw() { }; 
+  // { val = 0; for (int i = 0; i < D; i++) dval[i] = 0; }  // !
+
+  /// copy constructor
+  AutoDiff  (const AutoDiff & ad2) throw()
+  {
+    val = ad2.val;
+    for (int i = 0; i < D; i++)
+      dval[i] = ad2.dval[i];
+  }
+
+  /// initial object with constant value
+  AutoDiff  (SCAL aval) throw()
+  {
+    val = aval;
+    for (int i = 0; i < D; i++)
+      dval[i] = 0;
+  }
+
+  /// init object with (val, e_diffindex)
+  AutoDiff  (SCAL aval, int diffindex)  throw()
+  {
+    val = aval;
+    for (int i = 0; i < D; i++)
+      dval[i] = 0;
+    dval[diffindex] = 1;
+  }
+
+  /// assign constant value
+  AutoDiff & operator= (SCAL aval) throw()
+  {
+    val = aval;
+    for (int i = 0; i < D; i++)
+      dval[i] = 0;
+    return *this;
+  }
+
+  /// returns value
+  SCAL Value() const throw() { return val; }
+
+  /// returns partial derivative
+  SCAL DValue (int i) const throw() { return dval[i]; }
+
+  /// access value
+  SCAL & Value() throw() { return val; }
+
+  /// accesses partial derivative 
+  SCAL & DValue (int i) throw() { return dval[i]; }
+
+  /// 
+  AutoDiff<D,SCAL> & operator+= (const AutoDiff<D,SCAL> & y) throw()
+  {
+    val += y.val;
+    for (int i = 0; i < D; i++)
+      dval[i] += y.dval[i];
+    return *this;
+  }
+
+  ///
+  AutoDiff<D,SCAL> & operator-= (const AutoDiff<D,SCAL> & y) throw()
+  {
+    val -= y.val;
+    for (int i = 0; i < D; i++)
+      dval[i] -= y.dval[i];
+    return *this;
+
+  }
+
+  ///
+  AutoDiff<D,SCAL> & operator*= (const AutoDiff<D,SCAL> & y) throw()
+  {
+    for (int i = 0; i < D; i++)
+      {
+	// dval[i] *= y.val;
+	// dval[i] += val * y.dval[i];
+        dval[i] = dval[i] * y.val + val * y.dval[i];
+      }
+    val *= y.val;
+    return *this;
+  }
+
+  ///
+  AutoDiff<D,SCAL> & operator*= (const SCAL & y) throw()
+  {
+    val *= y;
+    for (int i = 0; i < D; i++)
+      dval[i] *= y;
+    return *this;
+  }
+
+  ///
+  AutoDiff<D,SCAL> & operator/= (const SCAL & y) throw()
+  {
+    SCAL iy = 1.0 / y;
+    val *= iy;
+    for (int i = 0; i < D; i++)
+      dval[i] *= iy;
+    return *this;
+  }
+
+  /// 
+  bool operator== (SCAL val2) throw()
+  {
+    return val == val2;
+  }
+
+  ///
+  bool operator!= (SCAL val2) throw()
+  {
+    return val != val2;
+  }
+
+  ///
+  bool operator< (SCAL val2) throw()
+  {
+    return val < val2;
+  }
+  
+  ///
+  bool operator> (SCAL val2) throw()
+  {
+    return val > val2;
+  }
+};
+
+
+//@{  AutoDiff helper functions.
+
+/// prints AutoDiff
+template<int D, typename SCAL>
+inline ostream & operator<< (ostream & ost, const AutoDiff<D,SCAL> & x)
+{
+  ost << x.Value() << ", D = ";
+  for (int i = 0; i < D; i++)
+    ost << x.DValue(i) << " ";
+  return ost;
+}
+
+/// AutoDiff plus AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator+ (const AutoDiff<D,SCAL> & x, const AutoDiff<D,SCAL> & y) throw()
+{
+  AutoDiff<D,SCAL> res;
+  res.Value () = x.Value()+y.Value();
+  // AutoDiff<D,SCAL> res(x.Value()+y.Value());
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = x.DValue(i) + y.DValue(i);
+  return res;
+}
+
+
+/// AutoDiff minus AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator- (const AutoDiff<D,SCAL> & x, const AutoDiff<D,SCAL> & y) throw()
+{
+  AutoDiff<D,SCAL> res;
+  res.Value() = x.Value()-y.Value();
+  // AutoDiff<D,SCAL> res (x.Value()-y.Value());
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = x.DValue(i) - y.DValue(i);
+  return res;
+}
+
+/// double plus AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator+ (double x, const AutoDiff<D,SCAL> & y) throw()
+{
+  AutoDiff<D,SCAL> res;
+  res.Value() = x+y.Value();
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = y.DValue(i);
+  return res;
+}
+
+/// AutoDiff plus double
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator+ (const AutoDiff<D,SCAL> & y, double x) throw()
+{
+  AutoDiff<D,SCAL> res;
+  res.Value() = x+y.Value();
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = y.DValue(i);
+  return res;
+}
+
+
+/// minus AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator- (const AutoDiff<D,SCAL> & x) throw()
+{
+  AutoDiff<D,SCAL> res;
+  res.Value() = -x.Value();
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = -x.DValue(i);
+  return res;
+}
+
+/// AutoDiff minus double
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator- (const AutoDiff<D,SCAL> & x, double y) throw()
+{
+  AutoDiff<D,SCAL> res;
+  res.Value() = x.Value()-y;
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = x.DValue(i);
+  return res;
+}
+
+///
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator- (double x, const AutoDiff<D,SCAL> & y) throw()
+{
+  AutoDiff<D,SCAL> res;
+  res.Value() = x-y.Value();
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = -y.DValue(i);
+  return res;
+}
+
+
+/// double times AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator* (double x, const AutoDiff<D,SCAL> & y) throw()
+{
+  AutoDiff<D,SCAL> res;
+  res.Value() = x*y.Value();
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = x*y.DValue(i);
+  return res;
+}
+
+/// AutoDiff times double
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator* (const AutoDiff<D,SCAL> & y, double x) throw()
+{
+  AutoDiff<D,SCAL> res;
+  res.Value() = x*y.Value();
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = x*y.DValue(i);
+  return res;
+}
+
+/// AutoDiff times AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator* (const AutoDiff<D,SCAL> & x, const AutoDiff<D,SCAL> & y) throw()
+{
+  AutoDiff<D,SCAL> res;
+  SCAL hx = x.Value();
+  SCAL hy = y.Value();
+
+  res.Value() = hx*hy;
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = hx*y.DValue(i) + hy*x.DValue(i);
+
+  return res;
+}
+
+/// AutoDiff times AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> sqr (const AutoDiff<D,SCAL> & x) throw()
+{
+  AutoDiff<D,SCAL> res;
+  SCAL hx = x.Value();
+  res.Value() = hx*hx;
+  hx *= 2;
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = hx*x.DValue(i);
+  return res;
+}
+
+/// Inverse of AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> Inv (const AutoDiff<D,SCAL> & x)
+{
+  AutoDiff<D,SCAL> res(1.0 / x.Value());
+  for (int i = 0; i < D; i++)
+    res.DValue(i) = -x.DValue(i) / (x.Value() * x.Value());
+  return res;
+}
+
+
+/// AutoDiff div AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator/ (const AutoDiff<D,SCAL> & x, const AutoDiff<D,SCAL> & y)
+{
+  return x * Inv (y);
+}
+
+/// AutoDiff div double
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator/ (const AutoDiff<D,SCAL> & x, double y)
+{
+  return (1/y) * x;
+}
+
+/// double div AutoDiff
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> operator/ (double x, const AutoDiff<D,SCAL> & y)
+{
+  return x * Inv(y);
+}
+
+
+
+
+template<int D, typename SCAL>
+inline AutoDiff<D,SCAL> fabs (const AutoDiff<D,SCAL> & x)
+{
+  double abs = fabs (x.Value());
+  AutoDiff<D,SCAL> res( abs );
+  if (abs != 0.0)
+    for (int i = 0; i < D; i++)
+      res.DValue(i) = x.DValue(i) / abs;
+  else
+    for (int i = 0; i < D; i++)
+      res.DValue(i) = 0.0;
+  return res;
+}
+
+//@}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/autoptr.hpp b/contrib/Netgen/libsrc/general/autoptr.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..352a6105f88566d3693857ca394c9cfcf177ab63
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/autoptr.hpp
@@ -0,0 +1,36 @@
+#ifndef FILE_AUTOPTR
+#define FILE_AUTOPTR
+
+/**************************************************************************/
+/* File:   autoptr.hpp                                                    */
+/* Author: STL, Joachim Schoeberl                                         */
+/* Date:   29. Dec. 02                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+template <typename T>
+class AutoPtr
+{
+private:
+  T * ptr;
+public:
+  typedef T* pT;
+  explicit AutoPtr (T * p = 0)  { ptr = p; }
+  ~AutoPtr () { delete ptr; }
+  
+  T & operator*() const { return *ptr; }
+  T* operator->() const { return ptr; }
+  T *& Ptr() { return ptr; }
+  T * Ptr() const { return ptr; }
+  void Reset(T * p = 0) { if (p != ptr) { delete ptr; ptr = p; } }
+  operator bool () { return ptr != 0; }
+private:
+  AutoPtr (AutoPtr &) { ; }
+  AutoPtr & operator= (AutoPtr &) { ; }
+};
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/bitarray.cpp b/contrib/Netgen/libsrc/general/bitarray.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1c36e5fc043b42213f48bc8a06807a7f9b357762
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/bitarray.cpp
@@ -0,0 +1,132 @@
+/**************************************************************************/
+/* File:   bitarray.cc                                                    */
+/* Autho: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/* 
+   data type BitArray
+*/
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+
+namespace netgen
+{
+  //using namespace netgen;
+
+  BitArray :: BitArray ()
+  {
+    size = 0;
+    data = NULL;
+  }
+
+  BitArray :: BitArray (int asize)
+  {
+    size = 0;
+    data = NULL;
+    SetSize (asize);
+  }
+
+  BitArray :: ~BitArray ()
+  {
+    delete [] data;
+  }
+
+  void BitArray :: SetSize (int asize)
+  {
+    if (size == asize) return;
+    delete [] data;
+
+    size = asize;
+    data = new unsigned char [Addr (size)+1];
+  }
+
+  void BitArray :: Set ()
+  {
+    if (!size) return;
+    for (int i = 0; i <= Addr (size); i++)
+      data[i] = UCHAR_MAX;
+  }
+
+  void BitArray :: Clear ()
+  {
+    if (!size) return;
+    for (int i = 0; i <= Addr (size); i++)
+      data[i] = 0;
+  }
+
+
+
+  void BitArray :: Invert ()
+  {
+    if (!size) return;
+    for (int i = 0; i <= Addr (size); i++)
+      data[i] ^= 255;
+  }
+
+  void BitArray :: And (const BitArray & ba2)
+  {
+    if (!size) return;
+    for (int i = 0; i <= Addr (size); i++)
+      data[i] &= ba2.data[i];
+  }
+
+
+  void BitArray :: Or (const BitArray & ba2)
+  {
+    if (!size) return;
+    for (int i = 0; i <= Addr (size); i++)
+      data[i] |= ba2.data[i];
+  }
+
+
+
+
+
+
+
+
+
+
+
+  template <int BASE>
+  void BitArrayChar<BASE> :: Set ()
+  {
+    data = 1;
+  }
+
+  template <int BASE>
+  void BitArrayChar<BASE> :: Clear ()
+  {
+    data = 0;
+  }
+
+
+  template <int BASE>
+  void BitArrayChar<BASE> :: Invert ()
+  {
+    for (int i = BASE; i < data.Size()+BASE; i++)
+      data[i] = 1 - data[i];
+  }
+
+  template <int BASE>
+  void BitArrayChar<BASE> :: And (const BitArrayChar & ba2)
+  {
+    for (int i = BASE; i < data.Size()+BASE; i++)
+      data[i] &= ba2.data[i];
+  }
+  
+
+  template <int BASE>
+  void BitArrayChar<BASE> :: Or (const BitArrayChar & ba2)
+  {
+    for (int i = BASE; i < data.Size()+BASE; i++)
+      data[i] |= ba2.data[i];
+  }
+  
+
+  template class BitArrayChar<0>;
+  template class BitArrayChar<1>;
+}
diff --git a/contrib/Netgen/libsrc/general/bitarray.hpp b/contrib/Netgen/libsrc/general/bitarray.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..64a4ee6e60e663a4dae9c457b7b5de9511c6f307
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/bitarray.hpp
@@ -0,0 +1,227 @@
+#ifndef FILE_BitArray
+#define FILE_BitArray
+
+/**************************************************************************/
+/* File:   bitarray.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+#include <limits.h>
+
+namespace netgen
+{
+
+
+/**
+   data type BitArray
+   
+   BitArray is a compressed array of Boolean information. By Set and Clear
+   the whole array or one bit can be set or reset, respectively. 
+   Test returns the state of the accoring bit.
+   No range checking is done.
+
+   index ranges from 0 to size-1
+*/
+class BitArray
+{
+  INDEX size;
+  unsigned char * data;
+
+public:
+  BitArray ();
+  ///
+  BitArray (INDEX asize);
+  ///
+  ~BitArray ();
+
+  /// 
+  void SetSize (INDEX asize);
+  ///
+  INDEX Size () const
+  {
+    return size;
+  }
+
+  ///
+  void Set ();
+  ///
+  void Set (INDEX i)
+  {
+    data[Addr(i)] |= Mask(i);
+  }
+  
+  void Clear ();
+
+
+  void Clear (INDEX i)
+  {
+    data[Addr(i)] &= ~Mask(i);
+  }
+
+  bool Test (INDEX i) const
+  {
+    return (data[i / CHAR_BIT] & (char(1) << (i % CHAR_BIT) ) ) ? true : false;
+  }
+
+  ///
+  void Invert ();
+  ///
+  void And (const BitArray & ba2);
+  ///
+  void Or (const BitArray & ba2);
+private:
+  ///
+  inline unsigned char Mask (INDEX i) const
+  {
+    return char(1) << (i % CHAR_BIT);
+  }
+  ///
+  inline INDEX Addr (INDEX i) const
+  {
+  return (i / CHAR_BIT);
+  }
+
+  ///
+  BitArray & operator= (BitArray &);
+  ///
+  BitArray (const BitArray &);
+};
+
+
+
+// print bitarray
+inline ostream & operator<< (ostream & s, const BitArray & a)
+{
+  for (int i = 1; i <= a.Size(); i++)
+    {
+      s << int (a.Test(i));
+      if (i % 40 == 0) s << "\n";
+    }
+  if (a.Size() % 40 != 0) s << "\n";
+  return s;
+}
+
+
+/*
+inline
+INDEX BitArray :: Size () const
+  {
+  return size;
+  }
+
+inline
+unsigned char BitArray :: Mask (INDEX i) const
+  {
+  return char(1) << (i % CHAR_BIT);
+  }
+
+inline
+INDEX BitArray :: Addr (INDEX i) const
+  {
+  return (i / CHAR_BIT);
+  }
+inline
+void BitArray :: Set (INDEX i)
+  {
+  data[Addr(i)] |= Mask(i);
+  }
+
+inline
+void BitArray :: Clear (INDEX i)
+  {
+  data[Addr(i)] &= ~Mask(i);
+  }
+
+
+inline
+int BitArray :: Test (INDEX i) const
+  {
+  return (data[i / CHAR_BIT] & (char(1) << (i % CHAR_BIT) ) ) ? 1 : 0;
+  }
+
+*/
+
+
+
+
+
+
+/**
+   data type BitArrayChar
+   
+   BitArray is an array of Boolean information. By Set and Clear
+   the whole array or one bit can be set or reset, respectively. 
+   Test returns the state of the accoring bit.
+   No range checking is done.
+*/
+template <int BASE = 1>
+class BitArrayChar
+{
+  ///
+  Array<char,BASE> data;
+
+public:
+  ///
+  BitArrayChar ()
+  { ; }
+  ///
+  BitArrayChar (int asize)
+    : data(asize)
+  { ; }
+  ///
+  ~BitArrayChar ()
+  { ; }
+
+  ///
+  void SetSize (int asize)
+  { data.SetSize(asize); }
+
+  ///
+  inline int Size () const
+  { return data.Size(); }
+
+  ///
+  void Set ();
+  ///
+  inline void Set (int i)
+  { data[i] = 1; }
+  ///
+  void Clear ();
+  ///
+  inline void Clear (int i)
+  { data[i] = 0; }
+  ///
+  inline int Test (int i) const
+  { return data[i]; }
+  ///
+  void Invert ();
+  ///
+  void And (const BitArrayChar & ba2);
+  ///
+  void Or (const BitArrayChar & ba2);
+private:
+  ///  copy bitarray is not supported
+  BitArrayChar & operator= (BitArrayChar &) { return *this; }
+  ///  copy bitarray is not supported
+  BitArrayChar (const BitArrayChar &) { ; }
+};
+
+
+
+
+template <int BASE>
+inline ostream & operator<< (ostream & s, const BitArrayChar<BASE> & a)
+{
+  for (int i = BASE; i < a.Size()+BASE; i++)
+    {
+      s << a.Test(i);
+      if ( (i-BASE) % 40 == 39) s << "\n";
+    }
+  if (a.Size() % 40 != 0) s << "\n";
+  return s;
+}
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/dynamicmem.cpp b/contrib/Netgen/libsrc/general/dynamicmem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..19713f0c09528001850ebf54de7951b47333a28e
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/dynamicmem.cpp
@@ -0,0 +1,201 @@
+#include <myadt.hpp>
+
+using namespace std;
+
+namespace netgen
+{
+
+  BaseDynamicMem * BaseDynamicMem::first = 0;
+  BaseDynamicMem * BaseDynamicMem::last = 0;
+
+
+  BaseDynamicMem :: BaseDynamicMem ()
+  {
+    prev = last;
+    next = 0;
+
+    if (last) last->next = this;
+    last = this;
+    if (!first) first = this;
+
+    size = 0;
+    ptr = 0;
+    name = 0;
+  }
+ 
+  BaseDynamicMem :: ~BaseDynamicMem ()
+  {
+    Free();
+
+    if (next) next->prev = prev;
+    else last = prev;
+    if (prev) prev->next = next;
+    else first = next;
+
+    delete [] name;
+  }
+
+  void BaseDynamicMem :: SetName (const char * aname)
+  {
+    delete [] name;
+    if (aname)
+      {
+	name = new char[strlen(aname)+1];
+	strcpy (name, aname);
+      }
+  }
+
+
+  void BaseDynamicMem :: Alloc (size_t s)
+  {
+    size = s;
+    ptr = new char[s];
+
+    if (!ptr)
+      {
+	cerr << "BaseynamicMem, cannot allocate " << s << " bytes" << endl;
+	Print ();
+	throw ("BaseDynamicMem::Alloc: out of memory");
+      }
+    // ptr = (char*)malloc (s);
+    // ptr = (char*) _mm_malloc (s,16);
+  }
+
+  void BaseDynamicMem :: ReAlloc (size_t s)
+  {
+    if (size == s) return;
+
+    char * old = ptr;
+    ptr = new char[s];
+
+    if (!ptr)
+      {
+	cerr << "BaseynamicMem, cannot Reallocate " << s << " bytes" << endl;
+	Print ();
+	throw ("BaseDynamicMem::Alloc: out of memory");
+      }
+
+
+    // ptr = (char*)malloc(s);
+    // ptr = (char*) _mm_malloc (s,16);
+    memmove (ptr, old, (s < size) ? s : size);
+    delete [] old;
+    // free (old);
+    // _mm_free (old);
+    size = s;
+  }
+
+  void BaseDynamicMem :: Free ()
+  {
+    delete [] ptr;
+    // free (ptr);
+    // _mm_free (ptr);
+    ptr = 0;
+  }
+
+  void BaseDynamicMem :: Swap (BaseDynamicMem & m2)
+  {
+    size_t hi;
+    char * cp;
+    hi = size; size  = m2.size; m2.size = hi;
+    cp = ptr; ptr = m2.ptr; m2.ptr = cp;
+    cp = name; name = m2.name; m2.name = cp;
+  }
+
+
+  void BaseDynamicMem :: Print ()
+  {
+    cout << "****************** Dynamic Mem Report ****************" << endl;
+    BaseDynamicMem * p = first;
+    size_t mem = 0;
+    int cnt = 0;
+    while (p)
+      {
+	mem += p->size;
+	cnt++;
+
+	cout << setw(10) << p->size << " Bytes";
+	cout << ", addr = " << (void*)p->ptr;
+	if (p->name)
+	  cout << " in block " << p->name;
+	cout << endl;
+
+	p = p->next;
+      }
+
+    if (mem > 100000000)
+      cout << "memory in dynamic memory: " << mem/1048576 << " MB" << endl;
+    else if (mem > 100000)
+      cout << "memory in dynamic memory: " << mem/1024 << " kB" << endl;
+    else
+      cout << "memory in dynamic memory: " << mem << " Bytes" << endl;
+    cout << "number of blocks:         " << cnt << endl;
+    //  cout << "******************************************************" << endl;
+  }
+
+
+#ifdef __INTEL_COMPILER
+#pragma warning(push)
+#pragma warning(disable:1684)
+#endif
+
+  void BaseDynamicMem :: GetUsed (int nr, char * ch)
+  {
+    BaseDynamicMem * p = first;
+
+    for (int i = 0; i < nr; i++)
+      ch[i] = '0';
+
+    while (p)
+      {
+        long unsigned hptr = (long long unsigned) (p->ptr);
+	// uintptr_t hptr = reinterpret_cast<uintptr_t>(p->ptr); //??
+
+	hptr /= (1024*1024);
+	hptr /= (4096/nr);
+
+	size_t blocks = p->size / (1024*1024);
+	blocks /= (4096/nr);
+	
+	// cout << "ptr = " << (void*)(p->ptr) << ", size = " << p->size << ", hptr = " << hptr << " blocks = " << blocks << endl;
+
+	for (size_t i = 0; i <= blocks; i++)
+	  ch[hptr+i] = '1';
+
+	p = p->next;
+      }
+    
+    {
+
+      /*
+    BaseMoveableMem * pm = BaseMoveableMem::first;
+    while (pm)
+      {
+        long unsigned hptr = (long unsigned) pm->ptr;
+        // uintptr_t hptr = reinterpret_cast<uintptr_t>(pm->ptr);
+
+	hptr /= (1024*1024);
+	hptr /= (4096/nr);
+
+	size_t blocks = pm->size / (1024*1024);
+	blocks /= (4096/nr);
+	
+	// cout << "moveable, ptr = " << (void*)(pm->ptr) << ", size = " << pm->size << ", hptr = " << hptr << " blocks = " << blocks << endl;
+
+	for (size_t i = 0; i <= blocks; i++)
+	  ch[hptr+i] = '1';
+
+	pm = pm->next;
+      }
+      */
+    }
+
+
+
+  }
+
+#ifdef __INTEL_COMPILER
+#pragma warning(pop)
+#endif
+
+}
diff --git a/contrib/Netgen/libsrc/general/dynamicmem.hpp b/contrib/Netgen/libsrc/general/dynamicmem.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a080cbdaa24fafd5abb9207570546b97aaf151eb
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/dynamicmem.hpp
@@ -0,0 +1,98 @@
+#ifndef FILE_DYNAMICMEM
+#define FILE_DYNAMICMEM
+
+/**************************************************************************/
+/* File:   dynamicmem.hpp                                                 */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   12. Feb. 2003                                                  */
+/**************************************************************************/
+
+namespace netgen
+{
+
+
+class BaseDynamicMem
+{
+private:
+  static BaseDynamicMem *first, *last;
+
+  BaseDynamicMem *prev, *next;
+  size_t size;
+  char * ptr;
+  char * name;
+
+protected:
+  BaseDynamicMem ();
+  ~BaseDynamicMem ();
+  void Alloc (size_t s);
+  void ReAlloc (size_t s);
+  void Free ();
+  char * Ptr() { return ptr; }
+  const char * Ptr() const { return ptr; }
+  void Swap (BaseDynamicMem & m2);
+public:
+  void SetName (const char * aname);
+  static void Print ();
+  static void GetUsed (int nr, char * ch);
+};
+
+
+template <typename T>
+class DynamicMem : public BaseDynamicMem
+{
+public:
+  DynamicMem ()
+    : BaseDynamicMem () 
+  {
+    ;
+  }
+  DynamicMem (size_t s)
+    : BaseDynamicMem () 
+  {
+    Alloc (s);
+  }
+  void Alloc (size_t s)
+  {
+    BaseDynamicMem::Alloc (sizeof(T) * s);
+  }
+  void ReAlloc (size_t s)
+  {
+    BaseDynamicMem::ReAlloc (sizeof(T) * s);
+  }
+  void Free ()
+  {
+    BaseDynamicMem::Free ();
+  }
+
+  const T * Ptr() const
+  {
+    return reinterpret_cast<const T*> (BaseDynamicMem::Ptr());
+  }
+
+  T * Ptr()
+  {
+    return reinterpret_cast<T*> (BaseDynamicMem::Ptr());
+  }
+
+  operator const T* () const
+  {
+    return reinterpret_cast<const T*> (BaseDynamicMem::Ptr());
+  }
+
+  operator T* () 
+  {
+    return reinterpret_cast<T*> (BaseDynamicMem::Ptr());
+  }
+
+  void Swap (DynamicMem<T> & m2)
+  {
+    BaseDynamicMem::Swap (m2);
+  }
+protected:
+  DynamicMem (const DynamicMem & m);
+  DynamicMem & operator= (const DynamicMem & m);
+};
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/flags.cpp b/contrib/Netgen/libsrc/general/flags.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e5916f8758273cea7078d8d7ae794aba4b8d528f
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/flags.cpp
@@ -0,0 +1,330 @@
+/**************************************************************************/
+/* File:   flags.cc                                                       */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   10. Oct. 96                                                    */
+/**************************************************************************/
+
+/* 
+   Datatype Flags
+*/
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+namespace netgen
+{
+  //using namespace netgen;
+
+  Flags :: Flags ()
+  {
+    ;
+  }
+  
+  Flags :: ~Flags ()
+  {
+    DeleteFlags ();
+  }
+  
+  void Flags :: DeleteFlags ()
+  {
+    for (int i = 0; i < strflags.Size(); i++)
+      delete [] strflags[i];
+    for (int i = 0; i < numlistflags.Size(); i++)
+      delete numlistflags[i];
+    strflags.DeleteAll();
+    numflags.DeleteAll();
+    defflags.DeleteAll();
+    strlistflags.DeleteAll();
+    numlistflags.DeleteAll();
+  }
+  
+  void Flags :: SetFlag (const char * name, const char * val)
+  {
+    char * hval = new char[strlen (val) + 1];
+    strcpy (hval, val);
+    strflags.Set (name, hval);
+  }
+  
+  void Flags :: SetFlag (const char * name, double val)
+  {
+    numflags.Set (name, val);
+  }
+  
+  void Flags :: SetFlag (const char * name)
+  {
+    defflags.Set (name, 1);
+  }
+
+
+  void Flags :: SetFlag (const char * name, const Array<char*> & val)
+  {
+    Array<char*> * strarray = new Array<char*>;
+    for (int i = 1; i <= val.Size(); i++)
+      {
+	strarray->Append (new char[strlen(val.Get(i))+1]);
+	strcpy (strarray->Last(), val.Get(i));
+      }
+    strlistflags.Set (name, strarray);
+  }
+
+  void Flags :: SetFlag (const char * name, const Array<double> & val)
+  {
+    Array<double> * numarray = new Array<double>;
+    for (int i = 1; i <= val.Size(); i++)
+      numarray->Append (val.Get(i));
+    numlistflags.Set (name, numarray);
+  }
+
+
+
+
+  
+  const char * 
+  Flags :: GetStringFlag (const char * name, const char * def) const
+  {
+    if (strflags.Used (name))
+      return strflags.Get(name);
+    else
+      return def;
+  }
+
+  double Flags :: GetNumFlag (const char * name, double def) const
+  {
+    if (numflags.Used (name))
+      return numflags.Get(name);
+    else
+      return def;
+  }
+  
+  const double * Flags :: GetNumFlagPtr (const char * name) const
+  {
+    if (numflags.Used (name))
+      return & ((SYMBOLTABLE<double>&)numflags).Elem(name);
+    else
+      return NULL;
+  }
+  
+  double * Flags :: GetNumFlagPtr (const char * name) 
+  {
+    if (numflags.Used (name))
+      return & ((SYMBOLTABLE<double>&)numflags).Elem(name);
+    else
+      return NULL;
+  }
+  
+  bool Flags :: GetDefineFlag (const char * name) const
+  {
+    return defflags.Used (name);
+  }
+
+
+  const Array<char*> & 
+  Flags :: GetStringListFlag (const char * name) const
+  {
+    if (strlistflags.Used (name))
+      return *strlistflags.Get(name);
+    else
+      {
+	static Array<char*> dummy_array(0);
+	return dummy_array;
+      }
+  }
+
+  const Array<double> & 
+  Flags ::GetNumListFlag (const char * name) const
+  {
+    if (numlistflags.Used (name))
+      return *numlistflags.Get(name);
+    else
+      {
+	static Array<double> dummy_array(0);
+	return dummy_array;
+      }
+  }
+
+
+  bool Flags :: StringFlagDefined (const char * name) const
+  {
+    return strflags.Used (name);
+  }
+
+  bool Flags :: NumFlagDefined (const char * name) const
+  {
+    return numflags.Used (name);
+  }
+
+  bool Flags :: StringListFlagDefined (const char * name) const
+  {
+    return strlistflags.Used (name);
+  }
+
+  bool Flags :: NumListFlagDefined (const char * name) const
+  {
+    return numlistflags.Used (name);
+  }
+
+
+  void Flags :: SaveFlags (const char * filename) const 
+  {
+    int i;
+    ofstream outfile (filename);
+  
+    for (i = 1; i <= strflags.Size(); i++)
+      outfile << strflags.GetName(i) << " = " << strflags.Get(i) << endl;
+    for (i = 1; i <= numflags.Size(); i++)
+      outfile << numflags.GetName(i) << " = " << numflags.Get(i) << endl;
+    for (i = 1; i <= defflags.Size(); i++)
+      outfile << defflags.GetName(i) << endl;
+  }
+ 
+
+
+  void Flags :: PrintFlags (ostream & ost) const 
+  {
+    int i;
+  
+    for (i = 1; i <= strflags.Size(); i++)
+      ost << strflags.GetName(i) << " = " << strflags.Get(i) << endl;
+    for (i = 1; i <= numflags.Size(); i++)
+      ost << numflags.GetName(i) << " = " << numflags.Get(i) << endl;
+    for (i = 1; i <= defflags.Size(); i++)
+      ost << defflags.GetName(i) << endl;
+  }
+ 
+
+  void Flags :: LoadFlags (const char * filename) 
+  {
+    char name[100], str[100];
+    char ch;
+    double val;
+    ifstream infile(filename);
+
+    //  (*logout) << "Load flags from " << filename << endl << endl;
+    while (infile.good())
+      {
+	infile >> name;
+	if (strlen (name) == 0) break;
+
+	if (name[0] == '/' && name[1] == '/')
+	  {
+	    //	  (*logout) << "comment: ";
+	    ch = 0;
+	    while (ch != '\n' && infile.good())
+	      {
+		ch = infile.get();
+		//	      (*logout) << ch;
+	      }
+	    continue;
+	  }
+
+	//      (*logout)  << name;
+	ch = 0;
+	infile >> ch;
+	if (ch != '=')
+	  {
+	    //	  (*logout) << endl;
+	    infile.putback (ch);
+	    SetFlag (name);
+	  }
+	else
+	  {
+	    infile >> val;
+	    if (!infile.good())
+	      {
+		infile.clear();
+		infile >> str;
+		SetFlag (name, str);
+		//	      (*logout) << " = " << str << endl;
+	      }
+	    else
+	      {
+		SetFlag (name, val);
+		//	      (*logout) << " = " << val << endl;
+	      }
+	  }
+      }
+    //  (*logout) << endl;
+  }
+
+
+  void Flags :: SetCommandLineFlag (const char * st)
+  {
+    //  cout << "clflag = " << st << endl;
+    istringstream inst( (char *)st);
+    // istrstream defined with char *  (not const char *  ?????)
+
+    char name[100];
+    double val;
+
+
+    if (st[0] != '-')
+      {
+	cerr << "flag must start with '-'" << endl;
+	return;
+      }
+  
+    const char * pos = strchr (st, '=');
+  
+    if (!pos)
+      {
+	//      (cout) << "Add def flag: " << st+1 << endl;
+	SetFlag (st+1);
+      }
+    else
+      {
+	//      cout << "pos = " << pos << endl;
+
+	strncpy (name, st+1, (pos-st)-1);
+	name[pos-st-1] = 0;
+
+	//      cout << "name = " << name << endl;
+
+	pos++;
+	char * endptr = NULL;
+
+	val = strtod (pos, &endptr);
+
+	//      cout << "val = " << val << endl;
+
+	if (endptr == pos)
+	  {
+	    //	  (cout) << "Add String Flag: " << name << " = " << pos << endl;
+	    SetFlag (name, pos);
+	  }
+	else
+	  {
+	    //	  (cout) << "Add Num Flag: " << name << " = " << val << endl;
+	    SetFlag (name, val);
+	  }
+      }
+
+
+    /*
+      inst >> name;
+      (*mycout) << "name = " << name << endl;
+
+      ch = 0;
+      inst >> ch;
+      if (ch != '=')
+      {
+      SetFlag (name);
+      }
+      else
+      {
+      inst >> val;
+      if (!inst.good())
+      {
+      inst.clear();
+      inst >> str;
+      SetFlag (name, str);
+      (*mycout) << "str = " << str << endl;
+      }
+      else
+      {
+      SetFlag (name, val);
+      (*mycout) << "val = " << val << endl;
+      }
+      }
+    */
+  }
+}
diff --git a/contrib/Netgen/libsrc/general/flags.hpp b/contrib/Netgen/libsrc/general/flags.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..810fbfbe6b09348db9be959bd2555de160435b18
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/flags.hpp
@@ -0,0 +1,88 @@
+#ifndef FILE_FLAGS
+#define FILE_FLAGS
+
+
+/**************************************************************************/
+/* File:   flags.hh                                                       */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   10. Oct. 96                                                   */
+/**************************************************************************/
+
+namespace netgen
+{
+
+/** 
+   Flag - Table.
+   A flag table maintains string variables, numerical 
+   variables and boolean flags.
+*/
+class Flags 
+{
+  ///
+  SYMBOLTABLE<char *> strflags;
+  ///
+  SYMBOLTABLE<double> numflags;
+  ///
+  SYMBOLTABLE<int> defflags;
+  ///
+  SYMBOLTABLE<Array<char*>*> strlistflags;
+  ///
+  SYMBOLTABLE<Array<double>*> numlistflags;
+public:
+  ///
+  DLL_HEADER Flags ();
+  ///
+  DLL_HEADER ~Flags ();
+  
+  /// Deletes all flags
+  DLL_HEADER void DeleteFlags ();
+  /// Sets string flag, overwrite if exists
+  DLL_HEADER void SetFlag (const char * name, const char * val);
+  /// Sets numerical flag, overwrite if exists
+  DLL_HEADER void SetFlag (const char * name, double val);
+  /// Sets boolean flag
+  DLL_HEADER void SetFlag (const char * name);
+  /// Sets string arary falg
+  DLL_HEADER void SetFlag (const char * name, const Array<char*> & val);
+  /// Sets double array flag
+  DLL_HEADER void SetFlag (const char * name, const Array<double> & val);
+  
+  /// Save flags to file
+  DLL_HEADER void SaveFlags (const char * filename) const;
+  /// write flags to stream
+  DLL_HEADER void PrintFlags (ostream & ost) const;
+  /// Load flags from file
+  DLL_HEADER void LoadFlags (const char * filename);
+  /// set flag of form -name=hello -val=0.5 -defined
+  DLL_HEADER void SetCommandLineFlag (const char * st);
+
+  /// Returns string flag, default value if not exists
+  DLL_HEADER const char * GetStringFlag (const char * name, const char * def) const;
+  /// Returns numerical flag, default value if not exists
+  DLL_HEADER double GetNumFlag (const char * name, double def) const;
+  /// Returns address of numerical flag, null if not exists
+  DLL_HEADER const double * GetNumFlagPtr (const char * name) const;
+  /// Returns address of numerical flag, null if not exists
+  DLL_HEADER double * GetNumFlagPtr (const char * name);
+  /// Returns boolean flag
+  DLL_HEADER bool GetDefineFlag (const char * name) const;
+  /// Returns string list flag, empty array if not exist
+  DLL_HEADER const Array<char*> & GetStringListFlag (const char * name) const;
+  /// Returns num list flag, empty array if not exist
+  DLL_HEADER const Array<double> & GetNumListFlag (const char * name) const;
+
+
+  /// Test, if string flag is defined
+  DLL_HEADER bool StringFlagDefined (const char * name) const;
+  /// Test, if num flag is defined
+  DLL_HEADER bool NumFlagDefined (const char * name) const;
+  /// Test, if string list flag is defined
+  DLL_HEADER bool StringListFlagDefined (const char * name) const;
+  /// Test, if num list flag is defined
+  DLL_HEADER bool NumListFlagDefined (const char * name) const;
+};
+
+}
+  
+#endif
+
diff --git a/contrib/Netgen/libsrc/general/hashtabl.cpp b/contrib/Netgen/libsrc/general/hashtabl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..53d7eafb46f2b788f3c3df7f70e187fe6e75222f
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/hashtabl.cpp
@@ -0,0 +1,326 @@
+/**************************************************************************/
+/* File:   hashtabl.cpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/* 
+   Abstract data type HASHTABLE
+*/
+
+#include <algorithm>
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+namespace netgen
+{
+  //using namespace netgen;
+
+  void INDEX_4 :: Sort ()
+  {
+    if (i[0] > i[1]) Swap (i[0], i[1]);
+    if (i[2] > i[3]) Swap (i[2], i[3]);
+    if (i[0] > i[2]) Swap (i[0], i[2]);
+    if (i[1] > i[3]) Swap (i[1], i[3]);
+    if (i[1] > i[2]) Swap (i[1], i[2]);
+  }
+
+
+
+  void INDEX_4Q :: Sort ()
+  {
+    if (min2 (i[1], i[2]) < min2 (i[0], i[3]))
+      { Swap (i[0], i[1]); Swap (i[2], i[3]);}
+    if (i[3] < i[0])
+      { Swap (i[0], i[3]); Swap (i[1], i[2]);}
+    if (i[3] < i[1])
+      { Swap (i[1], i[3]); }
+  }
+
+
+  ostream & operator<<(ostream  & s, const INDEX_2 & i2)
+  {
+    return s << i2.I1() << ", " << i2.I2();
+  }
+
+  ostream & operator<<(ostream  & s, const INDEX_3 & i3)
+  {
+    return s << i3.I1() << ", " << i3.I2() << ", " << i3.I3();
+  }
+
+  ostream & operator<<(ostream  & s, const INDEX_4 & i4)
+  {
+    return s << i4.I1() << ", " << i4.I2() << ", " << i4.I3() << ", " << i4.I4();
+  }
+
+  ostream & operator<<(ostream  & s, const INDEX_4Q & i4)
+  {
+    return s << i4.I1() << ", " << i4.I2() << ", " << i4.I3() << ", " << i4.I4();
+  }
+
+
+  int BASE_INDEX_HASHTABLE :: Position (int bnr, const INDEX & ind) const
+  {
+    for (int i = 1; i <= hash.EntrySize (bnr); i++)
+      if (hash.Get(bnr, i) == ind)
+	return i;
+    return 0;
+  }
+
+
+
+  /*
+  int BASE_INDEX_2_HASHTABLE :: Position (int bnr, const INDEX_2 & ind) const
+  {
+    int i;
+    for (i = 1; i <= hash.EntrySize (bnr); i++)
+      if (hash.Get(bnr, i) == ind)
+	return i;
+    return 0;
+  }
+  */  
+
+  void BASE_INDEX_2_HASHTABLE :: PrintStat (ostream & ost) const
+  {
+    int n = hash.Size();
+    int i;
+    int sumn = 0, sumnn = 0;
+
+    for (i = 1; i <= n; i++)
+      {
+	sumn += hash.EntrySize(i);
+	sumnn += sqr (hash.EntrySize(i));
+      }
+
+    ost << "Hashtable: " << endl
+	<< "size             : " << n << endl
+	<< "elements per row : " << (double(sumn) / double(n)) << endl
+	<< "av. acces time   : " 
+	<< (sumn ? (double (sumnn) / double(sumn)) : 0) << endl;
+  }
+
+
+  /*
+    int BASE_INDEX_3_HASHTABLE :: Position (int bnr, const INDEX_3 & ind) const
+    {
+    int i;
+    const INDEX_3 * pi = &hash.Get(bnr, 1);
+    int n = hash.EntrySize(bnr);
+    for (i = 1; i <= n; ++i, ++pi)
+    {
+    if (*pi == ind)
+    return i;
+    }
+
+    return 0;
+    }
+  */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  BASE_INDEX_CLOSED_HASHTABLE ::
+  BASE_INDEX_CLOSED_HASHTABLE (int size)
+    : hash(size)
+  {
+    hash.SetName ("index-hashtable, hash");
+
+    invalid = -1;
+    for (int i = 1; i <= size; i++)
+      hash.Elem(i) = invalid;
+  }
+
+  void BASE_INDEX_CLOSED_HASHTABLE ::
+  BaseSetSize (int size)
+  {
+    hash.SetSize(size);
+    for (int i = 1; i <= size; i++)
+      hash.Elem(i) = invalid;
+  }
+
+  int BASE_INDEX_CLOSED_HASHTABLE ::
+  Position2 (const INDEX & ind) const
+  {
+    int i = HashValue(ind);
+    while (1)
+      {
+	i++;
+	if (i > hash.Size()) i = 1;
+	if (hash.Get(i) == ind) return i;
+	if (hash.Get(i) == invalid) return 0;
+      }
+  }
+
+  int BASE_INDEX_CLOSED_HASHTABLE ::
+  PositionCreate2 (const INDEX & ind, int & apos) 
+  {
+    int i = HashValue(ind);
+    int startpos = i;
+    while (1)
+      {
+	i++;
+	if (i > hash.Size()) i = 1;
+	if (hash.Get(i) == ind) 
+	  {
+	    apos = i;
+	    return 0;
+	  }
+	if (hash.Get(i) == invalid) 
+	  {
+	    hash.Elem(i) = ind;
+	    apos = i;
+	    return 1;
+	  }
+	if (i == startpos)
+	  throw NgException ("Try to set new element in full closed hashtable");
+      }
+  }
+
+  int BASE_INDEX_CLOSED_HASHTABLE :: UsedElements () const
+  {
+    int n = hash.Size();
+    int cnt = 0;
+    for (int i = 1; i <= n; i++)
+      if (hash.Get(i) != invalid)
+	cnt++;
+    return cnt;
+  }
+
+
+
+
+
+
+
+
+
+
+
+  BASE_INDEX_2_CLOSED_HASHTABLE ::
+  BASE_INDEX_2_CLOSED_HASHTABLE (int size)
+    : hash(size)
+  {
+    hash.SetName ("i2-hashtable, hash");
+
+    invalid = -1;
+    for (int i = 1; i <= size; i++)
+      hash.Elem(i).I1() = invalid;
+  }
+
+  void BASE_INDEX_2_CLOSED_HASHTABLE ::
+  BaseSetSize (int size)
+  {
+    hash.SetSize(size);
+    for (int i = 1; i <= size; i++)
+      hash.Elem(i).I1() = invalid;
+  }
+
+
+  int BASE_INDEX_2_CLOSED_HASHTABLE ::
+  Position2 (const INDEX_2 & ind) const
+  {
+    int i = HashValue(ind);
+    while (1)
+      {
+	i++;
+	if (i > hash.Size()) i = 1;
+	if (hash.Get(i) == ind) return i;
+	if (hash.Get(i).I1() == invalid) return 0;
+      }
+  }
+
+  int BASE_INDEX_2_CLOSED_HASHTABLE ::
+  PositionCreate2 (const INDEX_2 & ind, int & apos) 
+  {
+    int i = HashValue(ind);
+    int startpos = i;
+    while (1)
+      {
+	i++;
+	if (i > hash.Size()) i = 1;
+	if (hash.Get(i) == ind) 
+	  {
+	    apos = i;
+	    return 0;
+	  }
+	if (hash.Get(i).I1() == invalid) 
+	  {
+	    hash.Elem(i) = ind;
+	    apos = i;
+	    return 1;
+	  }
+	if (i == startpos)
+	  throw NgException ("Try to set new element in full closed hashtable");
+      }
+  }
+
+  int BASE_INDEX_2_CLOSED_HASHTABLE :: UsedElements () const
+  {
+    int n = hash.Size();
+    int cnt = 0;
+    for (int i = 1; i <= n; i++)
+      if (hash.Get(i).I1() != invalid)
+	cnt++;
+    return cnt;
+  }
+
+
+
+
+
+
+
+
+  void BASE_INDEX_3_CLOSED_HASHTABLE ::
+  BaseSetSize (int size)
+  {
+    hash.SetSize(size);
+    for (int i = 0; i < size; i++)
+      hash[i].I1() = invalid;
+  }
+
+  bool BASE_INDEX_3_CLOSED_HASHTABLE ::
+  PositionCreate2 (const INDEX_3 & ind, int & apos) 
+  {
+    int i = HashValue(ind);
+    int startpos = i;
+    while (1)
+      {
+        /*
+	i++;
+	if (i >= hash.Size()) i = 0;
+        */
+        i = (i+1) % hash.Size();
+	if (hash[i] == ind) 
+	  {
+	    apos = i;
+	    return false;
+	  }
+	if (hash[i].I1() == invalid) 
+	  {
+	    hash[i] = ind;
+	    apos = i;
+	    return true;
+	  }
+	if (i == startpos)
+	  throw NgException ("Try to set new element in full closed hashtable");
+      }
+  }
+}
+
diff --git a/contrib/Netgen/libsrc/general/hashtabl.hpp b/contrib/Netgen/libsrc/general/hashtabl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bd82518240856e3d6223311e31d4eeb482dab127
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/hashtabl.hpp
@@ -0,0 +1,1362 @@
+#ifndef FILE_HASHTABL
+#define FILE_HASHTABL
+
+/**************************************************************************/
+/* File:   hashtabl.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+/**
+   Abstract data type HASHTABLE.
+   Hash is done by one INDEX
+*/
+class BASE_INDEX_HASHTABLE
+{
+protected:
+  /// keys are stored in this table
+  TABLE<INDEX,1> hash;
+  
+public:
+  ///
+  BASE_INDEX_HASHTABLE (int size)
+    : hash (size) { };
+  
+protected:
+  ///
+  int HashValue (const INDEX & ind) const
+    {
+      return ind % hash.Size() + 1;
+    }
+
+  ///
+  int Position (int bnr, const INDEX & ind) const;
+};
+
+///
+template <class T>
+class INDEX_HASHTABLE : private BASE_INDEX_HASHTABLE
+{
+  ///
+  TABLE<T,1> cont;
+  
+public: 
+  ///
+  inline INDEX_HASHTABLE (int size);
+  ///
+  inline void Set (const INDEX & hash, const T & acont);
+  ///
+  inline const T & Get (const INDEX & ahash) const;
+  ///
+  inline bool Used (const INDEX & ahash) const;
+  ///
+  inline int GetNBags () const;
+  ///
+  inline int GetBagSize (int bnr) const;
+  ///
+  inline void GetData (int bnr, int colnr, INDEX & ahash, T & acont) const;
+
+  ///
+  inline void PrintMemInfo (ostream & ost) const;
+};
+
+
+
+
+
+
+
+
+
+
+///
+class BASE_INDEX_2_HASHTABLE
+{
+protected:
+  ///
+  TABLE<INDEX_2> hash;
+  
+public:
+  ///
+  BASE_INDEX_2_HASHTABLE (int size)
+    : hash (size) { };
+
+  ///
+  void PrintStat (ostream & ost) const;
+  void BaseSetSize(int s) {hash.SetSize(s);}
+protected:
+  ///
+  int HashValue (const INDEX_2 & ind) const
+    {
+      return (ind.I1() + ind.I2()) % hash.Size() + 1;
+    }
+  ///
+  int Position (int bnr, const INDEX_2 & ind) const
+  {
+    int i;
+    for (i = 1; i <= hash.EntrySize (bnr); i++)
+      if (hash.Get(bnr, i) == ind)
+	return i;
+    return 0;
+  }
+};
+
+
+///
+template <class T>
+class INDEX_2_HASHTABLE : public BASE_INDEX_2_HASHTABLE
+{
+  ///
+  TABLE<T> cont;
+  
+public:
+  ///
+ INDEX_2_HASHTABLE (int size)
+   : BASE_INDEX_2_HASHTABLE (size), cont(size)
+  { ; }  
+
+  ///
+  void SetSize(int s) 
+  { 
+    cont.SetSize(s); 
+    BaseSetSize(s);
+  }
+
+  ///
+  void Set (const INDEX_2 & ahash, const T & acont)
+  {
+    int bnr = HashValue (ahash);
+      int pos = Position (bnr, ahash);
+      if (pos)
+	cont.Set (bnr, pos, acont);
+      else
+	{
+	  hash.Add1 (bnr, ahash);
+	  cont.Add1 (bnr, acont);
+	}    
+  }
+  
+  ///
+  const T & Get (const INDEX_2 & ahash) const
+  {
+    int bnr = HashValue (ahash);
+    int pos = Position (bnr, ahash);
+    return cont.Get (bnr, pos);
+  }
+  
+  ///
+  bool Used (const INDEX_2 & ahash) const
+  {
+    return (Position (HashValue (ahash), ahash)) ? 1 : 0;
+  }
+ ///
+  int GetNBags () const
+  {
+    return cont.Size();
+  }
+  
+  ///
+  int GetBagSize (int bnr) const
+  {
+    return cont.EntrySize (bnr);
+  }
+    
+  ///
+  void GetData (int bnr, int colnr, 
+		INDEX_2 & ahash, T & acont) const
+  {
+    ahash = hash.Get(bnr, colnr);
+    acont = cont.Get(bnr, colnr);
+  }
+
+  ///
+  void SetData (int bnr, int colnr, 
+		const INDEX_2 & ahash, const T & acont) 
+  {
+    hash.Set(bnr, colnr, ahash);
+    cont.Set(bnr, colnr, acont);
+  }
+  
+  ///
+  void PrintMemInfo (ostream & ost) const
+  {
+    ost << "Hash: " << endl;
+    hash.PrintMemInfo (ost);
+    ost << "Cont: " << endl;
+    cont.PrintMemInfo (ost);
+  }
+
+
+  void DeleteData ()
+  {
+    int n = hash.Size();
+    hash.SetSize (n);
+    cont.SetSize (n);
+  }
+
+
+  class Iterator
+  {
+    const INDEX_2_HASHTABLE & ht;    
+    int bagnr, pos;
+  public:
+    Iterator (const INDEX_2_HASHTABLE & aht,
+	      int abagnr, int apos)
+      : ht(aht), bagnr(abagnr), pos(apos)
+    { ; }
+
+    int BagNr() const { return bagnr; }
+    int Pos() const { return pos; }
+
+    void operator++ (int)
+    {
+      // cout << "begin Operator ++: bagnr = " << bagnr << " -  pos = " << pos << endl;
+      pos++;
+      while (bagnr < ht.GetNBags() && 
+	     pos == ht.GetBagSize(bagnr+1))
+	{
+	  pos = 0;
+	  bagnr++;
+	}
+      // cout << "end Operator ++: bagnr = " << bagnr << " - pos = " << pos << endl;
+    }
+
+    bool operator != (int i) const
+    {
+      return bagnr != i;
+    }
+
+  };
+  
+  Iterator Begin () const
+  {
+    Iterator it(*this, 0, -1);
+    it++;
+    return it;
+  }
+
+  int End() const
+  {
+    return GetNBags();
+  }
+
+  void GetData (const Iterator & it,
+		INDEX_2 & ahash, T & acont) const
+  {
+    ahash = hash[it.BagNr()][it.Pos()];
+    acont = cont[it.BagNr()][it.Pos()];
+  }
+
+  const INDEX_2 & GetHash (const Iterator & it) const
+  { return hash[it.BagNr()][it.Pos()]; }
+
+  const T & GetData (const Iterator & it) const
+  { return cont[it.BagNr()][it.Pos()]; }
+};
+
+
+
+template <typename T> 
+inline ostream & operator<< (ostream & ost, const INDEX_2_HASHTABLE<T> & ht)
+{
+  for (typename INDEX_2_HASHTABLE<T>::Iterator it = ht.Begin();
+       it != ht.End(); it++)
+    {
+      ost << ht.GetHash(it) << ": " << ht.GetData(it) << endl;
+    }
+
+  return ost;
+}
+
+
+
+
+
+
+
+///
+class BASE_INDEX_3_HASHTABLE
+{
+protected:
+  ///
+  TABLE<INDEX_3> hash;
+
+public:
+  ///
+  BASE_INDEX_3_HASHTABLE (int size)
+    : hash (size) { };
+
+protected:
+  ///
+  int HashValue (const INDEX_3 & ind) const
+    {
+      return (ind.I1() + ind.I2() + ind.I3()) % hash.Size() + 1;
+    }
+
+  ///
+  int Position (int bnr, const INDEX_3 & ind) const
+  {
+    const INDEX_3 * pi = &hash.Get(bnr, 1);
+    int n = hash.EntrySize(bnr);
+    for (int i = 1; i <= n; ++i, ++pi)
+      {
+	if (*pi == ind)
+	return i;
+      }
+    
+    return 0;
+  }
+
+
+};
+
+
+///
+template <class T>
+class INDEX_3_HASHTABLE : private BASE_INDEX_3_HASHTABLE
+{
+  ///
+  TABLE<T> cont;
+
+public:
+  ///
+  inline INDEX_3_HASHTABLE (int size);
+  ///
+  inline void Set (const INDEX_3 & ahash, const T & acont);
+  ///
+  inline const T & Get (const INDEX_3 & ahash) const;
+  ///
+  inline bool Used (const INDEX_3 & ahash) const;
+  ///
+  inline int GetNBags () const;
+  ///
+  inline int GetBagSize (int bnr) const;
+  ///
+  inline void SetData (int bnr, int colnr, const INDEX_3 & ahash, const T & acont);
+  ///
+  inline void GetData (int bnr, int colnr, INDEX_3 & ahash, T & acont) const;
+  /// returns position, if not existing, will create (create == return 1)
+  inline int PositionCreate (const INDEX_3 & ahash, int & bnr, int & colnr);
+  ///
+  inline void SetSize (int size);
+
+  ///
+  inline void PrepareSet (const INDEX_3 & ahash);
+  ///
+  inline void AllocateElements ();
+
+  ///
+  inline void PrintMemInfo (ostream & ost) const;
+  ///
+  inline void DeleteData ();
+
+
+
+
+
+
+
+
+
+  class Iterator
+  {
+    const INDEX_3_HASHTABLE & ht;    
+    int bagnr, pos;
+  public:
+    Iterator (const INDEX_3_HASHTABLE & aht,
+	      int abagnr, int apos)
+      : ht(aht), bagnr(abagnr), pos(apos)
+    { ; }
+
+    int BagNr() const { return bagnr; }
+    int Pos() const { return pos; }
+
+    void operator++ (int)
+    {
+      // cout << "begin Operator ++: bagnr = " << bagnr << " -  pos = " << pos << endl;
+      pos++;
+      while (bagnr < ht.GetNBags() && 
+	     pos == ht.GetBagSize(bagnr+1))
+	{
+	  pos = 0;
+	  bagnr++;
+	}
+      // cout << "end Operator ++: bagnr = " << bagnr << " - pos = " << pos << endl;
+    }
+
+    bool operator != (int i) const
+    {
+      return bagnr != i;
+    }
+
+  };
+  
+  Iterator Begin () const
+  {
+    Iterator it(*this, 0, -1);
+    it++;
+    return it;
+  }
+
+  int End() const
+  {
+    return GetNBags();
+  }
+
+  void GetData (const Iterator & it,
+		INDEX_3 & ahash, T & acont) const
+  {
+    ahash = hash[it.BagNr()][it.Pos()];
+    acont = cont[it.BagNr()][it.Pos()];
+  }
+
+  const INDEX_3 & GetHash (const Iterator & it) const
+  { return hash[it.BagNr()][it.Pos()]; }
+
+  const T & GetData (const Iterator & it) const
+  { return cont[it.BagNr()][it.Pos()]; }
+
+
+
+};
+
+
+
+
+
+
+template <typename T> 
+inline ostream & operator<< (ostream & ost, const INDEX_3_HASHTABLE<T> & ht)
+{
+  for (typename INDEX_3_HASHTABLE<T>::Iterator it = ht.Begin();
+       it != ht.End(); it++)
+    {
+      ost << ht.GetHash(it) << ": " << ht.GetData(it) << endl;
+    }
+
+  return ost;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/// Closed Hashing HT
+
+class BASE_INDEX_CLOSED_HASHTABLE
+{
+protected:
+  ///
+  MoveableArray<INDEX> hash;
+  ///
+  int invalid;
+public:
+  ///
+  BASE_INDEX_CLOSED_HASHTABLE (int size);
+
+  int Size() const { return hash.Size(); }
+  int UsedPos (int pos) const { return ! (hash.Get(pos) == invalid); }
+  int UsedElements () const;
+
+  ///
+  int HashValue (const INDEX & ind) const
+  {
+    return (3*ind) % hash.Size() + 1;
+  }
+
+
+  int Position (const INDEX & ind) const
+  {
+    int i = HashValue(ind);
+    while (1)
+      {
+	if (hash.Get(i) == ind) return i;
+	if (hash.Get(i) == invalid) return 0;
+	i++;
+	if (i > hash.Size()) i = 1;
+      }
+  }
+
+  int CalcPositionCosts (const INDEX & ind) const
+  {
+    int i = HashValue(ind);
+    int costs = 1;
+    while (1)
+      {
+	if (hash.Get(i) == ind) return costs;
+	if (hash.Get(i) == invalid) return costs;
+	i++;
+	if (i > hash.Size()) i = 1;
+	costs++;
+      }
+  }
+
+
+
+  // returns 1, if new postion is created
+  int PositionCreate (const INDEX & ind, int & apos)
+  {
+    int i = HashValue (ind);
+    if (hash.Get(i) == ind) 
+      {
+	apos = i;
+	return 0;
+      }
+    if (hash.Get(i) == invalid)
+      {
+	hash.Elem(i) = ind; 
+	apos = i;
+	return 1;
+      }
+    return PositionCreate2 (ind, apos);    
+  }
+
+protected:
+  int Position2 (const INDEX & ind) const;
+  int PositionCreate2 (const INDEX & ind, int & apos);
+  void BaseSetSize (int asize);
+};
+
+
+template <class T>
+class INDEX_CLOSED_HASHTABLE : public BASE_INDEX_CLOSED_HASHTABLE
+{
+  ///
+  MoveableArray<T> cont;
+
+public:
+  ///
+  INDEX_CLOSED_HASHTABLE (int size)
+    : BASE_INDEX_CLOSED_HASHTABLE(size), cont(size)
+  {
+    cont.SetName ("ind-hashtable, contents");
+  }
+
+
+  void Set (const INDEX & ahash, const T & acont)
+  {
+    int pos;
+    PositionCreate (ahash, pos);
+    hash.Elem(pos) = ahash;
+    cont.Elem(pos) = acont;
+  }
+
+  const T & Get (const INDEX & ahash) const
+  {
+    int pos = Position (ahash);
+    return cont.Get(pos);
+  }
+
+  ///
+  bool Used (const INDEX & ahash) const
+  {
+    int pos = Position (ahash);
+    return (pos != 0);
+  }
+  
+  
+  ///
+  inline void SetData (int pos, const INDEX & ahash, const T & acont)
+  {
+    hash.Elem(pos) = ahash;
+    cont.Elem(pos) = acont;
+  }
+
+  ///
+  void GetData (int pos, INDEX & ahash, T & acont) const
+  {
+    ahash = hash.Get(pos);
+    acont = cont.Get(pos);
+  }
+  
+  ///
+  inline void SetData (int pos, const T & acont)
+  {
+    cont.Elem(pos) = acont;
+  }
+  
+  ///
+  void GetData (int pos, T & acont) const
+  {
+    acont = cont.Get(pos);
+  }
+  
+  ///
+  const T & GetData (int pos) { return cont.Get(pos); }
+  ///
+  inline void SetSize (int size)
+  {
+    BaseSetSize(size);
+  cont.SetSize(size);
+  }
+  
+  ///
+  inline void DeleteData ()
+  { SetSize (cont.Size()); }
+
+  void SetName (const char * aname)
+  {
+    cont.SetName(aname);
+    hash.SetName(aname);
+  }
+};
+
+
+
+
+
+
+
+/// Closed Hashing HT
+
+class BASE_INDEX_2_CLOSED_HASHTABLE
+{
+protected:
+  ///
+  MoveableArray<INDEX_2> hash;
+  ///
+  int invalid;
+public:
+  ///
+  BASE_INDEX_2_CLOSED_HASHTABLE (int size);
+
+  int Size() const { return hash.Size(); }
+  int UsedPos (int pos) const { return ! (hash.Get(pos).I1() == invalid); }
+  int UsedElements () const;
+
+  ///
+  int HashValue (const INDEX_2 & ind) const
+    {
+      return (ind.I1() + 71 * ind.I2()) % hash.Size() + 1;
+    }
+
+
+  int Position (const INDEX_2 & ind) const
+  {
+    int i = HashValue(ind);
+    while (1)
+      {
+	if (hash.Get(i) == ind) return i;
+	if (hash.Get(i).I1() == invalid) return 0;
+	i++;
+	if (i > hash.Size()) i = 1;
+      }
+  }
+
+  // returns 1, if new postion is created
+  int PositionCreate (const INDEX_2 & ind, int & apos)
+  {
+    int i = HashValue (ind);
+    if (hash.Get(i) == ind) 
+      {
+	apos = i;
+	return 0;
+      }
+    if (hash.Get(i).I1() == invalid)
+      {
+	hash.Elem(i) = ind; 
+	apos = i;
+	return 1;
+      }
+    return PositionCreate2 (ind, apos);    
+  }
+
+protected:
+  ///
+
+  int Position2 (const INDEX_2 & ind) const;
+  int PositionCreate2 (const INDEX_2 & ind, int & apos);
+  void BaseSetSize (int asize);
+};
+
+
+template <class T>
+class INDEX_2_CLOSED_HASHTABLE : public BASE_INDEX_2_CLOSED_HASHTABLE
+{
+  ///
+  MoveableArray<T> cont;
+
+public:
+  ///
+  inline INDEX_2_CLOSED_HASHTABLE (int size);
+  ///
+  inline void Set (const INDEX_2 & ahash, const T & acont);
+  ///
+  inline const T & Get (const INDEX_2 & ahash) const;
+  ///
+  inline bool Used (const INDEX_2 & ahash) const;
+  ///
+  inline void SetData (int pos, const INDEX_2 & ahash, const T & acont);
+  ///
+  inline void GetData (int pos, INDEX_2 & ahash, T & acont) const;
+  ///
+  inline void SetData (int pos, const T & acont);
+  ///
+  inline void GetData (int pos, T & acont) const;
+  ///
+  const T & GetData (int pos) { return cont.Get(pos); }
+  ///
+  inline void SetSize (int size);
+  ///
+  inline void PrintMemInfo (ostream & ost) const;
+  ///
+  inline void DeleteData ()
+  { SetSize (cont.Size()); }
+
+  void SetName (const char * aname)
+  {
+    cont.SetName(aname);
+    hash.SetName(aname);
+  }
+};
+
+
+
+template <typename T> 
+inline ostream & operator<< (ostream & ost, const INDEX_2_CLOSED_HASHTABLE<T> & ht)
+{
+  for (int i = 0; i < ht.Size(); i++)
+    if (ht.UsedPos(i))
+      {
+	INDEX_2 hash;
+	T data;
+	ht.GetData (i, hash, data);
+	ost << "hash = " << hash << ", data = " << data << endl;
+      }
+  return ost;
+}
+
+
+
+
+
+
+class BASE_INDEX_3_CLOSED_HASHTABLE
+{
+protected:
+  MoveableArray<INDEX_3> hash;
+  int invalid;
+
+protected: 
+  BASE_INDEX_3_CLOSED_HASHTABLE (int size)
+    : hash(size)
+  {
+    hash.SetName ("i3-hashtable, hash");
+    invalid = -1;
+    for (int i = 0; i < size; i++)
+      hash[i].I1() = invalid;
+  }
+
+public:
+  int Size() const 
+  {
+    return hash.Size(); 
+  }
+
+  bool UsedPos (int pos) const 
+  { 
+    return ! (hash[pos].I1() == invalid); 
+  }
+
+  int UsedElements () const
+  {
+    int n = hash.Size();
+    int cnt = 0;
+    for (int i = 0; i < n; i++)
+      if (hash[i].I1() != invalid)
+	cnt++;
+    return cnt;
+  }
+
+  int HashValue (const INDEX_3 & ind) const
+  {
+    return (ind.I1() + 15 * ind.I2() + 41 * ind.I3()) % hash.Size();
+  }
+  
+  int Position (const INDEX_3 & ind) const
+  {
+    int i = HashValue(ind);
+    while (1)
+      {
+	if (hash[i] == ind) return i;
+	if (hash[i].I1() == invalid) return -1;
+        i = (i+1) % hash.Size();
+      }
+  }
+
+  int Costs (const INDEX_3 & ind) const
+  {
+    int i = HashValue(ind);
+    int c = 1;
+    while (1)
+      {
+	if (hash[i] == ind) return c;
+	if (hash[i].I1() == invalid) return c;
+        i = (i+1) % hash.Size();
+        c++;
+      }
+  }
+
+
+  
+  // returns true, if new postion is created
+  bool PositionCreate (const INDEX_3 & ind, int & apos)
+  {
+    int i = HashValue (ind);
+    if (hash[i] == ind) 
+      {
+	apos = i;
+	return false;
+      }
+    if (hash[i].I1() == invalid)
+      {
+	hash[i] = ind; 
+	apos = i;
+	return true;
+      }
+    return PositionCreate2 (ind, apos);    
+  }
+
+protected:
+  bool PositionCreate2 (const INDEX_3 & ind, int & apos);
+  void BaseSetSize (int asize);
+};
+
+
+
+template <class T>
+class INDEX_3_CLOSED_HASHTABLE : public BASE_INDEX_3_CLOSED_HASHTABLE
+{
+  MoveableArray<T,0> cont;
+
+public:
+  INDEX_3_CLOSED_HASHTABLE (int size)
+    : BASE_INDEX_3_CLOSED_HASHTABLE(size), cont(size)
+  {
+    cont.SetName ("i3-hashtable, contents");
+  }
+  
+  void Set (const INDEX_3 & ahash, const T & acont)
+  {
+    int pos;
+    PositionCreate (ahash, pos);
+    hash[pos] = ahash;
+    cont[pos] = acont;
+  }
+
+  const T & Get (const INDEX_3 & ahash) const
+  {
+    return cont[Position (ahash)];
+  }
+
+  bool Used (const INDEX_3 & ahash) const
+  {
+    return (Position (ahash) != -1);
+  }
+
+  void SetData (int pos, const INDEX_3 & ahash, const T & acont)
+  {
+    hash[pos] = ahash;
+    cont[pos] = acont;
+  }
+
+  void GetData (int pos, INDEX_3 & ahash, T & acont) const
+  {
+    ahash = hash[pos];
+    acont = cont[pos];
+  }
+
+  void SetData (int pos, const T & acont)
+  {
+    cont[pos] = acont;
+  }
+
+  void GetData (int pos, T & acont) const
+  {
+    acont = cont[pos];
+  }
+
+  const T & GetData (int pos) const
+  {
+    return cont[pos];
+  }
+
+   void SetSize (int size)
+  {
+    BaseSetSize(size);
+    cont.SetSize(size);
+  }
+
+  void PrintMemInfo (ostream & ost) const
+  {
+    cout << "Hashtable: " << Size() 
+         << " entries of size " << sizeof(INDEX_3) << " + " << sizeof(T) 
+         << " = " << Size() * (sizeof(INDEX_3) + sizeof(T)) << " bytes" << endl;
+    
+  }
+
+  void DeleteData ()
+  { 
+    SetSize (cont.Size()); 
+  }
+
+  void SetName (const char * aname)
+  {
+    cont.SetName(aname);
+    hash.SetName(aname);
+  }
+};
+
+
+
+template <typename T> 
+inline ostream & operator<< (ostream & ost, const INDEX_3_CLOSED_HASHTABLE<T> & ht)
+{
+  for (int i = 0; i < ht.Size(); i++)
+    if (ht.UsedPos(i))
+      {
+	INDEX_3 hash;
+	T data;
+	ht.GetData (i, hash, data);
+	ost << "hash = " << hash << ", data = " << data << endl;
+      }
+  return ost;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+template<class T>
+inline INDEX_3_HASHTABLE<T> :: INDEX_3_HASHTABLE (int size)
+  : BASE_INDEX_3_HASHTABLE (size), cont(size)
+{
+  ;
+}
+
+template<class T>	
+inline int INDEX_3_HASHTABLE<T> :: PositionCreate (const INDEX_3 & ahash, int & bnr, int & colnr)
+{
+  bnr = HashValue (ahash);
+  colnr = Position (bnr, ahash);
+  if (!colnr)
+    {
+      hash.Add (bnr, ahash);
+      cont.AddEmpty (bnr);
+      colnr = cont.EntrySize (bnr);
+      return 1;
+    }
+  return 0;
+}
+
+
+template<class T>
+inline void INDEX_3_HASHTABLE<T> :: Set (const INDEX_3 & ahash, const T & acont)
+{
+  int bnr = HashValue (ahash);
+  int pos = Position (bnr, ahash);
+  if (pos)
+    cont.Set (bnr, pos, acont);
+  else
+    {
+      hash.Add1 (bnr, ahash);
+      cont.Add1 (bnr, acont);
+    }
+}
+
+template<class T>
+inline const T & INDEX_3_HASHTABLE<T> :: Get (const INDEX_3 & ahash) const
+{
+  int bnr = HashValue (ahash);
+  int pos = Position (bnr, ahash);
+  return cont.Get (bnr, pos);
+}
+
+template<class T>
+inline bool INDEX_3_HASHTABLE<T> :: Used (const INDEX_3 & ahash) const
+{
+  return (Position (HashValue (ahash), ahash)) ? 1 : 0;
+}
+
+template<class T>
+inline int INDEX_3_HASHTABLE<T> :: GetNBags () const
+{
+  return cont.Size();
+}
+
+template<class T>
+inline int INDEX_3_HASHTABLE<T> :: GetBagSize (int bnr) const
+{
+  return cont.EntrySize (bnr);
+}
+
+template<class T>
+inline void INDEX_3_HASHTABLE<T> :: GetData (int bnr, int colnr, INDEX_3 & ahash, T & acont) const
+{
+  ahash = hash.Get(bnr, colnr);
+  acont = cont.Get(bnr, colnr);
+}    
+
+template<class T>
+inline void INDEX_3_HASHTABLE<T> :: SetData (int bnr, int colnr, const INDEX_3 & ahash, const T & acont)
+{
+  hash.Set(bnr, colnr, ahash);
+  cont.Set(bnr, colnr, acont);
+}    
+
+template<class T>
+inline void INDEX_3_HASHTABLE<T> :: SetSize (int size)
+{
+  hash.SetSize (size);
+  cont.SetSize (size);
+}
+
+template<class T>
+inline void INDEX_3_HASHTABLE<T> :: DeleteData ()
+{
+  int n = hash.Size();
+  hash.SetSize (n);
+  cont.SetSize (n);
+}
+
+template<class T>
+inline void INDEX_3_HASHTABLE<T> :: PrepareSet (const INDEX_3 & ahash)
+{
+  int bnr = HashValue (ahash);
+  hash.IncSizePrepare (bnr-1);
+  cont.IncSizePrepare (bnr-1);
+}
+
+
+template<class T>
+inline void INDEX_3_HASHTABLE<T> :: AllocateElements ()
+{
+  hash.AllocateElementsOneBlock();
+  cont.AllocateElementsOneBlock();
+}
+
+
+
+template<class T>
+inline void INDEX_3_HASHTABLE<T> :: PrintMemInfo (ostream & ost) const
+  {
+    ost << "Hash: " << endl;
+    hash.PrintMemInfo (ost);
+    ost << "Cont: " << endl;
+    cont.PrintMemInfo (ost);
+  }
+
+
+
+
+
+template<class T>
+inline INDEX_HASHTABLE<T> :: INDEX_HASHTABLE (int size)
+  : BASE_INDEX_HASHTABLE (size), cont(size)
+  {
+    ;
+  }
+	
+template<class T>
+inline void INDEX_HASHTABLE<T> :: Set (const INDEX & ahash, const T & acont)
+    {
+    int bnr = HashValue (ahash);
+    int pos = Position (bnr, ahash);
+    if (pos)
+      cont.Set (bnr, pos, acont);
+    else
+      {
+      hash.Add (bnr, ahash);
+      cont.Add (bnr, acont);
+      }
+    }
+
+template<class T>
+inline const T & INDEX_HASHTABLE<T> :: Get (const INDEX & ahash) const
+    {
+    int bnr = HashValue (ahash);
+    int pos = Position (bnr, ahash);
+    return cont.Get (bnr, pos);
+    }
+
+template<class T>
+inline bool INDEX_HASHTABLE<T> :: Used (const INDEX & ahash) const
+    {
+    return (Position (HashValue (ahash), ahash)) ? 1 : 0;
+    }
+
+template<class T>
+inline int INDEX_HASHTABLE<T> :: GetNBags () const
+    {
+    return hash.Size();
+    }
+
+template<class T>
+inline int INDEX_HASHTABLE<T> :: GetBagSize (int bnr) const
+    {
+    return hash.EntrySize(bnr);
+    }
+
+template<class T>
+inline void INDEX_HASHTABLE<T> :: GetData (int bnr, int colnr, INDEX & ahash, T & acont) const
+    {
+    ahash = hash.Get(bnr, colnr);   
+    acont = cont.Get(bnr, colnr);
+    }
+    
+template<class T>
+inline void INDEX_HASHTABLE<T> :: PrintMemInfo (ostream & ost) const
+  {
+    ost << "Hash: " << endl;
+    hash.PrintMemInfo (ost);
+    ost << "Cont: " << endl;
+    cont.PrintMemInfo (ost);
+  }
+
+
+    
+    
+    
+    
+    
+
+
+
+
+
+
+
+
+/* *********** Closed Hashing ************************* */
+
+
+
+template<class T>
+inline INDEX_2_CLOSED_HASHTABLE<T> :: 
+INDEX_2_CLOSED_HASHTABLE (int size)
+  : BASE_INDEX_2_CLOSED_HASHTABLE(size), cont(size)
+{
+  cont.SetName ("i2-hashtable, contents");
+}
+
+template<class T>
+inline void INDEX_2_CLOSED_HASHTABLE<T> :: 
+Set (const INDEX_2 & ahash, const T & acont)
+{
+  int pos;
+  PositionCreate (ahash, pos);
+  hash.Elem(pos) = ahash;
+  cont.Elem(pos) = acont;
+}
+
+template<class T>
+inline const T & INDEX_2_CLOSED_HASHTABLE<T> :: 
+Get (const INDEX_2 & ahash) const
+{
+  int pos = Position (ahash);
+  return cont.Get(pos);
+}
+
+template<class T>
+inline bool INDEX_2_CLOSED_HASHTABLE<T> :: 
+Used (const INDEX_2 & ahash) const
+{
+  int pos = Position (ahash);
+  return (pos != 0);
+}
+
+template<class T>
+inline void INDEX_2_CLOSED_HASHTABLE<T> :: 
+SetData (int pos, const INDEX_2 & ahash, const T & acont)
+{
+  hash.Elem(pos) = ahash;
+  cont.Elem(pos) = acont;
+}
+  
+template<class T>
+inline void INDEX_2_CLOSED_HASHTABLE<T> :: 
+GetData (int pos, INDEX_2 & ahash, T & acont) const
+{
+  ahash = hash.Get(pos);
+  acont = cont.Get(pos);
+}
+
+template<class T>
+inline void INDEX_2_CLOSED_HASHTABLE<T> :: 
+SetData (int pos, const T & acont)
+{
+  cont.Elem(pos) = acont;
+}
+  
+template<class T>
+inline void INDEX_2_CLOSED_HASHTABLE<T> :: 
+GetData (int pos, T & acont) const
+{
+  acont = cont.Get(pos);
+}
+
+
+template<class T>
+inline void INDEX_2_CLOSED_HASHTABLE<T> :: 
+SetSize (int size)
+{
+  BaseSetSize(size);
+  cont.SetSize(size);
+}
+
+
+  
+template<class T>
+inline void INDEX_2_CLOSED_HASHTABLE<T> :: 
+PrintMemInfo (ostream & ost) const
+{
+  cout << "Hashtable: " << Size() 
+       << " entries of size " << sizeof(INDEX_2) << " + " << sizeof(T) 
+       << " = " << Size() * (sizeof(INDEX_2) + sizeof(T)) << " bytes." 
+       << " Used els: " << UsedElements() 
+       << endl;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+template<class T>
+inline INDEX_3_CLOSED_HASHTABLE<T> :: 
+INDEX_3_CLOSED_HASHTABLE (int size)
+  : BASE_INDEX_3_CLOSED_HASHTABLE(size), cont(size)
+{
+  cont.SetName ("i3-hashtable, contents");
+}
+
+template<class T>
+inline void INDEX_3_CLOSED_HASHTABLE<T> :: 
+Set (const INDEX_3 & ahash, const T & acont)
+{
+  int pos;
+  PositionCreate (ahash, pos);
+  hash.Elem(pos) = ahash;
+  cont.Elem(pos) = acont;
+}
+
+template<class T>
+inline const T & INDEX_3_CLOSED_HASHTABLE<T> :: 
+Get (const INDEX_3 & ahash) const
+{
+  int pos = Position (ahash);
+  return cont[pos];
+}
+
+template<class T>
+inline bool INDEX_3_CLOSED_HASHTABLE<T> :: 
+Used (const INDEX_3 & ahash) const
+{
+  int pos = Position (ahash);
+  return (pos != 0);
+}
+
+template<class T>
+inline void INDEX_3_CLOSED_HASHTABLE<T> :: 
+SetData (int pos, const INDEX_3 & ahash, const T & acont)
+{
+  hash.Elem(pos) = ahash;
+  cont.Elem(pos) = acont;
+}
+  
+template<class T>
+inline void INDEX_3_CLOSED_HASHTABLE<T> :: 
+GetData (int pos, INDEX_3 & ahash, T & acont) const
+{
+  ahash = hash.Get(pos);
+  acont = cont.Get(pos);
+}
+
+template<class T>
+inline void INDEX_3_CLOSED_HASHTABLE<T> :: 
+SetData (int pos, const T & acont)
+{
+  cont.Elem(pos) = acont;
+}
+  
+template<class T>
+inline void INDEX_3_CLOSED_HASHTABLE<T> :: 
+GetData (int pos, T & acont) const
+{
+  acont = cont.Get(pos);
+}
+
+template<class T>
+inline const T & INDEX_3_CLOSED_HASHTABLE<T> :: 
+GetData (int pos) const
+{
+  return cont.Get(pos);
+}
+
+
+template<class T>
+inline void INDEX_3_CLOSED_HASHTABLE<T> :: 
+SetSize (int size)
+{
+  BaseSetSize(size);
+  cont.SetSize(size);
+}
+  
+template<class T>
+inline void INDEX_3_CLOSED_HASHTABLE<T> :: 
+PrintMemInfo (ostream & ost) const
+{
+  cout << "Hashtable: " << Size() 
+       << " entries of size " << sizeof(INDEX_3) << " + " << sizeof(T) 
+       << " = " << Size() * (sizeof(INDEX_3) + sizeof(T)) << " bytes" << endl;
+}
+*/
+
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/mpi_interface.hpp b/contrib/Netgen/libsrc/general/mpi_interface.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..53f225794633faa378c61f65440cf54cd448e73c
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/mpi_interface.hpp
@@ -0,0 +1,300 @@
+#ifndef FILE_PARALLEL
+#define FILE_PARALLEL
+
+
+
+#ifdef VTRACE
+#include "vt_user.h"
+#else
+  #define VT_USER_START(n)
+  #define VT_USER_END(n)
+  #define VT_TRACER(n)
+#endif
+
+
+namespace netgen
+{
+
+  extern DLL_HEADER int id, ntasks;
+  
+
+#ifdef PARALLEL
+  
+  enum { MPI_TAG_CMD = 110 };
+  enum { MPI_TAG_MESH = 210 };
+  enum { MPI_TAG_VIS = 310 };
+
+  extern MPI_Comm mesh_comm;
+
+  template <class T>
+  MPI_Datatype MyGetMPIType ( ) { cerr << "ERROR in GetMPIType() -- no type found" << endl;return 0;}
+
+  template <>
+  inline MPI_Datatype MyGetMPIType<int> ( ) 
+  { return MPI_INT; }
+  
+  template <>
+  inline MPI_Datatype MyGetMPIType<double> ( ) 
+  { return MPI_DOUBLE; }
+
+  
+  template <int S> class Vec;
+  template <> 
+  inline MPI_Datatype MyGetMPIType<Vec<3> > ()
+  {
+    static MPI_Datatype MPI_T = 0;
+    if (!MPI_T)
+      {
+	MPI_Type_contiguous ( 3, MPI_DOUBLE, &MPI_T);
+	MPI_Type_commit ( &MPI_T );
+      }
+    return MPI_T;
+  };
+
+
+  inline void MyMPI_Send (int i, int dest, int tag)
+  {
+    int hi = i;
+    MPI_Send( &hi, 1, MPI_INT, dest, tag, MPI_COMM_WORLD);
+  }
+
+  inline void MyMPI_Recv (int & i, int src, int tag)
+  {
+    MPI_Status status;
+    MPI_Recv( &i, 1, MPI_INT, src, tag, MPI_COMM_WORLD, &status);
+  }
+
+
+
+  inline void MyMPI_Send (const string & s, int dest, int tag)
+  {
+    MPI_Send( const_cast<char*> (s.c_str()), s.length(), MPI_CHAR, dest, tag, MPI_COMM_WORLD);
+  }
+
+  inline void MyMPI_Recv (string & s, int src, int tag)
+  {
+    MPI_Status status;
+    int len;
+    MPI_Probe (src, tag, MPI_COMM_WORLD, &status);
+    MPI_Get_count (&status, MPI_CHAR, &len);
+    s.assign (len, ' ');
+    MPI_Recv( &s[0], len, MPI_CHAR, src, tag, MPI_COMM_WORLD, &status);
+  }
+
+ 
+
+
+  template <class T, int BASE>
+  inline void MyMPI_Send (FlatArray<T, BASE> s, int dest, int tag)
+  {
+    MPI_Send( &s.First(), s.Size(), MyGetMPIType<T>(), dest, tag, MPI_COMM_WORLD);
+  }
+
+  template <class T, int BASE>
+  inline void MyMPI_Recv ( FlatArray<T, BASE> s, int src, int tag)
+  {
+    MPI_Status status;
+    MPI_Recv( &s.First(), s.Size(), MyGetMPIType<T>(), src, tag, MPI_COMM_WORLD, &status);
+  }
+
+  template <class T, int BASE>
+  inline void MyMPI_Recv ( Array <T, BASE> & s, int src, int tag)
+  {
+    MPI_Status status;
+    int len;
+    MPI_Probe (src, tag, MPI_COMM_WORLD, &status);
+    MPI_Get_count (&status, MyGetMPIType<T>(), &len);
+
+    s.SetSize (len);
+    MPI_Recv( &s.First(), len, MyGetMPIType<T>(), src, tag, MPI_COMM_WORLD, &status);
+  }
+
+  template <class T, int BASE>
+  inline int MyMPI_Recv ( Array <T, BASE> & s, int tag)
+  {
+    MPI_Status status;
+    int len;
+    MPI_Probe (MPI_ANY_SOURCE, tag, MPI_COMM_WORLD, &status);
+
+    int src = status.MPI_SOURCE;
+
+    MPI_Get_count (&status, MyGetMPIType<T>(), &len);
+
+    s.SetSize (len);
+    MPI_Recv( &s.First(), len, MyGetMPIType<T>(), src, tag, MPI_COMM_WORLD, &status);
+
+    return src;
+  }
+
+
+  /*
+  template <class T, int BASE>
+  inline void MyMPI_ISend (FlatArray<T, BASE> s, int dest, int tag, MPI_Request & request)
+  {
+    MPI_Isend( &s.First(), s.Size(), MyGetMPIType<T>(), dest, tag, MPI_COMM_WORLD, & request);
+  }
+
+
+  template <class T, int BASE>
+  inline void MyMPI_IRecv (FlatArray<T, BASE> s, int dest, int tag, MPI_Request & request)
+  {
+    MPI_Irecv( &s.First(), s.Size(), MyGetMPIType<T>(), dest, tag, MPI_COMM_WORLD, & request);
+  }
+  */
+
+  template <class T, int BASE>
+  inline MPI_Request MyMPI_ISend (FlatArray<T, BASE> s, int dest, int tag, MPI_Comm comm = MPI_COMM_WORLD)
+  {
+    MPI_Request request;
+    MPI_Isend( &s.First(), s.Size(), MyGetMPIType<T>(), dest, tag, comm, &request);
+    return request;
+  }
+
+
+  template <class T, int BASE>
+  inline MPI_Request MyMPI_IRecv (FlatArray<T, BASE> s, int dest, int tag, MPI_Comm comm = MPI_COMM_WORLD)
+  {
+    MPI_Request request;
+    MPI_Irecv( &s.First(), s.Size(), MyGetMPIType<T>(), dest, tag, comm, &request);
+    return request;
+  }
+
+  /*
+  template <class T, int BASE>
+  inline void MyMPI_ISend (FlatArray<T, BASE> s, int dest, int tag)
+  {
+    MPI_Request request;
+    MPI_Isend( &s.First(), s.Size(), MyGetMPIType<T>(), dest, tag, MPI_COMM_WORLD, &request);
+    MPI_Request_free (&request);
+  }
+
+
+  template <class T, int BASE>
+  inline void MyMPI_IRecv (FlatArray<T, BASE> s, int dest, int tag)
+  {
+    MPI_Request request;
+    MPI_Irecv( &s.First(), s.Size(), MyGetMPIType<T>(), dest, tag, MPI_COMM_WORLD, &request);
+    MPI_Request_free (&request);
+  }
+  */
+
+  inline void MyMPI_SendCmd (const char * cmd)
+  {
+    char buf[100];
+    strcpy (buf, cmd);
+    // MPI_Bcast (&buf, 100, MPI_CHAR, 0, MPI_COMM_WORLD);
+
+    for (int dest = 1; dest < ntasks; dest++)
+      MPI_Bsend( &buf, 100, MPI_CHAR, dest, MPI_TAG_CMD, MPI_COMM_WORLD);
+  }
+
+  inline string MyMPI_RecvCmd ()
+  {
+    char buf[100];
+    // MPI_Bcast (&buf, 100, MPI_CHAR, 0, MPI_COMM_WORLD);
+
+    MPI_Status status;
+    int flag;
+    do
+      {
+	MPI_Iprobe (0, MPI_TAG_CMD, MPI_COMM_WORLD, &flag, &status);
+	if (!flag) usleep (1000);
+      }
+    while (!flag);
+    MPI_Recv( &buf, 100, MPI_CHAR, 0, MPI_TAG_CMD, MPI_COMM_WORLD, &status);
+    
+    return string(buf);
+  }
+
+  template <class T>
+  inline void MyMPI_Bcast (T & s, MPI_Comm comm = MPI_COMM_WORLD)
+  {
+    MPI_Bcast (&s, 1, MyGetMPIType<T>(), 0, comm);
+  }
+
+  template <class T>
+  inline void MyMPI_Bcast (Array<T, 0> & s, MPI_Comm comm = MPI_COMM_WORLD)
+  {
+    int size = s.Size();
+    MyMPI_Bcast (size, comm);
+    if (id != 0) s.SetSize (size);
+    MPI_Bcast (&s[0], size, MyGetMPIType<T>(), 0, comm);
+  }
+
+  template <class T>
+  inline void MyMPI_Bcast (Array<T, 0> & s, int root, MPI_Comm comm = MPI_COMM_WORLD)
+  {
+    int id;
+    MPI_Comm_rank(comm, &id);
+
+    int size = s.Size();
+    MPI_Bcast (&size, 1, MPI_INT, root, comm);
+    if (id != root) s.SetSize (size);
+    if ( !size ) return;
+    MPI_Bcast (&s[0], size, MyGetMPIType<T>(), root, comm);
+  }
+
+  template <class T, class T2>
+  inline void MyMPI_Allgather (const T & send, FlatArray<T2> recv, MPI_Comm comm)
+  {
+    MPI_Allgather( const_cast<T*> (&send), 1, MyGetMPIType<T>(), &recv[0], 1, MyGetMPIType<T2>(), comm);
+  }
+
+  template <class T, class T2>
+  inline void MyMPI_Alltoall (FlatArray<T> send, FlatArray<T2> recv, MPI_Comm comm)
+  {
+    MPI_Alltoall( &send[0], 1, MyGetMPIType<T>(), &recv[0], 1, MyGetMPIType<T2>(), comm);
+  }
+
+//   template <class T, class T2>
+//   inline void MyMPI_Alltoall_Block (FlatArray<T> send, FlatArray<T2> recv, int blocklen, MPI_Comm comm)
+//   {
+//     MPI_Alltoall( &send[0], blocklen, MyGetMPIType<T>(), &recv[0], blocklen, MyGetMPIType<T2>(), comm);
+//   }
+
+
+
+  /*
+  inline void MyMPI_Send (  int *& s, int len,  int dest, int tag)
+  {
+    int hlen = len;
+    MPI_Send( &hlen, 1, MPI_INT, dest, tag, MPI_COMM_WORLD);
+    MPI_Send( s, len, MPI_INT, dest, tag, MPI_COMM_WORLD);
+  }
+
+
+  inline void MyMPI_Recv ( int *& s, int & len, int src, int tag)
+  {
+    MPI_Status status;
+    MPI_Recv( &len, 1, MPI_INT, src, tag, MPI_COMM_WORLD, &status);
+    if ( s ) 
+      delete [] s;
+    s = new int [len];
+    MPI_Recv( s, len, MPI_INT, src, tag, MPI_COMM_WORLD, &status);
+  }
+
+
+
+  inline void MyMPI_Send ( double * s, int len,  int dest, int tag)
+  {
+     MPI_Send( &len, 1, MPI_INT, dest, tag, MPI_COMM_WORLD);
+     MPI_Send( s, len, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD);
+  }
+
+
+  inline void MyMPI_Recv ( double *& s, int & len, int src, int tag)
+  {
+    MPI_Status status;
+    MPI_Recv( &len, 1, MPI_INT, src, tag, MPI_COMM_WORLD, &status);
+    if ( s )
+      delete [] s;
+    s = new double [len];
+    MPI_Recv( s, len, MPI_DOUBLE, src, tag, MPI_COMM_WORLD, &status);
+  }
+  */
+
+#endif // PARALLEL
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/myadt.hpp b/contrib/Netgen/libsrc/general/myadt.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..100e7a3a0be6636302aafbf1fb4c34791b4df1f1
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/myadt.hpp
@@ -0,0 +1,48 @@
+#ifndef FILE_MYADT
+#define FILE_MYADT
+
+/**************************************************************************/
+/* File:   myadt.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/* 
+   include for all abstract data types
+*/
+
+
+
+#include "../include/mystdlib.h"
+#include "../include/mydefs.hpp"
+
+
+#include "ngexception.hpp"
+#include "parthreads.hpp"
+// #include "moveablemem.hpp"
+#include "dynamicmem.hpp"
+
+#include "template.hpp"
+#include "array.hpp"
+#include "table.hpp"
+#include "hashtabl.hpp"
+
+
+#include "symbolta.hpp"
+#include "bitarray.hpp"
+#include "flags.hpp"
+#include "spbita2d.hpp"
+
+#include "seti.hpp"
+#include "optmem.hpp"
+#include "autoptr.hpp"
+#include "sort.hpp"
+#include "stack.hpp"
+#include "mystring.hpp"
+#include "profiler.hpp"
+
+#include "mpi_interface.hpp"
+#include "netgenout.hpp"
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/mystring.cpp b/contrib/Netgen/libsrc/general/mystring.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc59c80ee3d7128345ea2d0d16fd49390f0c9674
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/mystring.cpp
@@ -0,0 +1,426 @@
+
+//**************************************************************
+//
+// filename:             mystring.cpp
+//
+// project:              doctoral thesis
+//
+// autor:                Dipl.-Ing. Gerstmayr Johannes
+//
+// generated:            20.12.98
+// last change:          20.12.98
+// description:          implementation for strings
+// remarks:
+//
+//**************************************************************
+
+// string class
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+
+namespace netgen
+{
+
+  void ReadEnclString(istream & in, string & str, const char encl)
+  {
+    char currchar;
+    str = "";
+
+    in.get(currchar);
+    while(in && (currchar == ' ' || currchar == '\t' || currchar == '\n') )
+      in.get(currchar);
+	
+    if(currchar == encl)
+      {
+	in.get(currchar);
+	while(in && currchar != encl)
+	  {
+	    str += currchar;
+	    in.get(currchar);
+	  }
+      }
+    else
+      {
+	in.putback(currchar);
+	in >> str;
+      }
+  }
+	    
+	
+    
+  
+
+
+void DefaultStringErrHandler()
+{
+  cerr << "Error : string operation out of range\n" << flush;
+}
+
+void (*MyStr::ErrHandler)() = DefaultStringErrHandler;
+
+  /*     
+MyStr::MyStr()
+{
+  length = 0;
+  str = shortstr;
+  str[0] = 0;
+}
+  */
+
+MyStr::MyStr(const char *s)
+{
+  length = unsigned(strlen(s));
+
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy(str, s);
+}
+
+/*
+MyStr::MyStr(char s)
+{
+  length = 1;
+  str = shortstr;
+  str[0] = s;
+  str[1] = (char)0;
+}
+*/
+
+MyStr::MyStr(const MyStr& s)
+{
+  length = s.length;
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy(str, s.str);
+}
+
+MyStr::MyStr(int i)
+{
+  char buffer[32];
+  sprintf(buffer, "%d", i);
+  length = unsigned(strlen(buffer));
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy(str, buffer);
+}
+
+MyStr::MyStr(void * p)
+{
+  char buffer[32];
+  sprintf(buffer, "%p", p);
+  length = unsigned(strlen(buffer));
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy(str, buffer);
+}
+
+
+MyStr::MyStr(long l)
+{
+  char buffer[32];
+  sprintf(buffer, "%ld", l);
+  length = unsigned(strlen(buffer));
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy(str, buffer);
+}
+
+MyStr::MyStr(double d)
+{
+  char buffer[32];
+  //if (fabs(d) < 1E-100) {d = 0;}
+  sprintf(buffer, "%g", d);
+  length = unsigned(strlen(buffer));
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy(str, buffer);
+}
+
+MyStr::MyStr(const Point3d& p)
+{
+  char buffer[80];
+  //if (fabs(d) < 1E-100) {d = 0;}
+  sprintf(buffer, "[%g, %g, %g]", p.X(), p.Y(), p.Z());
+  length = unsigned(strlen(buffer));
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy(str, buffer);
+}
+
+MyStr::MyStr(const Vec3d& p)
+{
+  char buffer[80];
+  //if (fabs(d) < 1E-100) {d = 0;}
+  sprintf(buffer, "[%g, %g, %g]", p.X(), p.Y(), p.Z());
+  length = unsigned(strlen(buffer));
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy(str, buffer);
+}
+
+MyStr::MyStr(unsigned n, int)
+{
+  length = n;
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  str[n] = 0;
+}
+
+MyStr::MyStr(const string & st)
+{
+  length = unsigned(st.length());
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy (str, st.c_str());
+}
+
+
+
+MyStr MyStr::Left(unsigned r)
+{
+  if(r > length)
+    {
+      MyStr::ErrHandler();
+      MyStr s;
+      return s;
+    }
+  else
+    {
+      MyStr tmp(r, 0);
+      strncpy(tmp.str, str, r);
+      return tmp;
+    }
+}
+
+MyStr MyStr::Right(unsigned l)
+{
+  if(l > length)
+    {
+      MyStr::ErrHandler();
+      MyStr s;
+      return s;
+    }
+  else
+    {
+      MyStr tmp(l, 0);
+      strncpy(tmp.str, str + length - l, l);
+      return tmp;
+    }
+}
+
+MyStr& MyStr::InsertAt(unsigned pos, const MyStr& s)
+{
+  if(pos > length)
+    {
+      MyStr::ErrHandler();
+      return *this;
+    }
+  int newLength = length + s.length;
+  char *tmp = new char[newLength + 1];
+  strncpy(tmp, str, pos);
+  strcpy(tmp + pos, s.str);
+  strcpy(tmp + pos + s.length, str + pos);
+
+  if (length > SHORTLEN) delete [] str;
+  length = newLength;
+  if (length > SHORTLEN)
+    str = tmp;
+  else
+    {
+      strcpy (shortstr, tmp);
+      delete [] tmp;
+      str = shortstr;
+    }
+  return *this;
+}
+
+MyStr &MyStr::WriteAt(unsigned pos, const MyStr& s)
+{
+  if(pos > length)
+  {
+    MyStr::ErrHandler();
+    return *this;
+  }
+  unsigned n = length - pos;
+  if(s.length < n)
+    n = s.length;
+  strncpy(str + pos, s.str, n);
+  return *this;
+}
+
+void MyStr::ConvertTextToExcel()
+{
+  /*
+  for (int i = 0; i < Length(); i++)
+    {
+      if ((*this)[i]==',') {(*this)[i] = ';';}
+      else if ((*this)[i]=='.') {(*this)[i] = ',';}
+    }
+  */
+}
+
+void MyStr::ConvertExcelToText()
+{
+  /*
+  for (int i = 0; i < Length(); i++)
+    {
+      if ((*this)[i]==',') {(*this)[i] = '.';}
+      else if ((*this)[i]==';') {(*this)[i] = ',';}
+    }
+  */
+}
+
+MyStr& MyStr::operator = (const MyStr& s)
+{
+  if (length > SHORTLEN) delete [] str;
+  length = s.length;
+  if (length > SHORTLEN)
+    str = new char[length + 1];
+  else
+    str = shortstr;
+  strcpy(str, s.str);
+  return *this;
+}
+
+MyStr operator + (const MyStr& s1, const MyStr& s2)
+{
+  MyStr tmp(s1.length + s2.length, 0);
+  if (s1.length != 0) strcpy(tmp.str, s1.str);
+  if (s2.length != 0) strcpy(tmp.str + s1.length, s2.str);
+  return tmp;
+}
+
+void MyStr::operator += (const MyStr& s)
+{
+  if (length+s.length <= SHORTLEN)
+    {
+      if (s.length != 0) strcpy(shortstr + length, s.str);
+    }
+  else
+    {
+      char *tmp = new char[length + s.length + 1];
+      if (length != 0) strcpy(tmp, str);
+      if (s.length != 0) strcpy(tmp + length, s.str);
+      if (length > SHORTLEN) delete [] str;
+      length += s.length;
+      str = tmp;
+    }
+}
+
+char& MyStr::operator [] (unsigned n)
+{
+  static char dummy;
+  if(n < length)
+    return str[n];
+  else
+  {
+    MyStr::ErrHandler();
+    return dummy;
+  }
+}
+
+char MyStr::operator [] (unsigned n) const
+{
+  static char dummy;
+  if(n < length)
+    return str[n];
+  else
+  {
+    MyStr::ErrHandler();
+    return dummy;
+  }
+}
+
+MyStr MyStr::operator () (unsigned l, unsigned r)
+{
+  if((l > r) || (r > length))
+  {
+    MyStr::ErrHandler();
+    MyStr s;
+    return s;
+  }
+  else
+  {
+    int n = r - l + 1;
+    MyStr tmp(n, 0);
+    strncpy(tmp.str, str + 1, n);
+    return tmp;
+  }
+}
+
+string MyStr::cpp_string(void) const
+{
+  string aux(str,length);
+  return aux;
+}
+
+/*
+istream& operator >> (istream& is, MyStr& s)
+{
+  const int buflen = 1000;
+  char buffer[buflen+1];
+
+  int end = 0;
+  s = "";
+  MyStr str;
+
+  while (!end)
+  {
+    is.get(buffer, buflen);
+    str = MyStr(buffer);
+    s += str;
+    if (is.peek() == EOF) {end = 1;}
+  }
+
+  return is;
+}
+*/
+/*
+#ifdef __borland
+::ifstream& operator >> (::ifstream& is, MyStr& s)       // wb
+{                                                        // wb
+  const int buflen = 1000;                               // wb
+  char buffer[buflen+1];                                 // wb
+                                                         // wb
+  int end = 0;                                           // wb
+  s = "";                                                // wb
+  MyStr str;                                             // wb
+                                                         // wb
+  while (!end)                                           // wb
+  {                                                      // wb
+    is.get(buffer, buflen);                              // wb
+    str = MyStr(buffer);                                 // wb
+    s += str;                                            // wb
+    if (is.peek() == EOF) {end = 1;}                     // wb
+  }                                                      // wb
+                                                         // wb
+  return is;                                             // wb
+}          
+
+#endif
+*/
+}
diff --git a/contrib/Netgen/libsrc/general/mystring.hpp b/contrib/Netgen/libsrc/general/mystring.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..49cc7001faaf8d955c94d143e85ec54fe6b0be15
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/mystring.hpp
@@ -0,0 +1,220 @@
+
+//**************************************************************
+//
+// filename:             mystring.h
+//
+// project:              doctoral thesis, program smart
+//
+// autor:                Dipl.-Ing. Gerstmayr Johannes
+//
+// generated:            20.12.98
+// last change:          20.12.98
+// description:          base class for strings
+// remarks:              string with n characters has
+//                       0..n-1 characters and at pos n a 0
+//
+//**************************************************************
+
+
+#ifndef MYSTRING__H
+#define MYSTRING__H
+
+namespace netgen
+{
+
+class Point3d;
+class Vec3d;
+
+
+// extract string str which is enclosed by the given character encl from a given string in
+void ReadEnclString(istream & in, string & str, const char encl);
+
+
+class MyStr;
+
+MyStr operator + (const MyStr &, const MyStr &);
+int operator == (const MyStr &, const MyStr &);
+int operator < (const MyStr &, const MyStr &);
+int operator <= (const MyStr &, const MyStr &);
+int operator > (const MyStr &, const MyStr &);
+int operator >= (const MyStr &, const MyStr &);
+int operator != (const MyStr &, const MyStr &);
+ostream& operator << (ostream &, const MyStr &);
+istream& operator >> (istream &, MyStr &);
+
+class MyStr
+{
+public:
+  DLL_HEADER MyStr();
+  DLL_HEADER MyStr(const char *);
+  DLL_HEADER MyStr(char);
+  DLL_HEADER MyStr(const MyStr &);
+  DLL_HEADER MyStr(int);
+  DLL_HEADER MyStr(void *);
+  DLL_HEADER MyStr(long);
+  DLL_HEADER MyStr(double);
+  DLL_HEADER MyStr(const Point3d& p);
+  DLL_HEADER MyStr(const Vec3d& p);
+  DLL_HEADER MyStr(const string & st);
+
+  ~MyStr();
+  MyStr Left(unsigned);
+  MyStr Right(unsigned);
+  MyStr& InsertAt(unsigned, const MyStr &);
+  MyStr& WriteAt(unsigned, const MyStr &);
+  unsigned Length() const;
+  int Find(const char);
+  int Find(const char *);
+  int Find(const MyStr &);
+  MyStr& operator = (const MyStr &);
+  friend MyStr operator + (const MyStr &, const MyStr &);
+  void operator += (const MyStr &);
+  char* c_str();
+  string cpp_string(void) const;
+
+  //change every ',' -> ';', '.' -> ','
+  void ConvertTextToExcel();
+  //change every ','->'.', ';'->','
+  void ConvertExcelToText();
+
+  MyStr operator () (unsigned, unsigned);
+  operator int();
+  operator double();
+  operator long();
+  operator char *();
+  char& operator [] (unsigned int);
+  char operator [] (unsigned int) const;
+
+  friend int operator == (const MyStr &, const MyStr &);
+  friend int operator < (const MyStr &, const MyStr &);
+  friend int operator <= (const MyStr &, const MyStr &);
+  friend int operator > (const MyStr &, const MyStr &);
+  friend int operator >= (const MyStr &, const MyStr &);
+  friend int operator != (const MyStr &, const MyStr &);
+  friend ostream& operator << (ostream &, const MyStr &);
+  friend istream& operator >> (istream &, MyStr &);
+  static void SetToErrHandler(void (*)());
+private:
+  MyStr(unsigned, int);
+  char *str;
+  unsigned length;
+  enum { SHORTLEN = 24 };
+  char shortstr[SHORTLEN+1];
+  static void(*ErrHandler)();
+};
+
+
+inline MyStr::MyStr()
+{
+  length = 0;
+  str = shortstr;
+  str[0] = 0;
+}
+
+inline MyStr::MyStr(char s)
+{
+  length = 1;
+  str = shortstr;
+  str[0] = s;
+  str[1] = (char)0;
+}
+
+inline MyStr::~MyStr()
+{
+  if (length > SHORTLEN)
+    delete [] str;
+}
+
+inline unsigned MyStr::Length() const
+{
+  return length;
+}
+
+inline int MyStr::Find(const char c)
+{
+  char *pos = strchr(str, int(c));
+  return pos ? int(pos - str) : -1;
+}
+
+inline int MyStr::Find(const MyStr &s)
+{
+  char *pos = strstr(str, s.str);
+  return pos ? int(pos - str) : -1;
+}
+
+inline int MyStr::Find(const char *s)
+{
+  char *pos = strstr(str, s);
+  return pos ? int(pos - str) : -1;
+}
+
+inline MyStr::operator int()
+{
+  return atoi(str);
+}
+
+inline MyStr::operator double()
+{
+  return atof(str);
+}
+
+inline MyStr::operator long()
+{
+  return atol(str);
+}
+
+inline MyStr::operator char *()
+{
+  return str;
+}
+
+inline char* MyStr::c_str()
+{
+  return str;
+}
+
+
+inline int operator == (const MyStr &s1, const MyStr& s2)
+{
+  return strcmp(s1.str, s2.str) == 0;
+}
+
+inline int operator < (const MyStr &s1, const MyStr& s2)
+{
+  return strcmp(s1.str, s2.str) < 0;
+}
+
+inline int operator <= (const MyStr &s1, const MyStr& s2)
+{
+  return strcmp(s1.str, s2.str) <= 0;
+}
+
+inline int operator > (const MyStr &s1, const MyStr& s2)
+{
+  return strcmp(s1.str, s2.str) > 0;
+}
+
+inline int operator >= (const MyStr &s1, const MyStr& s2)
+{
+  return strcmp(s1.str, s2.str) >= 0;
+}
+
+inline int operator != (const MyStr &s1, const MyStr& s2)
+{
+  return !(s1 == s2);
+}
+
+inline ostream& operator << (ostream& os, const MyStr& s)
+{
+  return os << s.str;
+}
+
+inline void MyStr::SetToErrHandler(void (*Handler)())
+{
+  ErrHandler = Handler;
+};
+
+}
+#endif
+
+   
diff --git a/contrib/Netgen/libsrc/general/netgenout.hpp b/contrib/Netgen/libsrc/general/netgenout.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..06b23c22d59f0fc7058f384ffd02c9a7ee03799f
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/netgenout.hpp
@@ -0,0 +1,187 @@
+#ifndef NETGEN_OUT_STREAM_HPP__
+#define NETGEN_OUT_STREAM_HPP__
+
+// #include <ostream>
+// #include <mystdlib.h>
+// #include <meshing.hpp>
+
+namespace netgen
+{
+
+#ifdef PARALLEL
+extern int id;
+extern int ntasks;
+#endif
+DLL_HEADER extern int printmessage_importance;
+
+
+
+class Imp
+{
+  int importance;
+public:
+  Imp () : importance(0) { ; }
+
+  Imp ( int aimportance ) : importance(aimportance) { ; }
+
+  int GetImp () const { return importance; }
+};
+
+
+class Proc
+{
+  int proc;
+public:
+  Proc () : proc(0) { ; }
+
+  Proc ( int aproc ) : proc(aproc) { ; }
+
+  int GetProc () const { return proc; }
+};
+
+class Procs
+{
+  const netgen::FlatArray<int> procs;
+
+public:
+
+  Procs ( const netgen::FlatArray<int> & aprocs ) : procs (aprocs) { ; }
+
+  const netgen::FlatArray<int> & GetProcs () const { return procs; }
+};
+
+
+
+class NetgenOutStream
+{
+  ostream * out;
+
+  bool print;
+  bool printheader;
+
+
+public:
+  NetgenOutStream() :
+    out(&std::cout),
+    print(1),
+    printheader(1)
+  {
+    ;
+  }  
+
+  NetgenOutStream(ostream * aout, Imp imp ) :
+    out(aout),
+    printheader(1)
+  { 
+    if ( netgen::printmessage_importance >= imp.GetImp() )
+      print = true;
+    else
+      print = false;
+  }
+
+  NetgenOutStream(ostream * aout, Proc proc ) :
+    out(aout),
+    printheader(1)
+  { 
+#ifdef PARALLEL
+    if ( netgen::id == proc.GetProc() )
+      print = true;
+    else
+      print = false;
+#else
+    if ( 0 == proc.GetProc() )
+      print = true;
+    else
+      print = false;
+
+#endif
+  }
+
+  NetgenOutStream(ostream * aout, Procs & procs ) :
+    out(aout),
+    printheader(1)
+  { 
+#ifdef PARALLEL
+    if ( procs.GetProcs().Contains(netgen::id) )
+      print = true;
+    else
+      print = false;
+#else
+    if ( procs.GetProcs().Contains(0) )
+      print = true;
+    else
+      print = false;
+
+#endif
+  }
+
+  ostream & OStream ()
+  {
+    return *out;
+  }
+
+  template <typename T>
+  NetgenOutStream & operator<< (T & var)
+  {
+    if ( print )
+      {
+#ifdef PARALLEL
+	if ( printheader )
+	  {
+	    *out << "proc " << netgen::id << ": ";
+	    printheader = false;
+	  }
+#endif
+	*out << var;
+      }
+    return (*this); 
+  }
+
+  NetgenOutStream& operator<< (ostream& ( *pf )(ostream&))
+  {
+    if ( print )
+      *out << (*pf) ;
+
+    return (*this);
+  }
+
+  NetgenOutStream& operator<< (ios& ( *pf )(ios&))
+  {
+    if ( print)
+      *out << (*pf) ;
+
+    printheader = 1;
+
+    return (*this);
+  }
+
+  NetgenOutStream& operator<< (ios_base& ( *pf )(ios_base&))
+  {
+    if (print )
+      *out << (*pf) ;
+    return (*this);
+  }
+
+
+};
+
+
+NetgenOutStream operator<< ( ostream & ost, Imp  imp );
+NetgenOutStream operator<< ( ostream & ost, Proc proc );
+NetgenOutStream operator<< ( ostream & ost, Procs & procs );
+// {
+//   return ( NetgenOutStream ( &ost, imp.GetImp() ) );
+// }
+
+// template <typename T>
+// NetgenOutStream& operator<< (NetgenOutStream& out, T c )
+// {
+//   out.OStream() << c << endl;
+//   return out;
+// }
+
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/ngexception.cpp b/contrib/Netgen/libsrc/general/ngexception.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2496f6b3be3b2b410727d4cd04234a3015d10df0
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/ngexception.cpp
@@ -0,0 +1,33 @@
+/**************************************************************************/
+/* File:   ngexception.cpp                                                */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   16. Jan. 02                                                    */
+/**************************************************************************/
+
+#include <myadt.hpp>
+
+namespace netgen
+{
+  //using namespace netgen;
+
+
+
+  NgException :: NgException (const string & s) 
+    : what(s)
+  {
+    ; 
+  }
+
+
+  NgException :: ~NgException () 
+  {
+    ;
+  }
+
+  /// append string to description
+  void NgException :: Append (const string & s)
+  { 
+    what += s; 
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/general/ngexception.hpp b/contrib/Netgen/libsrc/general/ngexception.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..70dc0a4a5d3a7aec7748fbc1c271f8296986087a
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/ngexception.hpp
@@ -0,0 +1,33 @@
+#ifndef FILE_NGEXCEPTION
+#define FILE_NGEXCEPTION
+
+/**************************************************************************/
+/* File:   ngexception.hpp                                                */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   16. Jan. 2002                                                  */
+/**************************************************************************/
+
+namespace netgen
+{
+
+/// Base class for all ng exceptions
+class NgException 
+{
+  /// verbal description of exception
+  string what;
+public:
+  ///
+  DLL_HEADER NgException (const string & s);
+  ///
+  DLL_HEADER virtual ~NgException ();
+
+  /// append string to description
+  DLL_HEADER void Append (const string & s);
+  //  void Append (const char * s);
+  
+  /// verbal description of exception
+  const string & What() const { return what; }
+};
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/optmem.cpp b/contrib/Netgen/libsrc/general/optmem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..adb4b36e996c8fd2b0b413776ed742232430f587
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/optmem.cpp
@@ -0,0 +1,63 @@
+/**************************************************************************/
+/* File:   optmem.cc                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   04. Apr. 97                                                    */
+/**************************************************************************/
+
+/* 
+   Abstract data type Array
+*/
+
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+namespace netgen
+{
+  //using namespace netgen;
+
+  BlockAllocator :: BlockAllocator (unsigned asize, unsigned ablocks)
+    : bablocks (0)
+  {
+    if (asize < sizeof(void*))
+      asize = sizeof(void*);
+    size = asize;
+    blocks = ablocks;
+    freelist = NULL;
+  }
+
+  BlockAllocator :: ~BlockAllocator ()
+  {
+    for (int i = 0; i < bablocks.Size(); i++)
+      delete [] bablocks[i];
+  }
+
+  void * BlockAllocator :: Alloc ()
+  {
+    //  return new char[size];
+    if (!freelist)
+      {
+	// cout << "freelist = " << freelist << endl;
+	// cout << "BlockAlloc: " << size*blocks << endl;
+	char * hcp = new char [size * blocks];
+	bablocks.Append (hcp);
+	bablocks.Last() = hcp;
+	for (unsigned i = 0; i < blocks-1; i++)
+	  *(void**)&(hcp[i * size]) = &(hcp[ (i+1) * size]);
+	*(void**)&(hcp[(blocks-1)*size]) = NULL;
+	freelist = hcp;
+      }
+
+    void * p = freelist;
+    freelist = *(void**)freelist;
+    return p;
+  }
+
+  /*
+  void BlockAllocator :: Free (void * p)
+  {
+    *(void**)p = freelist;
+    freelist = p;
+  }
+  */
+}
diff --git a/contrib/Netgen/libsrc/general/optmem.hpp b/contrib/Netgen/libsrc/general/optmem.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cd55f16ea524f9b417ac5855baf000bb32758d90
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/optmem.hpp
@@ -0,0 +1,62 @@
+#ifndef FILE_OPTMEM
+#define FILE_OPTMEM
+
+/**************************************************************************/
+/* File:   optmem.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   04. Apr. 97                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+/** 
+    Optimized Memory allocation classes
+*/
+
+class BlockAllocator
+{
+private:
+  ///
+  unsigned size, blocks;
+  ///
+  void * freelist;
+  ///
+  Array<char*> bablocks;
+public:
+  ///
+  BlockAllocator (unsigned asize, unsigned ablocks = 100);
+  ///
+  ~BlockAllocator ();
+  ///
+
+  void * Alloc ();
+  /*
+  {
+    if (!freelist)
+      Alloc2();
+
+    void * p = freelist;
+    // freelist = *(void**)freelist;
+    freelist = *static_cast<void**> (freelist);
+
+    return p;
+  }
+  */
+
+
+  ///
+  void Free (void * p)
+  {
+    *(void**)p = freelist;
+    freelist = p;
+  }
+  
+
+private:
+  //  void Alloc2 ();
+};
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/parthreads.cpp b/contrib/Netgen/libsrc/general/parthreads.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81e9d0b6cce14da76c8e1cb196666a9fab2c3fc1
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/parthreads.cpp
@@ -0,0 +1,40 @@
+/**************************************************************************/
+/* File:   parthreads.cpp                                                 */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+/*
+
+namespace netgen
+{
+  using namespace netgen;
+
+#ifdef WIN32
+
+  NgLock :: NgLock (NgMutex & mut)
+    : sl(&mut.cs)
+  {
+    ;
+  }
+
+  void NgLock :: Lock ()
+  {
+    sl.Lock();
+  }
+  void NgLock :: UnLock ()
+  {
+    sl.Unlock();
+  }
+
+
+#else
+
+#endif
+}
+
+*/
diff --git a/contrib/Netgen/libsrc/general/parthreads.hpp b/contrib/Netgen/libsrc/general/parthreads.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c103790955599e387ff5438f42c46bd9db9b271b
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/parthreads.hpp
@@ -0,0 +1,192 @@
+#ifndef FILE_PARTHREADS
+#define FILE_PARTHREADS
+
+/**************************************************************************/
+/* File:   parthreads.hh                                                  */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   22. Nov. 2000                                                  */
+/**************************************************************************/
+
+/*
+  Parallel thread, Mutex,
+*/
+
+namespace netgen
+{
+
+#ifdef NO_PARALLEL_THREADS
+
+class NgMutex { };
+
+class NgLock
+{
+public:
+  NgLock (NgMutex & mut, bool lock = 0) { ; }
+  void Lock () { ; }
+  void UnLock () { ; }
+};
+
+
+#else
+
+#ifdef _MSC_VER
+
+#ifdef MSVC_EXPRESS
+// #include <pthread.h>
+
+
+class NgMutex
+{
+  pthread_mutex_t mut;
+public:
+  NgMutex ()
+  {
+    pthread_mutex_init (&mut, NULL);
+   }
+  friend class NgLock;
+};
+
+class NgLock
+{
+  pthread_mutex_t & mut;
+  bool locked;
+public:
+  NgLock (NgMutex & ngmut, bool lock = false)
+    : mut (ngmut.mut)
+  {
+    if (lock)
+      pthread_mutex_lock (&mut);
+
+    locked = lock;
+  };
+
+  ~NgLock()
+  {
+    if (locked)
+      pthread_mutex_unlock (&mut);
+  }
+
+  void Lock ()
+  {
+    pthread_mutex_lock (&mut);
+    locked = true;
+  }
+  void UnLock ()
+  {
+    pthread_mutex_unlock (&mut);
+    locked = false;
+  }
+  /*
+  int TryLock ()
+  {
+    return pthread_mutex_trylock (&mut);
+  }
+  */
+};
+
+#else // Using MS VC++ Standard / Enterprise / Professional edition...
+
+
+class NgMutex
+{
+  CCriticalSection cs;
+
+public:
+  NgMutex ()
+  { ; }
+  friend class NgLock;
+};
+
+class NgLock
+{
+  CSingleLock sl;
+  bool locked;
+public:
+  NgLock (NgMutex & mut, bool lock = 0)
+    : sl(&mut.cs)
+  {
+    if (lock) sl.Lock();
+    locked = lock;
+  }
+
+  ~NgLock ()
+  {
+    if (locked) sl.Unlock();
+  }
+
+  void Lock ()
+  {
+    sl.Lock();
+    locked = 1;
+  }
+
+  void UnLock ()
+  {
+    sl.Unlock();
+    locked = 0;
+  }
+};
+
+#endif // MSVC_EXPRESS
+
+#else
+
+
+// #include <pthread.h>
+
+class NgMutex
+{
+  pthread_mutex_t mut;
+public:
+  NgMutex ()
+  {
+    pthread_mutex_init (&mut, NULL);
+   }
+  friend class NgLock;
+};
+
+class NgLock
+{
+  pthread_mutex_t & mut;
+  bool locked;
+public:
+  NgLock (NgMutex & ngmut, bool lock = false)
+    : mut (ngmut.mut)
+  {
+    if (lock)
+      pthread_mutex_lock (&mut);
+
+    locked = lock;
+  };
+
+  ~NgLock()
+  {
+    if (locked)
+      pthread_mutex_unlock (&mut);
+  }
+
+  void Lock ()
+  {
+    pthread_mutex_lock (&mut);
+    locked = true;
+  }
+  void UnLock ()
+  {
+    pthread_mutex_unlock (&mut);
+    locked = false;
+  }
+  /*
+  int TryLock ()
+  {
+    return pthread_mutex_trylock (&mut);
+  }
+  */
+};
+
+#endif
+
+#endif
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/profiler.cpp b/contrib/Netgen/libsrc/general/profiler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e3f85e9e09a88333df497a1b13beb4b8d353460f
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/profiler.cpp
@@ -0,0 +1,120 @@
+/**************************************************************************/
+/* File:   profiler.cpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   19. Apr. 2002                                                  */
+/**************************************************************************/
+
+
+#include <myadt.hpp>
+#include <cstdlib>
+
+namespace netgen
+{
+  //using namespace netgen;
+
+  long int NgProfiler::tottimes[SIZE];
+  long int NgProfiler::starttimes[SIZE];
+  long int NgProfiler::counts[SIZE];
+  string NgProfiler::names[SIZE];
+  int NgProfiler::usedcounter[SIZE];
+  
+
+  NgProfiler :: NgProfiler()
+  {
+    for (int i = 0; i < SIZE; i++)
+      {
+	tottimes[i] = 0;
+	usedcounter[i] = 0;
+      }
+
+    total_timer = CreateTimer ("total CPU time");
+    StartTimer (total_timer);
+    envNGPROFILE = getenv ("NGPROFILE");
+  }
+
+  NgProfiler :: ~NgProfiler()
+  {
+#ifndef PARALLEL
+    StopTimer (total_timer);
+#endif
+
+    //ofstream prof;
+    //prof.open("ng.prof");
+
+    // ofstream-constructor may be called after STL-stuff is destructed,
+    // which leads to an "order of destruction"-problem,
+    // thus we use the C-variant:
+
+    if (envNGPROFILE)
+      {
+	char filename[100];
+#ifdef PARALLEL
+	sprintf (filename, "netgen.prof.%d", id);
+#else
+	sprintf (filename, "netgen.prof");
+#endif
+	
+	if (id == 0) printf ("write profile to file netgen.prof\n"); 
+	FILE *prof = fopen(filename,"w");
+	Print (prof);
+	fclose(prof);
+      }
+  }
+
+
+//   void NgProfiler :: Print (ostream & prof)
+//   {
+//     for (int i = 0; i < SIZE; i++)
+//       if (counts[i] != 0 || usedcounter[i] != 0)
+// 	{
+// 	  prof.setf (ios::fixed, ios::floatfield);
+// 	  prof.setf (ios::showpoint);
+
+// 	  prof // << "job " << setw(3) << i 
+// 	    << "calls " << setw(8) << counts[i] 
+// 	    << ", time " << setprecision(2) << setw(6) << double(tottimes[i]) / CLOCKS_PER_SEC << " sec";
+
+// 	  if (usedcounter[i]) 
+// 	    prof << " " << names[i];
+// 	  else
+// 	    prof << " " << i;
+	    
+// 	  prof << endl;
+// 	}
+//   }
+
+
+  void NgProfiler :: Print (FILE * prof)
+  {
+    for (int i = 0; i < SIZE; i++)
+      if (counts[i] != 0 || usedcounter[i] != 0)
+	{
+	  //fprintf(prof,"job %3i calls %8i, time %6.2f sec",i,counts[i],double(tottimes[i]) / CLOCKS_PER_SEC);
+	  fprintf(prof,"calls %8li, time %6.2f sec",counts[i],double(tottimes[i]) / CLOCKS_PER_SEC);
+	  if(usedcounter[i])
+	    fprintf(prof," %s",names[i].c_str());
+	  else
+	    fprintf(prof," %i",i);
+	  fprintf(prof,"\n");
+	}
+  }
+
+  int NgProfiler :: CreateTimer (const string & name)
+  {
+    for (int i = SIZE-1; i > 0; i--)
+      if(names[i] == name)
+	return i;
+
+    for (int i = SIZE-1; i > 0; i--)
+      if (!usedcounter[i])
+	{
+	  usedcounter[i] = 1;
+	  names[i] = name;
+	  return i;
+	}
+    return -1;
+  }
+
+
+  NgProfiler prof;
+}
diff --git a/contrib/Netgen/libsrc/general/profiler.hpp b/contrib/Netgen/libsrc/general/profiler.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..27e028eb44de8d5e0b0e7e412658a7f9bdd7771a
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/profiler.hpp
@@ -0,0 +1,68 @@
+#ifndef FILE_NG_PROFILER
+#define FILE_NG_PROFILER
+
+/**************************************************************************/
+/* File:   profiler.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   5. Jan. 2005                                                  */
+/**************************************************************************/
+
+
+
+#ifdef VTRACE
+#include "vt_user.h"
+#else
+  #define VT_USER_START(n)
+  #define VT_USER_END(n)
+  #define VT_TRACER(n)
+#endif
+
+namespace netgen
+{
+
+class NgProfiler
+{
+  enum { SIZE = 1000 };
+
+  static long int tottimes[SIZE];
+  static long int starttimes[SIZE];
+  static long int counts[SIZE];
+  static string names[SIZE];
+  static int usedcounter[SIZE];
+
+  bool envNGPROFILE;
+  int total_timer;
+  
+public: 
+  NgProfiler();
+  ~NgProfiler();
+  static int CreateTimer (const string & name);
+
+  static void StartTimer (int nr) 
+  { 
+    starttimes[nr] = clock(); counts[nr]++; 
+    // VT_USER_START (const_cast<char*> (names[nr].c_str())); 
+    VT_USER_START ( (char * const) (names[nr].c_str())); 
+  }
+  static void StopTimer (int nr) 
+  { 
+    tottimes[nr] += clock()-starttimes[nr]; 
+    VT_USER_END (const_cast<char*> (names[nr].c_str())); 
+  }
+
+  //static void Print (ostream & ost);
+  static void Print (FILE * prof);
+
+  class RegionTimer
+  {
+    int nr;
+  public:
+    RegionTimer (int anr) : nr(anr)
+      { StartTimer (nr); }
+    ~RegionTimer () { StopTimer (nr); }
+  };
+};
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/seti.cpp b/contrib/Netgen/libsrc/general/seti.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e7f5b2ea74b1ee42c2f1299fd0a3a5b908eb1852
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/seti.cpp
@@ -0,0 +1,70 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+
+namespace netgen
+{
+  //using namespace netgen;
+
+  IndexSet :: IndexSet (int maxind)
+  {
+    SetMaxIndex (maxind);
+  }
+
+  IndexSet :: ~IndexSet ()
+  {
+    Clear();
+  }
+
+
+  void IndexSet :: SetMaxIndex (int maxind)
+  {
+    if (maxind > flags.Size())
+      {
+	flags.SetSize (2 * maxind);
+	flags.Clear();
+      }
+  }
+
+  /*
+    int IndexSet :: IsIn (int ind) const
+    {
+    return flags.Test (ind);
+    }
+  */
+
+  /*
+    void IndexSet :: Add (int ind)
+    {
+    if (ind > flags.Size())
+    {
+    cerr << "out of range" << endl;
+    exit (1);
+    }
+
+    if (!flags.Test(ind))
+    {
+    set.Append (ind);
+    flags.Set (ind);
+    }
+    }
+  */
+
+  void IndexSet :: Del (int ind)
+  {
+    for (int i = 1; i <= set.Size(); i++)
+      if (set.Get(i) == ind)
+	{
+	  set.DeleteElement (ind);
+	  break;
+	}
+    flags.Clear (ind);
+  }
+
+  void IndexSet :: Clear ()
+  {
+    for (int i = 1; i <= set.Size(); i++)
+      flags.Clear (set.Get(i));
+    set.SetSize (0);
+  }
+}
diff --git a/contrib/Netgen/libsrc/general/seti.hpp b/contrib/Netgen/libsrc/general/seti.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4adbb09c5737c9462943b2debabcaf1649157d93
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/seti.hpp
@@ -0,0 +1,50 @@
+#ifndef FILE_SETI
+#define FILE_SETI
+
+
+/**************************************************************************/
+/* File:   seti.hh                                                        */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   20. Mar. 98                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+/**
+  Set of Integers
+  */
+class IndexSet
+{
+  Array<int> set;
+  BitArray flags;
+public:
+  IndexSet (int maxind);
+  
+  ~IndexSet ();
+  /// increase range to maxind
+  void SetMaxIndex (int maxind);
+  int IsIn (int ind) const
+  { 
+    return flags.Test (ind); 
+  }
+
+  void Add (int ind)
+  {
+    if (!flags.Test(ind))
+      {
+	set.Append (ind);
+	flags.Set (ind);
+      }
+  }
+
+  void Del (int ind);
+  void Clear ();
+  
+  const Array<int> & GetArray() { return set; }
+};
+
+}
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/general/sort.cpp b/contrib/Netgen/libsrc/general/sort.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a6adda1a04af85e6f3577b1f9a52c4a04c62e950
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/sort.cpp
@@ -0,0 +1,75 @@
+/**************************************************************************/
+/* File:   sort.cc                                                        */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   07. Jan. 00                                                    */
+/**************************************************************************/
+
+/* 
+   Sorting
+*/
+
+
+#include <algorithm>
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+namespace netgen
+{
+
+  void Sort (const Array<double> & values,
+	     Array<int> & order)
+  {
+    int n = values.Size();
+    int i, j;
+
+    order.SetSize (n);
+
+    for (i = 1; i <= n; i++)
+      order.Elem(i) = i;
+    for (i = 1; i <= n-1; i++)
+      for (j = 1; j <= n-1; j++)
+	if (values.Get(order.Elem(j)) > values.Get(order.Elem(j+1)))
+	  {
+	    Swap (order.Elem(j), order.Elem(j+1));
+	  }
+  }
+
+
+  void QuickSortRec (const Array<double> & values,
+		     Array<int> & order, 
+		     int left, int right)
+  {
+    int i, j;
+    double midval;
+
+    i = left;
+    j = right;
+    midval = values.Get(order.Get((i+j)/2));
+  
+    do
+      {
+	while (values.Get(order.Get(i)) < midval) i++;
+	while (midval < values.Get(order.Get(j))) j--;
+      
+	if (i <= j)
+	  {
+	    Swap (order.Elem(i), order.Elem(j));
+	    i++; j--;
+	  }
+      }
+    while (i <= j);
+    if (left < j) QuickSortRec (values, order, left, j);
+    if (i < right) QuickSortRec (values, order, i, right);
+  }
+
+  void QuickSort (const Array<double> & values,
+		 Array<int> & order)
+  {
+    int i, n = values.Size();
+    order.SetSize (n);
+    for (i = 1; i <= n; i++)
+      order.Elem(i) = i;
+
+    QuickSortRec (values, order, 1, order.Size());
+  }
+}
diff --git a/contrib/Netgen/libsrc/general/sort.hpp b/contrib/Netgen/libsrc/general/sort.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3a9b41db9fc1e45ec898a18f19222a72f513b68f
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/sort.hpp
@@ -0,0 +1,46 @@
+#ifndef FILE_SORT
+#define FILE_SORT
+
+/**************************************************************************/
+/* File:   sort.hh                                                        */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   07. Jan. 00                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+// order(i) is sorted index of element i
+extern void Sort (const Array<double> & values,
+		  Array<int> & order);
+
+extern void QuickSort (const Array<double> & values,
+		      Array<int> & order);
+
+
+
+
+template <class T>
+inline void BubbleSort (int size, T * data)
+{
+  T hv;
+  for (int i = 0; i < size; i++)
+    for (int j = i+1; j < size; j++)
+      if (data[i] > data[j])
+	{
+	  hv = data[i];
+	  data[i] = data[j];
+	  data[j] = hv;
+	}
+}
+
+template <class T>
+inline void BubbleSort (Array<T> & data)
+{
+  if(data.Size() > 0)
+	  BubbleSort (data.Size(), &data[data.Begin()]);
+}
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/spbita2d.cpp b/contrib/Netgen/libsrc/general/spbita2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..45e864137030ae7b0cd0e317f1e346589ceb1f00
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/spbita2d.cpp
@@ -0,0 +1,172 @@
+/**************************************************************************/
+/* File:   spbita2d.cpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/* 
+   Implementation of sparse 2 dimensional bitarray
+*/
+
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+namespace netgen
+{
+  //using namespace netgen;
+
+  SPARSE_BIT_Array_2D :: SPARSE_BIT_Array_2D (int ah, int aw)
+  {
+    lines = NULL;
+    SetSize (ah, aw);
+  }
+
+  SPARSE_BIT_Array_2D :: ~SPARSE_BIT_Array_2D ()
+  {
+    DeleteElements ();
+    delete lines;
+  }
+
+
+  void SPARSE_BIT_Array_2D :: SetSize (int ah, int aw)
+  {
+    DeleteElements();
+    if (lines)
+      {
+	delete lines;
+	lines = NULL;
+      }
+
+    if (!aw) aw = ah;
+
+    height = ah;
+    width = aw;
+
+    if (!ah) return;
+    lines = new linestruct[ah];
+
+    if (lines)
+      {
+	for (int i = 0; i < ah; i++)
+	  {
+	    lines[i].size = 0;
+	    lines[i].maxsize = 0;
+	    lines[i].col = NULL;
+	  }
+      }
+    else
+      {
+	height = width = 0;
+	MyError ("SPARSE_Array::SetSize: Out of memory");
+      }
+  }
+
+
+
+  void SPARSE_BIT_Array_2D :: DeleteElements ()
+  {
+    if (lines)
+      {
+	for (int i = 0; i < height; i++)
+	  {
+	    if (lines[i].col)
+	      {
+		delete [] lines[i].col;
+		lines[i].col = NULL;
+		lines[i].size = 0;
+		lines[i].maxsize = 0;
+	      }
+	  }
+      }
+  }
+
+
+  int SPARSE_BIT_Array_2D :: Test (int i, int j) const
+  {
+    int k, max, *col;
+
+    if (!lines) return 0;
+    if (i < 1 || i > height) return 0;
+
+    col = lines[i-1].col;
+    max = lines[i-1].size;
+
+    for (k = 0; k < max; k++, col++)
+      if (*col == j) return 1;
+
+    return 0;
+  }
+
+
+
+  void SPARSE_BIT_Array_2D :: Set(int i, int j)
+  {
+    int k, max, *col;
+
+    i--;
+    col = lines[i].col;
+    max = lines[i].size;
+
+    for (k = 0; k < max; k++, col++)
+      if (*col == j)
+	return;
+
+    if (lines[i].size)
+      {
+	if (lines[i].size == lines[i].maxsize)
+	  {
+	    col = new int[lines[i].maxsize+2];
+	    if (col)
+	      {
+		lines[i].maxsize += 2;
+		memcpy (col, lines[i].col, sizeof (int) * lines[i].size);
+		delete [] lines[i].col;
+		lines[i].col = col;
+	      }
+	    else
+	      {
+		MyError ("SPARSE_BIT_Array::Set: Out of mem 1");
+		return;
+	      }
+	  }
+	else
+	  col = lines[i].col;
+
+	if (col)
+	  {
+	    k = lines[i].size-1;
+	    while (k >= 0 && col[k] > j)
+	      {
+		col[k+1] = col[k];
+		k--;
+	      }
+
+	    k++;
+	    lines[i].size++;
+	    col[k] = j;
+	    return;
+	  }
+	else
+	  {
+	    MyError ("SPARSE_Array::Set: Out of memory 2");
+	  }
+      }
+    else
+      {
+	lines[i].col = new int[4];
+	if (lines[i].col)
+	  {
+	    lines[i].maxsize = 4;
+	    lines[i].size = 1;
+	    lines[i].col[0] = j;
+	    return;
+	  }
+	else
+	  {
+	    MyError ("SparseMatrix::Elem: Out of memory 3");
+	  }
+      }
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/general/spbita2d.hpp b/contrib/Netgen/libsrc/general/spbita2d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba451fc789f96d17179aa6a71d0a26e45191e62c
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/spbita2d.hpp
@@ -0,0 +1,59 @@
+#ifndef FILE_SPBITA2D
+#define FILE_SPBITA2D
+
+/**************************************************************************/
+/* File:   spbita2d.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/** 
+   Implementation of sparse 2 dimensional bitarray
+*/
+
+namespace netgen
+{
+
+class SPARSE_BIT_Array_2D
+  {
+  class linestruct { public: INDEX size; INDEX maxsize; INDEX * col; };
+
+  ///
+  linestruct * lines;
+  ///
+  INDEX height, width;
+
+  public:
+
+  ///
+  SPARSE_BIT_Array_2D (INDEX ah = 0, INDEX aw = 0);
+  ///
+  ~SPARSE_BIT_Array_2D ();
+
+  ///
+  void SetSize (INDEX ah, INDEX aw = 0);
+  ///
+  void DeleteElements ();
+
+  ///
+  int Get (INDEX i, INDEX j) const;
+
+  ///
+  INDEX Height () const { return height; }
+  ///
+  INDEX Width () const { return width; }
+
+  ///
+  void Set (INDEX i, INDEX j);
+  ///
+  int Test (INDEX i, INDEX j) const;
+
+  ///
+  INDEX BitsInLine (INDEX i) const { return lines[i-1].size; }
+  ///
+  INDEX GetIndex (INDEX i, INDEX nr) const { return lines[i-1].col[nr-1]; }
+  };
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/stack.hpp b/contrib/Netgen/libsrc/general/stack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..83adee640c7858050b001bf2dcc307f79ab6de8d
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/stack.hpp
@@ -0,0 +1,114 @@
+#ifndef FILE_STACK
+#define FILE_STACK
+
+/*****************************************************************************/
+/*  File: stack.hh                                                           */
+/*  Author: Wolfram Muehlhuber                                               */
+/*  Date: September 98                                                       */
+/*****************************************************************************/
+
+/*
+  
+  Stack class, based on a resizable array
+
+ */
+
+
+// #include "array.hpp"
+
+namespace netgen
+{
+
+///
+template <class T> class STACK
+{
+public:
+  ///
+  inline STACK (INDEX asize = 0, INDEX ainc = 0);
+  ///
+  inline ~STACK ();
+
+  ///
+  inline void Push (const T & el);
+  ///
+  inline T & Pop ();
+  ///
+  const inline T & Top () const;
+  ///
+  inline int IsEmpty () const;
+  ///
+  inline void MakeEmpty ();
+
+private:
+  ///
+  Array<T> elems;
+  ///
+  INDEX size;
+};
+
+
+
+
+/*
+  
+  Stack class, based on a resizable array
+
+ */
+
+template <class T>
+inline STACK<T> :: STACK (INDEX asize, INDEX ainc)
+  : elems(asize, ainc)
+{
+  size = 0;
+}
+
+
+template <class T>
+inline STACK<T> :: ~STACK ()
+{
+  ;
+}
+
+
+template <class T> 
+inline void STACK<T> :: Push (const T & el)
+{
+  if (size < elems.Size())
+    elems.Elem(++size) = el;
+  else
+    {
+      elems.Append(el);
+      size++;
+    }
+}
+
+
+template <class T> 
+inline T & STACK<T> :: Pop ()
+{
+  return elems.Elem(size--);
+}
+
+
+template <class T>
+const inline T & STACK<T> :: Top () const
+{
+  return elems.Get(size);
+}
+
+template <class T>
+inline int STACK<T> :: IsEmpty () const
+{
+  return (size == 0);
+}
+
+
+template <class T>
+inline void STACK<T> :: MakeEmpty ()
+{
+  size = 0;
+}
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/symbolta.cpp b/contrib/Netgen/libsrc/general/symbolta.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bd35ac7cb229c6370d599e0d9e108f794eb0a5fd
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/symbolta.cpp
@@ -0,0 +1,52 @@
+/**************************************************************************/
+/* File:   symbolta.cc                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/* 
+   Abstract data type Symbol Table
+*/
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+
+#ifndef FILE_SYMBOLTABLECC
+#define FILE_SYMBOLTABLECC
+// necessary for SGI ????
+
+
+namespace netgen
+{
+  //using namespace netgen;
+
+  BASE_SYMBOLTABLE :: BASE_SYMBOLTABLE ()
+  {
+    ;
+  }
+
+
+  BASE_SYMBOLTABLE :: ~BASE_SYMBOLTABLE()
+  {
+    DelNames();
+  }
+
+
+  void BASE_SYMBOLTABLE :: DelNames()
+  {
+    for (int i = 0; i < names.Size(); i++)
+      delete [] names[i];
+    names.SetSize (0);
+  }
+
+  int BASE_SYMBOLTABLE :: Index (const char * name) const
+  {
+    if (!name) return 0;
+    for (int i = 0; i < names.Size(); i++)
+      if (strcmp (names[i], name) == 0) return i+1;
+    return 0;
+  }
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/symbolta.hpp b/contrib/Netgen/libsrc/general/symbolta.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cfb6d9574b3b77de58c74a554a3aeb2057e093fc
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/symbolta.hpp
@@ -0,0 +1,161 @@
+#ifndef FILE_SYMBOLTA
+#define FILE_SYMBOLTA
+
+
+/**************************************************************************/
+/* File:   symbolta.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+/**
+   Base class for the generic SYMBOLTABLE.
+   An array of identifiers is maintained.
+*/
+class BASE_SYMBOLTABLE
+{
+protected:
+  /// identifiers
+  Array <char*> names;
+  
+public:
+  /// Constructor
+  BASE_SYMBOLTABLE ();
+  ///
+  ~BASE_SYMBOLTABLE ();
+  ///
+  void DelNames ();
+  /// Index of symbol name, returns 0 if not used.
+  int Index (const char * name) const;
+};
+
+
+/** 
+    Abstract data type Symbol Table.
+   
+    To a string an value of the generic type T is associated.
+    The string is not copied into the symbol table class!
+*/
+template <class T>
+class SYMBOLTABLE : public BASE_SYMBOLTABLE
+{
+private:
+  /// Associated data
+  Array <T> data;
+  
+public:
+  /// Creates a symboltable
+  inline SYMBOLTABLE ();
+  /// Returns size of symboltable
+  inline INDEX Size() const;
+  /// Returns reference to element, error if not used
+  inline T & Elem (const char * name);
+  /// Returns reference to i-th element
+  inline T & Elem (int i) 
+  { return data.Elem(i); }
+  /// Returns element, error if not used
+  inline const T & Get (const char * name) const;
+  /// Returns i-th element
+  inline const T & Get (int i) const;
+  /// Returns name of i-th element
+  inline const char* GetName (int i) const;
+  /// Associates el to the string name, overrides if name is used
+  inline void Set (const char * name, const T & el);
+  /// Checks whether name is used
+  inline bool Used (const char * name) const;
+  /// Deletes symboltable
+  inline void DeleteAll ();
+
+  inline T & operator[] (int i) 
+  { return data[i]; }
+  inline const T & operator[] (int i) const
+  { return data[i]; }
+
+private:
+  /// Prevents from copying symboltable by pointer assignment
+  SYMBOLTABLE<T> & operator= (SYMBOLTABLE<T> &);
+};
+
+
+
+
+template <class T>
+inline SYMBOLTABLE<T> :: SYMBOLTABLE () 
+{ 
+  ;
+}
+
+
+template <class T>
+inline INDEX SYMBOLTABLE<T> :: Size() const
+{
+  return data.Size();
+}
+
+template <class T>
+inline T & SYMBOLTABLE<T> :: Elem (const char * name)
+{
+  int i = Index (name);
+  if (i) 
+    return data.Elem (i);
+  else 
+    return data.Elem(1);
+}
+
+template <class T>
+inline const T & SYMBOLTABLE<T> :: Get (const char * name) const
+{
+  int i;
+  i = Index (name);
+  if (i) 
+    return data.Get(i);
+  else 
+    return data.Get(1);
+}
+
+template <class T>
+inline const T & SYMBOLTABLE<T> :: Get (int i) const
+{
+  return data.Get(i);
+}
+
+template <class T>
+inline const char* SYMBOLTABLE<T> :: GetName (int i) const
+{
+  return names.Get(i);
+}
+
+template <class T>
+inline void SYMBOLTABLE<T> :: Set (const char * name, const T & el)
+{
+  int i;
+  i = Index (name);
+  if (i) 
+    data.Set(i, el);
+  else
+    {
+      data.Append (el);
+      char * hname = new char [strlen (name) + 1];
+      strcpy (hname, name);
+      names.Append (hname);
+    }
+}
+
+template <class T>
+inline bool SYMBOLTABLE<T> :: Used (const char * name) const
+{
+  return (Index(name)) ? true : false;
+}
+
+template <class T>
+inline void SYMBOLTABLE<T> :: DeleteAll () 
+{	
+  DelNames();
+  data.DeleteAll();
+}
+
+}
+#endif
diff --git a/contrib/Netgen/libsrc/general/table.cpp b/contrib/Netgen/libsrc/general/table.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d9d6cf139daf90335322aa8cb2382f121cf50dde
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/table.cpp
@@ -0,0 +1,214 @@
+/**************************************************************************/
+/* File:   table.cpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/* 
+   Abstract data type TABLE
+*/
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+namespace netgen
+{
+  //using namespace netgen;
+
+  BASE_TABLE :: BASE_TABLE (int size)
+    : data(size)
+  {
+    for (int i = 0; i < size; i++)
+      {
+	data[i].maxsize = 0;
+	data[i].size = 0;
+	data[i].col = NULL;
+      }
+    oneblock = NULL;
+  }
+
+  BASE_TABLE :: BASE_TABLE (const FlatArray<int> & entrysizes, int elemsize)
+    : data(entrysizes.Size())
+  {
+    int i, cnt = 0;
+    int n = entrysizes.Size();
+
+    for (i = 0; i < n; i++)
+      cnt += entrysizes[i];
+    oneblock = new char[elemsize * cnt];
+    // mem_total_alloc_table += elemsize * cnt;
+
+    cnt = 0;
+    for (i = 0; i < n; i++)
+      {
+	data[i].maxsize = entrysizes[i];
+	data[i].size = 0;
+
+	data[i].col = &oneblock[elemsize * cnt];
+	cnt += entrysizes[i];
+      }
+  }
+
+  BASE_TABLE :: ~BASE_TABLE ()
+  {
+    if (oneblock)
+      delete [] oneblock;
+    else
+      {
+	for (int i = 0; i < data.Size(); i++)
+	  delete [] (char*)data[i].col;
+      }
+  }
+  
+  void BASE_TABLE :: SetSize (int size)
+  {
+    for (int i = 0; i < data.Size(); i++)
+      delete [] (char*)data[i].col;
+    
+    data.SetSize(size);
+    for (int i = 0; i < size; i++)
+      {
+	data[i].maxsize = 0;
+	data[i].size = 0;
+	data[i].col = NULL;
+      }    
+  }
+  
+  void BASE_TABLE :: ChangeSize (int size)
+  {
+    int oldsize = data.Size();
+    if (size == oldsize) 
+      return;
+
+    if (size < oldsize)
+      for (int i = size; i < oldsize; i++)
+	delete [] (char*)data[i].col;
+    
+    data.SetSize(size);
+
+    for (int i = oldsize; i < size; i++)
+      {
+	data[i].maxsize = 0;
+	data[i].size = 0;
+	data[i].col = NULL;
+      }    
+  }
+
+  void BASE_TABLE :: IncSize2 (int i, int elsize)
+  {
+#ifdef DEBUG
+    if (i < 0 || i >= data.Size())
+      {
+	MyError ("BASE_TABLE::Inc: Out of range");
+	return;
+      }
+#endif
+
+    linestruct & line = data[i];
+    if (line.size == line.maxsize)
+      {
+	void * p = new char [(line.maxsize+5) * elsize];
+      
+	memcpy (p, line.col, line.maxsize * elsize);
+	delete [] (char*)line.col;
+
+	line.col = p;
+	line.maxsize += 5;
+      }
+  
+    line.size++;
+  }
+
+
+
+
+  void BASE_TABLE :: SetEntrySize2 (int i, int newsize, int elsize)
+  {
+    linestruct & line = data[i];
+    if (newsize > line.maxsize)
+      {
+	void * p = new char [newsize * elsize];
+      
+	memcpy (p, line.col, min2 (newsize, line.size) * elsize);
+	delete [] (char*)line.col;
+
+	line.col = p;
+      }
+
+    line.size = newsize;
+  }
+
+
+
+
+
+  /*
+  void BASE_TABLE :: DecSize (int i)
+  {
+#ifdef DEBUG
+    if (i < 0 || i >= data.Size())
+      {
+	MyError ("BASE_TABLE::Dec: Out of range");
+	return;
+      }
+#endif
+
+    linestruct & line = data[i];
+  
+#ifdef DEBUG
+    if (line.size == 0)
+      {
+	MyError ("BASE_TABLE::Dec: EntrySize < 0");
+	return;      
+      }
+#endif
+  
+    line.size--;
+  }
+  */
+
+
+
+  void BASE_TABLE :: AllocateElementsOneBlock (int elemsize)
+  {
+    int cnt = 0;
+    int n = data.Size();
+
+    for (int i = 0; i < n; i++)
+      cnt += data[i].maxsize;
+    oneblock = new char[elemsize * cnt];
+
+    cnt = 0;
+    for (int i = 0; i < n; i++)
+      {
+	data[i].size = 0;
+	data[i].col = &oneblock[elemsize * cnt];
+	cnt += data[i].maxsize;
+      }
+  }
+
+
+
+  int BASE_TABLE :: AllocatedElements () const
+  {
+    int els = 0;
+    for (int i = 0; i < data.Size(); i++)
+      els += data[i].maxsize;
+    return els;
+  }
+  
+  int BASE_TABLE :: UsedElements () const
+  {
+    int els = 0;
+    for (int i = 0; i < data.Size(); i++)
+      els += data[i].size;
+    return els;
+  }
+
+  void BASE_TABLE :: SetElementSizesToMaxSizes ()
+  {
+    for (int i = 0; i < data.Size(); i++)
+      data[i].size = data[i].maxsize;
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/general/table.hpp b/contrib/Netgen/libsrc/general/table.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f63b80746ad961145ad046c9ee726909ea720d7a
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/table.hpp
@@ -0,0 +1,239 @@
+#ifndef FILE_TABLE
+#define FILE_TABLE
+
+/**************************************************************************/
+/* File:   table.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+
+/// Base class to generic class TABLE.
+class BASE_TABLE
+{
+protected:
+  
+  ///
+  class linestruct
+  {
+  public:
+    ///
+    int size;
+    /// 
+    int maxsize;
+    ///
+    void * col;
+  };
+  
+  ///
+  Array<linestruct> data;
+  char * oneblock;
+
+public:
+  ///
+  BASE_TABLE (int size);
+  ///
+  BASE_TABLE (const FlatArray<int> & entrysizes, int elemsize);
+  ///
+  ~BASE_TABLE ();
+  ///
+  void SetSize (int size);
+  ///
+  void ChangeSize (int size);
+
+  /// increment size of entry i by one, i is 0-based
+  void IncSize (int i, int elsize)
+  {
+    if (data[i].size < data[i].maxsize)
+      data[i].size++;
+    else
+      IncSize2 (i, elsize);
+  }
+
+  void SetEntrySize (int i, int newsize, int elsize)
+  {
+    if (newsize < data[i].maxsize)
+      data[i].size = newsize;
+    else
+      SetEntrySize2 (i, newsize, elsize);
+  }
+
+  ///
+  void IncSize2 (int i, int elsize);
+  void SetEntrySize2 (int i, int newsize, int elsize);
+
+  //  void DecSize (int i);
+
+  ///
+  void AllocateElementsOneBlock (int elemsize);
+  
+  int AllocatedElements () const;
+  int UsedElements () const;
+
+  void SetElementSizesToMaxSizes ();
+};
+
+
+
+
+
+
+
+/** 
+   Abstract data type TABLE.
+   
+   To an integer i in the range from 1 to size a set of elements of the
+   generic type T is associated. 
+*/
+template <class T, int BASE = 0>
+class TABLE : public BASE_TABLE
+{
+public:
+  /// Creates table.
+  inline TABLE () : BASE_TABLE(0) { ; }
+
+  /// Creates table of size size
+  inline TABLE (int size) : BASE_TABLE (size) { ; }
+
+  /// Creates fixed maximal element size table
+  inline TABLE (const FlatArray<int,BASE> & entrysizes)
+    : BASE_TABLE (FlatArray<int> (entrysizes.Size(), const_cast<int*>(&entrysizes[BASE])), 
+		  sizeof(T))
+  { ; }
+  
+  /// Changes Size of table to size, deletes data
+  inline void SetSize (int size)
+  {
+    BASE_TABLE::SetSize (size);
+  }
+
+  /// Changes Size of table to size, keep data
+  inline void ChangeSize (int size)
+  {
+    BASE_TABLE::ChangeSize (size);
+  }
+
+
+  /// Inserts element acont into row i, BASE-based. Does not test if already used.
+  inline void Add (int i, const T & acont)
+  {
+    IncSize (i-BASE, sizeof (T));
+    ((T*)data[i-BASE].col)[data[i-BASE].size-1] = acont;
+  }
+
+
+  /// Inserts element acont into row i, 1-based. Does not test if already used.
+  inline void Add1 (int i, const T & acont)
+  {
+    IncSize (i-1, sizeof (T));
+    ((T*)data.Elem(i).col)[data.Elem(i).size-1] = acont;
+  }
+  
+  ///
+  void IncSizePrepare (int i)
+  {
+    data[i-BASE].maxsize++;
+  }
+
+
+  /// Inserts element acont into row i. BASE-based. Does not test if already used, assumes to have enough memory
+  inline void AddSave (int i, const T & acont)
+    {
+      ((T*)data[i-BASE].col)[data[i-BASE].size] = acont;
+      data[i-BASE].size++;
+    }
+
+  /// Inserts element acont into row i. 1-based. Does not test if already used, assumes to have mem
+  inline void AddSave1 (int i, const T & acont)
+    {
+      ((T*)data.Elem(i).col)[data.Elem(i).size] = acont;
+      data.Elem(i).size++;
+    }
+
+  /// Inserts element acont into row i. Does not test if already used.
+  inline void AddEmpty (int i)
+  {
+    IncSize (i-BASE, sizeof (T));
+  }
+
+  /** Set the nr-th element in the i-th row to acont.
+    Does not check for overflow. */
+  inline void Set (int i, int nr, const T & acont)
+    { ((T*)data.Get(i).col)[nr-1] = acont; }
+  /** Returns the nr-th element in the i-th row.
+    Does not check for overflow. */
+  inline const T & Get (int i, int nr) const
+    { return ((T*)data.Get(i).col)[nr-1]; }
+
+
+  /** Returns pointer to the first element in row i. */
+  inline const T * GetLine (int i) const
+  {
+    return ((const T*)data.Get(i).col);
+  }
+
+
+  /// Returns size of the table.
+  inline int Size () const
+  {
+    return data.Size();
+  }
+
+  /// Returns size of the i-th row.
+  inline int EntrySize (int i) const
+    { return data.Get(i).size; }
+
+  /*
+  inline void DecEntrySize (int i)
+    { DecSize(i); }
+  */
+  void AllocateElementsOneBlock ()
+    { BASE_TABLE::AllocateElementsOneBlock (sizeof(T)); }
+
+
+  inline void PrintMemInfo (ostream & ost) const
+  {
+    int els = AllocatedElements(); 
+    ost << "table: allocaed " << els 
+	<< " a " << sizeof(T) << " Byts = " 
+	<< els * sizeof(T) 
+	<< " bytes in " << Size() << " bags."
+	<< " used: " << UsedElements()
+	<< endl;
+  }
+
+  /// Access entry.
+  FlatArray<T> operator[] (int i) const
+  { 
+#ifdef DEBUG
+    if (i-BASE < 0 || i-BASE >= data.Size())
+      cout << "table out of range, i = " << i << ", s = " << data.Size() << endl;
+#endif
+
+    return FlatArray<T> (data[i-BASE].size, (T*)data[i-BASE].col);
+  }
+};
+
+
+template <class T, int BASE>
+inline ostream & operator<< (ostream & ost, const TABLE<T,BASE> & table)
+{
+  for (int i = BASE; i < table.Size()+BASE; i++)
+    {
+      ost << i << ": ";
+      FlatArray<T> row = table[i];
+      ost << "(" << row.Size() << ") ";
+      for (int j = 0; j < row.Size(); j++)
+	ost << row[j] << " ";
+      ost << endl;
+    }
+  return ost;
+}
+
+}
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/general/template.hpp b/contrib/Netgen/libsrc/general/template.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0bb83f273218521572b57319247827ae6970a19c
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/template.hpp
@@ -0,0 +1,460 @@
+#ifndef FILE_TEMPLATE
+#define FILE_TEMPLATE
+
+/**************************************************************************/
+/* File:   template.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+namespace netgen 
+{
+
+/*
+   templates, global types, defines and variables
+*/
+
+///	The following value may be adapted to the hardware !
+#ifndef CLOCKS_PER_SEC
+#define CLOCKS_PER_SEC 1000000
+#endif
+
+
+// #include <iostream>
+/** output stream for testing.
+  testout is opened by main */
+DLL_HEADER extern ostream * testout;
+
+/** use instead of cout */
+extern ostream * mycout;
+
+/** error output stream */
+extern ostream * myerr;
+
+/** Error messages display.
+  Error messages are displayed by this function */
+DLL_HEADER extern void MyError (const char * ch);
+
+
+/** Rings the bell.
+  Produces nr beeps. */
+extern void MyBeep (int nr = 1);
+
+
+template <class T>
+inline void Swap (T & a, T & b)
+{
+  T temp = a;
+  a = b;
+  b = temp;
+}
+
+/*
+template <class T>
+inline void swap (T & a, T & b)
+{
+  T temp = a;
+  a = b;
+  b = temp;
+}
+*/
+
+
+
+/**
+  INDEX is a typedef for (at least) 4-byte integer
+ */
+typedef int INDEX;
+
+/**
+  BOOL is a typedef for boolean variables
+  */
+// typedef int BOOL;
+
+typedef int ELIND;
+typedef int PIND;
+
+
+class twoint 
+{ 
+public: ///
+  int i1, i2; ///
+  twoint() {};
+  ///
+  twoint(int ii1, int ii2) {i1 = ii1; i2 = ii2;}
+  friend int operator== (const twoint& t1, const twoint& t2);
+  ///
+  void Swap() {int x = i1; i1 = i2; i2 = x;}
+  void Sort() {if (i1 > i2) {Swap();}}
+};
+
+inline int operator== (const twoint& t1, const twoint& t2) 
+{
+  return t1.i1 == t2.i1 && t1.i2 == t2.i2;
+}
+
+class threeint 
+{ 
+public: /// 
+  int i1, i2, i3; ///
+  threeint() {}; 
+  ///
+  threeint(int ii1, int ii2, int ii3) {i1 = ii1; i2 = ii2; i3 = ii3;}
+};
+
+///
+class twodouble
+{
+public:
+  ///
+  double d1, d2;
+  ///
+  twodouble() {d1 = 0; d2 = 0;};
+  ///
+  twodouble(double id1, double id2) {d1 = id1; d2 = id2;}
+  ///
+  void Swap() {double x = d1; d1 = d2; d2 = x;}
+};
+
+class fourint { public: int i1, i2, i3, i4; fourint() {}; };
+
+
+///
+class INDEX_2;
+ostream & operator<<(ostream  & s, const INDEX_2 & i2);
+
+
+class INDEX_2
+{
+  ///
+  INDEX i[2];
+
+public:
+  ///
+  INDEX_2 () { }
+  ///
+  INDEX_2 (INDEX ai1, INDEX ai2)
+    { i[0] = ai1; i[1] = ai2; }
+
+  ///
+  INDEX_2 (const INDEX_2 & in2)
+    { i[0] = in2.i[0]; i[1] = in2.i[1]; }
+
+  ///
+  int operator== (const INDEX_2 & in2) const
+    { return i[0] == in2.i[0] && i[1] == in2.i[1]; }
+
+  ///
+
+
+  INDEX_2 Sort ()
+  {
+    if (i[0] > i[1]) 
+      {
+	INDEX hi = i[0];
+	i[0] = i[1];
+	i[1] = hi;
+      }
+    return *this;
+  }
+
+  static INDEX_2 Sort (int i1, int i2)
+  {
+    if (i1 > i2)
+      return INDEX_2 (i2,i1);
+    else
+      return INDEX_2 (i1,i2);
+  }
+
+
+  ///
+  INDEX & I1 () { return i[0]; }
+  ///
+  INDEX & I2 () { return i[1]; }
+  ///
+  INDEX & I (int j) { return i[j-1]; }
+  ///
+  const INDEX & I1 () const { return i[0]; }
+  ///
+  const INDEX & I2 () const { return i[1]; }
+  ///
+  const INDEX & I (int j) const { return i[j-1]; }
+  ///
+  int & operator[] (int j) { return i[j]; }
+  ///
+  const int & operator[] (int j) const { return i[j]; }
+  ///
+  friend ostream & operator<<(ostream  & s, const INDEX_2 & i2);
+};
+
+
+///
+class INDEX_3
+{
+  ///
+  INDEX i[3];
+
+public:
+  ///
+  INDEX_3 () { }
+  ///
+  INDEX_3 (INDEX ai1, INDEX ai2, INDEX ai3)
+    { i[0] = ai1; i[1] = ai2; i[2] = ai3; }
+
+  ///
+  INDEX_3 (const INDEX_3 & in2)
+    { i[0] = in2.i[0]; i[1] = in2.i[1]; i[2] = in2.i[2]; }
+
+
+  static INDEX_3 Sort (INDEX_3 i3)
+  {
+    return i3.Sort();
+  }
+
+  static INDEX_3 Sort (int i1, int i2, int i3)
+  {
+    if (i1 > i2) Swap (i1, i2);
+    if (i2 > i3) Swap (i2, i3);
+    if (i1 > i2) Swap (i1, i2);
+    return INDEX_3 (i1, i2, i3);
+  }
+
+  INDEX_3 Sort ()
+  {
+    if (i[0] > i[1]) Swap (i[0], i[1]);
+    if (i[1] > i[2]) Swap (i[1], i[2]);
+    if (i[0] > i[1]) Swap (i[0], i[1]);
+    return *this;
+  }
+
+  int operator== (const INDEX_3 & in2) const
+    { return i[0] == in2.i[0] && i[1] == in2.i[1] && i[2] == in2.i[2];}
+
+  ///
+  INDEX & I1 () { return i[0]; }
+  ///
+  INDEX & I2 () { return i[1]; }
+  ///
+  INDEX & I3 () { return i[2]; }
+  ///
+  INDEX & I (int j) { return i[j-1]; }
+  ///
+  const INDEX & I1 () const { return i[0]; }
+  ///
+  const INDEX & I2 () const { return i[1]; }
+  ///
+  const INDEX & I3 () const { return i[2]; }
+  ///
+  const INDEX & I (int j) const { return i[j-1]; }
+  ///
+  int & operator[] (int j) { return i[j]; }
+  ///
+  const int & operator[] (int j) const { return i[j]; }
+
+  ///
+  friend ostream & operator<<(ostream  & s, const INDEX_3 & i3);
+};
+
+
+
+///
+class INDEX_4
+{
+  ///
+  INDEX i[4];
+
+public:
+  ///
+  INDEX_4 () { }
+  ///
+  INDEX_4 (INDEX ai1, INDEX ai2, INDEX ai3, INDEX ai4)
+    { i[0] = ai1; i[1] = ai2; i[2] = ai3; i[3] = ai4; }
+
+  ///
+  INDEX_4 (const INDEX_4 & in2)
+    { i[0] = in2.i[0]; i[1] = in2.i[1]; i[2] = in2.i[2]; i[3] = in2.i[3]; }
+
+  ///
+  void Sort ();
+
+  ///
+  int operator== (const INDEX_4 & in2) const
+    { return 
+	i[0] == in2.i[0] && i[1] == in2.i[1] && 
+	i[2] == in2.i[2] && i[3] == in2.i[3]; }
+
+  ///
+  INDEX & I1 () { return i[0]; }
+  ///
+  INDEX & I2 () { return i[1]; }
+  ///
+  INDEX & I3 () { return i[2]; }
+  ///
+  INDEX & I4 () { return i[3]; }
+  ///
+  INDEX & I (int j) { return i[j-1]; }
+  ///
+  const INDEX & I1 () const { return i[0]; }
+  ///
+  const INDEX & I2 () const { return i[1]; }
+  ///
+  const INDEX & I3 () const { return i[2]; }
+  ///
+  const INDEX & I4 () const { return i[3]; }
+  ///
+  const INDEX & I (int j) const { return i[j-1]; }
+  ///
+  int & operator[] (int j) { return i[j]; }
+  ///
+  const int & operator[] (int j) const { return i[j]; }
+
+  ///
+  friend ostream & operator<<(ostream  & s, const INDEX_4 & i4);
+};
+
+
+
+
+
+
+
+
+/// The sort preserves quads !!!
+class INDEX_4Q
+{
+  ///
+  INDEX i[4];
+
+public:
+  ///
+  INDEX_4Q () { }
+  ///
+  INDEX_4Q (INDEX ai1, INDEX ai2, INDEX ai3, INDEX ai4)
+    { i[0] = ai1; i[1] = ai2; i[2] = ai3; i[3] = ai4; }
+
+  ///
+  INDEX_4Q (const INDEX_4Q & in2)
+    { i[0] = in2.i[0]; i[1] = in2.i[1]; i[2] = in2.i[2]; i[3] = in2.i[3]; }
+
+  ///
+  void Sort ();
+
+  ///
+  int operator== (const INDEX_4Q & in2) const
+    { return 
+	i[0] == in2.i[0] && i[1] == in2.i[1] && 
+	i[2] == in2.i[2] && i[3] == in2.i[3]; }
+
+  ///
+  INDEX & I1 () { return i[0]; }
+  ///
+  INDEX & I2 () { return i[1]; }
+  ///
+  INDEX & I3 () { return i[2]; }
+  ///
+  INDEX & I4 () { return i[3]; }
+  ///
+  INDEX & I (int j) { return i[j-1]; }
+  ///
+  const INDEX & I1 () const { return i[0]; }
+  ///
+  const INDEX & I2 () const { return i[1]; }
+  ///
+  const INDEX & I3 () const { return i[2]; }
+  ///
+  const INDEX & I4 () const { return i[3]; }
+  ///
+  const INDEX & I (int j) const { return i[j-1]; }
+  ///
+  friend ostream & operator<<(ostream  & s, const INDEX_4Q & i4);
+};
+
+
+inline bool operator< (const INDEX_4 & a, const INDEX_4 & b)
+{
+  for (int j = 0; j < 4; j++)
+    {
+      if (a[j] < b[j]) return true;
+      if (a[j] > b[j]) return false;
+    }
+  return false;
+}
+
+
+
+
+
+
+
+
+
+
+///
+template <class T>
+inline T min2 (T a, T b)
+{
+  ///
+  return (a < b) ? a : b;
+}
+///
+template <class T>
+inline T max2 (T a, T b)
+{
+  ///
+  return (a > b) ? a : b;
+}
+///
+template <class T>
+inline T min3 (T a, T b, T c)
+{
+  ///
+  return (a < b) ? (a < c) ? a : c
+    : (b < c) ? b : c;
+}
+///
+template <class T>
+inline T max3 (T a, T b, T c)
+{
+  ///
+  return (a > b) ? ((a > c) ? a : c)
+    : ((b > c) ? b : c);
+}
+
+///
+
+///
+template <class T>
+inline int sgn (T a)
+{
+  return (a > 0) ? 1 : (   ( a < 0) ? -1 : 0 );
+}
+
+///
+template <class T>
+inline T sqr (const T a)
+{
+  return a * a; 
+}
+
+///
+template <class T>
+inline T pow3 (const T a)
+{
+  return a * a * a; 
+}
+
+
+
+/*
+template <class T>
+void BubbleSort (int size, T * data);
+
+template <class T>
+void MergeSort (int size, T * data, T * help);
+*/
+
+
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/geom2d/Makefile.am b/contrib/Netgen/libsrc/geom2d/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..82d95667b3a3218eb04cbdd466e229531945026b
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/Makefile.am
@@ -0,0 +1,14 @@
+noinst_HEADERS = geom2dmesh.hpp geometry2d.hpp vsgeom2d.hpp 
+
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc/include  $(TCL_INCLUDES)
+
+METASOURCES = AUTO
+lib_LTLIBRARIES = libgeom2d.la libgeom2dvis.la
+
+libgeom2d_la_SOURCES = genmesh2d.cpp geom2dmesh.cpp geometry2d.cpp
+libgeom2d_la_LIBADD = 	$(top_builddir)/libsrc/meshing/libmesh.la
+
+libgeom2dvis_la_SOURCES = geom2dpkg.cpp vsgeom2d.cpp
+libgeom2dvis_la_LIBADD = libgeom2d.la
+
+
diff --git a/contrib/Netgen/libsrc/geom2d/genmesh2d.cpp b/contrib/Netgen/libsrc/geom2d/genmesh2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..000e5ad67d7b035153a66fda35acb7b30875c431
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/genmesh2d.cpp
@@ -0,0 +1,542 @@
+#include <meshing.hpp>
+#include <geometry2d.hpp>
+
+namespace netgen
+{
+  extern DLL_HEADER MeshingParameters mparam;
+
+  extern void Optimize2d (Mesh & mesh, MeshingParameters & mp);
+
+
+
+  void CalcPartition (double l, double h, double h1, double h2,
+		      double hcurve, double elto0, Array<double> & points);
+
+  // partitionizes spline curve
+  void Partition (const SplineSegExt & spline,
+		  double h, double elto0,
+		  Mesh & mesh, Point3dTree & searchtree, int segnr) 
+  {
+    enum { D = 2 };
+    int i, j;
+    double l; // , r1, r2, ra;
+    double lold, dt, frac;
+    int n = 100;
+    Point<D> p, pold, mark, oldmark;
+    Array<double> curvepoints;
+    double edgelength, edgelengthold;
+    l = spline.Length();
+
+    double h1 = min (spline.StartPI().hmax, h/spline.StartPI().refatpoint);
+    double h2 = min (spline.EndPI().hmax, h/spline.EndPI().refatpoint);
+    double hcurve = min (spline.hmax, h/spline.reffak);
+
+    CalcPartition (l, h, h1, h2, hcurve, elto0, curvepoints);
+    //  cout << "curvepoints = " << curvepoints << endl;
+
+    dt = 1.0 / n;
+
+    l = 0;
+    j = 1;
+
+    pold = spline.GetPoint (0);
+    lold = 0;
+    oldmark = pold;
+    edgelengthold = 0;
+    Array<int> locsearch;
+    
+    for (i = 1; i <= n; i++)
+      {
+	p = spline.GetPoint (i*dt);
+	l = lold + Dist (p, pold);
+	while (j < curvepoints.Size() && (l >= curvepoints[j] || i == n))
+	  {
+	    frac = (curvepoints[j]-lold) / (l-lold);
+	    edgelength = i*dt + (frac-1)*dt;
+	    // mark = pold + frac * (p-pold);
+	    mark = spline.GetPoint (edgelength);
+	  
+	    // cout << "mark = " << mark << " =?= " << GetPoint (edgelength) << endl;
+
+	    {
+	      PointIndex pi1 = -1, pi2 = -1;
+	  
+	      Point3d mark3(mark(0), mark(1), 0);
+	      Point3d oldmark3(oldmark(0), oldmark(1), 0);
+
+	      Vec<3> v (1e-4*h, 1e-4*h, 1e-4*h);
+	      searchtree.GetIntersecting (oldmark3 - v, oldmark3 + v, locsearch);
+
+	      for (int k = 0; k < locsearch.Size(); k++)
+		if ( mesh[PointIndex(locsearch[k])].GetLayer() == spline.layer)
+		  pi1 = locsearch[k];
+	      // if (locsearch.Size()) pi1 = locsearch[0];
+	      
+	      searchtree.GetIntersecting (mark3 - v, mark3 + v, locsearch);
+	      for (int k = 0; k < locsearch.Size(); k++)
+		if ( mesh[PointIndex(locsearch[k])].GetLayer() == spline.layer)
+		  pi2 = locsearch[k];
+	      // if (locsearch.Size()) pi2 = locsearch[0];
+
+	      /*	    
+		for (PointIndex pk = PointIndex::BASE; 
+		pk < mesh.GetNP()+PointIndex::BASE; pk++)
+		{
+		if (Dist (mesh[pk], oldmark3) < 1e-4 * h) pi1 = pk;
+		if (Dist (mesh[pk], mark3) < 1e-4 * h) pi2 = pk;
+		}
+	      */
+	    
+	      
+	      //	    cout << "pi1 = " << pi1 << endl;
+	      //	    cout << "pi2 = " << pi2 << endl;
+	    
+	      if (pi1 == -1)
+		{
+		  pi1 = mesh.AddPoint(oldmark3, spline.layer);
+		  searchtree.Insert (oldmark3, pi1);
+		}
+	      if (pi2 == -1)
+		{
+		  pi2 = mesh.AddPoint(mark3, spline.layer);
+		  searchtree.Insert (mark3, pi2);
+		}
+
+	      Segment seg;
+	      seg.edgenr = segnr;
+	      seg.si = spline.bc; // segnr;
+	      seg[0] = pi1;
+	      seg[1] = pi2;
+	      seg.domin = spline.leftdom;
+	      seg.domout = spline.rightdom;
+	      seg.epgeominfo[0].edgenr = segnr;
+	      seg.epgeominfo[0].dist = edgelengthold;
+	      seg.epgeominfo[1].edgenr = segnr;
+	      seg.epgeominfo[1].dist = edgelength;
+	      seg.singedge_left = spline.hpref_left;
+	      seg.singedge_right = spline.hpref_right;
+	      mesh.AddSegment (seg);
+	    }
+	
+	    oldmark = mark;
+	    edgelengthold = edgelength;
+	    j++;
+	  }
+    
+	pold = p;
+	lold = l;
+      }
+  }
+
+
+
+  void SplineGeometry2d :: PartitionBoundary (double h, Mesh & mesh2d)
+  {
+    enum { D = 2 };
+    Box<D> bbox;
+    GetBoundingBox (bbox);
+    double dist = Dist (bbox.PMin(), bbox.PMax());
+    Point<3> pmin;
+    Point<3> pmax;
+  
+    pmin(2) = -dist; pmax(2) = dist;
+    for(int j=0;j<D;j++)
+      {
+	pmin(j) = bbox.PMin()(j);
+	pmax(j) = bbox.PMax()(j);
+      }
+
+    Point3dTree searchtree (pmin, pmax);
+
+    for (int i = 0; i < splines.Size(); i++)
+      for (int side = 0; side <= 1; side++)
+	{
+	  int dom = (side == 0) ? GetSpline(i).leftdom : GetSpline(i).rightdom;
+	  if (dom != 0) GetSpline(i).layer = GetDomainLayer (dom);
+	}
+
+    for (int i = 0; i < splines.Size(); i++)
+      if (GetSpline(i).copyfrom == -1)
+	{
+	  // astrid - set boundary meshsize to  domain meshsize h
+	  // if no domain mesh size is given, the max h value from the bounding box is used
+	  double minimum = min2 ( GetDomainMaxh ( GetSpline(i).leftdom ), GetDomainMaxh ( GetSpline(i).rightdom ) );
+	  double maximum = max2 ( GetDomainMaxh ( GetSpline(i).leftdom ), GetDomainMaxh ( GetSpline(i).rightdom ) );
+	  minimum = min2 ( minimum, h );
+	  maximum = min2 ( maximum, h);
+	  if ( minimum > 0 )
+	    // GetSpline(i).Partition(minimum, elto0, mesh2d, searchtree, i+1);
+	    Partition(GetSpline(i), minimum, elto0, mesh2d, searchtree, i+1);	    
+	  else if ( maximum > 0 )
+	    // GetSpline(i).Partition(maximum, elto0, mesh2d, searchtree, i+1);
+	    Partition(GetSpline(i), maximum, elto0, mesh2d, searchtree, i+1);
+	  else
+	    // GetSpline(i).Partition(h, elto0, mesh2d, searchtree, i+1);
+	    Partition(GetSpline(i), h, elto0, mesh2d, searchtree, i+1);
+	}
+      else
+	{
+	  CopyEdgeMesh (GetSpline(i).copyfrom, i+1, mesh2d, searchtree);
+	}
+  }
+
+
+
+
+
+
+  void SplineGeometry2d :: CopyEdgeMesh (int from, int to, Mesh & mesh, Point3dTree & searchtree)
+  {
+    const int D = 2;
+    int i;
+
+    Array<int, PointIndex::BASE> mappoints (mesh.GetNP());
+    Array<double, PointIndex::BASE> param (mesh.GetNP());
+    mappoints = -1;
+    param = 0;
+
+    Point3d pmin, pmax;
+    mesh.GetBox (pmin, pmax);
+    double diam2 = Dist2(pmin, pmax);
+
+    if (printmessage_importance>0)
+      cout << "copy edge, from = " << from << " to " << to << endl;
+  
+    for (i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	const Segment & seg = mesh.LineSegment(i);
+	if (seg.edgenr == from)
+	  {
+	    mappoints.Elem(seg[0]) = 1;
+	    param.Elem(seg[0]) = seg.epgeominfo[0].dist;
+
+	    mappoints.Elem(seg[1]) = 1;
+	    param.Elem(seg[1]) = seg.epgeominfo[1].dist;
+	  }
+      }
+
+    bool mapped = false;
+    for (i = 1; i <= mappoints.Size(); i++)
+      {
+	if (mappoints.Get(i) != -1)
+	  {
+	    Point<D> newp = splines.Get(to)->GetPoint (param.Get(i));
+	    Point<3> newp3;
+	    for(int j=0; j<min2(D,3); j++)
+	      newp3(j) = newp(j);
+	    for(int j=min2(D,3); j<3; j++)
+	      newp3(j) = 0;
+	  
+	    int npi = -1;
+	  
+	    for (PointIndex pi = PointIndex::BASE; 
+		 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+	      if (Dist2 (mesh.Point(pi), newp3) < 1e-12 * diam2)
+		npi = pi;
+	  
+	    if (npi == -1)
+	      {
+		npi = mesh.AddPoint (newp3);
+		searchtree.Insert (newp3, npi);
+	      }
+
+	    mappoints.Elem(i) = npi;
+
+	    mesh.GetIdentifications().Add (i, npi, to);
+	    mapped = true;
+	  }
+      }
+    if(mapped)
+      mesh.GetIdentifications().SetType(to,Identifications::PERIODIC);
+
+    // copy segments
+    int oldnseg = mesh.GetNSeg();
+    for (i = 1; i <= oldnseg; i++)
+      {
+	const Segment & seg = mesh.LineSegment(i);
+	if (seg.edgenr == from)
+	  {
+	    Segment nseg;
+	    nseg.edgenr = to;
+	    nseg.si = GetSpline(to-1).bc;      // splines.Get(to)->bc;
+	    nseg[0] = mappoints.Get(seg[0]);
+	    nseg[1] = mappoints.Get(seg[1]);
+	    nseg.domin = GetSpline(to-1).leftdom;
+	    nseg.domout = GetSpline(to-1).rightdom;
+	  
+	    nseg.epgeominfo[0].edgenr = to;
+	    nseg.epgeominfo[0].dist = param.Get(seg[0]);
+	    nseg.epgeominfo[1].edgenr = to;
+	    nseg.epgeominfo[1].dist = param.Get(seg[1]);
+	    mesh.AddSegment (nseg);
+	  }
+      }
+  }
+
+
+
+
+
+  void MeshFromSpline2D (SplineGeometry2d & geometry,
+			 Mesh *& mesh, 
+			 MeshingParameters & mp)
+  {
+    PrintMessage (1, "Generate Mesh from spline geometry");
+
+    double h = mp.maxh;
+
+    Box<2> bbox = geometry.GetBoundingBox ();
+
+    if (bbox.Diam() < h) 
+      {
+	h = bbox.Diam();
+	mp.maxh = h;
+      }
+
+    mesh = new Mesh;
+    mesh->SetDimension (2);
+
+    geometry.PartitionBoundary (h, *mesh);
+
+
+    // marks mesh points for hp-refinement
+    for (int i = 0; i < geometry.GetNP(); i++)
+      if (geometry.GetPoint(i).hpref)
+	{
+	  double mindist = 1e99;
+	  PointIndex mpi(0);
+	  Point<2> gp = geometry.GetPoint(i);
+	  Point<3> gp3(gp(0), gp(1), 0);
+	  for (PointIndex pi = PointIndex::BASE; 
+	       pi < mesh->GetNP()+PointIndex::BASE; pi++)
+	    if (Dist2(gp3, (*mesh)[pi]) < mindist)
+	      {
+		mpi = pi;
+		mindist = Dist2(gp3, (*mesh)[pi]);
+	      }
+	  (*mesh)[mpi].Singularity(1.);
+	}
+
+
+    int maxdomnr = 0;
+    for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++)
+      {
+	if ( (*mesh)[si].domin > maxdomnr) maxdomnr = (*mesh)[si].domin;
+	if ( (*mesh)[si].domout > maxdomnr) maxdomnr = (*mesh)[si].domout;
+      }
+
+    mesh->ClearFaceDescriptors();
+    for (int i = 1; i <= maxdomnr; i++)
+      mesh->AddFaceDescriptor (FaceDescriptor (i, 0, 0, i));
+
+    // set Array<string*> bcnames... 
+    // number of bcnames
+    int maxsegmentindex = 0;
+    for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++)
+      {
+	if ( (*mesh)[si].si > maxsegmentindex) maxsegmentindex = (*mesh)[si].si;
+      }
+
+    mesh->SetNBCNames(maxsegmentindex);
+
+    for ( int sindex = 0; sindex < maxsegmentindex; sindex++ )
+      mesh->SetBCName ( sindex, geometry.GetBCName( sindex+1 ) );
+
+    for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++)
+      (*mesh)[si].SetBCName ( (*mesh).GetBCNamePtr( (*mesh)[si].si-1 ) );
+
+    Point3d pmin(bbox.PMin()(0), bbox.PMin()(1), -bbox.Diam());
+    Point3d pmax(bbox.PMax()(0), bbox.PMax()(1), bbox.Diam());
+
+    mesh->SetLocalH (pmin, pmax, mparam.grading);
+    mesh->SetGlobalH (h);
+  
+    mesh->CalcLocalH(mparam.grading);
+
+    int bnp = mesh->GetNP(); // boundary points
+
+    int hquad = mparam.quad;
+
+
+    for (int domnr = 1; domnr <= maxdomnr; domnr++)
+      if (geometry.GetDomainTensorMeshing (domnr))
+        { // tensor product mesh
+          
+          Array<PointIndex, PointIndex::BASE> nextpi(bnp);
+          Array<int, PointIndex::BASE> si1(bnp), si2(bnp);
+          PointIndex firstpi;
+          
+          nextpi = -1;
+          si1 = -1;
+          si2 = -1;
+          for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++)
+            {
+              int p1 = -1, p2 = -2;
+
+              if ( (*mesh)[si].domin == domnr)
+                { p1 = (*mesh)[si][0]; p2 = (*mesh)[si][1]; }
+              if ( (*mesh)[si].domout == domnr)
+                { p1 = (*mesh)[si][1]; p2 = (*mesh)[si][0]; }
+              
+              if (p1 == -1) continue;
+
+              nextpi[p1] = p2;       // counter-clockwise
+              
+              int index = (*mesh)[si].si;
+              if (si1[p1] != index && si2[p1] != index)
+                { si2[p1] = si1[p1]; si1[p1] = index; }
+              if (si1[p2] != index && si2[p2] != index)
+                { si2[p2] = si1[p2]; si1[p2] = index; }
+            }
+
+          PointIndex c1(0), c2, c3, c4;  // 4 corner points
+          int nex = 1, ney = 1;
+
+          for (PointIndex pi = 1; pi <= si2.Size(); pi++)
+            if (si2[pi] != -1)
+              { c1 = pi; break; }      
+
+          for (c2 = nextpi[c1]; si2[c2] == -1; c2 = nextpi[c2], nex++);
+          for (c3 = nextpi[c2]; si2[c3] == -1; c3 = nextpi[c3], ney++);
+          for (c4 = nextpi[c3]; si2[c4] == -1; c4 = nextpi[c4]);
+
+
+
+          Array<PointIndex> pts ( (nex+1) * (ney+1) );   // x ... inner loop
+          pts = -1;
+
+          for (PointIndex pi = c1, i = 0; pi != c2; pi = nextpi[pi], i++)
+            pts[i] = pi;
+          for (PointIndex pi = c2, i = 0; pi != c3; pi = nextpi[pi], i++)
+            pts[(nex+1)*i+nex] = pi;
+          for (PointIndex pi = c3, i = 0; pi != c4; pi = nextpi[pi], i++)
+            pts[(nex+1)*(ney+1)-i-1] = pi;
+          for (PointIndex pi = c4, i = 0; pi != c1; pi = nextpi[pi], i++)
+            pts[(nex+1)*(ney-i)] = pi;
+
+
+          for (PointIndex pix = nextpi[c1], ix = 0; pix != c2; pix = nextpi[pix], ix++)
+            for (PointIndex piy = nextpi[c2], iy = 0; piy != c3; piy = nextpi[piy], iy++)
+              {
+                Point<3> p = (*mesh)[pix] + ( (*mesh)[piy] - (*mesh)[c2] );
+                pts[(nex+1)*(iy+1) + ix+1] = mesh -> AddPoint (p , 1, FIXEDPOINT);
+              }
+
+          for (int i = 0; i < ney; i++)
+            for (int j = 0; j < nex; j++)
+              {
+                Element2d el(QUAD);
+                el[0] = pts[i*(nex+1)+j];
+                el[1] = pts[i*(nex+1)+j+1];
+                el[2] = pts[(i+1)*(nex+1)+j+1];
+                el[3] = pts[(i+1)*(nex+1)+j];
+                el.SetIndex (domnr);
+
+                mesh -> AddSurfaceElement (el);
+              }
+        }
+
+
+
+
+    for (int domnr = 1; domnr <= maxdomnr; domnr++)
+      {
+        if (geometry.GetDomainTensorMeshing (domnr)) continue;
+
+	if ( geometry.GetDomainMaxh ( domnr ) > 0 )
+	  h = geometry.GetDomainMaxh(domnr);
+
+
+	PrintMessage (3, "Meshing domain ", domnr, " / ", maxdomnr);
+
+	int oldnf = mesh->GetNSE();
+
+        mparam.quad = hquad || geometry.GetDomainQuadMeshing (domnr);
+
+	Meshing2 meshing (mparam, Box<3> (pmin, pmax));
+
+	Array<int, PointIndex::BASE> compress(bnp);
+	compress = -1;
+	int cnt = 0;
+	for (PointIndex pi = PointIndex::BASE; pi < bnp+PointIndex::BASE; pi++)
+	  if ( (*mesh)[pi].GetLayer() == geometry.GetDomainLayer(domnr))
+	    {
+	      meshing.AddPoint ( (*mesh)[pi], pi);
+	      cnt++;
+	      compress[pi] = cnt;
+	    }
+
+	PointGeomInfo gi;
+	gi.trignum = 1;
+	for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++)
+	  {
+	    if ( (*mesh)[si].domin == domnr)
+	      {
+		meshing.AddBoundaryElement ( compress[(*mesh)[si][0]], 
+					     compress[(*mesh)[si][1]], gi, gi);
+	      }
+	    if ( (*mesh)[si].domout == domnr)
+	      {
+		meshing.AddBoundaryElement ( compress[(*mesh)[si][1]],
+					     compress[(*mesh)[si][0]], gi, gi);
+		
+	      }
+
+	  }
+
+
+	mparam.checkoverlap = 0;
+	
+	/*
+	if (!mparam.quad)
+	  meshing.Delaunay (*mesh, domnr, mparam);
+	else
+	*/
+	meshing.GenerateMesh (*mesh, mparam, h, domnr);
+
+	for (SurfaceElementIndex sei = oldnf; sei < mesh->GetNSE(); sei++)
+	  (*mesh)[sei].SetIndex (domnr);
+
+
+	// astrid
+	char * material;
+	geometry.GetMaterial( domnr, material );
+	if ( material )
+	  {
+	    (*mesh).SetMaterial ( domnr,  material );
+	  }
+
+      }
+
+    mparam.quad = hquad;
+
+
+
+    int hsteps = mp.optsteps2d;
+
+    mp.optimize2d = "smcm"; 
+    mp.optsteps2d = hsteps/2;
+    Optimize2d (*mesh, mp);
+
+    mp.optimize2d = "Smcm"; 
+    mp.optsteps2d = (hsteps+1)/2;
+    Optimize2d (*mesh, mp);
+
+    mp.optsteps2d = hsteps;
+
+    mesh->Compress();
+    mesh -> SetNextMajorTimeStamp();
+
+
+    extern DLL_HEADER void Render();
+    Render();
+  }
+
+
+
+
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/geom2d/geom2dmesh.cpp b/contrib/Netgen/libsrc/geom2d/geom2dmesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..87472d21c93c0568a61ad71f599f96a6c7441caf
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/geom2dmesh.cpp
@@ -0,0 +1,82 @@
+#include <meshing.hpp>
+#include <geometry2d.hpp>
+
+namespace netgen
+{
+
+  Refinement2d :: Refinement2d (const SplineGeometry2d & ageometry)
+    : Refinement(), geometry(ageometry)
+  {
+    ;
+  }
+
+  Refinement2d :: ~Refinement2d ()
+  {
+    ;
+  }
+  
+
+  void Refinement2d :: 
+  PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+		int surfi, 
+		const PointGeomInfo & gi1, 
+		const PointGeomInfo & gi2,
+		Point<3> & newp, PointGeomInfo & newgi) const
+  {
+    newp = p1+secpoint*(p2-p1);
+    newgi.trignum = 1;
+  }
+
+
+
+  void Refinement2d :: 
+  PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint, 
+		int surfi1, int surfi2, 
+		const EdgePointGeomInfo & ap1, 
+		const EdgePointGeomInfo & ap2,
+		Point<3> & newp, EdgePointGeomInfo & newgi) const
+  {
+    Point<2> p2d;
+  
+    p2d = geometry.GetSplines().Get(ap1.edgenr) -> 
+      GetPoint (((1-secpoint)*ap1.dist+secpoint*ap2.dist));
+  
+    //  (*testout) << "refine 2d line, ap1.dist, ap2.dist = " << ap1.dist << ", " << ap2.dist << endl;
+    //  (*testout) << "p1, p2 = " << p1 << p2 << ", newp = " << p2d << endl;
+
+    newp = Point3d (p2d(0), p2d(1), 0);
+    newgi.edgenr = ap1.edgenr;
+    newgi.dist = ((1-secpoint)*ap1.dist+secpoint*ap2.dist);
+  };
+
+
+
+  Vec<3> Refinement2d :: GetTangent (const Point<3> & p, int surfi1, int surfi2,
+                                     const EdgePointGeomInfo & ap1) const
+  {
+    Vec<2> t2d = geometry.GetSplines().Get(ap1.edgenr) -> GetTangent(ap1.dist);
+    return Vec<3> (t2d(0), t2d(1), 0);
+  }
+
+  Vec<3> Refinement2d :: GetNormal (const Point<3> & p, int surfi1, 
+                                    const PointGeomInfo & gi) const
+  {
+    return Vec<3> (0,0,1);
+  }
+
+
+  void Refinement2d :: ProjectToSurface (Point<3> & p, int surfi, const PointGeomInfo & /* gi */) const
+  {
+    p(2) = 0;
+  }
+
+
+  void Refinement2d :: ProjectToEdge (Point<3> & p, int surfi1, int surfi2, 
+                                      const EdgePointGeomInfo & egi) const
+  {
+    Point<2> p2d (p(0), p(1)), pp;
+    double t;
+    geometry.GetSplines().Get(egi.edgenr) -> Project (p2d, pp, t);
+    p = Point<3> (pp(0), pp(1), 0);
+  }
+}
diff --git a/contrib/Netgen/libsrc/geom2d/geom2dmesh.hpp b/contrib/Netgen/libsrc/geom2d/geom2dmesh.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9b72216df3bf590b3e51f4e8e0210416d7990969
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/geom2dmesh.hpp
@@ -0,0 +1,52 @@
+#ifndef FILE_GEOM2DMESH
+#define FILE_GEOM2DMESH
+
+/**************************************************************************/
+/* File:   geom2dmesh.hh                                                  */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   22. Jan. 01                                                    */
+/**************************************************************************/
+
+
+namespace netgen
+{
+
+  class Refinement2d : public Refinement
+  {
+    const class SplineGeometry2d & geometry;
+
+  public:
+    Refinement2d (const class SplineGeometry2d & ageometry);
+    virtual ~Refinement2d ();
+  
+    virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+			       int surfi, 
+			       const PointGeomInfo & gi1, 
+			       const PointGeomInfo & gi2,
+			       Point<3> & newp, PointGeomInfo & newgi) const;
+
+    virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+			       int surfi1, int surfi2, 
+			       const EdgePointGeomInfo & ap1, 
+			       const EdgePointGeomInfo & ap2,
+			       Point<3> & newp, EdgePointGeomInfo & newgi) const;
+
+
+    virtual Vec<3> GetTangent (const Point<3> & p, int surfi1, int surfi2,
+			       const EdgePointGeomInfo & ap1) const;
+
+    virtual Vec<3> GetNormal (const Point<3> & p, int surfi1, 
+			      const PointGeomInfo & gi) const;
+
+    virtual void ProjectToSurface (Point<3> & p, int surfi, const PointGeomInfo & /* gi */) const;
+
+    virtual void ProjectToEdge (Point<3> & p, int surfi1, int surfi2, 
+				const EdgePointGeomInfo & egi) const;			     
+  };
+
+
+}
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/geom2d/geom2dpkg.cpp b/contrib/Netgen/libsrc/geom2d/geom2dpkg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1456c6ac1d00a24801bed8f1393a37f20d94b43d
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/geom2dpkg.cpp
@@ -0,0 +1,72 @@
+#include <meshing.hpp>
+#include <geometry2d.hpp>
+#include <visual.hpp>
+
+#include "vsgeom2d.hpp"
+
+// extern "C" int Ng_CSG_Init (Tcl_Interp * interp);
+
+namespace netgen
+{
+	
+
+  extern DLL_HEADER NetgenGeometry * ng_geometry;
+  static VisualSceneGeometry2d vsgeom2d;
+
+
+
+  class SplineGeometryRegister : public GeometryRegister
+  {
+  public:
+    virtual NetgenGeometry * Load (string filename) const;
+    virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const;
+  };
+
+
+  NetgenGeometry *  SplineGeometryRegister :: Load (string filename) const
+  {
+    const char * cfilename = filename.c_str();
+    if (strcmp (&cfilename[strlen(cfilename)-4], "in2d") == 0)
+      {
+	PrintMessage (1, "Load 2D-Spline geometry file ", cfilename);
+	
+
+	ifstream infile(cfilename);
+
+	SplineGeometry2d * hgeom = new SplineGeometry2d();
+	hgeom -> Load (cfilename);
+	return hgeom;
+      }
+    
+    return NULL;
+  }
+
+
+
+  VisualScene * SplineGeometryRegister :: GetVisualScene (const NetgenGeometry * geom) const
+  {
+    SplineGeometry2d * geometry = dynamic_cast<SplineGeometry2d*> (ng_geometry);
+    if (geometry)
+      {
+	vsgeom2d.SetGeometry (geometry);
+	return &vsgeom2d;
+      }
+    return NULL;
+  }
+
+
+}
+
+
+using namespace netgen;
+#ifdef WIN32
+extern "C" __declspec(dllexport) int Ng_geom2d_Init (Tcl_Interp * interp);
+#else
+extern "C" int Ng_geom2d_Init (Tcl_Interp * interp);
+#endif
+
+int Ng_geom2d_Init (Tcl_Interp * interp)
+{
+  geometryregister.Append (new SplineGeometryRegister);
+  return TCL_OK;
+}
diff --git a/contrib/Netgen/libsrc/geom2d/geometry2d.cpp b/contrib/Netgen/libsrc/geom2d/geometry2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8b10c5ebce4834479f0fcc142a6967242417e3f
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/geometry2d.cpp
@@ -0,0 +1,938 @@
+/*
+
+2d Spline curve for Mesh generator
+
+*/
+
+#include <meshing.hpp>
+#include <geometry2d.hpp>
+
+namespace netgen
+{
+
+
+
+  SplineGeometry2d :: ~SplineGeometry2d()
+  {
+    for ( int i = 0; i < bcnames.Size(); i++ )
+      delete bcnames[i];
+    for (int i=0; i<materials.Size(); i++)
+      delete materials[i];
+  }
+
+
+  void SplineGeometry2d :: Load (const char * filename)
+  {
+
+    ifstream infile;
+    Point<2> x;
+    char buf[50];
+
+
+    infile.open (filename);
+  
+    if ( ! infile.good() )
+      throw NgException(string ("Input file '") + 
+			string (filename) +
+			string ("' not available!"));
+
+    TestComment ( infile );
+  
+    infile >> buf;   // file recognition
+
+    tensormeshing.SetSize(0);
+    quadmeshing.SetSize(0);
+
+    TestComment ( infile );
+    if ( strcmp (buf, "splinecurves2dnew") == 0 )
+      {
+	LoadDataNew ( infile );
+      }
+    else if ( strcmp (buf, "splinecurves2dv2") == 0 )
+      {
+	LoadDataV2 ( infile );
+      }
+    else
+      {
+	LoadData(infile );
+      }
+    infile.close();
+  }
+
+
+
+  // herbert: fixed TestComment
+  void SplineGeometry2d :: TestComment ( ifstream & infile )
+  {
+    bool comment = true;
+    char ch;
+    while ( comment == true && !infile.eof() ) {
+      infile.get(ch);
+      if ( ch == '#' ) { // skip comments
+	while (  ch != '\n' && !infile.eof() ) {
+	  infile.get(ch);
+	}
+      }
+      else if ( ch == '\n' )  { // skip empty lines
+	;
+      }
+      else if ( isspace(ch) ) { // skip whitespaces
+	; 
+      }
+      else { // end of comment
+	infile.putback(ch);
+	comment = false;
+      }
+    }
+    return;
+  }
+
+
+
+
+  void SplineGeometry2d :: LoadData ( ifstream & infile )
+  {      
+    enum { D = 2 };
+
+    int nump, numseg, leftdom, rightdom;
+    Point<D> x;
+    int hi1, hi2, hi3;
+    double hd;
+    char buf[50], ch;
+
+    materials.SetSize(0);
+    maxh.SetSize(0);
+    infile >> elto0;
+
+    TestComment ( infile );
+
+    infile >> nump;
+    for (int i = 0; i < nump; i++)
+      {
+	TestComment ( infile );
+	for(int j=0; j<D; j++)
+	  infile >> x(j);
+	infile >> hd;
+
+	Flags flags;
+
+	ch = 'a';
+	// infile >> ch;
+	do {
+	  infile.get (ch);
+	} while (isspace(ch) && ch != '\n');
+	while (ch == '-')
+	  {
+	    char flag[100];
+	    flag[0]='-';
+	    infile >> (flag+1);
+	    flags.SetCommandLineFlag (flag);
+	    ch = 'a';
+	    do {
+	      infile.get (ch);
+	    } while (isspace(ch) && ch != '\n');
+	  }
+    
+	if (infile.good())
+	  infile.putback (ch);
+
+	geompoints.Append (GeomPoint<D>(x, hd));
+	geompoints.Last().hpref = flags.GetDefineFlag ("hpref");
+	geompoints.Last().hmax = 1e99;
+      }
+
+    PrintMessage (3, nump, " points loaded");
+    TestComment ( infile );
+
+    infile >> numseg;
+    bcnames.SetSize(numseg);
+    for ( int i = 0; i < numseg; i++ )
+      bcnames[i] = 0; // "default";
+
+    SplineSeg<D> * spline = 0;
+
+    PrintMessage (3, numseg, " segments loaded");
+    for (int i = 0; i < numseg; i++)
+      {
+	TestComment ( infile );
+      
+	infile >> leftdom >> rightdom;
+
+	// cout << "add spline " << i << ", left = " << leftdom << ", right = " << rightdom << endl;
+      
+	infile >> buf;
+	// type of spline segement
+	if (strcmp (buf, "2") == 0)
+	  { // a line
+	    infile >> hi1 >> hi2;
+	    spline = new LineSeg<D>(geompoints[hi1-1],
+				    geompoints[hi2-1]);
+	  }
+	else if (strcmp (buf, "3") == 0)
+	  { // a rational spline
+	    infile >> hi1 >> hi2 >> hi3;
+	    spline = new SplineSeg3<D> (geompoints[hi1-1],
+					geompoints[hi2-1],
+					geompoints[hi3-1]);
+	  }
+	else if (strcmp (buf, "4") == 0)
+	  { // an arc
+	    infile >> hi1 >> hi2 >> hi3;
+	    spline = new CircleSeg<D> (geompoints[hi1-1],
+				       geompoints[hi2-1],
+				       geompoints[hi3-1]);
+	    // 	  break;
+	  }
+	else if (strcmp (buf, "discretepoints") == 0)
+	  {
+	    int npts;
+	    infile >> npts;
+	    Array< Point<D> > pts(npts);
+	    for (int j = 0; j < npts; j++)
+	      for(int k=0; k<D; k++)
+		infile >> pts[j](k);
+
+	    spline = new DiscretePointsSeg<D> (pts);
+	  }
+    
+
+	SplineSegExt * spex = new SplineSegExt (*spline);
+	
+	infile >> spex->reffak;
+	spex -> leftdom = leftdom;
+	spex -> rightdom = rightdom;
+	spex -> hmax = 1e99;
+	splines.Append (spex);
+
+
+	Flags flags;
+	ch = 'a';
+	infile >> ch;
+	while (ch == '-')
+	  {
+	    char flag[100];
+	    flag[0]='-';
+	    infile >> (flag+1);
+	    flags.SetCommandLineFlag (flag);
+	    ch = 'a';
+	    infile >> ch;
+	  }
+    
+	if (infile.good())
+	  infile.putback (ch);
+    
+	spex->bc = int (flags.GetNumFlag ("bc", i+1));
+	spex->hpref_left = int (flags.GetDefineFlag ("hpref")) || 
+	  int (flags.GetDefineFlag ("hprefleft"));
+	spex->hpref_right = int (flags.GetDefineFlag ("hpref")) || 
+	  int (flags.GetDefineFlag ("hprefright"));
+	spex->copyfrom = int (flags.GetNumFlag ("copy", -1));
+	if ( flags.StringFlagDefined("bcname") )
+	  {
+	    int mybc = spex->bc-1;
+	    delete bcnames[mybc];
+	    bcnames[mybc] = new string (flags.GetStringFlag("bcname","") );
+	  }
+      }
+  }
+
+
+
+
+  void SplineGeometry2d :: LoadDataNew ( ifstream & infile )
+  {
+    enum { D = 2 };
+    int nump, numseg, leftdom, rightdom;
+    Point<D> x;
+    int hi1, hi2, hi3;
+    double hd;
+    char buf[50], ch;
+    int pointnr;
+
+
+    TestComment ( infile );
+    infile >> elto0;
+    TestComment ( infile );
+      
+    infile >> nump;
+    geompoints.SetSize(nump);
+      
+    for (int i = 0; i < nump; i++)
+      {
+	TestComment ( infile );
+	infile >> pointnr;
+	if ( pointnr > nump )
+	  {
+	    throw NgException(string ("Point number greater than total number of points") );
+	  }
+	for(int j=0; j<D; j++)
+	  infile >> x(j);
+
+
+	// hd is now optional, default 1
+	//  infile >> hd;
+	hd = 1;
+
+	Flags flags;
+
+
+	// get flags, 
+	ch = 'a';
+	// infile >> ch;
+	do 
+	  {
+
+	    infile.get (ch);
+	    // if another int-value, set refinement flag to this value
+	    // (corresponding to old files)
+	    if ( int (ch) >= 48 && int(ch) <= 57 )
+	      {
+		infile.putback(ch);
+		infile >> hd;
+		infile.get(ch);
+	      }
+	  } 
+	while (isspace(ch) && ch != '\n');
+	while (ch == '-')
+	  {
+	    char flag[100];
+	    flag[0]='-';
+	    infile >> (flag+1);
+	    flags.SetCommandLineFlag (flag);
+	    ch = 'a';
+	    do {
+	      infile.get (ch);
+	    } while (isspace(ch) && ch != '\n');
+	  }
+    
+	if (infile.good())
+	  infile.putback (ch);
+
+	if ( hd == 1 )
+	  hd = flags.GetNumFlag ( "ref", 1.0);
+	//       geompoints.Append (GeomPoint<D>(x, hd));
+	geompoints[pointnr-1] = GeomPoint<D>(x, hd);
+	geompoints[pointnr-1].hpref = flags.GetDefineFlag ("hpref");
+      }
+
+    TestComment ( infile );
+
+    infile >> numseg;
+    bcnames.SetSize(numseg);
+    for ( int i = 0; i < numseg; i++ )
+      bcnames[i] = 0;//new"default";
+
+    SplineSeg<D> * spline = 0;
+    for (int i = 0; i < numseg; i++)
+      {
+	TestComment ( infile );
+      
+	infile >> leftdom >> rightdom;
+
+	// cout << "add spline " << i << ", left = " << leftdom << endl;
+
+	infile >> buf;
+	// type of spline segement
+	if (strcmp (buf, "2") == 0)
+	  { // a line
+	    infile >> hi1 >> hi2;
+	    spline = new LineSeg<D> (geompoints[hi1-1],
+				     geompoints[hi2-1]);
+	  }
+	else if (strcmp (buf, "3") == 0)
+	  { // a rational spline
+	    infile >> hi1 >> hi2 >> hi3;
+	    spline = new SplineSeg3<D> (geompoints[hi1-1],
+					geompoints[hi2-1],
+					geompoints[hi3-1]);
+	  }
+	else if (strcmp (buf, "4") == 0)
+	  { // an arc
+	    infile >> hi1 >> hi2 >> hi3;
+	    spline = new CircleSeg<D> (geompoints[hi1-1],
+				       geompoints[hi2-1],
+				       geompoints[hi3-1]);
+	    // 	  break;
+	  }
+	else if (strcmp (buf, "discretepoints") == 0)
+	  {
+	    int npts;
+	    infile >> npts;
+	    Array< Point<D> > pts(npts);
+	    for (int j = 0; j < npts; j++)
+	      for(int k=0; k<D; k++)
+		infile >> pts[j](k);
+
+	    spline = new DiscretePointsSeg<D> (pts);
+	  }
+    
+	//      infile >> spline->reffak;
+
+	SplineSegExt * spex = new SplineSegExt (*spline);
+
+	spex -> leftdom = leftdom;
+	spex -> rightdom = rightdom;
+	splines.Append (spex);
+
+	// hd is now optional, default 1
+	//  infile >> hd;
+	hd = 1;
+	infile >> ch;
+      
+	// get refinement parameter, if it is there
+	// infile.get (ch);
+	// if another int-value, set refinement flag to this value
+	// (corresponding to old files)
+	if ( int (ch) >= 48 && int(ch) <= 57 )
+	  {
+	    infile.putback(ch);
+	    infile >> hd;
+	    infile >> ch ;
+	  }
+      
+	Flags flags;
+	while (ch == '-')
+	  {
+	    char flag[100];
+	    flag[0]='-';
+	    infile >> (flag+1);
+	    flags.SetCommandLineFlag (flag);
+	    ch = 'a';
+	    infile >> ch;
+	  }
+    
+	if (infile.good())
+	  infile.putback (ch);
+    
+	spex->bc = int (flags.GetNumFlag ("bc", i+1));
+	spex->hpref_left = int (flags.GetDefineFlag ("hpref")) || 
+	  int (flags.GetDefineFlag ("hprefleft"));
+	spex->hpref_right = int (flags.GetDefineFlag ("hpref")) || 
+	  int (flags.GetDefineFlag ("hprefright"));
+	spex->copyfrom = int (flags.GetNumFlag ("copy", -1));
+	spex->reffak = flags.GetNumFlag ("ref", 1 );
+	spex->hmax = flags.GetNumFlag ("maxh", 1e99 );
+
+	if ( flags.StringFlagDefined("bcname") )
+	  {
+	    int mybc = spex->bc-1;
+	    if ( bcnames[mybc] ) delete bcnames[mybc];
+	    bcnames[mybc] = new string (flags.GetStringFlag("bcname","") );
+	  }
+
+	if ( hd != 1 )
+	  spex->reffak = hd;
+      }
+    if ( !infile.good() )
+      return;
+    TestComment ( infile );
+    int numdomains;
+    int domainnr;
+    char material[100];
+
+    if ( !infile.good() ) 
+      return;
+
+    infile >> numdomains;
+    materials.SetSize(numdomains) ;
+    maxh.SetSize ( numdomains ) ;
+    for ( int i = 0; i < numdomains; i++)
+      maxh[i] = 1000;
+
+    TestComment ( infile );
+
+    for ( int i=0; i<numdomains; i++)
+      materials [ i ] = new char (100);
+
+    for ( int i=0; i<numdomains && infile.good(); i++)
+      {
+	TestComment ( infile );
+	infile >> domainnr;
+	infile >> material;
+	strcpy(materials[domainnr-1], material);
+
+	Flags flags;
+	ch = 'a';
+	infile >> ch;
+	while (ch == '-')
+	  {
+	    char flag[100];
+	    flag[0]='-';
+	    infile >> (flag+1);
+	    flags.SetCommandLineFlag (flag);
+	    ch = 'a';
+	    infile >> ch;
+	  }
+    
+	if (infile.good())
+	  infile.putback (ch);
+	 
+	maxh[domainnr-1] = flags.GetNumFlag ( "maxh", 1000);
+      }
+    return;
+  }
+
+
+
+
+  void SplineGeometry2d :: LoadDataV2 ( ifstream & infile )
+  { 
+    enum { D = 2 };
+    // new parser by Astrid Sinwel
+    
+    PrintMessage (1, "Load 2D Geometry V2");
+    int nump, leftdom, rightdom;
+    Point<D> x;
+    int hi1, hi2, hi3;
+    double hd;
+    char buf[50], ch;
+    int pointnr;
+
+    string keyword;
+
+    Array < GeomPoint<D> > infilepoints (0);
+    Array <int> pointnrs (0);
+    nump = 0;
+    int numdomains = 0;
+
+
+    TestComment ( infile );
+    // refinement factor
+    infile >> elto0;
+    TestComment ( infile );
+      
+
+    // test if next ch is a letter, i.e. new keyword starts
+    bool ischar = false;
+
+    while ( infile.good() )
+      {
+	infile >> keyword;
+
+	ischar = false;
+
+	if ( keyword == "points" )
+	  {
+	    PrintMessage (3, "load points");
+	    infile.get(ch);
+	    infile.putback(ch);
+
+	    // test if ch is a letter
+	    if ( int(ch) >= 65 && int(ch) <=90 )
+	      ischar = true;
+	    if ( int(ch) >= 97 && int(ch) <= 122 )
+	      ischar = true;
+
+	    while ( ! ischar )
+	      {
+		TestComment ( infile );
+		infile >> pointnr;
+		// pointnrs 1-based
+		if ( pointnr > nump ) nump = pointnr; 
+		pointnrs.Append(pointnr);
+	      
+		for(int j=0; j<D; j++)
+		  infile >> x(j);
+		// hd is now optional, default 1
+		//  infile >> hd;
+		hd = 1;
+	      
+		Flags flags;
+	      
+	      
+		// get flags, 
+		ch = 'a';
+		// infile >> ch;
+		do 
+		  {
+		    infile.get (ch);
+		    // if another int-value, set refinement flag to this value
+		    // (corresponding to old files)
+		    if ( int (ch) >= 48 && int(ch) <= 57 )
+		      {
+			infile.putback(ch);
+			infile >> hd;
+			infile.get(ch);
+		      }
+		  } 
+		while (isspace(ch) && ch != '\n');
+		while (ch == '-')
+		  {
+		    char flag[100];
+		    flag[0]='-';
+		    infile >> (flag+1);
+		    flags.SetCommandLineFlag (flag);
+		    ch = 'a';
+		    do {
+		      infile.get (ch);
+		    } while (isspace(ch) && ch != '\n');
+		  }
+		if (infile.good())
+		  infile.putback (ch);
+	      
+		if ( hd == 1 )
+		  hd = flags.GetNumFlag ( "ref", 1.0);
+		//       geompoints.Append (GeomPoint<D>(x, hd));
+
+		infilepoints.Append ( GeomPoint<D>(x, hd) );
+		infilepoints.Last().hpref = flags.GetDefineFlag ("hpref");
+		infilepoints.Last().hmax = flags.GetNumFlag ("maxh", 1e99);
+
+		TestComment(infile);
+		infile.get(ch);
+		infile.putback(ch);
+
+		// test if letter
+		if ( int(ch) >= 65 && int(ch) <=90 )
+		  ischar = true;
+		if ( int(ch) >= 97 && int(ch) <= 122 )
+		  ischar = true;
+	      }
+
+	    //	  infile.putback (ch);
+
+	    geompoints.SetSize(nump);
+	    for ( int i = 0; i < nump; i++ )
+	      {
+		geompoints[pointnrs[i] - 1] = infilepoints[i];
+		geompoints[pointnrs[i] - 1].hpref = infilepoints[i].hpref; 
+	      }
+	    TestComment(infile);
+	  }
+
+	else if ( keyword == "segments" )
+	  {
+	    PrintMessage (3, "load segments");
+
+	    bcnames.SetSize(0);
+	    infile.get(ch);
+	    infile.putback(ch);
+	    int i = 0;
+
+	    // test if ch is a letter
+	    if ( int(ch) >= 65 && int(ch) <=90 )
+	      ischar = true;
+	    if ( int(ch) >= 97 && int(ch) <= 122 )
+	      ischar = true;
+
+	    while ( !ischar ) //ch != 'p' && ch != 'm' )
+	      {
+		i++;
+		TestComment ( infile );
+
+		SplineSeg<D> * spline = 0;
+		TestComment ( infile );
+		  
+		infile >> leftdom >> rightdom;
+	      
+		if ( leftdom > numdomains ) numdomains = leftdom;
+		if ( rightdom > numdomains ) numdomains = rightdom;
+
+	      
+		infile >> buf;
+		// type of spline segement
+		if (strcmp (buf, "2") == 0)
+		  { // a line
+		    infile >> hi1 >> hi2;
+		    spline = new LineSeg<D>(geompoints[hi1-1],
+					    geompoints[hi2-1]);
+		  }
+		else if (strcmp (buf, "3") == 0)
+		  { // a rational spline
+		    infile >> hi1 >> hi2 >> hi3;
+		    spline = new SplineSeg3<D> (geompoints[hi1-1],
+						geompoints[hi2-1],
+						geompoints[hi3-1]);
+		  }
+		else if (strcmp (buf, "4") == 0)
+		  { // an arc
+		    infile >> hi1 >> hi2 >> hi3;
+		    spline = new CircleSeg<D> (geompoints[hi1-1],
+					       geompoints[hi2-1],
+					       geompoints[hi3-1]);
+		  }
+		else if (strcmp (buf, "discretepoints") == 0)
+		  {
+		    int npts;
+		    infile >> npts;
+		    Array< Point<D> > pts(npts);
+		    for (int j = 0; j < npts; j++)
+		      for(int k=0; k<D; k++)
+			infile >> pts[j](k);
+		  
+		    spline = new DiscretePointsSeg<D> (pts);
+		  }
+		else if (strcmp (buf, "bsplinepoints") == 0)
+		  {
+		    int npts,order;
+		    infile >> npts;    
+		    infile >> order;
+		    Array< Point<D> > pts(npts);
+		    for (int j = 0; j < npts; j++)
+		      for(int k=0; k<D; k++)
+			infile >> pts[j](k);	    		    
+		    if(order<2)		      
+			cerr<<"Minimum order of 2 is required!!"<<endl;
+		    else if(order==2)
+		      spline = new BSplineSeg<D,2> (pts);
+		      else if(order==3)
+			spline = new BSplineSeg<D,3> (pts);
+		      else if(order==4)
+			spline = new BSplineSeg<D,4> (pts);
+		      else if(order>4)		      
+			cerr<<"Maximum allowed order is 4!!"<<endl;
+		  }
+	      
+		//      infile >> spline->reffak;
+		SplineSegExt * spex = new SplineSegExt (*spline);
+
+		spex -> leftdom = leftdom;
+		spex -> rightdom = rightdom;
+		splines.Append (spex);
+	      
+	      
+		// hd is now optional, default 1
+		//  infile >> hd;
+		hd = 1;
+		infile >> ch;
+	      
+		// get refinement parameter, if it is there
+		//infile.get (ch);
+		// if another int-value, set refinement flag to this value
+		// (corresponding to old files)
+
+		if ( int (ch) >= 48 && int(ch) <= 57 )
+		  {
+		    infile.putback(ch);
+		    infile >> hd;
+		    infile >> ch ;
+		  }
+
+		// get flags, 
+		Flags flags;
+		while (ch == '-')
+		  {
+		    char flag[100];
+		    flag[0]='-';
+		    infile >> (flag+1);
+		    flags.SetCommandLineFlag (flag);
+		    ch = 'a';
+		    infile >> ch;
+		  }
+	      
+		if (infile.good())
+		  infile.putback (ch);
+	      
+		spex->bc = int (flags.GetNumFlag ("bc", i+1));
+		spex->hpref_left = int (flags.GetDefineFlag ("hpref")) || 
+		  int (flags.GetDefineFlag ("hprefleft"));
+		spex->hpref_right = int (flags.GetDefineFlag ("hpref")) || 
+		  int (flags.GetDefineFlag ("hprefright"));
+		spex->copyfrom = int (flags.GetNumFlag ("copy", -1));
+		spex->reffak = flags.GetNumFlag ("ref", 1 );
+		spex->hmax = flags.GetNumFlag ("maxh", 1e99 );
+		if ( hd != 1 ) spex->reffak = hd;
+
+		if ( flags.StringFlagDefined("bcname") )
+		  {
+		    int mybc = spex->bc-1;
+		    for ( int ii = bcnames.Size(); ii <= mybc; ii++ )
+		      bcnames.Append ( new string ("default"));
+		    if ( bcnames[mybc] ) delete bcnames[mybc];
+		    bcnames[mybc] = new string (flags.GetStringFlag("bcname","") );
+		  }
+
+		TestComment(infile);
+		infile.get(ch);
+		infile.putback(ch);
+
+		// test if ch is a letter
+		if ( int(ch) >= 65 && int(ch) <=90 )
+		  ischar = true;
+		if ( int(ch) >= 97 && int(ch) <= 122 )
+		  ischar = true;
+
+	      }
+	  
+	    infile.get(ch);
+	    infile.putback(ch);
+	
+
+	  }
+	else if ( keyword == "materials" )
+	  {
+	    TestComment ( infile );
+	    int domainnr;
+	    char material[100];
+	  
+	    if ( !infile.good() ) 
+	      return;
+	  
+	    materials.SetSize(numdomains) ;
+	    maxh.SetSize ( numdomains ) ;
+	    for ( int i = 0; i < numdomains; i++)
+	      maxh[i] = 1000;
+	    quadmeshing.SetSize ( numdomains );
+	    quadmeshing = false;
+	    tensormeshing.SetSize ( numdomains );
+	    tensormeshing = false;
+	    layer.SetSize ( numdomains );
+	    layer = 1;
+
+	  
+	    TestComment ( infile );
+	  
+	    for ( int i=0; i<numdomains; i++)
+	      materials [ i ] = new char[100];
+	  
+	    for ( int i=0; i<numdomains && infile.good(); i++)
+	      {
+		TestComment ( infile );
+		infile >> domainnr;
+		infile >> material;
+
+		strcpy (materials[domainnr-1], material);
+	      
+		Flags flags;
+		ch = 'a';
+		infile >> ch;
+		while (ch == '-')
+		  {
+		    char flag[100];
+		    flag[0]='-';
+		    infile >> (flag+1);
+		    flags.SetCommandLineFlag (flag);
+		    ch = 'a';
+		    infile >> ch;
+		  }
+	      
+		if (infile.good())
+		  infile.putback (ch);
+	      
+		maxh[domainnr-1] = flags.GetNumFlag ( "maxh", 1000);
+		if (flags.GetDefineFlag("quad")) quadmeshing[domainnr-1] = true;
+		if (flags.GetDefineFlag("tensor")) tensormeshing[domainnr-1] = true;
+		layer[domainnr-1] = int(flags.GetNumFlag ("layer", 1));
+	      }
+	  }
+      }
+    return;
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+  void CalcPartition (double l, double h, double h1, double h2,
+		      double hcurve, double elto0, Array<double> & points)
+  {
+    // cout << "calcpart, h = " << h << ", h1 = " << h1 << ", h2 = " << h2 << ", hcurve = " << hcurve << endl;
+    int i, j, n, nel;
+    double sum, t, dt, fun, fperel, oldf, f;
+
+    n = 1000;
+
+    points.SetSize (0);
+
+    sum = 0;
+    dt = l / n;
+    t = 0.5 * dt;
+    for (i = 1; i <= n; i++)
+      {
+	fun = min3 (hcurve, t/elto0 + h1, (l-t)/elto0 + h2);
+	sum += dt / fun;
+	t += dt;
+      }
+
+    nel = int (sum+1);
+    fperel = sum / nel;
+
+    points.Append (0);
+
+    i = 1;
+    oldf = 0;
+    t = 0.5 * dt;
+    for (j = 1; j <= n && i < nel; j++)
+      {
+	fun = min3 (hcurve, t/elto0 + h1, (l-t)/elto0 + h2);
+
+	f = oldf + dt / fun;
+
+	while (f > i * fperel && i < nel)
+	  {
+	    points.Append ( (l/n) * (j-1 +  (i * fperel - oldf) / (f - oldf)) );
+	    i++;
+	  }
+	oldf = f;
+	t += dt;
+      }
+    points.Append (l);
+  }
+
+
+
+  string SplineGeometry2d :: GetBCName( const int  bcnr ) const
+  {
+    if ( bcnames.Size() >= bcnr)
+      if (bcnames[bcnr-1] )
+	return *bcnames[bcnr-1];
+    return "default";
+  }
+
+  string * SplineGeometry2d :: BCNamePtr( const int bcnr ) 
+  {
+    if ( bcnr > bcnames.Size() )
+      return 0;
+    else
+      return bcnames[bcnr-1];
+  }
+
+
+
+
+
+  void SplineGeometry2d :: GetMaterial( const int  domnr, char* & material )
+  {
+    if ( materials.Size() >= domnr)
+      material =  materials[domnr-1];
+    else 
+      material = 0;
+  }
+
+
+  double SplineGeometry2d :: GetDomainMaxh( const int  domnr )
+  {
+    if ( maxh.Size() >= domnr  && domnr > 0)
+      return maxh[domnr-1];
+    else
+      return -1;
+  }
+
+
+
+  extern void MeshFromSpline2D (SplineGeometry2d & geometry,
+				Mesh *& mesh, 
+				MeshingParameters & mp);
+
+
+  int SplineGeometry2d :: GenerateMesh (Mesh*& mesh, MeshingParameters & mparam,
+					int perfstepsstart, int perfstepsend)
+  {
+    MeshFromSpline2D (*this, mesh, mparam);
+    return 0;
+  }
+
+
+  Refinement & SplineGeometry2d :: GetRefinement () const
+  {
+    return * new Refinement2d (*this);
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/geom2d/geometry2d.hpp b/contrib/Netgen/libsrc/geom2d/geometry2d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef0bd015f5675af43de5a27e636129778109adaf
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/geometry2d.hpp
@@ -0,0 +1,184 @@
+#ifndef FILE_GEOMETRY2D
+#define FILE_GEOMETRY2D
+
+/* *************************************************************************/
+/* File:   geometry2d.hpp                                                  */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+
+
+// #include "../gprim/spline.hpp"
+// #include "../gprim/splinegeometry.hpp"
+#include "geom2dmesh.hpp"
+
+namespace netgen
+{
+
+  class SplineSegExt : public SplineSeg<2>
+  {
+  public:
+    const SplineSeg<2> & seg;
+    
+    /// left domain
+    int leftdom;
+    /// right domain
+    int rightdom;
+    /// refinement at line
+    double reffak;
+    /// maximal h;
+    double hmax;
+    /// boundary condition number
+    int bc;
+    /// copy spline mesh from other spline (-1.. do not copy)
+    int copyfrom;
+    /// perfrom anisotropic refinement (hp-refinement) to edge
+    bool hpref_left;
+    /// perfrom anisotropic refinement (hp-refinement) to edge
+    bool hpref_right;
+    ///
+    int layer;
+
+    SplineSegExt (const SplineSeg<2> & hseg) 
+      : seg(hseg)
+    {
+      layer = 1;
+    }
+
+    
+    virtual const GeomPoint<2> & StartPI () const 
+    { 
+      return seg.StartPI(); 
+    }
+
+    virtual const GeomPoint<2> & EndPI () const 
+    {
+      return seg.EndPI();
+    }
+
+    virtual Point<2> GetPoint (double t) const 
+    {
+      return seg.GetPoint(t);
+    }
+
+    virtual Vec<2> GetTangent (const double t) const
+    {
+      return seg.GetTangent(t);
+    }
+
+    virtual void GetDerivatives (const double t,  
+				 Point<2> & point,
+				 Vec<2> & first,
+				 Vec<2> & second) const
+    {
+      seg.GetDerivatives (t, point, first, second);
+    }
+
+    virtual void GetCoeff (Vector & coeffs) const 
+    {
+      seg.GetCoeff (coeffs);
+    }
+
+    virtual void GetPoints (int n, Array<Point<2> > & points) const
+    {
+      seg.GetPoints (n, points);
+    }
+
+    virtual double MaxCurvature () const 
+    {
+      return seg.MaxCurvature();
+    }
+
+    virtual string GetType () const
+    {
+      return seg.GetType();
+    }
+
+  };
+
+
+
+
+  class SplineGeometry2d : public SplineGeometry<2>, public NetgenGeometry
+  {
+  protected:
+    Array<char*> materials;
+    Array<double> maxh;
+    Array<bool> quadmeshing;
+    Array<bool> tensormeshing;
+    Array<int> layer;
+    Array<string*> bcnames;
+    double elto0;
+
+
+  public:
+    DLL_HEADER virtual ~SplineGeometry2d();
+
+    DLL_HEADER void Load (const char * filename);
+
+    DLL_HEADER void LoadData( ifstream & infile );
+    DLL_HEADER void LoadDataNew ( ifstream & infile );
+    DLL_HEADER void LoadDataV2 ( ifstream & infile );
+
+    void TestComment ( ifstream & infile ) ;
+
+    
+
+    const SplineSegExt & GetSpline (const int i) const 
+    { 
+      return dynamic_cast<const SplineSegExt&> (*splines[i]);
+    }
+
+    SplineSegExt & GetSpline (const int i) 
+    { 
+      return dynamic_cast<SplineSegExt&> (*splines[i]);
+    }
+
+    
+    DLL_HEADER virtual int GenerateMesh (Mesh*& mesh, MeshingParameters & mparam,
+			      int perfstepsstart, int perfstepsend);
+    
+    void PartitionBoundary (double h, Mesh & mesh2d);
+
+    void CopyEdgeMesh (int from, int to, Mesh & mesh2d, Point3dTree & searchtree);
+
+
+    void GetMaterial( const int  domnr, char* & material );
+
+    double GetDomainMaxh ( const int domnr );
+    bool GetDomainQuadMeshing ( int domnr ) 
+    { 
+      if ( quadmeshing.Size() ) return quadmeshing[domnr-1]; 
+      else return false;
+    }
+    bool GetDomainTensorMeshing ( int domnr ) 
+    { 
+      if ( tensormeshing.Size() ) return tensormeshing[domnr-1]; 
+      else return false;
+    }
+    int GetDomainLayer ( int domnr ) 
+    { 
+      if ( layer.Size() ) return layer[domnr-1]; 
+      else return 1;
+    }
+
+
+    string GetBCName ( const int bcnr ) const;
+
+    string * BCNamePtr ( const int bcnr );
+
+    
+    DLL_HEADER virtual Refinement & GetRefinement () const; 
+  };
+}
+
+
+
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/geom2d/spline2d.hpp b/contrib/Netgen/libsrc/geom2d/spline2d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..20affa49aa38b57f40b2524ce26b53d7d0563e34
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/spline2d.hpp
@@ -0,0 +1,234 @@
+
+
+
+
+das File sollte nicht mehr verwendet werden ---> spline.hpp
+
+
+
+
+
+
+
+#ifndef FILE_SPLINE2D
+#define FILE_SPLINE2D
+
+/**************************************************************************/
+/* File:   spline2d.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   24. Jul. 96                                                    */
+/**************************************************************************/
+
+
+/*
+  Spline curves for 2D mesh generation
+  */
+
+#include "spline.hpp"
+
+
+//#define OLDSPLINEVERSION
+#ifdef OLDSPLINEVERSION
+
+/// Geometry point
+class GeomPoint2d : public Point<2>
+{
+public:
+  /// refinement to point
+  double refatpoint;
+  bool hpref;
+
+  GeomPoint2d ()
+  { ; }
+
+  ///
+  GeomPoint2d (double ax, double ay, double aref = 1)
+    : Point<2> (ax, ay), refatpoint(aref) { ; }
+};
+
+
+
+/// base class for 2d - segment
+class SplineSegment
+{
+public:
+  /// left domain
+  int leftdom;
+  /// right domain
+  int rightdom;
+  /// refinement at line
+  double reffak;
+  /// boundary condition number
+  int bc;
+  /// copy spline mesh from other spline (-1.. do not copy)
+  int copyfrom;
+  /// perfrom anisotropic refinement (hp-refinement) to edge
+  bool hpref_left;
+  bool hpref_right;
+  /// calculates length of curve
+  virtual double Length () const;
+  /// returns point at curve, 0 <= t <= 1
+  virtual Point<2> GetPoint (double t) const = 0;
+  /// partitionizes curve
+  void Partition (double h, double elto0,
+		  Mesh & mesh, Point3dTree & searchtree, int segnr) const;
+  /// returns initial point on curve
+  virtual const GeomPoint2d & StartPI () const = 0;
+  /// returns terminal point on curve
+  virtual const GeomPoint2d & EndPI () const = 0;
+  /** writes curve description for fepp:
+    for implicitly given quadratic curves, the 6 coefficients of
+    the polynomial
+    $$ a x^2 + b y^2 + c x y + d x + e y + f = 0 $$
+    are written to ost */
+  void PrintCoeff (ostream & ost) const;
+
+  virtual void GetCoeff (Vector & coeffs) const = 0;
+
+  virtual void GetPoints (int n, Array<Point<2> > & points);
+
+  /** calculates lineintersections:
+      for lines $$ a x + b y + c = 0 $$ the interecting points are calculated
+      and stored in points */
+  virtual void LineIntersections (const double a, const double b, const double c,
+				  Array < Point<2> > & points, const double eps) const
+  {points.SetSize(0);}
+
+  virtual double MaxCurvature(void) const = 0;
+
+  virtual string GetType(void) const {return "splinebase";}
+};
+
+
+/// Straight line form p1 to p2
+class LineSegment : public SplineSegment
+{
+  ///
+  const GeomPoint2d &p1, &p2;
+public:
+  ///
+  LineSegment (const GeomPoint2d & ap1, const GeomPoint2d & ap2);
+  ///
+  virtual double Length () const;
+  ///
+  virtual Point<2> GetPoint (double t) const;
+  ///
+  virtual const GeomPoint2d & StartPI () const { return p1; };
+  ///
+  virtual const GeomPoint2d & EndPI () const { return p2; }
+  ///
+  //virtual void PrintCoeff (ostream & ost) const;
+  virtual void GetCoeff (Vector & coeffs) const;
+
+  virtual string GetType(void) const {return "line";}
+
+  virtual void LineIntersections (const double a, const double b, const double c,
+				  Array < Point<2> > & points, const double eps) const;
+
+  virtual double MaxCurvature(void) const {return 0;}
+};
+
+
+/// curve given by a rational, quadratic spline (including ellipses)
+class SplineSegment3 : public SplineSegment
+{
+  ///
+  const GeomPoint2d &p1, &p2, &p3;
+public:
+  ///
+  SplineSegment3 (const GeomPoint2d & ap1, 
+		  const GeomPoint2d & ap2, 
+		  const GeomPoint2d & ap3);
+  ///
+  virtual Point<2> GetPoint (double t) const;
+  ///
+  virtual const GeomPoint2d & StartPI () const { return p1; };
+  ///
+  virtual const GeomPoint2d & EndPI () const { return p3; }
+  ///
+  //virtual void PrintCoeff (ostream & ost) const;
+  virtual void GetCoeff (Vector & coeffs) const;
+
+  virtual string GetType(void) const {return "spline3";}
+
+  const GeomPoint2d & TangentPoint (void) const { return p2; }
+
+  virtual void LineIntersections (const double a, const double b, const double c,
+				  Array < Point<2> > & points, const double eps) const;
+
+  virtual double MaxCurvature(void) const;
+};
+
+
+// Gundolf Haase  8/26/97
+/// A circle
+class CircleSegment : public SplineSegment
+{
+  ///
+private:
+  const GeomPoint2d	&p1, &p2, &p3;
+  Point<2>		pm;
+  double		radius, w1,w3;
+public:
+  ///
+  CircleSegment (const GeomPoint2d & ap1, 
+		 const GeomPoint2d & ap2, 
+		 const GeomPoint2d & ap3);
+  ///
+  virtual Point<2> GetPoint (double t) const;
+  ///
+  virtual const GeomPoint2d & StartPI () const { return p1; }
+  ///
+  virtual const GeomPoint2d & EndPI () const { return p3; }
+  ///
+  //virtual void PrintCoeff (ostream & ost) const;
+  virtual void GetCoeff (Vector & coeffs) const;
+  ///
+  double Radius() const { return radius; }
+  ///
+  double StartAngle() const { return w1; }
+  ///
+  double EndAngle() const { return w3; }
+  ///
+  const Point<2> & MidPoint(void) const {return pm; }
+
+  virtual string GetType(void) const {return "circle";}
+
+  virtual void LineIntersections (const double a, const double b, const double c,
+				  Array < Point<2> > & points, const double eps) const;
+
+  virtual double MaxCurvature(void) const {return 1./radius;}
+};
+
+
+
+
+
+
+/// 
+class DiscretePointsSegment : public SplineSegment
+{
+  Array<Point<2> > pts;
+  GeomPoint2d p1, p2;
+public:
+  ///
+  DiscretePointsSegment (const Array<Point<2> > & apts);
+  ///
+  virtual ~DiscretePointsSegment ();
+  ///
+  virtual Point<2> GetPoint (double t) const;
+  ///
+  virtual const GeomPoint2d & StartPI () const { return p1; };
+  ///
+  virtual const GeomPoint2d & EndPI () const { return p2; }
+  ///
+  //virtual void PrintCoeff (ostream & /* ost */) const { ; }
+  virtual void GetCoeff (Vector & coeffs) const {;}
+
+  virtual double MaxCurvature(void) const {return 1;}
+};
+
+
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/geom2d/splinegeometry2.hpp b/contrib/Netgen/libsrc/geom2d/splinegeometry2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6b1428bedb65ebc0d64e06a43d49656542ac809b
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/splinegeometry2.hpp
@@ -0,0 +1,103 @@
+
+
+OLD IMPLEMENTATION, NOT USED ANYMORE!
+
+
+
+
+#ifndef FILE_SPLINEGEOMETRY2
+#define FILE_SPLINEGEOMETRY2
+
+/**************************************************************************/
+/* File:   splinegeometry2.hpp                                            */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   24. Jul. 96                                                    */
+/**************************************************************************/
+
+#include "splinegeometry.hpp"
+
+#ifdef OLDSPLINEGEOMETRY
+
+
+/// 
+extern void LoadBoundarySplines (const char * filename,
+				 Array<GeomPoint2d> & geompoints,
+				 Array<SplineSegment*> & splines, 
+				 double & elto0);
+///
+extern void PartitionBoundary (const Array<SplineSegment*> & splines,
+			       double h, double elto0,
+			       Mesh & mesh2d);
+
+
+class CSGScanner;
+
+class SplineGeometry2d
+{
+  Array<GeomPoint2d> geompoints;
+  Array<SplineSegment*> splines;
+  double elto0;
+
+
+private:
+  void AppendSegment(SplineSegment * spline, const int leftdomain, const int rightdomain,
+		     const int bc,
+		     const double reffac, const bool hprefleft, const bool hprefright,
+		     const int copyfrom);
+
+public:
+  ~SplineGeometry2d();
+
+  void Load (const char * filename);
+  void CSGLoad (CSGScanner & scan);
+  void PartitionBoundary (double h, Mesh & mesh2d);
+
+  void CopyEdgeMesh (int from, int to, Mesh & mesh2d, Point3dTree & searchtree);
+
+  const Array<SplineSegment*> & GetSplines () const
+  { return splines; }
+
+  int GetNSplines (void) const { return splines.Size(); }
+  string GetSplineType (const int i) const { return splines[i]->GetType(); }
+  SplineSegment & GetSpline (const int i) {return *splines[i];}
+  const SplineSegment & GetSpline (const int i) const {return *splines[i];}
+
+  void GetBoundingBox (Box<2> & box) const;
+
+  int GetNP () const { return geompoints.Size(); }
+  const GeomPoint2d & GetPoint(int i) const { return geompoints[i]; }
+
+  void SetGrading (const double grading);
+  void AppendPoint (const double x, const double y, const double reffac = 1., const bool hpref = false);
+  
+  void AppendLineSegment (const int n1, const int n2,
+			  const int leftdomain, const int rightdomain, const int bc = -1,
+			  const double reffac = 1.,
+			  const bool hprefleft = false, const bool hprefright = false,
+			  const int copyfrom = -1);
+  void AppendSplineSegment (const int n1, const int n2, const int n3,
+			    const int leftdomain, const int rightdomain, const int bc = -1,
+			    const double reffac = 1.,
+			    const bool hprefleft = false, const bool hprefright = false,
+			    const int copyfrom = -1);
+  void AppendCircleSegment (const int n1, const int n2, const int n3,
+			    const int leftdomain, const int rightdomain, const int bc = -1,
+			    const double reffac = 1.,
+			    const bool hprefleft = false, const bool hprefright = false,
+			    const int copyfrom = -1);
+  void AppendDiscretePointsSegment (const Array< Point<2> > & points, 
+				    const int leftdomain, const int rightdomain, const int bc = -1,
+				    const double reffac = 1.,
+				    const bool hprefleft = false, const bool hprefright = false,
+				    const int copyfrom = -1);
+  
+};
+
+
+void MeshFromSpline2D (SplineGeometry2d & geometry,
+		       Mesh *& mesh, 
+		       MeshingParameters & mp);
+
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/geom2d/vsgeom2d.cpp b/contrib/Netgen/libsrc/geom2d/vsgeom2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d1081453ad293c263185f409d25f5fe568b7602
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/vsgeom2d.cpp
@@ -0,0 +1,110 @@
+#include <meshing.hpp>
+#include <geometry2d.hpp>
+#include <visual.hpp>
+
+#include "vsgeom2d.hpp"
+
+namespace netgen
+{
+
+
+  /* *********************** Draw 2D Geometry **************** */
+
+
+  VisualSceneGeometry2d :: VisualSceneGeometry2d ()
+    : VisualScene()
+  {
+    ;
+  }
+
+  VisualSceneGeometry2d :: ~VisualSceneGeometry2d ()
+  {
+    ;
+  }
+
+
+
+  void VisualSceneGeometry2d :: DrawScene ()
+  {
+    if (changeval != geometry2d->GetSplines().Size())
+      BuildScene();
+    changeval = geometry2d->GetSplines().Size();
+
+  
+    glClearColor(backcolor, backcolor, backcolor, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    SetLight();
+
+    //  glEnable (GL_LIGHT0);
+    glDisable (GL_LIGHTING);
+    glPushMatrix();
+    glMultMatrixf (transformationmat);
+
+    //  SetClippingPlane ();
+
+    glShadeModel (GL_SMOOTH);
+    glEnable (GL_COLOR_MATERIAL);
+    glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+  
+    //  float mat_col[] = { 0, 0, 1, 1 };
+    //  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+    glColor3f (0, 0, 1);
+  
+
+    Array<Point<2> > points, otherpoints;
+
+    for (int i = 1; i <= geometry2d->GetSplines().Size(); i++)
+      {
+	geometry2d->GetSplines().Get(i)->GetPoints (20, points);
+      
+	glBegin (GL_LINE_STRIP);
+	for (int j = 0; j < points.Size(); j++)
+	  glVertex3d (points[j](0), points[j](1), 0);
+	glEnd(); 
+      }
+
+    glColor3f (1, 0, 0);
+
+    for (int i = 1; i <= geometry2d->GetSplines().Size(); i++)
+      {
+	int other = geometry2d->GetSpline(i-1).copyfrom;
+	if (other != -1)
+	  {
+	    geometry2d->GetSplines().Get(i)->GetPoints (6, points);
+	    geometry2d->GetSplines().Get(other)->GetPoints (6, otherpoints);
+	    glBegin (GL_LINES);
+	    for (int j = 1; j < 5; j++)
+	      {
+		glVertex3d (points[j](0), points[j](1), 0);
+		glVertex3d (otherpoints[j](0), otherpoints[j](1), 0);
+	      }
+	    glEnd ();
+	  }
+      }
+
+
+
+    glPopMatrix();
+  
+    DrawCoordinateCross ();
+    DrawNetgenLogo ();
+
+    glFinish();  
+  }
+
+
+  void VisualSceneGeometry2d :: BuildScene (int zoomall)
+  {
+    Box<2> bbox;
+
+    geometry2d->GetBoundingBox (bbox);
+  
+    Point<2> c = Center (bbox.PMin(), bbox.PMax());
+
+    center = Point3d (c(0), c(1), 0);
+    rad = Dist (bbox.PMin(), bbox.PMax()) / 2;
+
+    CalcTransformationMatrices();
+  }
+}
diff --git a/contrib/Netgen/libsrc/geom2d/vsgeom2d.hpp b/contrib/Netgen/libsrc/geom2d/vsgeom2d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ffb44711e15887d8a3b731c2b54a014822d0925e
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/vsgeom2d.hpp
@@ -0,0 +1,28 @@
+#ifndef FILE_VSGEOM2D
+#define FILE_VSGEOM2D
+
+/**************************************************************************/
+/* File:   vsgeom2d.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   05. Jan. 2011                                                  */
+/**************************************************************************/
+
+namespace netgen
+{
+
+  class VisualSceneGeometry2d : public VisualScene
+  {
+    const class SplineGeometry2d * geometry2d;
+  public:
+    VisualSceneGeometry2d ();
+    virtual ~VisualSceneGeometry2d ();
+    void SetGeometry (const class SplineGeometry2d * ageometry2d) { geometry2d = ageometry2d; }
+    virtual void BuildScene (int zoomall = 0);
+    virtual void DrawScene ();
+  };
+
+}
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/Makefile.am b/contrib/Netgen/libsrc/gprim/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..7d60c42d851448b29786fb6f9b547297160d14d1
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/Makefile.am
@@ -0,0 +1,7 @@
+noinst_HEADERS = adtree.hpp  geom3d.hpp     geomobjects2.hpp  geomops2.hpp  geomtest3d.hpp  transform3d.hpp geom2d.hpp  geomfuncs.hpp  geomobjects.hpp   geomops.hpp   gprim.hpp spline.hpp splinegeometry.hpp
+
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc/include
+METASOURCES = AUTO
+noinst_LTLIBRARIES = libgprim.la
+libgprim_la_SOURCES = adtree.cpp geom2d.cpp geom3d.cpp geomfuncs.cpp \
+	geomtest3d.cpp transform3d.cpp spline.cpp splinegeometry.cpp
diff --git a/contrib/Netgen/libsrc/gprim/adtree.cpp b/contrib/Netgen/libsrc/gprim/adtree.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..592a6238c44622bff9264ff4c513b6b9eef021ed
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/adtree.cpp
@@ -0,0 +1,2165 @@
+#include <mystdlib.h>
+
+
+#include <myadt.hpp>
+// class DenseMatrix;
+#include <gprim.hpp>
+
+namespace netgen
+{
+
+
+  /* ******************************* ADTree ******************************* */
+
+
+  ADTreeNode :: ADTreeNode(int adim)
+  {
+    pi = -1;
+
+    left = NULL;
+    right = NULL;
+    father = NULL;
+    nchilds = 0;
+    dim = adim;
+    data = new float [dim];
+    boxmin = NULL;
+    boxmax = NULL;
+  }
+
+
+
+
+  ADTreeNode :: ~ADTreeNode()
+  {
+    delete data;
+  }
+
+
+  ADTree :: ADTree (int adim, const float * acmin, 
+		    const float * acmax)
+    : ela(0), stack(1000), stackdir(1000)
+  {
+    dim = adim;
+    cmin = new float [dim];
+    cmax = new float [dim];
+    memcpy (cmin, acmin, dim * sizeof(float));
+    memcpy (cmax, acmax, dim * sizeof(float));
+
+    root = new ADTreeNode (dim);
+    root->sep = (cmin[0] + cmax[0]) / 2;
+    root->boxmin = new float [dim];
+    root->boxmax = new float [dim];
+    memcpy (root->boxmin, cmin, dim * sizeof(float));
+    memcpy (root->boxmax, cmax, dim * sizeof(float));
+  }
+
+  ADTree :: ~ADTree ()
+  {
+    ;
+  }
+
+  void ADTree :: Insert (const float * p, int pi)
+  {
+    ADTreeNode *node(NULL);
+    ADTreeNode *next;
+    int dir;
+    int lr(1);
+
+    float * bmin = new float [dim];
+    float * bmax = new float [dim];
+  
+    memcpy (bmin, cmin, dim * sizeof(float));
+    memcpy (bmax, cmax, dim * sizeof(float));
+
+
+    next = root;
+    dir = 0;
+    while (next)
+      {
+	node = next;
+
+	if (node->pi == -1)
+	  {    
+	    memcpy (node->data, p, dim * sizeof(float));
+	    node->pi = pi;
+
+	    if (ela.Size() < pi+1)
+	      ela.SetSize (pi+1);
+	    ela[pi] = node;
+
+	    return;
+	  }
+
+	if (node->sep > p[dir])
+	  {
+	    next = node->left;
+	    bmax[dir] = node->sep;
+	    lr = 0;
+	  }
+	else
+	  {
+	    next = node->right;
+	    bmin[dir] = node->sep;
+	    lr = 1;
+	  }
+
+	dir++;
+	if (dir == dim)
+	  dir = 0;
+      }
+
+
+    next = new ADTreeNode(dim);
+    memcpy (next->data, p, dim * sizeof(float));
+    next->pi = pi;
+    next->sep = (bmin[dir] + bmax[dir]) / 2;
+    next->boxmin = bmin;
+    next->boxmax = bmax;
+
+    if (ela.Size() < pi+1)
+      ela.SetSize (pi+1);
+    ela[pi] = next;
+
+
+    if (lr)
+      node->right = next;
+    else
+      node->left = next;
+    next -> father = node;
+
+    while (node)
+      {
+	node->nchilds++;
+	node = node->father;
+      }
+  }
+
+  void ADTree :: DeleteElement (int pi)
+  {
+    ADTreeNode * node = ela[pi];
+
+    node->pi = -1;
+
+    node = node->father;
+    while (node)
+      {
+	node->nchilds--;
+	node = node->father;
+      }
+  }
+
+
+  void ADTree :: SetCriterion (ADTreeCriterion & acriterion)
+  {
+    criterion = & acriterion;
+  }
+
+
+  void ADTree :: Reset ()
+  {
+    stack.Elem(1) = root;
+    stackdir.Elem(1) = 0;
+    stackindex = 1;
+  }
+
+
+  int ADTree:: Next ()
+  {
+    ADTreeNode *node;
+    int dir;
+
+    if (stackindex == 0)
+      return -1;
+
+    do 
+      {
+	node = stack.Get(stackindex);
+	dir = stackdir.Get(stackindex);
+	stackindex --;
+
+	if (criterion -> Eval(node))
+	  {
+	    int ndir = dir + 1;
+	    if (ndir == dim)
+	      ndir = 0;
+
+	    if (node -> left && criterion -> Eval (node->left))
+	      {
+		stackindex ++;
+		stack.Elem(stackindex) = node -> left;
+		stackdir.Elem(stackindex) = ndir;
+	      }
+	    if (node->right && criterion -> Eval (node -> right))
+	      {
+		stackindex++;
+		stack.Elem(stackindex) = node->right;
+		stackdir.Elem(stackindex) = ndir;
+	      }
+	  
+	    if (node -> pi != -1)
+	      return node->pi;
+	  }
+      }
+    while (stackindex > 0);
+
+    return -1;
+  }
+
+
+  void ADTree :: GetMatch (Array <int> & matches)
+  {
+    int nodenr;
+
+    Reset();
+
+    while ( (nodenr = Next()) != -1)
+      matches.Append (nodenr);
+  }
+
+
+  void ADTree :: PrintRec (ostream & ost, const ADTreeNode * node) const
+  {
+  
+    if (node->data)
+      {
+	ost << node->pi << ": ";
+	ost << node->nchilds << " childs, ";
+	for (int i = 0; i < dim; i++)
+	  ost << node->data[i] << " ";
+	ost << endl;
+      }
+    if (node->left)
+      {
+	ost << "l ";
+	PrintRec (ost, node->left);
+      }
+    if (node->right)
+      {
+	ost << "r ";
+	PrintRec (ost, node->right);
+      }
+  }
+
+
+  /* ******************************* ADTree3 ******************************* */
+
+
+  ADTreeNode3 :: ADTreeNode3()
+  {
+    pi = -1;
+
+    left = NULL;
+    right = NULL;
+    father = NULL;
+    nchilds = 0;
+  }
+
+  void ADTreeNode3 :: DeleteChilds ()
+  {
+    if (left)
+      {
+	left->DeleteChilds();
+	delete left;
+	left = NULL;
+      }
+    if (right)
+      {
+	right->DeleteChilds();
+	delete right;
+	right = NULL;
+      }
+  }
+
+
+  BlockAllocator ADTreeNode3 :: ball(sizeof (ADTreeNode3));
+
+
+  void * ADTreeNode3 :: operator new(size_t s)
+  {
+    return ball.Alloc();
+  }
+
+  void ADTreeNode3 :: operator delete (void * p)
+  {
+    ball.Free (p);
+  }
+
+
+
+
+
+
+
+  ADTree3 :: ADTree3 (const float * acmin, 
+		      const float * acmax)
+    : ela(0)
+  {
+    memcpy (cmin, acmin, 3 * sizeof(float));
+    memcpy (cmax, acmax, 3 * sizeof(float));
+
+    root = new ADTreeNode3;
+    root->sep = (cmin[0] + cmax[0]) / 2;
+  }
+
+  ADTree3 :: ~ADTree3 ()
+  {
+    root->DeleteChilds();
+    delete root;
+  }
+
+
+  void ADTree3 :: Insert (const float * p, int pi)
+  {
+    ADTreeNode3 *node(NULL);
+    ADTreeNode3 *next;
+    int dir;
+    int lr(0);
+
+    float bmin[3];
+    float bmax[3];
+  
+    memcpy (bmin, cmin, 3 * sizeof(float));
+    memcpy (bmax, cmax, 3 * sizeof(float));
+
+    next = root;
+    dir = 0;
+    while (next)
+      {
+	node = next;
+
+	if (node->pi == -1)
+	  {    
+	    memcpy (node->data, p, 3 * sizeof(float));
+	    node->pi = pi;
+
+	    if (ela.Size() < pi+1)
+	      ela.SetSize (pi+1);
+	    ela[pi] = node;
+
+	    return;
+	  }
+
+	if (node->sep > p[dir])
+	  {
+	    next = node->left;
+	    bmax[dir] = node->sep;
+	    lr = 0;
+	  }
+	else
+	  {
+	    next = node->right;
+	    bmin[dir] = node->sep;
+	    lr = 1;
+	  }
+
+	dir++;
+	if (dir == 3)
+	  dir = 0;
+      }
+
+
+    next = new ADTreeNode3;
+    memcpy (next->data, p, 3 * sizeof(float));
+    next->pi = pi;
+    next->sep = (bmin[dir] + bmax[dir]) / 2;
+
+
+    if (ela.Size() < pi+1)
+      ela.SetSize (pi+1);
+    ela[pi] = next;		
+
+
+    if (lr)
+      node->right = next;
+    else
+      node->left = next;
+    next -> father = node;
+
+    while (node)
+      {
+	node->nchilds++;
+	node = node->father;
+      }
+  }
+
+  void ADTree3 :: DeleteElement (int pi)
+  {
+    ADTreeNode3 * node = ela[pi];
+
+    node->pi = -1;
+
+    node = node->father;
+    while (node)
+      {
+	node->nchilds--;
+	node = node->father;
+      }
+  }
+
+  void ADTree3 :: GetIntersecting (const float * bmin, 
+				   const float * bmax,
+				   Array<int> & pis) const
+  {
+    static Array<ADTreeNode3*> stack(1000);
+    static Array<int> stackdir(1000);
+    ADTreeNode3 * node;
+    int dir, stacks;
+
+    stack.SetSize (1000);
+    stackdir.SetSize(1000);
+    pis.SetSize(0);
+
+    stack.Elem(1) = root;
+    stackdir.Elem(1) = 0;
+    stacks = 1;
+
+    while (stacks)
+      {
+	node = stack.Get(stacks);
+	dir = stackdir.Get(stacks); 
+	stacks--;
+
+	if (node->pi != -1)
+	  {
+	    if (node->data[0] >= bmin[0] && node->data[0] <= bmax[0] &&
+		node->data[1] >= bmin[1] && node->data[1] <= bmax[1] &&
+		node->data[2] >= bmin[2] && node->data[2] <= bmax[2])
+
+	      pis.Append (node->pi);
+	  }
+
+
+	int ndir = dir+1;
+	if (ndir == 3)
+	  ndir = 0;
+
+	if (node->left && bmin[dir] <= node->sep)
+	  {
+	    stacks++;
+	    stack.Elem(stacks) = node->left;
+	    stackdir.Elem(stacks) = ndir;
+	  }
+	if (node->right && bmax[dir] >= node->sep)
+	  {
+	    stacks++;
+	    stack.Elem(stacks) = node->right;
+	    stackdir.Elem(stacks) = ndir;
+	  }
+      }
+  }
+
+  void ADTree3 :: PrintRec (ostream & ost, const ADTreeNode3 * node) const
+  {
+  
+    if (node->data)
+      {
+	ost << node->pi << ": ";
+	ost << node->nchilds << " childs, ";
+	for (int i = 0; i < 3; i++)
+	  ost << node->data[i] << " ";
+	ost << endl;
+      }
+    if (node->left)
+      PrintRec (ost, node->left);
+    if (node->right)
+      PrintRec (ost, node->right);
+  }
+
+
+
+
+
+
+
+
+#ifdef ABC
+
+  /* ******************************* ADTree3Div ******************************* */
+
+
+  ADTreeNode3Div :: ADTreeNode3Div()
+  {
+    pi = 0;
+  
+    int i;
+    for (i = 0; i < ADTN_DIV; i++)
+      childs[i] = NULL;
+
+    father = NULL;
+    nchilds = 0;
+    minx = 0;
+    dist = 1;
+  }
+
+  void ADTreeNode3Div :: DeleteChilds ()
+  {
+    int i;
+    for (i = 0; i < ADTN_DIV; i++)
+      if (childs[i])
+	{
+	  childs[i]->DeleteChilds();
+	  delete childs[i];
+	  childs[i] = NULL;
+	}
+  }
+
+
+  BlockAllocator ADTreeNode3Div :: ball(sizeof (ADTreeNode3Div));
+
+  void * ADTreeNode3Div :: operator new(size_t)
+  {
+    return ball.Alloc();
+  }
+
+  void ADTreeNode3Div :: operator delete (void * p)
+  {
+    ball.Free (p);
+  }
+
+
+
+
+
+
+
+  ADTree3Div :: ADTree3Div (const float * acmin, 
+			    const float * acmax)
+    : ela(0)
+  {
+    memcpy (cmin, acmin, 3 * sizeof(float));
+    memcpy (cmax, acmax, 3 * sizeof(float));
+
+    root = new ADTreeNode3Div;
+
+    root->minx = cmin[0];
+    root->dist = (cmax[0] - cmin[0]) / ADTN_DIV;
+
+    //  root->sep = (cmin[0] + cmax[0]) / 2;
+  }
+
+  ADTree3Div :: ~ADTree3Div ()
+  {
+    root->DeleteChilds();
+    delete root;
+  }
+
+
+  void ADTree3Div :: Insert (const float * p, int pi)
+  {
+    ADTreeNode3Div *node;
+    ADTreeNode3Div *next;
+    int dir;
+    int bag;
+  
+    float bmin[3];
+    float bmax[3];
+  
+    memcpy (bmin, cmin, 3 * sizeof(float));
+    memcpy (bmax, cmax, 3 * sizeof(float));
+
+
+    next = root;
+    dir = 0;
+    while (next)
+      {
+	node = next;
+
+	if (!node->pi)
+	  {    
+	    memcpy (node->data, p, 3 * sizeof(float));
+	    node->pi = pi;
+
+	    if (ela.Size() < pi)
+	      ela.SetSize (pi);
+	    ela.Elem(pi) = node;
+
+	    return;
+	  }
+
+	double dx = (bmax[dir] - bmin[dir]) / ADTN_DIV;
+	bag = int ((p[dir]-bmin[dir]) / dx);
+
+	//      (*testout) << "insert, bag = " << bag << endl;
+
+	if (bag < 0) bag = 0;
+	if (bag >= ADTN_DIV) bag = ADTN_DIV-1;
+      
+	double nbmin = bmin[dir] + bag * dx;
+	double nbmax = bmin[dir] + (bag+1) * dx;
+
+	/*      
+		(*testout) << "bmin, max = " << bmin[dir] << "-" << bmax[dir]
+		<< " p = " << p[dir];
+	*/
+	next = node->childs[bag];
+	bmin[dir] = nbmin;
+	bmax[dir] = nbmax;
+
+	//      (*testout) << "new bmin, max = " << bmin[dir] << "-" << bmax[dir] << endl;
+
+      
+	/*      
+		if (node->sep > p[dir])
+		{
+		next = node->left;
+		bmax[dir] = node->sep;
+		lr = 0;
+		}
+		else
+		{
+		next = node->right;
+		bmin[dir] = node->sep;
+		lr = 1;
+		}
+	*/
+
+	dir++;
+	if (dir == 3)
+	  dir = 0;
+      }
+
+
+    next = new ADTreeNode3Div;
+    memcpy (next->data, p, 3 * sizeof(float));
+    next->pi = pi;
+
+    next->minx = bmin[dir];
+    next->dist = (bmax[dir] - bmin[dir]) / ADTN_DIV;
+    //  next->sep = (bmin[dir] + bmax[dir]) / 2;
+
+
+    if (ela.Size() < pi)
+      ela.SetSize (pi);
+    ela.Elem(pi) = next;
+
+    node->childs[bag] = next;
+    next -> father = node;
+
+    while (node)
+      {
+	node->nchilds++;
+	node = node->father;
+      }
+  }
+
+  void ADTree3Div :: DeleteElement (int pi)
+  {
+    ADTreeNode3Div * node = ela.Get(pi);
+
+    node->pi = 0;
+
+    node = node->father;
+    while (node)
+      {
+	node->nchilds--;
+	node = node->father;
+      }
+  }
+
+  void ADTree3Div :: GetIntersecting (const float * bmin, 
+				      const float * bmax,
+				      Array<int> & pis) const
+  {
+    static Array<ADTreeNode3Div*> stack(1000);
+    static Array<int> stackdir(1000);
+    ADTreeNode3Div * node;
+    int dir, i, stacks;
+
+    stack.SetSize (1000);
+    stackdir.SetSize(1000);
+    pis.SetSize(0);
+
+    stack.Elem(1) = root;
+    stackdir.Elem(1) = 0;
+    stacks = 1;
+
+    while (stacks)
+      {
+	node = stack.Get(stacks);
+	dir = stackdir.Get(stacks); 
+	stacks--;
+
+	if (node->pi)
+	  {
+	    if (node->data[0] >= bmin[0] && node->data[0] <= bmax[0] &&
+		node->data[1] >= bmin[1] && node->data[1] <= bmax[1] &&
+		node->data[2] >= bmin[2] && node->data[2] <= bmax[2])
+
+	      pis.Append (node->pi);
+	  }
+
+
+	int ndir = dir+1;
+	if (ndir == 3)
+	  ndir = 0;
+
+	int mini = int ( (bmin[dir] - node->minx) / node->dist );
+	int maxi = int ( (bmax[dir] - node->minx) / node->dist );
+      
+	//      (*testout) << "get int, mini, maxi = " << mini << ", " << maxi << endl;
+	if (mini < 0) mini = 0;
+	if (maxi >= ADTN_DIV) maxi = ADTN_DIV-1;
+
+	for (i = mini; i <= maxi; i++)
+	  if (node->childs[i])
+	    {
+	      stacks++;
+	      stack.Elem(stacks) = node->childs[i];
+	      stackdir.Elem(stacks) = ndir;
+	    }
+
+
+	/*
+	  if (node->left && bmin[dir] <= node->sep)
+	  {
+	  stacks++;
+	  stack.Elem(stacks) = node->left;
+	  stackdir.Elem(stacks) = ndir;
+	  }
+	  if (node->right && bmax[dir] >= node->sep)
+	  {
+	  stacks++;
+	  stack.Elem(stacks) = node->right;
+	  stackdir.Elem(stacks) = ndir;
+	  }
+	*/
+      }
+  }
+
+  void ADTree3Div :: PrintRec (ostream & ost, const ADTreeNode3Div * node) const
+  {
+  
+    if (node->data)
+      {
+	ost << node->pi << ": ";
+	ost << node->nchilds << " childs, ";
+	ost << " from " << node->minx << " - " << node->minx + node->dist*ADTN_DIV << "  ";
+	for (int i = 0; i < 3; i++)
+	  ost << node->data[i] << " ";
+	ost << endl;
+      }
+    int i;
+    for (i = 0; i < ADTN_DIV; i++)
+      if (node->childs[i])
+	PrintRec (ost, node->childs[i]);
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+  /* ******************************* ADTree3M ******************************* */
+
+
+  ADTreeNode3M :: ADTreeNode3M()
+  {
+    int i;
+    for (i = 0; i < ADTN_SIZE; i++)
+      pi[i] = 0;
+
+    left = NULL;
+    right = NULL;
+    father = NULL;
+    nchilds = 0;
+  }
+
+  void ADTreeNode3M :: DeleteChilds ()
+  {
+    if (left)
+      {
+	left->DeleteChilds();
+	delete left;
+	left = NULL;
+      }
+    if (right)
+      {
+	right->DeleteChilds();
+	delete right;
+	right = NULL;
+      }
+  }
+
+
+  BlockAllocator ADTreeNode3M :: ball(sizeof (ADTreeNode3M));
+
+  void * ADTreeNode3M :: operator new(size_t)
+  {
+    return ball.Alloc();
+  }
+
+  void ADTreeNode3M :: operator delete (void * p)
+  {
+    ball.Free (p);
+  }
+
+
+
+
+
+
+
+  ADTree3M :: ADTree3M (const float * acmin, 
+			const float * acmax)
+    : ela(0)
+  {
+    memcpy (cmin, acmin, 3 * sizeof(float));
+    memcpy (cmax, acmax, 3 * sizeof(float));
+
+    root = new ADTreeNode3M;
+    root->sep = (cmin[0] + cmax[0]) / 2;
+  }
+
+  ADTree3M :: ~ADTree3M ()
+  {
+    root->DeleteChilds();
+    delete root;
+  }
+
+
+  void ADTree3M :: Insert (const float * p, int pi)
+  {
+    ADTreeNode3M *node;
+    ADTreeNode3M *next;
+    int dir;
+    int lr;
+    int i;
+    float bmin[3];
+    float bmax[3];
+  
+    memcpy (bmin, cmin, 3 * sizeof(float));
+    memcpy (bmax, cmax, 3 * sizeof(float));
+
+    next = root;
+    dir = 0;
+    while (next)
+      {
+	node = next;
+
+	for (i = 0; i < ADTN_SIZE; i++)
+	  if (!node->pi[i])
+	    {    
+	      memcpy (node->data[i], p, 3 * sizeof(float));
+	      node->pi[i] = pi;
+	    
+	      if (ela.Size() < pi)
+		ela.SetSize (pi);
+	      ela.Elem(pi) = node;
+	    
+	      return;
+	    }
+
+	if (node->sep > p[dir])
+	  {
+	    next = node->left;
+	    bmax[dir] = node->sep;
+	    lr = 0;
+	  }
+	else
+	  {
+	    next = node->right;
+	    bmin[dir] = node->sep;
+	    lr = 1;
+	  }
+
+	dir++;
+	if (dir == 3)
+	  dir = 0;
+      }
+
+
+    next = new ADTreeNode3M;
+    memcpy (next->data[0], p, 3 * sizeof(float));
+    next->pi[0] = pi;
+    next->sep = (bmin[dir] + bmax[dir]) / 2;
+
+
+    if (ela.Size() < pi)
+      ela.SetSize (pi);
+    ela.Elem(pi) = next;
+
+
+    if (lr)
+      node->right = next;
+    else
+      node->left = next;
+    next -> father = node;
+
+    while (node)
+      {
+	node->nchilds++;
+	node = node->father;
+      }
+  }
+
+  void ADTree3M :: DeleteElement (int pi)
+  {
+    ADTreeNode3M * node = ela.Get(pi);
+
+    int i;
+    for (i = 0; i < ADTN_SIZE; i++)
+      if (node->pi[i] == pi)
+	node->pi[i] = 0;
+
+    node = node->father;
+    while (node)
+      {
+	node->nchilds--;
+	node = node->father;
+      }
+  }
+
+  void ADTree3M :: GetIntersecting (const float * bmin, 
+				    const float * bmax,
+				    Array<int> & pis) const
+  {
+    static Array<ADTreeNode3M*> stack(1000);
+    static Array<int> stackdir(1000);
+    ADTreeNode3M * node;
+    int dir, i, stacks;
+
+    stack.SetSize (1000);
+    stackdir.SetSize(1000);
+    pis.SetSize(0);
+
+    stack.Elem(1) = root;
+    stackdir.Elem(1) = 0;
+    stacks = 1;
+
+    while (stacks)
+      {
+	node = stack.Get(stacks);
+	dir = stackdir.Get(stacks); 
+	stacks--;
+
+	int * hpi = node->pi;
+	for (i = 0; i < ADTN_SIZE; i++)
+	  if (hpi[i])
+	    {
+	      float * datai = &node->data[i][0];
+	      if (datai[0] >= bmin[0] && datai[0] <= bmax[0] &&
+		  datai[1] >= bmin[1] && datai[1] <= bmax[1] &&
+		  datai[2] >= bmin[2] && datai[2] <= bmax[2])
+	      
+		pis.Append (node->pi[i]);
+	    }
+
+
+	int ndir = dir+1;
+	if (ndir == 3)
+	  ndir = 0;
+
+	if (node->left && bmin[dir] <= node->sep)
+	  {
+	    stacks++;
+	    stack.Elem(stacks) = node->left;
+	    stackdir.Elem(stacks) = ndir;
+	  }
+	if (node->right && bmax[dir] >= node->sep)
+	  {
+	    stacks++;
+	    stack.Elem(stacks) = node->right;
+	    stackdir.Elem(stacks) = ndir;
+	  }
+      }
+  }
+
+  void ADTree3M :: PrintRec (ostream & ost, const ADTreeNode3M * node) const
+  {
+  
+    if (node->data)
+      {
+	//      ost << node->pi << ": ";
+	ost << node->nchilds << " childs, ";
+	for (int i = 0; i < 3; i++)
+	  ost << node->data[i] << " ";
+	ost << endl;
+      }
+    if (node->left)
+      PrintRec (ost, node->left);
+    if (node->right)
+      PrintRec (ost, node->right);
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+  /* ******************************* ADTree3F ******************************* */
+
+
+  ADTreeNode3F :: ADTreeNode3F()
+  {
+    pi = 0;
+    father = NULL;
+    nchilds = 0;
+    int i;
+    for (i = 0; i < 8; i++)
+      childs[i] = NULL;
+  }
+
+  void ADTreeNode3F :: DeleteChilds ()
+  {
+    int i;
+
+    for (i = 0; i < 8; i++)
+      {
+	if (childs[i])
+	  childs[i]->DeleteChilds();
+	delete childs[i];
+	childs[i] = NULL;
+      }
+  }
+
+
+  BlockAllocator ADTreeNode3F :: ball(sizeof (ADTreeNode3F));
+
+  void * ADTreeNode3F :: operator new(size_t)
+  {
+    return ball.Alloc();
+  }
+
+  void ADTreeNode3F :: operator delete (void * p)
+  {
+    ball.Free (p);
+  }
+
+
+
+
+
+
+
+  ADTree3F :: ADTree3F (const float * acmin, 
+			const float * acmax)
+    : ela(0)
+  {
+    memcpy (cmin, acmin, 3 * sizeof(float));
+    memcpy (cmax, acmax, 3 * sizeof(float));
+
+    root = new ADTreeNode3F;
+    for (int i = 0; i < 3; i++)
+      root->sep[i] = (cmin[i] + cmax[i]) / 2;
+  }
+
+  ADTree3F :: ~ADTree3F ()
+  {
+    root->DeleteChilds();
+    delete root;
+  }
+
+
+  void ADTree3F :: Insert (const float * p, int pi)
+  {
+    ADTreeNode3F *node;
+    ADTreeNode3F *next;
+    int lr;
+
+    float bmin[3];
+    float bmax[3];
+    int i, dir;
+  
+    memcpy (bmin, cmin, 3 * sizeof(float));
+    memcpy (bmax, cmax, 3 * sizeof(float));
+
+
+    next = root;
+    while (next)
+      {
+	node = next;
+      
+	if (!node->pi)
+	  {    
+	    memcpy (node->data, p, 3 * sizeof(float));
+	    node->pi = pi;
+
+	    if (ela.Size() < pi)
+	      ela.SetSize (pi);
+	    ela.Elem(pi) = node;
+
+	    return;
+	  }
+
+	dir = 0;
+	for (i = 0; i < 3; i++)
+	  {
+	    if (node->sep[i] > p[i])
+	      {
+		bmax[i] = node->sep[i];
+	      }
+	    else
+	      {
+		bmin[i] = node->sep[i];
+		dir += (1 << i);
+	      }
+	  }
+	next = node->childs[dir];
+
+	/*
+	  if (node->sep > p[dir])
+	  {
+	  next = node->left;
+	  bmax[dir] = node->sep;
+	  lr = 0;
+	  }
+	  else
+	  {
+	  next = node->right;
+	  bmin[dir] = node->sep;
+	  lr = 1;
+	  }
+	*/
+      }
+
+
+    next = new ADTreeNode3F;
+    memcpy (next->data, p, 3 * sizeof(float));
+    next->pi = pi;
+
+    for (i = 0; i < 3; i++)
+      next->sep[i] = (bmin[i] + bmax[i]) / 2;
+  
+
+    if (ela.Size() < pi)
+      ela.SetSize (pi);
+    ela.Elem(pi) = next;
+
+    node->childs[dir] = next;
+    next->father = node;
+
+    while (node)
+      {
+	node->nchilds++;
+	node = node->father;
+      }
+  }
+
+  void ADTree3F :: DeleteElement (int pi)
+  {
+    ADTreeNode3F * node = ela.Get(pi);
+
+    node->pi = 0;
+
+    node = node->father;
+    while (node)
+      {
+	node->nchilds--;
+	node = node->father;
+      }
+  }
+
+  void ADTree3F :: GetIntersecting (const float * bmin, 
+				    const float * bmax,
+				    Array<int> & pis) const
+  {
+    static Array<ADTreeNode3F*> stack(1000);
+    ADTreeNode3F * node;
+    int dir, i, stacks;
+
+    stack.SetSize (1000);
+    pis.SetSize(0);
+
+    stack.Elem(1) = root;
+    stacks = 1;
+
+    while (stacks)
+      {
+	node = stack.Get(stacks);
+	stacks--;
+
+	if (node->pi)
+	  {
+	    if (node->data[0] >= bmin[0] && node->data[0] <= bmax[0] &&
+		node->data[1] >= bmin[1] && node->data[1] <= bmax[1] &&
+		node->data[2] >= bmin[2] && node->data[2] <= bmax[2])
+
+	      pis.Append (node->pi);
+	  }
+
+      
+	int i1min = (bmin[0] <= node->sep[0]) ? 0 : 1;
+	int i1max = (bmax[0] < node->sep[0]) ? 0 : 1;
+	int i2min = (bmin[1] <= node->sep[1]) ? 0 : 1;
+	int i2max = (bmax[1] < node->sep[1]) ? 0 : 1;
+	int i3min = (bmin[2] <= node->sep[2]) ? 0 : 1;
+	int i3max = (bmax[2] < node->sep[2]) ? 0 : 1;
+
+	int i1, i2, i3;
+	for (i1 = i1min; i1 <= i1max; i1++)
+	  for (i2 = i2min; i2 <= i2max; i2++)
+	    for (i3 = i3min; i3 <= i3max; i3++)
+	      {
+		i = i1+2*i2+4*i3;
+		if (node->childs[i])
+		  {
+		    stacks++;
+		    stack.Elem(stacks) = node->childs[i];
+		  }
+	      }
+      
+	/*
+	  if (node->left && bmin[dir] <= node->sep)
+	  {
+	  stacks++;
+	  stack.Elem(stacks) = node->left;
+	  stackdir.Elem(stacks) = ndir;
+	  }
+	  if (node->right && bmax[dir] >= node->sep)
+	  {
+	  stacks++;
+	  stack.Elem(stacks) = node->right;
+	  stackdir.Elem(stacks) = ndir;
+	  }
+	*/
+      }
+  }
+
+  void ADTree3F :: PrintRec (ostream & ost, const ADTreeNode3F * node) const
+  {
+    int i;
+    if (node->data)
+      {
+	ost << node->pi << ": ";
+	ost << node->nchilds << " childs, ";
+	for (i = 0; i < 3; i++)
+	  ost << node->data[i] << " ";
+	ost << endl;
+      }
+
+    for (i = 0; i < 8; i++)
+      if (node->childs[i])
+	PrintRec (ost, node->childs[i]);
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+  /* ******************************* ADTree3FM ******************************* */
+
+
+  ADTreeNode3FM :: ADTreeNode3FM()
+  {
+    father = NULL;
+    nchilds = 0;
+    int i;
+
+    for (i = 0; i < ADTN_SIZE; i++)
+      pi[i] = 0;
+
+    for (i = 0; i < 8; i++)
+      childs[i] = NULL;
+  }
+
+  void ADTreeNode3FM :: DeleteChilds ()
+  {
+    int i;
+
+    for (i = 0; i < 8; i++)
+      {
+	if (childs[i])
+	  childs[i]->DeleteChilds();
+	delete childs[i];
+	childs[i] = NULL;
+      }
+  }
+
+
+  BlockAllocator ADTreeNode3FM :: ball(sizeof (ADTreeNode3FM));
+
+  void * ADTreeNode3FM :: operator new(size_t)
+  {
+    return ball.Alloc();
+  }
+
+  void ADTreeNode3FM :: operator delete (void * p)
+  {
+    ball.Free (p);
+  }
+
+
+
+
+
+
+
+  ADTree3FM :: ADTree3FM (const float * acmin, 
+			  const float * acmax)
+    : ela(0)
+  {
+    memcpy (cmin, acmin, 3 * sizeof(float));
+    memcpy (cmax, acmax, 3 * sizeof(float));
+
+    root = new ADTreeNode3FM;
+    for (int i = 0; i < 3; i++)
+      root->sep[i] = (cmin[i] + cmax[i]) / 2;
+  }
+
+  ADTree3FM :: ~ADTree3FM ()
+  {
+    root->DeleteChilds();
+    delete root;
+  }
+
+
+  void ADTree3FM :: Insert (const float * p, int pi)
+  {
+    ADTreeNode3FM *node;
+    ADTreeNode3FM *next;
+    int lr;
+
+    float bmin[3];
+    float bmax[3];
+    int i, dir;
+  
+    memcpy (bmin, cmin, 3 * sizeof(float));
+    memcpy (bmax, cmax, 3 * sizeof(float));
+
+    next = root;
+    while (next)
+      {
+	node = next;
+      
+	for (i = 0; i < ADTN_SIZE; i++)
+	  if (!node->pi[i])
+	    {    
+	      memcpy (node->data[i], p, 3 * sizeof(float));
+	      node->pi[i] = pi;
+	    
+	      if (ela.Size() < pi)
+		ela.SetSize (pi);
+	      ela.Elem(pi) = node;
+	    
+	      return;
+	    }
+
+	dir = 0;
+	for (i = 0; i < 3; i++)
+	  {
+	    if (node->sep[i] > p[i])
+	      {
+		bmax[i] = node->sep[i];
+	      }
+	    else
+	      {
+		bmin[i] = node->sep[i];
+		dir += (1 << i);
+	      }
+	  }
+	next = node->childs[dir];
+
+	/*
+	  if (node->sep > p[dir])
+	  {
+	  next = node->left;
+	  bmax[dir] = node->sep;
+	  lr = 0;
+	  }
+	  else
+	  {
+	  next = node->right;
+	  bmin[dir] = node->sep;
+	  lr = 1;
+	  }
+	*/
+      }
+
+
+    next = new ADTreeNode3FM;
+    memcpy (next->data[0], p, 3 * sizeof(float));
+    next->pi[0] = pi;
+
+    for (i = 0; i < 3; i++)
+      next->sep[i] = (bmin[i] + bmax[i]) / 2;
+  
+
+    if (ela.Size() < pi)
+      ela.SetSize (pi);
+    ela.Elem(pi) = next;
+
+    node->childs[dir] = next;
+    next->father = node;
+
+    while (node)
+      {
+	node->nchilds++;
+	node = node->father;
+      }
+  }
+
+  void ADTree3FM :: DeleteElement (int pi)
+  {
+    ADTreeNode3FM * node = ela.Get(pi);
+
+    int i;
+    for (i = 0; i < ADTN_SIZE; i++)
+      if (node->pi[i] == pi)
+	node->pi[i] = 0;
+
+    node = node->father;
+    while (node)
+      {
+	node->nchilds--;
+	node = node->father;
+      }
+  }
+
+  void ADTree3FM :: GetIntersecting (const float * bmin, 
+				     const float * bmax,
+				     Array<int> & pis) const
+  {
+    static Array<ADTreeNode3FM*> stack(1000);
+    ADTreeNode3FM * node;
+    int dir, i, stacks;
+
+    stack.SetSize (1000);
+    pis.SetSize(0);
+
+    stack.Elem(1) = root;
+    stacks = 1;
+
+    while (stacks)
+      {
+	node = stack.Get(stacks);
+	stacks--;
+
+	int * hpi = node->pi;
+	for (i = 0; i < ADTN_SIZE; i++)
+	  if (hpi[i])
+	    {
+	      float * datai = &node->data[i][0];
+	      if (datai[0] >= bmin[0] && datai[0] <= bmax[0] &&
+		  datai[1] >= bmin[1] && datai[1] <= bmax[1] &&
+		  datai[2] >= bmin[2] && datai[2] <= bmax[2])
+	      
+		pis.Append (node->pi[i]);
+	    }
+
+	/*
+	  if (node->pi)
+	  {
+	  if (node->data[0] >= bmin[0] && node->data[0] <= bmax[0] &&
+	  node->data[1] >= bmin[1] && node->data[1] <= bmax[1] &&
+	  node->data[2] >= bmin[2] && node->data[2] <= bmax[2])
+
+	  pis.Append (node->pi);
+	  }
+	*/
+      
+	int i1min = (bmin[0] <= node->sep[0]) ? 0 : 1;
+	int i1max = (bmax[0] < node->sep[0]) ? 0 : 1;
+	int i2min = (bmin[1] <= node->sep[1]) ? 0 : 1;
+	int i2max = (bmax[1] < node->sep[1]) ? 0 : 1;
+	int i3min = (bmin[2] <= node->sep[2]) ? 0 : 1;
+	int i3max = (bmax[2] < node->sep[2]) ? 0 : 1;
+
+	int i1, i2, i3;
+	for (i1 = i1min; i1 <= i1max; i1++)
+	  for (i2 = i2min; i2 <= i2max; i2++)
+	    for (i3 = i3min; i3 <= i3max; i3++)
+	      {
+		i = i1+2*i2+4*i3;
+		if (node->childs[i])
+		  {
+		    stacks++;
+		    stack.Elem(stacks) = node->childs[i];
+		  }
+	      }
+      
+	/*
+	  if (node->left && bmin[dir] <= node->sep)
+	  {
+	  stacks++;
+	  stack.Elem(stacks) = node->left;
+	  stackdir.Elem(stacks) = ndir;
+	  }
+	  if (node->right && bmax[dir] >= node->sep)
+	  {
+	  stacks++;
+	  stack.Elem(stacks) = node->right;
+	  stackdir.Elem(stacks) = ndir;
+	  }
+	*/
+      }
+  }
+
+  void ADTree3FM :: PrintRec (ostream & ost, const ADTreeNode3FM * node) const
+  {
+    int i;
+    if (node->data)
+      {
+	ost << node->pi << ": ";
+	ost << node->nchilds << " childs, ";
+	for (i = 0; i < 3; i++)
+	  ost << node->data[i] << " ";
+	ost << endl;
+      }
+
+    for (i = 0; i < 8; i++)
+      if (node->childs[i])
+	PrintRec (ost, node->childs[i]);
+  }
+
+
+
+
+#endif
+
+
+
+
+
+
+  /* ******************************* ADTree6 ******************************* */
+
+
+  ADTreeNode6 :: ADTreeNode6()
+  {
+    pi = -1;
+
+    left = NULL;
+    right = NULL;
+    father = NULL;
+    nchilds = 0;
+  }
+
+  void ADTreeNode6 :: DeleteChilds ()
+  {
+    if (left)
+      {
+	left->DeleteChilds();
+	delete left;
+	left = NULL;
+      }
+    if (right)
+      {
+	right->DeleteChilds();
+	delete right;
+	right = NULL;
+      }
+  }
+
+
+  BlockAllocator ADTreeNode6 :: ball (sizeof (ADTreeNode6));
+  void * ADTreeNode6 :: operator new(size_t s)
+  {
+    return ball.Alloc();
+  }
+
+  void ADTreeNode6 :: operator delete (void * p)
+  {
+    ball.Free (p);
+  }
+
+
+
+
+
+  ADTree6 :: ADTree6 (const float * acmin, 
+		      const float * acmax)
+    : ela(0)
+  {
+    memcpy (cmin, acmin, 6 * sizeof(float));
+    memcpy (cmax, acmax, 6 * sizeof(float));
+
+    root = new ADTreeNode6;
+    root->sep = (cmin[0] + cmax[0]) / 2;
+  }
+
+  ADTree6 :: ~ADTree6 ()
+  {
+    root->DeleteChilds();
+    delete root;
+  }
+
+  void ADTree6 :: Insert (const float * p, int pi)
+  {
+    ADTreeNode6 *node(NULL);
+    ADTreeNode6 *next;
+    int dir;
+    int lr(0);
+
+    float bmin[6];
+    float bmax[6];
+
+  
+    memcpy (bmin, cmin, 6 * sizeof(float));
+    memcpy (bmax, cmax, 6 * sizeof(float));
+
+    next = root;
+    dir = 0;
+    while (next)
+      {
+	node = next;
+
+	if (node->pi == -1)
+	  {    
+	    memcpy (node->data, p, 6 * sizeof(float));
+	    node->pi = pi;
+
+	    if (ela.Size() < pi+1)
+	      ela.SetSize (pi+1);
+	    ela[pi] = node;
+
+	    return;
+	  }
+
+	if (node->sep > p[dir])
+	  {
+	    next = node->left;
+	    bmax[dir] = node->sep;
+	    lr = 0;
+	  }
+	else
+	  {
+	    next = node->right;
+	    bmin[dir] = node->sep;
+	    lr = 1;
+	  }
+
+	dir++;
+	if (dir == 6) dir = 0;
+      }
+
+
+    next = new ADTreeNode6;
+    memcpy (next->data, p, 6 * sizeof(float));
+    next->pi = pi;
+    next->sep = (bmin[dir] + bmax[dir]) / 2;
+
+    if (ela.Size() < pi+1)
+      ela.SetSize (pi+1);
+    ela[pi] = next;
+
+    if (lr)
+      node->right = next;
+    else
+      node->left = next;
+    next -> father = node;
+
+    while (node)
+      {
+	node->nchilds++;
+	node = node->father;
+      }
+  }
+
+  void ADTree6 :: DeleteElement (int pi)
+  {
+    ADTreeNode6 * node = ela[pi];
+
+    node->pi = -1;
+
+    node = node->father;
+    while (node)
+      {
+	node->nchilds--;
+	node = node->father;
+      }
+  }
+
+  void ADTree6 :: PrintMemInfo (ostream & ost) const
+  {
+    ost << Elements() << " elements a " << sizeof(ADTreeNode6) 
+	<< " Bytes = "
+	<< Elements() * sizeof(ADTreeNode6) << endl;
+    ost << "maxind = " << ela.Size() << " = " << sizeof(ADTreeNode6*) * ela.Size() << " Bytes" << endl;
+  }
+
+
+
+  class inttn6 {
+  public:
+    int dir;
+    ADTreeNode6 * node;
+  };
+
+
+
+
+  void ADTree6 :: GetIntersecting (const float * bmin, 
+				   const float * bmax,
+				   Array<int> & pis) const
+  {
+    static Array<inttn6> stack(10000);
+
+    stack.SetSize (10000);
+    pis.SetSize(0);
+
+    stack[0].node = root;
+    stack[0].dir = 0;
+    int stacks = 0;
+
+    while (stacks >= 0)
+      {
+	ADTreeNode6 * node = stack[stacks].node;
+	int dir = stack[stacks].dir; 
+
+	stacks--;
+
+	if (node->pi != -1)
+	  {
+	    if (node->data[0] > bmax[0] || 
+		node->data[1] > bmax[1] || 
+		node->data[2] > bmax[2] || 
+		node->data[3] < bmin[3] || 
+		node->data[4] < bmin[4] || 
+		node->data[5] < bmin[5])
+	      ;
+	    else
+	      pis.Append (node->pi);
+	  }
+
+	int ndir = (dir+1) % 6;
+
+	if (node->left && bmin[dir] <= node->sep)
+	  {
+	    stacks++;
+	    stack[stacks].node = node->left;
+	    stack[stacks].dir = ndir;
+	  }
+	if (node->right && bmax[dir] >= node->sep)
+	  {
+	    stacks++;
+	    stack[stacks].node = node->right;
+	    stack[stacks].dir = ndir;
+	  }
+      }
+  }
+
+  void ADTree6 :: PrintRec (ostream & ost, const ADTreeNode6 * node) const
+  {
+  
+    if (node->data)
+      {
+	ost << node->pi << ": ";
+	ost << node->nchilds << " childs, ";
+	for (int i = 0; i < 6; i++)
+	  ost << node->data[i] << " ";
+	ost << endl;
+      }
+    if (node->left)
+      PrintRec (ost, node->left);
+    if (node->right)
+      PrintRec (ost, node->right);
+  }
+
+
+  int ADTree6 :: DepthRec (const ADTreeNode6 * node) const
+  {
+    int ldepth = 0;
+    int rdepth = 0;
+
+    if (node->left)
+      ldepth = DepthRec(node->left);
+    if (node->right)
+      rdepth = DepthRec(node->right);
+    return 1 + max2 (ldepth, rdepth);
+  }
+
+  int ADTree6 :: ElementsRec (const ADTreeNode6 * node) const
+  {
+    int els = 1;
+    if (node->left)
+      els += ElementsRec(node->left);
+    if (node->right)
+      els += ElementsRec(node->right);
+    return els;
+  }
+
+
+
+
+
+
+#ifdef ABC
+
+  /* ******************************* ADTree6F ******************************* */
+
+
+  ADTreeNode6F :: ADTreeNode6F()
+  {
+    pi = 0;
+    father = NULL;
+    nchilds = 0;
+    int i;
+    for (i = 0; i < 64; i++)
+      childs[i] = NULL;
+  }
+
+  void ADTreeNode6F :: DeleteChilds ()
+  {
+    int i;
+
+    for (i = 0; i < 64; i++)
+      {
+	if (childs[i])
+	  childs[i]->DeleteChilds();
+	delete childs[i];
+	childs[i] = NULL;
+      }
+  }
+
+
+  BlockAllocator ADTreeNode6F :: ball(sizeof (ADTreeNode6F));
+
+  void * ADTreeNode6F :: operator new(size_t)
+  {
+    return ball.Alloc();
+  }
+
+  void ADTreeNode6F :: operator delete (void * p)
+  {
+    ball.Free (p);
+  }
+
+
+
+
+
+
+
+  ADTree6F :: ADTree6F (const float * acmin, 
+			const float * acmax)
+    : ela(0)
+  {
+    memcpy (cmin, acmin, 6 * sizeof(float));
+    memcpy (cmax, acmax, 6 * sizeof(float));
+
+    root = new ADTreeNode6F;
+    for (int i = 0; i < 6; i++)
+      root->sep[i] = (cmin[i] + cmax[i]) / 2;
+  }
+
+  ADTree6F :: ~ADTree6F ()
+  {
+    root->DeleteChilds();
+    delete root;
+  }
+
+
+  void ADTree6F :: Insert (const float * p, int pi)
+  {
+    ADTreeNode6F *node;
+    ADTreeNode6F *next;
+    int lr;
+
+    float bmin[6];
+    float bmax[6];
+    int i, dir;
+  
+    memcpy (bmin, cmin, 6 * sizeof(float));
+    memcpy (bmax, cmax, 6 * sizeof(float));
+
+    next = root;
+    while (next)
+      {
+	node = next;
+      
+	if (!node->pi)
+	  {    
+	    memcpy (node->data, p, 6 * sizeof(float));
+	    node->pi = pi;
+
+	    if (ela.Size() < pi)
+	      ela.SetSize (pi);
+	    ela.Elem(pi) = node;
+
+	    return;
+	  }
+
+	dir = 0;
+	for (i = 0; i < 6; i++)
+	  {
+	    if (node->sep[i] > p[i])
+	      {
+		bmax[i] = node->sep[i];
+	      }
+	    else
+	      {
+		bmin[i] = node->sep[i];
+		dir += (1 << i);
+	      }
+	  }
+	next = node->childs[dir];
+
+	/*
+	  if (node->sep > p[dir])
+	  {
+	  next = node->left;
+	  bmax[dir] = node->sep;
+	  lr = 0;
+	  }
+	  else
+	  {
+	  next = node->right;
+	  bmin[dir] = node->sep;
+	  lr = 1;
+	  }
+	*/
+      }
+
+
+    next = new ADTreeNode6F;
+    memcpy (next->data, p, 6 * sizeof(float));
+    next->pi = pi;
+
+    for (i = 0; i < 6; i++)
+      next->sep[i] = (bmin[i] + bmax[i]) / 2;
+  
+
+    if (ela.Size() < pi)
+      ela.SetSize (pi);
+    ela.Elem(pi) = next;
+
+    node->childs[dir] = next;
+    next->father = node;
+
+    while (node)
+      {
+	node->nchilds++;
+	node = node->father;
+      }
+  }
+
+  void ADTree6F :: DeleteElement (int pi)
+  {
+    ADTreeNode6F * node = ela.Get(pi);
+
+    node->pi = 0;
+
+    node = node->father;
+    while (node)
+      {
+	node->nchilds--;
+	node = node->father;
+      }
+  }
+
+  void ADTree6F :: GetIntersecting (const float * bmin, 
+				    const float * bmax,
+				    Array<int> & pis) const
+  {
+    static Array<ADTreeNode6F*> stack(1000);
+    ADTreeNode6F * node;
+    int dir, i, stacks;
+
+    stack.SetSize (1000);
+    pis.SetSize(0);
+
+    stack.Elem(1) = root;
+    stacks = 1;
+
+    while (stacks)
+      {
+	node = stack.Get(stacks);
+	stacks--;
+
+	if (node->pi)
+	  {
+	    if (
+		node->data[0] >= bmin[0] && node->data[0] <= bmax[0] &&
+		node->data[1] >= bmin[1] && node->data[1] <= bmax[1] &&
+		node->data[2] >= bmin[2] && node->data[2] <= bmax[2] &&
+		node->data[3] >= bmin[3] && node->data[3] <= bmax[3] &&
+		node->data[4] >= bmin[4] && node->data[4] <= bmax[4] &&
+		node->data[5] >= bmin[5] && node->data[5] <= bmax[5]
+		)
+
+	      pis.Append (node->pi);
+	  }
+
+      
+	int i1min = (bmin[0] <= node->sep[0]) ? 0 : 1;
+	int i1max = (bmax[0] < node->sep[0]) ? 0 : 1;
+	int i2min = (bmin[1] <= node->sep[1]) ? 0 : 1;
+	int i2max = (bmax[1] < node->sep[1]) ? 0 : 1;
+	int i3min = (bmin[2] <= node->sep[2]) ? 0 : 1;
+	int i3max = (bmax[2] < node->sep[2]) ? 0 : 1;
+
+	int i4min = (bmin[3] <= node->sep[3]) ? 0 : 1;
+	int i4max = (bmax[3] <  node->sep[3]) ? 0 : 1;
+	int i5min = (bmin[4] <= node->sep[4]) ? 0 : 1;
+	int i5max = (bmax[4] <  node->sep[4]) ? 0 : 1;
+	int i6min = (bmin[5] <= node->sep[5]) ? 0 : 1;
+	int i6max = (bmax[5] <  node->sep[5]) ? 0 : 1;
+
+	int i1, i2, i3, i4, i5, i6;
+	for (i1 = i1min; i1 <= i1max; i1++)
+	  for (i2 = i2min; i2 <= i2max; i2++)
+	    for (i3 = i3min; i3 <= i3max; i3++)
+	      for (i4 = i4min; i4 <= i4max; i4++)
+		for (i5 = i5min; i5 <= i5max; i5++)
+		  for (i6 = i6min; i6 <= i6max; i6++)
+		    {
+		      i = i1 + 2*i2 + 4*i3 + 8*i4 + 16*i5 +32*i6;
+		      if (node->childs[i])
+			{
+			  stacks++;
+			  stack.Elem(stacks) = node->childs[i];
+			}
+		    }
+      
+	/*
+	  if (node->left && bmin[dir] <= node->sep)
+	  {
+	  stacks++;
+	  stack.Elem(stacks) = node->left;
+	  stackdir.Elem(stacks) = ndir;
+	  }
+	  if (node->right && bmax[dir] >= node->sep)
+	  {
+	  stacks++;
+	  stack.Elem(stacks) = node->right;
+	  stackdir.Elem(stacks) = ndir;
+	  }
+	*/
+      }
+  }
+
+  void ADTree6F :: PrintRec (ostream & ost, const ADTreeNode6F * node) const
+  {
+    int i;
+    if (node->data)
+      {
+	ost << node->pi << ": ";
+	ost << node->nchilds << " childs, ";
+	for (i = 0; i < 6; i++)
+	  ost << node->data[i] << " ";
+	ost << endl;
+      }
+
+    for (i = 0; i < 64; i++)
+      if (node->childs[i])
+	PrintRec (ost, node->childs[i]);
+  }
+
+
+
+#endif
+
+
+
+  /* ************************************* Point3dTree ********************** */
+
+
+
+  Point3dTree :: Point3dTree (const Point<3> & pmin, const Point<3> & pmax)
+  {
+    float pmi[3], pma[3];
+    for (int i = 0; i < 3; i++)
+      {
+	pmi[i] = pmin(i);
+	pma[i] = pmax(i);
+      }
+    tree = new ADTree3 (pmi, pma);
+  }
+
+  Point3dTree :: ~Point3dTree ()
+  {
+    delete tree;
+  }
+
+
+
+  void Point3dTree :: Insert (const Point<3> & p, int pi)
+  {
+    float pd[3];
+    pd[0] = p(0);
+    pd[1] = p(1);
+    pd[2] = p(2);
+    tree->Insert (pd, pi);
+  }
+
+  void Point3dTree :: GetIntersecting (const Point<3> & pmin, const Point<3> & pmax, 
+				       Array<int> & pis) const
+  {
+    float pmi[3], pma[3];
+    for (int i = 0; i < 3; i++)
+      {
+	pmi[i] = pmin(i);
+	pma[i] = pmax(i);
+      }
+    tree->GetIntersecting (pmi, pma, pis);
+  }
+
+
+
+
+
+
+
+
+
+
+  Box3dTree :: Box3dTree (const Point<3> & apmin, const Point<3> & apmax)
+  {
+    boxpmin = apmin;
+    boxpmax = apmax;
+    float tpmin[6], tpmax[6];
+    for (int i = 0; i < 3; i++)
+      {
+	tpmin[i] = tpmin[i+3] = boxpmin(i);
+	tpmax[i] = tpmax[i+3] = boxpmax(i);
+      }
+    tree = new ADTree6 (tpmin, tpmax);
+  }
+
+  Box3dTree :: ~Box3dTree ()
+  {
+    delete tree;
+  }
+
+  void Box3dTree :: Insert (const Point<3> & bmin, const Point<3> & bmax, int pi)
+  {
+    float tp[6];
+
+    for (int i = 0; i < 3; i++)
+      {
+	tp[i] = bmin(i);
+	tp[i+3] = bmax(i);
+      }
+
+    tree->Insert (tp, pi);
+  }
+
+  void Box3dTree ::GetIntersecting (const Point<3> & pmin, const Point<3> & pmax, 
+				    Array<int> & pis) const
+  {
+    float tpmin[6];
+    float tpmax[6];
+
+    for (int i = 0; i < 3; i++)
+      {
+	tpmin[i] = boxpmin(i);
+	tpmax[i] = pmax(i);
+      
+	tpmin[i+3] = pmin(i);
+	tpmax[i+3] = boxpmax(i);
+      }
+
+    tree->GetIntersecting (tpmin, tpmax, pis);
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/gprim/adtree.hpp b/contrib/Netgen/libsrc/gprim/adtree.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d0f02b6deea1f133c9b61cb625fe48a5e5c4a728
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/adtree.hpp
@@ -0,0 +1,486 @@
+#ifndef FILE_ADTREE
+#define FILE_ADTREE
+
+/* *************************************************************************/
+/* File:   adtree.hh                                                       */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   16. Feb. 98                                                     */
+/* Redesigned by Wolfram Muehlhuber, May 1998                              */
+/* *************************************************************************/
+
+
+namespace netgen
+{
+
+/**
+  Alternating Digital Tree
+ */
+
+// #include "../include/mystdlib.h"
+// #include "../include/myadt.hpp"
+
+class ADTreeNode
+{
+public:
+  ADTreeNode *left, *right, *father;
+  int dim;
+  float sep;
+  float *data;
+  float *boxmin;
+  float *boxmax;
+  int pi;
+  int nchilds;
+
+  ADTreeNode (int adim);
+  ~ADTreeNode ();
+
+  friend class ADTree;
+};
+
+
+class ADTreeCriterion
+{
+public:
+  ADTreeCriterion() { }
+  virtual int Eval (const ADTreeNode * node) const = 0;
+};
+
+
+class ADTree
+{
+  int dim;
+  ADTreeNode * root;
+  float *cmin, *cmax;
+  Array<ADTreeNode*> ela;
+  const ADTreeCriterion * criterion; 
+
+  Array<ADTreeNode*> stack;
+  Array<int> stackdir;
+  int stackindex;
+
+public:
+  ADTree (int adim, const float * acmin, 
+	   const float * acmax);
+  ~ADTree ();
+
+  void Insert (const float * p, int pi);
+  // void GetIntersecting (const float * bmin, const float * bmax,
+  //			Array<int> & pis) const;
+  void SetCriterion (ADTreeCriterion & acriterion);
+  void Reset ();
+  int Next ();
+  void GetMatch (Array<int> & matches);
+
+  void DeleteElement (int pi);
+
+
+  void Print (ostream & ost) const
+    { PrintRec (ost, root); }
+
+  void PrintRec (ostream & ost, const ADTreeNode * node) const;
+};
+
+
+
+class ADTreeNode3
+{
+public:
+  ADTreeNode3 *left, *right, *father;
+  float sep;
+  float data[3];
+  int pi;
+  int nchilds;
+
+  ADTreeNode3 ();
+  void DeleteChilds ();
+  friend class ADTree3;
+
+  static BlockAllocator ball;
+  void * operator new(size_t);
+  void operator delete (void *);
+};
+
+
+class ADTree3
+{
+  ADTreeNode3 * root;
+  float cmin[3], cmax[3];
+  Array<ADTreeNode3*> ela;
+
+public:
+  ADTree3 (const float * acmin, 
+	   const float * acmax);
+  ~ADTree3 ();
+
+  void Insert (const float * p, int pi);
+  void GetIntersecting (const float * bmin, const float * bmax,
+			Array<int> & pis) const;
+  
+  void DeleteElement (int pi);
+
+
+  void Print (ostream & ost) const
+    { PrintRec (ost, root); }
+
+  void PrintRec (ostream & ost, const ADTreeNode3 * node) const;
+};
+
+
+/*
+
+// divide each direction
+#define ADTN_DIV 10
+class ADTreeNode3Div
+{
+public:
+  ADTreeNode3Div *father;
+  ADTreeNode3Div *childs[ADTN_DIV];
+
+  float minx, dist;
+  float data[3];
+  int pi;
+  int nchilds;
+
+  ADTreeNode3Div ();
+  void DeleteChilds ();
+  friend class ADTree3Div;
+
+  static BlockAllocator ball;
+  void * operator new(size_t);
+  void operator delete (void *);
+};
+
+
+class ADTree3Div
+{
+  ADTreeNode3Div * root;
+  float cmin[3], cmax[3];
+  Array<ADTreeNode3Div*> ela;
+
+public:
+  ADTree3Div (const float * acmin, 
+	   const float * acmax);
+  ~ADTree3Div ();
+
+  void Insert (const float * p, int pi);
+  void GetIntersecting (const float * bmin, const float * bmax,
+			Array<int> & pis) const;
+  
+  void DeleteElement (int pi);
+
+
+  void Print (ostream & ost) const
+    { PrintRec (ost, root); }
+
+  void PrintRec (ostream & ost, const ADTreeNode3Div * node) const;
+};
+
+
+
+
+#define ADTN_SIZE 10
+
+// multiple entries
+class ADTreeNode3M
+{
+public:
+  ADTreeNode3M *left, *right, *father;
+  float sep;
+  float data[ADTN_SIZE][3];
+  int pi[ADTN_SIZE];
+  int nchilds;
+
+  ADTreeNode3M ();
+  void DeleteChilds ();
+  friend class ADTree3M;
+
+  static BlockAllocator ball;
+  void * operator new(size_t);
+  void operator delete (void *);
+};
+
+
+class ADTree3M
+{
+  ADTreeNode3M * root;
+  float cmin[3], cmax[3];
+  Array<ADTreeNode3M*> ela;
+
+public:
+  ADTree3M (const float * acmin, 
+	   const float * acmax);
+  ~ADTree3M ();
+
+  void Insert (const float * p, int pi);
+  void GetIntersecting (const float * bmin, const float * bmax,
+			Array<int> & pis) const;
+  
+  void DeleteElement (int pi);
+
+
+  void Print (ostream & ost) const
+    { PrintRec (ost, root); }
+
+  void PrintRec (ostream & ost, const ADTreeNode3M * node) const;
+};
+
+
+
+
+
+
+class ADTreeNode3F
+{
+public:
+  ADTreeNode3F *father;
+  ADTreeNode3F *childs[8];
+  float sep[3];
+  float data[3];
+  int pi;
+  int nchilds;
+
+  ADTreeNode3F ();
+  void DeleteChilds ();
+  friend class ADTree3F;
+
+  static BlockAllocator ball;
+  void * operator new(size_t);
+  void operator delete (void *);
+};
+
+// fat tree
+class ADTree3F
+{
+  ADTreeNode3F * root;
+  float cmin[3], cmax[3];
+  Array<ADTreeNode3F*> ela;
+
+public:
+  ADTree3F (const float * acmin, 
+	   const float * acmax);
+  ~ADTree3F ();
+
+  void Insert (const float * p, int pi);
+  void GetIntersecting (const float * bmin, const float * bmax,
+			Array<int> & pis) const;
+  
+  void DeleteElement (int pi);
+
+
+  void Print (ostream & ost) const
+    { PrintRec (ost, root); }
+
+  void PrintRec (ostream & ost, const ADTreeNode3F * node) const;
+};
+
+
+
+
+class ADTreeNode3FM
+{
+public:
+  ADTreeNode3FM *father;
+  ADTreeNode3FM *childs[8];
+  float sep[3];
+  float data[ADTN_SIZE][3];
+  int pi[ADTN_SIZE];
+  int nchilds;
+
+  ADTreeNode3FM ();
+  void DeleteChilds ();
+  friend class ADTree3FM;
+
+  static BlockAllocator ball;
+  void * operator new(size_t);
+  void operator delete (void *);
+};
+
+// fat tree
+class ADTree3FM
+{
+  ADTreeNode3FM * root;
+  float cmin[3], cmax[3];
+  Array<ADTreeNode3FM*> ela;
+
+public:
+  ADTree3FM (const float * acmin, 
+	   const float * acmax);
+  ~ADTree3FM ();
+
+  void Insert (const float * p, int pi);
+  void GetIntersecting (const float * bmin, const float * bmax,
+			Array<int> & pis) const;
+  
+  void DeleteElement (int pi);
+
+
+  void Print (ostream & ost) const
+    { PrintRec (ost, root); }
+
+  void PrintRec (ostream & ost, const ADTreeNode3FM * node) const;
+};
+
+
+
+*/
+
+
+
+
+
+class ADTreeNode6
+{
+public:
+  ADTreeNode6 *left, *right, *father;
+  float sep;
+  float data[6];
+  int pi;
+  int nchilds;
+
+  ADTreeNode6 ();
+  void DeleteChilds ();
+  friend class ADTree6;
+
+  static BlockAllocator ball;
+  void * operator new(size_t);
+  void operator delete (void *);
+};
+
+
+class ADTree6
+{
+  ADTreeNode6 * root;
+  float cmin[6], cmax[6];
+  Array<ADTreeNode6*> ela;
+
+public:
+  ADTree6 (const float * acmin, 
+	   const float * acmax);
+  ~ADTree6 ();
+
+  void Insert (const float * p, int pi);
+  void GetIntersecting (const float * bmin, const float * bmax,
+			Array<int> & pis) const;
+  
+  void DeleteElement (int pi);
+
+  
+  void Print (ostream & ost) const
+  { PrintRec (ost, root); }
+  int Depth () const
+  { return DepthRec (root); }
+  int Elements () const
+  { return ElementsRec (root); }
+
+  void PrintRec (ostream & ost, const ADTreeNode6 * node) const;
+  int DepthRec (const ADTreeNode6 * node) const;
+  int ElementsRec (const ADTreeNode6 * node) const;
+
+  void PrintMemInfo (ostream & ost) const;
+};
+
+
+
+
+/*
+
+class ADTreeNode6F
+{
+public:
+  ADTreeNode6F * father;
+  ADTreeNode6F * childs[64];
+  
+  float sep[6];
+  float data[6];
+  int pi;
+  int nchilds;
+
+  ADTreeNode6F ();
+  void DeleteChilds ();
+  friend class ADTree6F;
+
+  static BlockAllocator ball;
+  void * operator new(size_t);
+  void operator delete (void *);
+};
+
+
+class ADTree6F
+{
+  ADTreeNode6F * root;
+  float cmin[6], cmax[6];
+  Array<ADTreeNode6F*> ela;
+
+public:
+  ADTree6F (const float * acmin, 
+	   const float * acmax);
+  ~ADTree6F ();
+
+  void Insert (const float * p, int pi);
+  void GetIntersecting (const float * bmin, const float * bmax,
+			Array<int> & pis) const;
+  
+  void DeleteElement (int pi);
+
+
+  void Print (ostream & ost) const
+    { PrintRec (ost, root); }
+  int Depth () const
+    { return DepthRec (root); }
+
+  void PrintRec (ostream & ost, const ADTreeNode6F * node) const;
+  int DepthRec (const ADTreeNode6F * node) const;
+};
+
+
+
+
+
+
+
+*/
+
+
+
+
+
+class Point3dTree 
+{
+  ADTree3 * tree;
+
+public:
+  DLL_HEADER Point3dTree (const Point<3> & pmin, const Point<3> & pmax);
+  DLL_HEADER ~Point3dTree ();
+  DLL_HEADER void Insert (const Point<3> & p, int pi);
+  void DeleteElement (int pi) 
+    { tree->DeleteElement(pi); }
+  DLL_HEADER void GetIntersecting (const Point<3> & pmin, const Point<3> & pmax, 
+			Array<int> & pis) const;
+  const ADTree3 & Tree() const { return *tree; };
+};
+
+
+
+class Box3dTree
+{
+  ADTree6 * tree;
+  Point<3> boxpmin, boxpmax;
+public:
+  Box3dTree (const Point<3> & apmin, const Point<3> & apmax);
+  ~Box3dTree ();
+  void Insert (const Point<3> & bmin, const Point<3> & bmax, int pi);
+  void Insert (const Box<3> & box, int pi)
+  {
+    Insert (box.PMin(), box.PMax(), pi);
+  }
+  void DeleteElement (int pi) 
+    { tree->DeleteElement(pi); }
+  void GetIntersecting (const Point<3> & pmin, const Point<3> & pmax, 
+			Array<int> & pis) const;
+
+  const ADTree6 & Tree() const { return *tree; };
+};
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/geom2d.cpp b/contrib/Netgen/libsrc/gprim/geom2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..34263348cc327ba2a652e6d7ec6c44c5af96018f
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geom2d.cpp
@@ -0,0 +1,489 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+
+#ifndef M_PI
+#define M_PI        3.14159265358979323846
+#endif
+
+namespace netgen
+{
+
+ostream & operator<<(ostream  & s, const Point2d & p)
+{
+  return s << "(" << p.px << ", " << p.py << ")";
+}
+
+ostream & operator<<(ostream  & s, const Vec2d & v)
+{
+  return s << "(" << v.vx << ", " << v.vy << ")";
+}
+
+#ifdef none
+ostream & operator<<(ostream  & s, const Line2d & l)
+  {
+  return s << l.p1 << "-" << l.p2;
+}
+
+ostream & operator<<(ostream  & s, const TRIANGLE2D & t)
+{
+  return s << t.p1 << "-" << t.p2 << "-" << t.p3;
+}
+#endif
+
+
+double Fastatan2 (double x, double y)
+{
+  if (y > 0)
+    {
+      if (x > 0)
+	return y / (x+y);
+      else
+	return 1 - x / (y-x);
+    }
+  else if (y < 0)
+    {
+      if (x < 0)
+	return 2 + y / (x+y);
+      else
+	return 3 - x / (y-x);
+    }
+  else 
+    {
+      if (x >= 0)
+	return 0;
+      else
+	return 2;
+    }
+}
+
+
+double Angle (const Vec2d & v)
+{
+  if (v.X() == 0 && v.Y() == 0)
+    return 0;
+    
+  double ang = atan2 (v.Y(), v.X());
+  if (ang < 0) ang+= 2 * M_PI;
+  return ang;
+}
+
+double FastAngle (const Vec2d & v)
+{
+  return Fastatan2 (v.X(), v.Y());
+}
+
+double Angle (const Vec2d & v1, const Vec2d & v2)
+{
+  double ang = Angle(v2) - Angle(v1);
+  if (ang < 0) ang += 2 * M_PI;
+  return ang;
+}
+
+double FastAngle (const Vec2d & v1, const Vec2d & v2)
+{
+  double ang = FastAngle(v2) - FastAngle(v1);
+  if (ang < 0) ang += 4;
+  return ang;
+}
+
+/*
+int CW (const Point2d & p1,const Point2d & p2,const Point2d & p3)
+{
+  return Cross (p2 - p1, p3 - p2) < 0;
+}
+
+int CCW (const Point2d & p1,const Point2d & p2,const Point2d & p3)
+{
+  return Cross (p2 - p1, p3 - p2) > 0;
+}
+*/
+
+double  Dist2(const Line2d & g, const Line2d & h )
+  {
+  double   dd = 0.0, d1,d2,d3,d4;
+  Point2d  cp = CrossPoint(g,h);
+  
+  if ( Parallel(g,h) || !IsOnLine(g,cp) || !IsOnLine(h,cp) )
+    {
+      d1 = Dist2(g.P1(),h.P1());
+      d2 = Dist2(g.P1(),h.P2());
+      d3 = Dist2(g.P2(),h.P1());
+      d4 = Dist2(g.P2(),h.P2());
+      if (d1<d2)  d2 = d1;
+      if (d3<d4)  d4 = d3;
+      dd = ( d2 < d4 ) ? d2 : d4;
+    }
+  return dd;
+}
+
+
+Point2d CrossPoint (const Line2d & l1, const Line2d & l2)
+  {
+  double den = Cross (l1.Delta(), l2.Delta());
+  double num = Cross ( (l2.P1() - l1.P1()), l2.Delta());
+
+  if (den == 0)
+    return l1.P1();
+  else
+    return l1.P1() + (num/den) * l1.Delta();
+}
+
+
+int CrossPointBarycentric (const Line2d & l1, const Line2d & l2,
+			   double & lam1, double & lam2)
+{
+  // p = l1.1 + lam1 (l1.2-l1.1) = l2.1 + lam2 (l2.2-l2.1)
+  double a11 = l1.p2.X() - l1.p1.X();
+  double a21 = l1.p2.Y() - l1.p1.Y();
+  double a12 = -(l2.p2.X() - l2.p1.X());
+  double a22 = -(l2.p2.Y() - l2.p1.Y());
+
+  double b1 = l2.p1.X() - l1.p1.X();
+  double b2 = l2.p1.Y() - l1.p1.Y();
+  
+  double det = a11*a22 - a12 * a21;
+  if (det == 0)
+    return 1;
+    
+  lam1 = (a22 * b1 - a12 * b2) / det;
+  lam2 = (a11 * b2 - a21 * b1) / det;
+  return 0;
+}
+
+
+
+
+int Parallel (const Line2d & l1, const Line2d & l2, double peps)
+  {
+  double p = fabs (Cross (l1.Delta(), l2.Delta()));
+  //  (*mycout) << endl << p << "  " <<  l1.Length() << "  " << l2.Length() << endl;
+  return p <= peps * l1.Length() * l2.Length();
+}
+
+int IsOnLine (const Line2d & l, const Point2d & p, double heps)
+  {
+  double c1 = (p - l.P1()) * l.Delta();
+  double c2 = (p - l.P2()) * l.Delta();
+  double d = fabs (Cross ( (p - l.P1()), l.Delta()));
+  double len2 = l.Length2();
+
+  return c1 >= -heps * len2 && c2 <= heps * len2 && d <= heps * len2;
+}
+
+#ifdef none
+int IsOnLine (const PLine2d & l, const Point2d & p, double heps)
+  {
+  double c1 = (p - l.P1()) * l.Delta();
+  double c2 = (p - l.P2()) * l.Delta();
+  double d = fabs (Cross ( (p - l.P1()), l.Delta()));
+  double len2 = l.Length2();
+
+  return c1 >= -heps * len2 && c2 <= heps * len2 && d <= heps * len2;
+}
+
+int IsOnLongLine (const Line2d & l, const Point2d & p)
+  {
+  double d = fabs (Cross ( (p - l.P1()), l.Delta()));
+  return d <= EPSGEOM * l.Length();
+}
+
+int Hit (const Line2d & l1, const Line2d & l2, double heps)
+  {
+  double den =  Cross ( (l1.P2() - l1.P1()), (l2.P1() - l2.P2()));
+  double num1 = Cross ( (l2.P1() - l1.P1()), (l2.P1() - l2.P2()));
+  double num2 = Cross ( (l1.P2() - l1.P1()), (l2.P1() - l1.P1()));
+  num1 *= sgn (den);
+  num2 *= sgn (den);
+  den = fabs (den);
+
+  int ch = (-den * heps <= num1 && num1 <= den * (1 + heps) &&
+	    -den * heps <= num2 && num2 <= den * (1 + heps));
+  return ch;
+}
+
+
+void Line2d :: GetNormal (Line2d & n) const
+{
+  double 	ax  = P2().X()-P1().X(),
+    ay  = P2().Y()-P1().Y();
+  Point2d 	mid(P1().X()+.5*ax, P1().Y()+.5*ay);
+ 
+ n=Line2d(mid,Point2d(mid.X()+ay,mid.Y()-ax)) ;
+}
+
+Vec2d Line2d :: NormalDelta () const
+{
+ Line2d tmp;
+ GetNormal(tmp);
+ return tmp.Delta();
+}
+
+int TRIANGLE2D :: IsOn (const Point2d & p) const
+  {
+  return IsOnLine (Line2d (p1, p2), p) ||
+         IsOnLine (Line2d (p1, p3), p) ||
+         IsOnLine (Line2d (p2, p3), p);
+  }
+
+
+int TRIANGLE2D :: IsIn (const Point2d & p) const
+{
+  return ::CW(p, p1, p2) == ::CW(p, p2, p3) &&
+         ::CW(p, p1, p2) == ::CW(p, p3, p1);
+}
+
+
+
+int PTRIANGLE2D :: IsOn (const Point2d & p) const
+{
+  return IsOnLine (Line2d (*p1, *p2), p) ||
+         IsOnLine (Line2d (*p1, *p3), p) ||
+         IsOnLine (Line2d (*p2, *p3), p);
+}
+
+
+int PTRIANGLE2D :: IsIn (const Point2d & p) const
+{
+  return ::CW(p, *p1, *p2) == ::CW(p, *p2, *p3) &&
+         ::CW(p, *p1, *p2) == ::CW(p, *p3, *p1);
+}
+
+#endif
+
+
+
+
+
+
+Polygon2d :: Polygon2d ()
+{
+  ;
+}
+
+Polygon2d :: ~Polygon2d ()
+{
+  ;
+}
+
+void Polygon2d :: AddPoint (const Point2d & p)
+{ 
+  points.Append(p); 
+}
+
+
+double Polygon2d :: HArea () const
+{
+  int i;
+  double ar = 0;
+  for (i = 1; i <= points.Size(); i++)
+    {
+      const Point2d & p1 = points.Get(i);
+      const Point2d & p2 = points.Get(i%points.Size()+1);
+      ar += 
+	(p2.X()-p1.X()) * p1.Y() -
+	(p2.Y()-p1.Y()) * p1.X();
+    }
+  return ar/2;
+  /*
+  CURSOR c;
+  double ar = 0;
+  Point2d * p1, * p2, p0 = Point2d(0, 0);
+  Vec2d v1, v2 = Vec2d(1, 0);
+
+  p2 = points[points.Last()];
+  for (c = points.First(); c != points.Head(); c++)
+    {
+    p1 = p2;
+    p2 = points[c];
+    ar += Cross ( (*p2-*p1), (*p1 - p0));
+    }
+  return ar / 2;
+  */
+}
+
+
+int Polygon2d :: IsOn (const Point2d & p) const
+{
+  int i;
+  for (i = 1; i <= points.Size(); i++)
+    {
+      const Point2d & p1 = points.Get(i);
+      const Point2d & p2 = points.Get(i%points.Size()+1);
+      if (IsOnLine (Line2d(p1, p2), p)) return 1;
+    }
+  return 0;
+  /*
+  CURSOR c;
+  Point2d * p1, * p2;
+  
+  p2 = points[points.Last()];
+  for (c = points.First(); c != points.Head(); c++)
+    {
+      p1 = p2;
+      p2 = points[c];
+      if (IsOnLine (Line2d(*p1, *p2), p)) return 1;
+    }
+  return 0;
+  */
+}
+
+
+int Polygon2d :: IsIn (const Point2d & p) const
+{
+  int i;
+  double sum = 0, ang;
+  for (i = 1; i <= points.Size(); i++)
+    {
+      const Point2d & p1 = points.Get(i);
+      const Point2d & p2 = points.Get(i%points.Size()+1);
+      ang = Angle ( (p1 - p), (p2 - p) );
+      if (ang > M_PI) ang -= 2 * M_PI;
+      sum += ang;
+    }
+  return fabs(sum) > M_PI;
+  /*
+  CURSOR c;
+  Point2d * p1, * p2;
+  double sum = 0, ang;
+
+  p2 = points[points.Last()];
+  for (c = points.First(); c != points.Head(); c++)
+    {
+    p1 = p2;
+    p2 = points[c];
+    ang = Angle ( (*p1 - p), (*p2 - p) );
+    if (ang > M_PI) ang -= 2 * M_PI;
+    sum += ang;
+    }
+
+  return fabs(sum) > M_PI;
+  */
+}
+
+int Polygon2d :: IsConvex () const
+  {
+    /*
+  Point2d *p, *pold, *pnew;
+  char cw;
+  CURSOR c;
+
+  if (points.Length() < 3) return 0;
+
+  c = points.Last();
+  p = points[c];
+  c--;
+  pold = points[c];
+  pnew = points[points.First()];
+  cw = ::CW (*pold, *p, *pnew);
+
+  for (c = points.First(); c != points.Head(); c++)
+    {
+    pnew = points[c];
+    if (cw != ::CW (*pold, *p, *pnew))
+      return 0;
+    pold = p;
+    p = pnew;
+    }
+    */
+    return 0;
+  }
+
+
+int Polygon2d :: IsStarPoint (const Point2d & p) const
+  {
+    /*
+  Point2d *pnew, *pold;
+  char cw;
+  CURSOR c;
+
+  if (points.Length() < 3) return 0;
+
+  pold = points[points.Last()];
+  pnew = points[points.First()];
+
+  cw = ::CW (p, *pold, *pnew);
+
+  for (c = points.First(); c != points.Head(); c++)
+    {
+    pnew = points[c];
+    if (cw != ::CW (p, *pold, *pnew))
+      return 0;
+    pold = pnew;
+    }
+  return 1;
+    */
+    return 0;
+  }
+
+
+Point2d Polygon2d :: Center () const
+  {
+    /*
+  double ai, a = 0, x = 0, y = 0;
+  Point2d * p, *p2;
+  Point2d p0 = Point2d(0, 0);
+  CURSOR c;
+
+  p2 = points[points.Last()];
+
+  for (c = points.First(); c != points.Head(); c++)
+    {
+    p = points[c];
+    ai = Cross (*p2 - p0, *p - p0);
+    x += ai / 3 * (p2->X() + p->X());
+    y += ai / 3 * (p2->Y() + p->Y());
+    a+= ai;
+    p2 = p;
+    }
+  if (a != 0)
+    return Point2d (x / a, y / a);
+  else
+    return Point2d (0, 0);
+    */
+    return Point2d (0, 0);
+  }
+
+
+
+Point2d Polygon2d :: EqualAreaPoint () const
+  {
+    /*
+  double a11 = 0, a12 = 0, a21= 0, a22 = 0;
+  double b1 = 0, b2 = 0, dx, dy;
+  double det;
+  Point2d * p, *p2;
+  CURSOR c;
+
+  p = points[points.Last()];
+
+  for (c = points.First(); c != points.Head(); c++)
+    {
+    p2 = p;
+    p = points[c];
+
+    dx = p->X() - p2->X();
+    dy = p->Y() - p2->Y();
+
+    a11 += sqr (dy);
+    a12 -= dx * dy;
+    a21 -= dx * dy;
+    a22 += sqr (dx);
+    b1 -= dy * (p->X() * p2->Y() - p2->X() * p->Y());
+    b2 -= dx * (p->Y() * p2->X() - p2->Y() * p->X());
+    }
+
+  det = a11 * a22 - a21 * a12;
+
+  if (det != 0)
+    return Point2d ( (b1 * a22 - b2 * a12) / det,
+                     (a11 * b2 - a21 * b1) / det);
+  else
+    return Point2d (0, 0);
+*/
+    return Point2d (0, 0);
+  }
+
+
+}
diff --git a/contrib/Netgen/libsrc/gprim/geom2d.hpp b/contrib/Netgen/libsrc/gprim/geom2d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..334df09ca532ed2c7a03f3059b27cc2a88d4f398
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geom2d.hpp
@@ -0,0 +1,886 @@
+#ifndef FILE_GEOM2D
+#define FILE_GEOM2D
+
+/* *************************************************************************/
+/* File:   geom2d.hh                                                       */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   5. Aug. 95                                                      */
+/* *************************************************************************/
+
+namespace netgen 
+{
+
+  /* Geometric Algorithms */
+
+#define EPSGEOM 1E-5
+
+
+  // extern void MyError (const char * ch);
+
+  class Point2d;
+  class Vec2d;
+
+  class LINE2D;
+  class Line2d;
+  class PLine2d;
+  class TRIANGLE2D;
+  class PTRIANGLE2D;
+
+
+  inline Vec2d operator- (const Point2d & p1, const Point2d & p2);
+  inline Point2d operator- (const Point2d & p1, const Vec2d & v);
+  inline Point2d operator+ (const Point2d & p1, const Vec2d & v);
+  inline Point2d Center (const Point2d & p1, const Point2d & p2);
+
+  inline void PpSmV (const Point2d & p1, double s, const Vec2d & v, Point2d & p2);
+  inline void PmP (const Point2d & p1, const Point2d & p2, Vec2d & v);
+  ostream & operator<<(ostream  & s, const Point2d & p);
+  inline Vec2d operator- (const Point2d & p1, const Point2d & p2);
+  inline Point2d operator- (const Point2d & p1, const Vec2d & v);
+  inline Point2d operator+ (const Point2d & p1, const Vec2d & v);
+  inline Vec2d operator- (const Vec2d & p1, const Vec2d & v);
+  inline Vec2d operator+ (const Vec2d & p1, const Vec2d & v);
+  inline Vec2d operator* (double scal, const Vec2d & v);
+  DLL_HEADER double Angle (const Vec2d & v);
+  DLL_HEADER double FastAngle (const Vec2d & v);
+  DLL_HEADER double Angle (const Vec2d & v1, const Vec2d & v2);
+  DLL_HEADER double FastAngle (const Vec2d & v1, const Vec2d & v2);
+  ostream & operator<<(ostream  & s, const Vec2d & v);
+  double Dist2(const Line2d & g, const Line2d & h );		// GH
+  int Near (const Point2d & p1, const Point2d & p2, const double eps);
+
+  int Parallel (const Line2d & l1, const Line2d & l2, double peps = EPSGEOM);
+  int IsOnLine (const Line2d & l, const Point2d & p, double heps = EPSGEOM);
+  int IsOnLongLine (const Line2d & l, const Point2d & p);
+  int Hit (const Line2d & l1, const Line2d & l2, double heps = EPSGEOM);
+  ostream & operator<<(ostream  & s, const Line2d & l);
+  DLL_HEADER Point2d CrossPoint (const PLine2d & l1, const PLine2d & l2);
+  DLL_HEADER Point2d CrossPoint (const Line2d & l1, const Line2d & l2);
+  int Parallel (const PLine2d & l1, const PLine2d & l2, double peps = EPSGEOM);
+  int IsOnLine (const PLine2d & l, const Point2d & p, double heps = EPSGEOM);
+  int IsOnLongLine (const PLine2d & l, const Point2d & p);
+  int Hit (const PLine2d & l1, const Line2d & l2, double heps = EPSGEOM);
+  ostream & operator<<(ostream  & s, const Line2d & l);
+  ostream & operator<<(ostream  & s, const TRIANGLE2D & t); 
+  ostream & operator<<(ostream & s, const PTRIANGLE2D & t);
+  double Dist2 (const Point2d & p1, const Point2d & p2);
+
+  ///
+  class Point2d
+  {
+    ///
+    friend class Vec2d;
+
+  protected:
+    ///
+    double px, py;
+
+  public:
+    ///
+    Point2d() { /* px = py = 0; */ }
+    ///
+    Point2d(double ax, double ay) { px = ax; py = ay; }
+    ///
+    Point2d(const Point2d & p2) { px = p2.px; py = p2.py; }
+
+    Point2d (const Point<2> & p2)
+    {
+      px = p2(0);
+      py = p2(1);
+    }
+    ///
+    Point2d & operator= (const Point2d & p2)
+    { px = p2.px; py = p2.py; return *this; }
+    
+    ///
+    int operator== (const Point2d & p2) const			// GH
+    { return (px == p2.px  &&  py == p2.py) ; }
+
+    ///
+    double & X() { return px; }
+    ///
+    double & Y() { return py; }
+    ///
+    double X() const { return px; }
+    ///
+    double Y() const { return py; }
+
+    operator Point<2> () const
+    {
+      return Point<2> (px, py);
+    }
+
+
+    ///
+    friend inline Vec2d operator- (const Point2d & p1, const Point2d & p2);
+    ///
+    friend inline Point2d operator- (const Point2d & p1, const Vec2d & v);
+    ///
+    friend inline Point2d operator+ (const Point2d & p1, const Vec2d & v);
+
+    ///
+    friend inline Point2d Center (const Point2d & p1, const Point2d & p2);
+
+    const Point2d & SetToMin (const Point2d & p2)
+    {
+      if (p2.px < px) px = p2.px;
+      if (p2.py < py) py = p2.py;
+      return *this;
+    }
+
+
+    ///
+    const Point2d & SetToMax (const Point2d & p2)
+    {
+      if (p2.px > px) px = p2.px;
+      if (p2.py > py) py = p2.py;
+      return *this;
+    }
+
+    ///
+    friend double Dist (const Point2d & p1, const Point2d & p2)
+    { return sqrt ( (p1.px - p2.px) * (p1.px - p2.px) +
+		    (p1.py - p2.py) * (p1.py - p2.py) ); }
+    //    { return sqrt ( sqr (p1.X()-p2.X()) + sqr (p1.Y()-p2.Y()) ); }
+
+    ///
+    friend double Dist2 (const Point2d & p1, const Point2d & p2)
+    { return ( (p1.px - p2.px) * (p1.px - p2.px) +
+	       (p1.py - p2.py) * (p1.py - p2.py) ); }
+    //    { return sqr (p1.X()-p2.X()) + sqr (p1.Y()-p2.Y()) ; }
+
+
+    /**
+       Points clock-wise ?
+       Are the points (p1, p2, p3) clock-wise ?
+    */
+    friend inline int CW (const Point2d & p1, const Point2d & p2, const Point2d & p3)
+    {
+      //      return Cross (p2 - p1, p3 - p2) < 0;      
+      return
+	(p2.px - p1.px) * (p3.py - p2.py) - 
+	(p2.py - p1.py) * (p3.px - p2.px) < 0;
+    }
+    /**
+       Points counter-clock-wise ?
+       Are the points (p1, p2, p3) counter-clock-wise ?
+    */
+    friend inline bool CCW (const Point2d & p1, const Point2d & p2, const Point2d & p3)
+    {
+      //      return Cross (p2 - p1, p3 - p2) > 0;
+      return
+	(p2.px - p1.px) * (p3.py - p2.py) - 
+	(p2.py - p1.py) * (p3.px - p2.px) > 0;
+    }  /**
+	  Points counter-clock-wise ?
+	  Are the points (p1, p2, p3) counter-clock-wise ?
+       */
+    friend inline bool CCW (const Point2d & p1, const Point2d & p2, const Point2d & p3, double eps)
+    {
+      //      return Cross (p2 - p1, p3 - p2) > 0;
+      double ax = p2.px - p1.px;
+      double ay = p2.py - p1.py;
+      double bx = p3.px - p2.px;
+      double by = p3.py - p2.py;
+
+      return ax*by - ay*bx > eps*eps*max2(ax*ax+ay*ay,bx*bx+by*by);
+    }
+
+    ///
+    friend inline void PpSmV (const Point2d & p1, double s, const Vec2d & v, Point2d & p2);
+    ///
+    friend inline void PmP (const Point2d & p1, const Point2d & p2, Vec2d & v);
+
+    ///
+    friend ostream & operator<<(ostream  & s, const Point2d & p);
+  };
+
+
+  inline int Near (const Point2d & p1, const Point2d & p2, 
+		   const double eps = 1e-4 )
+  { 
+    return  Dist2(p1,p2) <= eps*eps; 
+  }
+
+
+
+
+
+
+  ///
+  class Vec2d
+  {
+  protected:
+    ///
+    double vx, vy;
+
+  public:
+    ///
+    Vec2d() { /* vx = vy = 0; */ }
+    ///
+    Vec2d(double ax, double ay)
+    { vx = ax; vy = ay; }
+    ///
+    Vec2d(const Vec2d & v2) { vx = v2.vx; vy = v2.vy; }
+
+    ///
+    explicit Vec2d(const Vec<2> & v2) { vx = v2(0); vy = v2(1); }
+
+    ///
+    Vec2d(const Point2d & p1, const Point2d & p2)
+    { vx = p2.px - p1.px; vy = p2.py - p1.py; }
+    
+    ///
+    Vec2d & operator= (const Vec2d & p2)
+    { vx = p2.vx; vy = p2.vy; return *this; }
+
+    ///
+    double & X() { return vx; }
+    ///
+    double & Y() { return vy; }
+    ///
+    double X() const { return vx; }
+    ///
+    double Y() const { return vy; }
+
+    ///
+    double Length() const { return sqrt (vx * vx + vy * vy); }
+    ///
+    double Length2() const { return vx * vx + vy * vy; }
+
+    void GetNormal (Vec2d & n) const { n.vx=-vy; n.vy=vx; }		// GH
+
+    ///
+    inline Vec2d & operator+= (const Vec2d & v2);
+    ///
+    inline Vec2d & operator-= (const Vec2d & v2);
+    ///
+    inline Vec2d & operator*= (double s);
+    ///
+    inline Vec2d & operator/= (double s);
+
+    ///
+    friend inline Vec2d operator- (const Point2d & p1, const Point2d & p2);
+    ///
+    friend inline Point2d operator- (const Point2d & p1, const Vec2d & v);
+    ///
+    friend inline Point2d operator+ (const Point2d & p1, const Vec2d & v);
+    ///
+    friend inline Vec2d operator- (const Vec2d & p1, const Vec2d & v);
+    ///
+    friend inline Vec2d operator+ (const Vec2d & p1, const Vec2d & v);
+    ///
+    friend inline Vec2d operator* (double scal, const Vec2d & v);
+
+    ///
+    friend double operator* (const Vec2d & v1, const Vec2d & v2)
+    { return v1.X() * v2.X() + v1.Y() * v2.Y(); }
+
+
+    ///
+    friend double Cross (const Vec2d & v1, const Vec2d & v2)
+    { return double(v1.X()) * double(v2.Y()) -
+	double(v1.Y()) * double(v2.X()); }
+
+    ///
+    friend inline void PpSmV (const Point2d & p1, double s, const Vec2d & v, Point2d & p2);
+    ///
+    friend inline void PmP (const Point2d & p1, const Point2d & p2, Vec2d & v);
+
+    ///						Angle in [0,2*PI)
+
+    ///
+    friend DLL_HEADER double Angle (const Vec2d & v);
+    ///
+    friend DLL_HEADER double FastAngle (const Vec2d & v);
+    ///
+    friend DLL_HEADER double Angle (const Vec2d & v1, const Vec2d & v2);
+    ///
+    friend DLL_HEADER double FastAngle (const Vec2d & v1, const Vec2d & v2);
+
+    ///
+    friend ostream & operator<<(ostream  & s, const Vec2d & v);
+  };
+
+
+
+  ///
+  class Line2d
+  {
+  protected:
+    ///
+    Point2d p1, p2;
+
+  public:
+    ///
+    Line2d() : p1(), p2() { };
+    ///
+    Line2d(const Point2d & ap1, const Point2d & ap2)
+    { p1 = ap1; p2 = ap2; }
+
+    ///
+    Line2d & operator= (const Line2d & l2)
+    { p1 = l2.p1; p2 = l2.p2; return *this;}
+
+    ///
+    Point2d & P1() { return p1; }
+    ///
+    Point2d & P2() { return p2; }
+    ///
+    const Point2d & P1() const { return p1; }
+    ///
+    const Point2d & P2() const { return p2; }
+
+    ///
+    double XMax() const { return max2 (p1.X(), p2.X()); }
+    ///
+    double YMax() const { return max2 (p1.Y(), p2.Y()); }
+    ///
+    double XMin() const { return min2 (p1.X(), p2.X()); }
+    ///
+    double YMin() const { return min2 (p1.Y(), p2.Y()); }
+
+    ///
+    Vec2d Delta () const { return Vec2d (p2.X()-p1.X(), p2.Y()-p1.Y()); }
+    ///
+    double Length () const { return Delta().Length(); }
+    ///
+    double Length2 () const
+    { return sqr (p1.X() - p2.X()) +
+	sqr (p1.Y() - p2.Y()); }
+
+    void GetNormal (Line2d & n) const;					// GH
+    Vec2d NormalDelta () const;						// GH
+
+    /// square of the distance between two 2d-lines.
+    friend double Dist2(const Line2d & g, const Line2d & h );		// GH
+
+    ///
+    friend DLL_HEADER Point2d CrossPoint (const Line2d & l1, const Line2d & l2);
+    /// returns 1 iff parallel
+    friend int CrossPointBarycentric (const Line2d & l1, const Line2d & l2,
+				      double & lam1, double & lam2);
+    
+    ///
+    friend int Parallel (const Line2d & l1, const Line2d & l2, double peps);
+    ///
+    friend int IsOnLine (const Line2d & l, const Point2d & p, double heps);
+    ///
+    friend int IsOnLongLine (const Line2d & l, const Point2d & p);
+    ///
+    friend int Hit (const Line2d & l1, const Line2d & l2, double heps);
+
+    ///
+    friend ostream & operator<<(ostream  & s, const Line2d & l);
+  };
+
+
+#ifdef NONE
+  ///
+  class PLine2d
+  {
+  protected:
+    ///
+    Point2d const * p1, *p2;
+
+  public:
+    ///
+    PLine2d() { };
+    ///
+    PLine2d(Point2d const * ap1, Point2d const * ap2)
+    { p1 = ap1; p2 = ap2; }
+
+    ///
+    PLine2d & operator= (const PLine2d & l2)
+    { p1 = l2.p1; p2 = l2.p2; return *this;}
+
+    ///
+    const Point2d *& P1() { return p1; }
+    ///
+    const Point2d *& P2() { return p2; }
+    ///
+    const Point2d & P1() const { return *p1; }
+    ///
+    const Point2d & P2() const { return *p2; }
+
+    ///
+    double XMax() const { return max2 (p1->X(), p2->X()); }
+    ///
+    double YMax() const { return max2 (p1->Y(), p2->Y()); }
+    ///
+    double XMin() const { return min2 (p1->X(), p2->X()); }
+    ///
+    double YMin() const { return min2 (p1->Y(), p2->Y()); }
+
+
+    ///
+    Vec2d Delta () const { return Vec2d (p2->X()-p1->X(), p2->Y()-p1->Y()); }
+    ///
+    double Length () const { return Delta().Length(); }
+    ///
+    double Length2 () const
+    { return sqr (p1->X() - p2->X()) +
+	sqr (p1->Y() - p2->Y()); }
+
+
+    
+    ///
+    friend Point2d CrossPoint (const PLine2d & l1, const PLine2d & l2);
+    ///
+    friend int Parallel (const PLine2d & l1, const PLine2d & l2, double peps);
+    ///
+    friend int IsOnLine (const PLine2d & l, const Point2d & p, double heps);
+    ///
+    friend int IsOnLongLine (const PLine2d & l, const Point2d & p);
+    ///
+    friend int Hit (const PLine2d & l1, const Line2d & l2, double heps);
+
+    ///
+    friend ostream & operator<<(ostream  & s, const Line2d & l);
+  };
+
+
+
+  ///
+  class ILINE
+  {
+    ///
+    INDEX i[2];
+
+  public:
+    ///
+    ILINE() {};
+    ///
+    ILINE(INDEX i1, INDEX i2) { i[0] = i1; i[1] = i2; }
+    ///
+    ILINE(const ILINE & l) { i[0] = l.i[0]; i[1] = l.i[1]; }
+
+    ///
+    ILINE & operator= (const ILINE & l)
+    { i[0] = l.i[0]; i[1] = l.i[1]; return *this; }
+
+    ///
+    const INDEX & I(int ai) const { return i[ai-1]; }
+    ///
+    const INDEX & X() const { return i[0]; }
+    ///
+    const INDEX & Y() const { return i[1]; }
+    ///
+    const INDEX & I1() const { return i[0]; }
+    ///
+    const INDEX & I2() const { return i[1]; }
+
+    ///
+    INDEX & I(int ai) { return i[ai-1]; }
+    ///
+    INDEX & X() { return i[0]; }
+    ///
+    INDEX & Y() { return i[1]; }
+    ///
+    INDEX & I1() { return i[0]; }
+    ///
+    INDEX & I2() { return i[1]; }
+  };
+
+
+
+
+  ///
+  class TRIANGLE2D
+  {
+  private:
+    ///
+    Point2d p1, p2, p3;
+
+  public:
+    ///
+    TRIANGLE2D() { };
+    ///
+    TRIANGLE2D (const Point2d & ap1, const Point2d & ap2,
+		const Point2d & ap3)
+    { p1 = ap1; p2 = ap2; p3 = ap3;}
+
+    ///
+    TRIANGLE2D & operator= (const TRIANGLE2D & t2)
+    { p1 = t2.p1; p2 = t2.p2; p3 = t2.p3; return *this; }
+
+    ///
+    Point2d & P1() { return p1; }
+    ///
+    Point2d & P2() { return p2; }
+    ///
+    Point2d & P3() { return p3; }
+    ///
+    const Point2d & P1() const { return p1; }
+    ///
+    const Point2d & P2() const { return p2; }
+    ///
+    const Point2d & P3() const { return p3; }
+
+    ///
+    double XMax() const { return max3 (p1.X(), p2.X(), p3.X()); }
+    ///
+    double YMax() const { return max3 (p1.Y(), p2.Y(), p3.Y()); }
+    ///
+    double XMin() const { return min3 (p1.X(), p2.X(), p3.X()); }
+    ///
+    double YMin() const { return min3 (p1.Y(), p2.Y(), p3.Y()); }
+
+    ///
+    inline Point2d Center () const
+    { return Point2d( (p1.X()+p2.X()+p3.X())/3, (p1.Y()+p2.Y()+p3.Y())/3); }
+
+    ///
+    int Regular() const;
+    /// 
+    int CW () const;
+    ///
+    int CCW () const;
+
+    ///
+    int IsOn (const Point2d & p) const;
+    ///
+    int IsIn (const Point2d & p) const;
+    ///
+    friend ostream & operator<<(ostream  & s, const TRIANGLE2D & t);
+  };
+
+
+  ///
+  class PTRIANGLE2D
+  {
+  private:
+    ///
+    Point2d const *p1, *p2, *p3;
+
+  public:
+    ///
+    PTRIANGLE2D() { };
+    ///
+    PTRIANGLE2D (const Point2d * ap1, const Point2d * ap2,
+		 const Point2d * ap3)
+    { p1 = ap1; p2 = ap2; p3 = ap3;}
+
+    ///
+    PTRIANGLE2D & operator= (const PTRIANGLE2D & t2)
+    { p1 = t2.p1; p2 = t2.p2; p3 = t2.p3; return *this; }
+
+    ///
+    const Point2d *& P1() { return p1; }
+    ///
+    const Point2d *& P2() { return p2; }
+    ///
+    const Point2d *& P3() { return p3; }
+    ///
+    const Point2d * P1() const { return p1; }
+    ///
+    const Point2d * P2() const { return p2; }
+    ///
+    const Point2d * P3() const { return p3; }
+
+    ///
+    double XMax() const { return max3 (p1->X(), p2->X(), p3->X()); }
+    ///
+    double YMax() const { return max3 (p1->Y(), p2->Y(), p3->Y()); }
+    ///
+    double XMin() const { return min3 (p1->X(), p2->X(), p3->X()); }
+    ///
+    double YMin() const { return min3 (p1->Y(), p2->Y(), p3->Y()); }
+
+    ///
+    Point2d Center () const
+    { return Point2d( (p1->X()+p2->X()+p3->X())/3, (p1->Y()+p2->Y()+p3->Y())/3); }
+
+
+    ///
+    int Regular() const;
+    ///
+    int CW () const;
+    ///
+    int CCW () const;
+
+    ///
+    int IsOn (const Point2d & p) const;
+    ///
+    int IsIn (const Point2d & p) const;
+    ///
+    friend ostream & operator<<(ostream & s, const PTRIANGLE2D & t);
+  };
+#endif
+
+
+
+  class Polygon2d
+  {
+  protected:
+    Array<Point2d> points;
+  
+  public:
+    Polygon2d ();
+    ~Polygon2d ();
+  
+    void AddPoint (const Point2d & p);
+    int GetNP() const { return points.Size(); }
+    void GetPoint (int i, Point2d & p) const
+    { p = points.Get(i); }
+    void GetLine (int i, Point2d & p1, Point2d & p2) const
+    { p1 = points.Get(i); p2 = points.Get(i%points.Size()+1); }
+
+    double Area () const { return fabs (HArea()); }
+    int CW () const { return HArea() > 0; }
+    int CCW () const { return HArea() < 0; }
+  
+    int IsOn (const Point2d & p) const;
+    int IsIn (const Point2d & p) const;
+
+    int IsConvex () const;
+
+    int IsStarPoint (const Point2d & p) const;
+    Point2d Center() const;
+    Point2d EqualAreaPoint () const;
+  private:
+    double HArea () const;
+  };
+
+
+  /** Cheap approximation to atan2.
+      A monotone function of atan2(x,y) is computed.
+  */
+  extern double Fastatan2 (double x, double y);
+
+
+  inline Vec2d & Vec2d :: operator+= (const Vec2d & v2)
+  {
+    vx += v2.vx;
+    vy += v2.vy;
+    return *this;
+  }
+
+  inline Vec2d & Vec2d :: operator-= (const Vec2d & v2)
+  {
+    vx -= v2.vx;
+    vy -= v2.vy;
+    return *this;
+  }
+
+  inline Vec2d & Vec2d :: operator*= (double s)
+  {
+    vx *= s;
+    vy *= s;
+    return *this;
+  }
+
+
+  inline Vec2d & Vec2d :: operator/= (double s)
+  {
+    if (s != 0)
+      {
+	vx /= s;
+	vy /= s;
+      }
+    else
+      {
+	MyError ("Vec2d::operator /=: Division by zero");
+      }
+    return *this;
+  }
+
+
+
+  inline Vec2d operator- (const Point2d & p1, const Point2d & p2)
+  {
+    return Vec2d (p1.X() - p2.X(), p1.Y() - p2.Y());
+  }
+
+
+  inline Point2d operator- (const Point2d & p1, const Vec2d & v)
+  {
+    return Point2d (p1.X() - v.X(), p1.Y() - v.Y());
+  }
+
+
+  inline Point2d operator+ (const Point2d & p1, const Vec2d & v)
+  {
+    return Point2d (p1.X() + v.X(), p1.Y() + v.Y());
+  }
+
+
+  inline Point2d Center (const Point2d & p1, const Point2d & p2)
+  {
+    return Point2d ((p1.X() + p2.X()) / 2, (p1.Y() + p2.Y()) / 2);
+  }
+
+
+  inline Vec2d operator- (const Vec2d & v1, const Vec2d & v2)
+  {
+    return Vec2d (v1.X() - v2.X(), v1.Y() - v2.Y());
+  }
+
+
+  inline Vec2d operator+ (const Vec2d & v1, const Vec2d & v2)
+  {
+    return Vec2d (v1.X() + v2.X(), v1.Y() + v2.Y());
+  }
+
+
+  inline Vec2d operator* (double scal, const Vec2d & v)
+  {
+    return Vec2d (scal * v.X(), scal * v.Y());
+  }
+
+
+  inline void PpSmV (const Point2d & p1, double s,
+		     const Vec2d & v, Point2d & p2)
+  {
+    p2.X() = p1.X() + s * v.X();
+    p2.Y() = p1.Y() + s * v.Y();
+  }
+
+
+  inline void PmP (const Point2d & p1, const Point2d & p2, Vec2d & v)
+  {
+    v.X() = p1.X() - p2.X();
+    v.Y() = p1.Y() - p2.Y();
+  }
+
+
+
+
+
+#ifdef none
+  inline int TRIANGLE2D :: Regular() const
+  {
+    return fabs(Cross ( p2 - p1, p3 - p2)) > EPSGEOM;
+  }
+
+
+  inline int TRIANGLE2D :: CW () const
+  {
+    return Cross ( p2 - p1, p3 - p2) < 0;
+  }
+
+
+  inline int TRIANGLE2D :: CCW () const
+  {
+    return Cross ( p2 - p1, p3 - p2) > 0;
+  }
+
+
+
+
+  inline int PTRIANGLE2D :: Regular() const
+  {
+    return fabs(Cross ( *p2 - *p1, *p3 - *p2)) > EPSGEOM;
+  }
+
+
+  inline int PTRIANGLE2D :: CW () const
+  {
+    return Cross ( *p2 - *p1, *p3 - *p2) < 0;
+  }
+
+
+  inline int PTRIANGLE2D :: CCW () const
+  {
+    return Cross ( *p2 - *p1, *p3 - *p2) > 0;
+  }
+
+
+#endif
+
+
+  ///
+  class Mat2d
+  {
+  protected:
+    ///
+    double coeff[4];
+
+  public:
+    ///
+    Mat2d() { coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0; }
+    ///
+    Mat2d(double a11, double a12, double a21, double a22)
+    { coeff[0] = a11; coeff[1] = a12; coeff[2] = a21; coeff[3] = a22; }
+    ///
+    Mat2d(const Mat2d & m2)
+    { for (int i = 0; i < 4; i++) coeff[i] = m2.Get(i); }
+
+    ///
+    double & Elem (INDEX i, INDEX j) { return coeff[2*(i-1)+j-1]; }
+    ///
+    double & Elem (INDEX i) {return coeff[i]; }
+    ///
+    double Get (INDEX i, INDEX j) const { return coeff[2*(i-1)+j-1]; }
+    ///
+    double Get (INDEX i) const {return coeff[i]; }
+
+    ///  
+    double Det () const { return coeff[0] * coeff[3] - coeff[1] * coeff[2]; }
+
+    ///
+    void Mult (const Vec2d & v, Vec2d & prod) const;
+    ///
+    void MultTrans (const Vec2d & v , Vec2d & prod) const;
+    ///
+    void Solve (const Vec2d & rhs, Vec2d & x) const;
+    /// Solves mat * x = rhs, but using a positive definite matrix instead of mat
+    void SolvePositiveDefinite (const Vec2d & rhs, Vec2d & x) const;
+    /// add a term \alpha * v * v^T
+    void AddDiadicProduct (double alpha, Vec2d & v);
+  };
+
+
+
+  inline void Mat2d :: Mult (const Vec2d & v, Vec2d & prod) const
+  {
+    prod.X() = coeff[0] * v.X() + coeff[1] * v.Y();
+    prod.Y() = coeff[2] * v.X() + coeff[3] * v.Y();
+  }
+
+
+  inline  void Mat2d :: MultTrans (const Vec2d & v, Vec2d & prod) const
+  {
+    prod.X() = coeff[0] * v.X() + coeff[2] * v.Y();
+    prod.Y() = coeff[1] * v.X() + coeff[3] * v.Y();
+  }
+
+
+
+  inline void Mat2d :: Solve (const Vec2d & rhs, Vec2d & x) const
+  {
+    double det = Det();
+  
+    if (det == 0)
+      MyError ("Mat2d::Solve: zero determinant");
+    else
+      {
+	x.X() = (coeff[3] * rhs.X() - coeff[1] * rhs.Y()) / det;
+	x.Y() = (-coeff[2] * rhs.X() + coeff[0] * rhs.Y()) / det;
+      }
+  }
+
+
+  inline void Mat2d :: SolvePositiveDefinite (const Vec2d & rhs, Vec2d & x) const
+  {
+    double a = max2(coeff[0], 1e-8);
+    double b = coeff[1] / a;
+    double c = coeff[2] / a;
+    double d = max2(coeff[3] - a *b * c, 1e-8);
+
+    x.X() = (rhs.X() - b * rhs.Y()) / a;
+    x.Y() = rhs.Y() / d - c * x.X();
+  }
+
+
+  inline void Mat2d :: AddDiadicProduct (double alpha, Vec2d & v)
+  {
+    coeff[0] += alpha * v.X() * v.X();
+    coeff[1] += alpha * v.X() * v.Y();
+    coeff[2] += alpha * v.Y() * v.X();
+    coeff[3] += alpha * v.Y() * v.Y();
+  }
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/geom3d.cpp b/contrib/Netgen/libsrc/gprim/geom3d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..04ecd13764d22a5fd460d1a2fafdf6d307c8f0ed
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geom3d.cpp
@@ -0,0 +1,731 @@
+#include <algorithm>
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+
+namespace netgen
+{
+ostream & operator<<(ostream  & s, const Point3d & p)
+  {
+  return s << "(" << p.x[0] << ", " << p.x[1] << ", " << p.x[2] << ")";
+  }
+
+ostream & operator<<(ostream  & s, const Vec3d & v)
+  {
+  return s << "(" << v.x[0] << ", " << v.x[1] << ", " << v.x[2] << ")";
+  }
+
+double Angle (const Vec3d & v1, const Vec3d & v2)
+{
+  double co = (v1 * v2) / (v1.Length() * v2.Length());
+  if (co > 1) co = 1;
+  if (co < -1) co = -1;
+  return acos ( co );
+}
+
+
+void Vec3d :: GetNormal (Vec3d & n) const
+  {
+  if (fabs (X()) > fabs (Z()))
+    {
+    n.X() = -Y();
+    n.Y() = X();
+    n.Z() = 0;
+    }
+  else
+    {
+    n.X() = 0;
+    n.Y() = Z();
+    n.Z() = -Y();
+    }
+  double len = n.Length();
+  if (len == 0)
+    {
+    n.X() = 1;
+    n.Y() = n.Z() = 0;
+    }
+  else
+    n /= len;
+  }
+
+/*
+ostream & operator<<(ostream  & s, const ROTDenseMatrix3D & r)
+  {
+  return s << "{ (" << r.txx << ", " << r.txy << ", " << r.txz << ") , ("
+                    << r.tyx << ", " << r.tyy << ", " << r.tyz << ") , ("
+                    << r.tzx << ", " << r.tzy << ", " << r.tzz << ") }";
+  }
+*/
+
+/*
+Vec3d operator- (const Point3d & p1, const Point3d & p2)
+  {
+  return Vec3d (p1.X() - p2.X(), p1.Y() - p2.Y(),p1.Z() - p2.Z());
+  }
+
+Point3d operator- (const Point3d & p1, const Vec3d & v)
+  {
+  return Point3d (p1.X() - v.X(), p1.Y() - v.Y(),p1.Z() - v.Z());
+  }
+
+Point3d operator+ (const Point3d & p1, const Vec3d & v)
+  {
+  return Point3d (p1.X() + v.X(), p1.Y() + v.Y(),p1.Z() + v.Z());
+  }
+
+Vec3d operator- (const Vec3d & v1, const Vec3d & v2)
+  {
+  return Vec3d (v1.X() - v2.X(), v1.Y() - v2.Y(),v1.Z() - v2.Z());
+  }
+
+Vec3d operator+ (const Vec3d & v1, const Vec3d & v2)
+  {
+  return Vec3d (v1.X() + v2.X(), v1.Y() + v2.Y(),v1.Z() + v2.Z());
+  }
+
+Vec3d operator* (double scal, const Vec3d & v)
+  {
+  return Vec3d (scal * v.X(), scal * v.Y(), scal * v.Z());
+  }
+*/
+/*
+double operator* (const Vec3d & v1, const Vec3d & v2)
+  {
+  return v1.X() * v2.X() + v1.Y() * v2.Y() + v1.Z() * v2.Z();
+  }
+
+double Cross (const Vec3d & v1, const Vec3d & v2)
+  {
+  return v1.X() * v2.Y() - v1.Y() * v2.X();
+  }
+*/
+
+/*
+void ROTDenseMatrix3D :: CalcRotMat(double ag, double bg, double lg, double size2, Vec3d r)
+  {
+  size = size2;
+  txx=size * ( cos(bg) * cos(lg) );
+  txy=size * ( cos(bg) * sin(lg) );
+  txz=size * (-sin(bg)           );
+
+  tyx=size * ( sin(ag) * sin(bg) * cos(lg) - cos(ag) * sin(lg) );
+  tyy=size * ( sin(ag) * sin(bg) * sin(lg) + cos(ag) * cos(lg) );
+  tyz=size * ( sin(ag) * cos(bg)                               );
+
+  tzx=size * ( cos(ag) * sin(bg) * cos(lg) + sin(ag) * sin(lg) );
+  tzy=size * ( cos(ag) * sin(bg) * sin(lg) - sin(ag) * cos(lg) );
+  tzz=size * ( cos(ag) * cos(bg)                               );
+
+  deltaR=r;
+  }
+ROTDenseMatrix3D :: ROTDenseMatrix3D(double ag, double bg, double lg, double size2, Vec3d r)
+  {CalcRotMat(ag, bg, lg, size2, r); }
+
+ROTDenseMatrix3D :: ROTDenseMatrix3D(Vec3d rot2)
+  {
+  Vec3d r2(0,0,0);
+  CalcRotMat(rot2.X(), rot2.Y(), rot2.Z(), 1, r2);
+  }
+
+ROTDenseMatrix3D ROTDenseMatrix3D :: INV()
+  {
+  ROTDenseMatrix3D rinv(txx/sqr(size),tyx/sqr(size),tzx/sqr(size),
+                   txy/sqr(size),tyy/sqr(size),tzy/sqr(size),
+                   txz/sqr(size),tyz/sqr(size),tzz/sqr(size),
+                   1/size,deltaR);
+  return rinv;
+  }
+
+Vec3d operator* (const ROTDenseMatrix3D & r, const Vec3d & v)
+  {
+  return Vec3d (r.XX() * v.X() + r.XY() * v.Y() + r.XZ() * v.Z(),
+                r.YX() * v.X() + r.YY() * v.Y() + r.YZ() * v.Z(),
+                r.ZX() * v.X() + r.ZY() * v.Y() + r.ZZ() * v.Z() );
+  }
+
+Point3d operator* (const ROTDenseMatrix3D & r, const Point3d & p)
+  {
+  return Point3d (r.XX() * p.X() + r.XY() * p.Y() + r.XZ() * p.Z(),
+                  r.YX() * p.X() + r.YY() * p.Y() + r.YZ() * p.Z(),
+                  r.ZX() * p.X() + r.ZY() * p.Y() + r.ZZ() * p.Z() );
+  }
+*/
+
+
+
+
+
+
+
+Box3d :: Box3d ( double aminx, double amaxx,
+                 double aminy, double amaxy,
+                 double aminz, double amaxz )
+{
+  minx[0] = aminx; maxx[0] = amaxx;
+  minx[1] = aminy; maxx[1] = amaxy;
+  minx[2] = aminz; maxx[2] = amaxz;
+}
+
+Box3d :: Box3d ( const Box3d & b2 )
+{
+  for (int i = 0; i < 3; i++)
+    {
+      minx[i] = b2.minx[i];
+      maxx[i] = b2.maxx[i];
+    }
+}
+
+Box3d :: Box3d ( const Box<3> & b2 )
+{
+  for (int i = 0; i < 3; i++)
+    {
+      minx[i] = b2.PMin()(i);
+      maxx[i] = b2.PMax()(i);
+    }
+}
+
+
+/*
+int Box3d :: Intersect (const Box3d & box2) const
+{
+  int i;
+  for (i = 0; i <= 2; i++)
+    if (minx[i] > box2.maxx[i] || maxx[i] < box2.minx[i])
+      return 0;
+  return 1;
+}
+*/
+
+/*
+void Box3d :: SetPoint (const Point3d & p)
+{
+  minx[0] = maxx[0] = p.X();
+  minx[1] = maxx[1] = p.Y();
+  minx[2] = maxx[2] = p.Z();
+}
+
+void Box3d :: AddPoint (const Point3d & p)
+{
+  if (p.X() < minx[0]) minx[0] = p.X();
+  if (p.X() > maxx[0]) maxx[0] = p.X();
+  if (p.Y() < minx[1]) minx[1] = p.Y();
+  if (p.Y() > maxx[1]) maxx[1] = p.Y();
+  if (p.Z() < minx[2]) minx[2] = p.Z();
+  if (p.Z() > maxx[2]) maxx[2] = p.Z();
+}
+*/
+
+void Box3d :: GetPointNr (int i, Point3d & point) const
+{
+  i--;
+  point.X() = (i & 1) ? maxx[0] : minx[0];
+  point.Y() = (i & 2) ? maxx[1] : minx[1];
+  point.Z() = (i & 4) ? maxx[2] : minx[2];
+}
+
+
+void Box3d :: Increase (double d)
+{
+  for (int i = 0; i <= 2; i++)
+    {
+      minx[i] -= d;
+      maxx[i] += d;
+    }
+}
+
+void Box3d :: IncreaseRel (double /* rel */)
+{
+  for (int i = 0; i <= 2; i++)
+    {
+      double d = 0.5 * (maxx[i] - minx[i]);
+      minx[i] -= d;
+      maxx[i] += d;
+    }
+}
+
+
+Box3d :: Box3d (const Point3d& p1, const Point3d& p2)
+{
+  minx[0] = min2 (p1.X(), p2.X());
+  minx[1] = min2 (p1.Y(), p2.Y());
+  minx[2] = min2 (p1.Z(), p2.Z());
+  maxx[0] = max2 (p1.X(), p2.X());
+  maxx[1] = max2 (p1.Y(), p2.Y());
+  maxx[2] = max2 (p1.Z(), p2.Z());
+}
+
+const Box3d& Box3d :: operator+=(const Box3d& b)
+{
+  minx[0] = min2 (minx[0], b.minx[0]);
+  minx[1] = min2 (minx[1], b.minx[1]);
+  minx[2] = min2 (minx[2], b.minx[2]);
+  maxx[0] = max2 (maxx[0], b.maxx[0]);
+  maxx[1] = max2 (maxx[1], b.maxx[1]);
+  maxx[2] = max2 (maxx[2], b.maxx[2]);
+
+  return *this;
+}
+
+Point3d Box3d :: MaxCoords() const
+{
+  return Point3d(maxx[0], maxx[1], maxx[2]);
+}
+
+Point3d Box3d :: MinCoords() const
+{
+  return Point3d(minx[0], minx[1], minx[2]);
+}
+
+/*
+void Box3d :: CreateNegMinMaxBox()
+{
+  minx[0] = MAXDOUBLE;
+  minx[1] = MAXDOUBLE;
+  minx[2] = MAXDOUBLE;
+  maxx[0] = MINDOUBLE;
+  maxx[1] = MINDOUBLE;
+  maxx[2] = MINDOUBLE;
+
+}
+*/
+
+void Box3d :: WriteData(ofstream& fout) const
+{
+  for(int i = 0; i < 3; i++)
+    {
+      fout << minx[i] << " " << maxx[i] << " ";
+    }
+  fout << "\n";
+}
+
+void Box3d :: ReadData(ifstream& fin)
+{
+  for(int i = 0; i < 3; i++)
+    {
+      fin >> minx[i];
+      fin >> maxx[i];
+    }
+}
+
+
+
+
+Box3dSphere :: Box3dSphere ( double aminx, double amaxx,
+			     double aminy, double amaxy,
+			     double aminz, double amaxz )
+  : Box3d (aminx, amaxx, aminy, amaxy, aminz, amaxz)
+{
+  CalcDiamCenter ();
+}
+
+
+void Box3dSphere :: CalcDiamCenter ()
+{
+  diam = sqrt( sqr (maxx[0] - minx[0]) +
+	       sqr (maxx[1] - minx[1]) + 
+	       sqr (maxx[2] - minx[2]));
+  
+  c.X() = 0.5 * (minx[0] + maxx[0]);
+  c.Y() = 0.5 * (minx[1] + maxx[1]);
+  c.Z() = 0.5 * (minx[2] + maxx[2]);
+  
+  inner = min2 ( min2 (maxx[0] - minx[0], maxx[1] - minx[1]), maxx[2] - minx[2]) / 2;
+}
+
+
+void Box3dSphere :: GetSubBox (int i, Box3dSphere & sbox) const
+{
+  i--;
+  if (i & 1)
+    {
+      sbox.minx[0] = c.X();
+      sbox.maxx[0] = maxx[0];
+    }
+  else
+    {
+      sbox.minx[0] = minx[0];
+      sbox.maxx[0] = c.X();
+    }
+  if (i & 2)
+    {
+      sbox.minx[1] = c.Y();
+      sbox.maxx[1] = maxx[1];
+    }
+  else
+    {
+      sbox.minx[1] = minx[1];
+      sbox.maxx[1] = c.Y();
+    }
+  if (i & 4)
+    {
+      sbox.minx[2] = c.Z();
+      sbox.maxx[2] = maxx[2];
+    }
+  else
+    {
+      sbox.minx[2] = minx[2];
+      sbox.maxx[2] = c.Z();
+    }
+  
+  //  sbox.CalcDiamCenter ();
+
+  sbox.c.X() = 0.5 * (sbox.minx[0] + sbox.maxx[0]);
+  sbox.c.Y() = 0.5 * (sbox.minx[1] + sbox.maxx[1]);
+  sbox.c.Z() = 0.5 * (sbox.minx[2] + sbox.maxx[2]);
+  sbox.diam = 0.5 * diam;
+  sbox.inner = 0.5 * inner;
+}
+
+
+
+
+/*
+double Determinant (const Vec3d & col1,
+		    const Vec3d & col2,
+		    const Vec3d & col3)
+{
+  return
+    col1.x[0] * ( col2.x[1] * col3.x[2] - col2.x[2] * col3.x[1]) +
+    col1.x[1] * ( col2.x[2] * col3.x[0] - col2.x[0] * col3.x[2]) +
+    col1.x[2] * ( col2.x[0] * col3.x[1] - col2.x[1] * col3.x[0]);
+}
+*/
+
+void Transpose (Vec3d & v1, Vec3d & v2, Vec3d & v3)
+{
+  Swap (v1.Y(), v2.X());
+  Swap (v1.Z(), v3.X());
+  Swap (v2.Z(), v3.Y());
+}
+
+
+int SolveLinearSystem (const Vec3d & col1, const Vec3d & col2,
+		       const Vec3d & col3, const Vec3d & rhs,
+		       Vec3d & sol)
+{
+  // changed by MW
+  double matrix[3][3];
+  double locrhs[3];
+  int retval = 0;
+
+  for(int i=0; i<3; i++)
+    {
+      matrix[i][0] = col1.X(i+1);
+      matrix[i][1] = col2.X(i+1);
+      matrix[i][2] = col3.X(i+1);
+      locrhs[i] = rhs.X(i+1);
+    }
+
+  for(int i=0; i<2; i++)
+    {
+      int pivot = i;
+      double maxv = fabs(matrix[i][i]);
+      for(int j=i+1; j<3; j++)
+	if(fabs(matrix[j][i]) > maxv)
+	  {
+	    maxv = fabs(matrix[j][i]);
+	    pivot = j;
+	  }
+
+      if(fabs(maxv) > 1e-40)
+	{
+	  if(pivot != i)
+	    {
+	      swap(matrix[i][0],matrix[pivot][0]);
+	      swap(matrix[i][1],matrix[pivot][1]);
+	      swap(matrix[i][2],matrix[pivot][2]);
+	      swap(locrhs[i],locrhs[pivot]);
+	    }
+	  for(int j=i+1; j<3; j++)
+	    {
+	      double fac = matrix[j][i] / matrix[i][i];
+	      
+	      for(int k=i+1; k<3; k++)
+		matrix[j][k] -= fac*matrix[i][k];
+	      locrhs[j] -= fac*locrhs[i];
+	    }
+	}
+      else
+	retval = 1;
+    }
+
+  if(fabs(matrix[2][2]) < 1e-40)
+    retval = 1;
+
+  if(retval != 0)
+    return retval;
+  
+
+  for(int i=2; i>=0; i--)
+    {
+      double sum = locrhs[i];
+      for(int j=2; j>i; j--)
+	sum -= matrix[i][j]*sol.X(j+1);
+
+      sol.X(i+1) = sum/matrix[i][i];
+    }
+
+  return 0;
+  
+  
+  
+
+
+  /*
+  double det = Determinant (col1, col2, col3);
+  if (fabs (det) < 1e-40)
+    return 1;
+  
+  sol.X() = Determinant (rhs, col2, col3) / det;
+  sol.Y() = Determinant (col1, rhs, col3) / det;
+  sol.Z() = Determinant (col1, col2, rhs) / det;
+
+  return 0;
+  */
+  /*
+  Vec3d cr;
+  Cross (col1, col2, cr);
+  double det = cr * col3;
+
+  if (fabs (det) < 1e-40)
+    return 1;
+
+  if (fabs(cr.Z()) > 1e-12)
+    {
+      // solve for 3. component
+      sol.Z() = (cr * rhs) / det;
+      
+      // 2x2 system for 1. and 2. component
+      double res1 = rhs.X() - sol.Z() * col3.X();
+      double res2 = rhs.Y() - sol.Z() * col3.Y();
+      
+      sol.X() = (col2.Y() * res1 - col2.X() * res2) / cr.Z();
+      sol.Y() = (col1.X() * res2 - col1.Y() * res1) / cr.Z();
+  
+    }
+  else
+    {
+      det = Determinant (col1, col2, col3);
+      if (fabs (det) < 1e-40)
+	return 1;
+      
+      sol.X() = Determinant (rhs, col2, col3) / det;
+      sol.Y() = Determinant (col1, rhs, col3) / det;
+      sol.Z() = Determinant (col1, col2, rhs) / det;
+    }
+
+  return 0;
+  */
+}
+
+
+int SolveLinearSystemLS (const Vec3d & col1,
+			 const Vec3d & col2,
+			 const Vec2d & rhs,
+			 Vec3d & sol)
+{
+  double a11 = col1 * col1;
+  double a12 = col1 * col2;
+  double a22 = col2 * col2;
+  
+  double det = a11 * a22 - a12 * a12;
+
+  if (det*det <= 1e-24 * a11 * a22)
+    {
+      sol = Vec3d (0, 0, 0);
+      return 1;
+    }
+  
+  Vec2d invrhs;
+  invrhs.X() = ( a22 * rhs.X() - a12 * rhs.Y()) / det;
+  invrhs.Y() = (-a12 * rhs.X() + a11 * rhs.Y()) / det;
+
+  sol.X() = invrhs.X() * col1.X() + invrhs.Y() * col2.X();
+  sol.Y() = invrhs.X() * col1.Y() + invrhs.Y() * col2.Y();
+  sol.Z() = invrhs.X() * col1.Z() + invrhs.Y() * col2.Z();
+
+  return 0;
+
+  /*
+  Vec3d inv1, inv2;
+  int err = 
+    PseudoInverse (col1, col2, inv1, inv2);
+
+   sol = rhs.X() * inv1 + rhs.Y() * inv2;
+   return err;
+  */
+}
+
+int SolveLinearSystemLS2 (const Vec3d & col1,
+			 const Vec3d & col2,
+			 const Vec2d & rhs,
+			 Vec3d & sol, double & x, double & y)
+{
+  double a11 = col1 * col1;
+  double a12 = col1 * col2;
+  double a22 = col2 * col2;
+  
+  double det = a11 * a22 - a12 * a12;
+
+  if (fabs (det) <= 1e-12 * col1.Length() * col2.Length() || 
+      col1.Length2() == 0 || col2.Length2() == 0)
+    {
+      sol = Vec3d (0, 0, 0);
+      x = 0; y = 0;
+      return 1;
+    }
+  
+  Vec2d invrhs;
+  invrhs.X() = ( a22 * rhs.X() - a12 * rhs.Y()) / det;
+  invrhs.Y() = (-a12 * rhs.X() + a11 * rhs.Y()) / det;
+
+  sol.X() = invrhs.X() * col1.X() + invrhs.Y() * col2.X();
+  sol.Y() = invrhs.X() * col1.Y() + invrhs.Y() * col2.Y();
+  sol.Z() = invrhs.X() * col1.Z() + invrhs.Y() * col2.Z();
+
+  x = invrhs.X();
+  y = invrhs.Y();
+
+  return 0;
+
+  /*
+  Vec3d inv1, inv2;
+  int err = 
+    PseudoInverse (col1, col2, inv1, inv2);
+
+   sol = rhs.X() * inv1 + rhs.Y() * inv2;
+   return err;
+  */
+}
+
+int PseudoInverse (const Vec3d & col1,
+		   const Vec3d & col2,
+		   Vec3d & inv1,
+		   Vec3d & inv2)
+{
+  double a11 = col1 * col1;
+  double a12 = col1 * col2;
+  double a22 = col2 * col2;
+  
+  double det = a11 * a22 - a12 * a12;
+
+  if (fabs (det) < 1e-12 * col1.Length() * col2.Length())
+    {
+      inv1 = Vec3d (0, 0, 0);
+      inv2 = Vec3d (0, 0, 0);
+      return 1;
+    }
+
+  double ia11 = a22 / det;
+  double ia12 = -a12 / det;
+  double ia22 = a11 / det;
+
+  inv1 = ia11 * col1 + ia12 * col2;
+  inv2 = ia12 * col1 + ia22 * col2;
+
+  return 0;
+}
+
+
+
+
+QuadraticFunction3d :: 
+QuadraticFunction3d (const Point3d & p, const Vec3d & v)
+{
+  Vec3d hv(v);
+  hv /= (hv.Length() + 1e-12);
+  Vec3d t1, t2;
+  hv.GetNormal (t1);
+  Cross (hv, t1, t2);
+  
+  double t1p = t1.X() * p.X() + t1.Y() * p.Y() + t1.Z() * p.Z();
+  double t2p = t2.X() * p.X() + t2.Y() * p.Y() + t2.Z() * p.Z();
+  c0 = sqr (t1p) + sqr (t2p);
+  cx = -2 * (t1p * t1.X() + t2p * t2.X());
+  cy = -2 * (t1p * t1.Y() + t2p * t2.Y());
+  cz = -2 * (t1p * t1.Z() + t2p * t2.Z());
+
+  cxx = t1.X() * t1.X() + t2.X() * t2.X();
+  cyy = t1.Y() * t1.Y() + t2.Y() * t2.Y();
+  czz = t1.Z() * t1.Z() + t2.Z() * t2.Z();
+
+  cxy = 2 * t1.X() * t1.Y() + 2 * t2.X() * t2.Y();
+  cxz = 2 * t1.X() * t1.Z() + 2 * t2.X() * t2.Z();
+  cyz = 2 * t1.Y() * t1.Z() + 2 * t2.Y() * t2.Z();
+
+  /*
+  (*testout) << "c0 = " << c0
+	     << " clin = " << cx << " " << cy << " " << cz 
+	     << " cq = " << cxx << " " << cyy << " " << czz
+	     << cxy << " " << cyz << " " << cyz << endl;
+  */
+}
+
+// QuadraticFunction3d gqf (Point3d (0,0,0), Vec3d (1, 0, 0));
+
+
+
+
+
+void referencetransform :: Set (const Point3d & p1, const Point3d & p2,
+                                const Point3d & p3, double ah)
+{
+  ex = p2 - p1;
+  ex /= ex.Length();
+  ey = p3 - p1;
+  ey -= (ex * ey) * ex;
+  ey /= ey.Length();
+  ez = Cross (ex, ey);
+  rp = p1;
+  h = ah;
+
+  exh = ah * ex;
+  eyh = ah * ey;
+  ezh = ah * ez;
+  ah = 1 / ah;
+  ex_h = ah * ex;
+  ey_h = ah * ey;
+  ez_h = ah * ez;
+}
+
+void referencetransform :: ToPlain (const Point3d & p, Point3d & pp) const
+{
+  Vec3d v;
+  v = p - rp;
+  pp.X() = (ex_h * v);
+  pp.Y() = (ey_h * v);
+  pp.Z() = (ez_h * v);
+}
+
+void referencetransform :: ToPlain (const Array<Point3d> & p,
+                                    Array<Point3d> & pp) const
+{
+  Vec3d v;
+  int i;
+
+  pp.SetSize (p.Size());
+  for (i = 1; i <= p.Size(); i++)
+    {
+      v = p.Get(i) - rp;
+      pp.Elem(i).X() = (ex_h * v);
+      pp.Elem(i).Y() = (ey_h * v);
+      pp.Elem(i).Z() = (ez_h * v);
+    }
+}
+
+void referencetransform :: FromPlain (const Point3d & pp, Point3d & p) const
+{
+  Vec3d v;
+  //  v = (h * pp.X()) * ex + (h * pp.Y()) * ey + (h * pp.Z()) * ez;
+  //  p = rp + v;
+  v.X() = pp.X() * exh.X() + pp.Y() * eyh.X() + pp.Z() * ezh.X();
+  v.Y() = pp.X() * exh.Y() + pp.Y() * eyh.Y() + pp.Z() * ezh.Y();
+  v.Z() = pp.X() * exh.Z() + pp.Y() * eyh.Z() + pp.Z() * ezh.Z();
+  p.X() = rp.X() + v.X();
+  p.Y() = rp.Y() + v.Y();
+  p.Z() = rp.Z() + v.Z();
+}
+
+
+}
diff --git a/contrib/Netgen/libsrc/gprim/geom3d.hpp b/contrib/Netgen/libsrc/gprim/geom3d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..05216d8f0ff97648a16a443f5a825ac9ec005060
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geom3d.hpp
@@ -0,0 +1,746 @@
+#ifndef FILE_GEOM3D
+#define FILE_GEOM3D
+
+/* *************************************************************************/
+/* File:   geom3d.hh                                                       */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   5. Aug. 95                                                      */
+/* *************************************************************************/
+
+namespace netgen
+{
+
+
+  extern DLL_HEADER void MyError (const char * ch);
+
+  class Point3d;
+  class Vec3d;
+
+  inline Vec3d operator- (const Point3d & p1, const Point3d & p2);
+  inline Point3d operator- (const Point3d & p1, const Vec3d & v);
+  inline Point3d operator+ (const Point3d & p1, const Vec3d & v);
+  Point3d & Add (double d, const Vec3d & v);
+  Point3d & Add2 (double d, const Vec3d & v,
+		  double d2, const Vec3d & v2);
+  inline Point3d Center (const Point3d & p1, const Point3d & p2);
+  inline Point3d Center (const Point3d & p1, const Point3d & p2, const Point3d & p3);
+  inline Point3d Center (const Point3d & p1, const Point3d & p2, 
+			 const Point3d & p3, const Point3d & p4);
+  ostream & operator<<(ostream  & s, const Point3d & p);
+  inline Vec3d operator- (const Vec3d & p1, const Vec3d & v);
+  inline Vec3d operator+ (const Vec3d & p1, const Vec3d & v);
+  inline Vec3d operator* (double scal, const Vec3d & v);
+  inline double operator* (const Vec3d & v1, const Vec3d & v2);
+  inline Vec3d Cross (const Vec3d & v1, const Vec3d & v2);
+  inline void Cross (const Vec3d & v1, const Vec3d & v2, Vec3d & prod);
+  double Angle (const Vec3d & v);
+  double FastAngle (const Vec3d & v);
+  double Angle (const Vec3d & v1, const Vec3d & v2);
+  double FastAngle (const Vec3d & v1, const Vec3d & v2);
+  ostream & operator<<(ostream  & s, const Vec3d & v);
+  void Transpose (Vec3d & v1, Vec3d & v2, Vec3d & v3);
+  int SolveLinearSystem (const Vec3d & col1,
+			 const Vec3d & col2,
+			 const Vec3d & col3,
+			 const Vec3d & rhs,
+			 Vec3d & sol);
+  int SolveLinearSystemLS (const Vec3d & col1,
+			   const Vec3d & col2,
+			   const Vec2d & rhs,
+			   Vec3d & sol);
+  int SolveLinearSystemLS2 (const Vec3d & col1,
+			    const Vec3d & col2,
+			    const Vec2d & rhs, 
+			    Vec3d & sol,
+			    double & x, double & y);
+  int PseudoInverse (const Vec3d & col1,
+		     const Vec3d & col2,
+		     Vec3d & inv1,
+		     Vec3d & inv2);
+  double Determinant (const Vec3d & col1,
+		      const Vec3d & col2,
+		      const Vec3d & col3);
+
+  inline double Dist2 (const Point3d & p1, const Point3d & p2);
+
+  /// Point in R3
+  class Point3d
+  {
+  protected:
+    ///
+    double x[3];
+  
+  public:
+    ///
+    Point3d () { x[0] = x[1] = x[2] = 0; }
+    ///
+    Point3d(double ax, double ay, double az) 
+    { x[0] = ax; x[1] = ay; x[2] = az; }
+    ///
+    Point3d(double ax[3])
+    { x[0] = ax[0]; x[1] = ax[1]; x[2] = ax[2]; }
+
+    ///
+    Point3d(const Point3d & p2) 
+    { x[0] = p2.x[0]; x[1] = p2.x[1]; x[2] = p2.x[2]; }
+
+    Point3d (const Point<3> & p2)
+    {
+      for (int i = 0; i < 3; i++)
+	x[i] = p2(i);
+    }
+  
+    ///
+    Point3d & operator= (const Point3d & p2)
+    { x[0] = p2.x[0]; x[1] = p2.x[1]; x[2] = p2.x[2]; return *this; }
+  
+    ///
+    int operator== (const Point3d& p) const
+    { return (x[0] == p.x[0] && x[1] == p.x[1] && x[2] == p.x[2]); }
+  
+    ///
+    double & X() { return x[0]; }
+    ///
+    double & Y() { return x[1]; }
+    ///
+    double & Z() { return x[2]; }
+    ///
+    double X() const { return x[0]; }
+    ///
+    double Y() const { return x[1]; }
+    ///
+    double Z() const { return x[2]; }
+    ///
+    double & X(int i) { return x[i-1]; }
+    ///
+    double X(int i) const { return x[i-1]; }
+    ///
+    const Point3d & SetToMin (const Point3d & p2)
+    {
+      if (p2.x[0] < x[0]) x[0] = p2.x[0];
+      if (p2.x[1] < x[1]) x[1] = p2.x[1];
+      if (p2.x[2] < x[2]) x[2] = p2.x[2];
+      return *this;
+    }
+
+    ///
+    const Point3d & SetToMax (const Point3d & p2)
+    {
+      if (p2.x[0] > x[0]) x[0] = p2.x[0];
+      if (p2.x[1] > x[1]) x[1] = p2.x[1];
+      if (p2.x[2] > x[2]) x[2] = p2.x[2];
+      return *this;
+    }
+
+    ///
+    friend inline Vec3d operator- (const Point3d & p1, const Point3d & p2);
+    ///
+    friend inline Point3d operator- (const Point3d & p1, const Vec3d & v);
+    ///
+    friend inline Point3d operator+ (const Point3d & p1, const Vec3d & v);
+    ///
+    inline Point3d & operator+= (const Vec3d & v);
+    inline Point3d & operator-= (const Vec3d & v);
+    ///
+    inline Point3d & Add (double d, const Vec3d & v);
+    ///
+    inline Point3d & Add2 (double d, const Vec3d & v,
+			   double d2, const Vec3d & v2);
+    ///
+    friend inline double Dist (const Point3d & p1, const Point3d & p2)
+    { return sqrt (  (p1.x[0]-p2.x[0]) * (p1.x[0]-p2.x[0]) + 
+		     (p1.x[1]-p2.x[1]) * (p1.x[1]-p2.x[1]) +
+                     (p1.x[2]-p2.x[2]) * (p1.x[2]-p2.x[2])); }
+    ///
+    inline friend double Dist2 (const Point3d & p1, const Point3d & p2)
+    { return  (  (p1.x[0]-p2.x[0]) * (p1.x[0]-p2.x[0]) + 
+		 (p1.x[1]-p2.x[1]) * (p1.x[1]-p2.x[1]) +
+		 (p1.x[2]-p2.x[2]) * (p1.x[2]-p2.x[2])); }
+
+    ///
+    friend inline Point3d Center (const Point3d & p1, const Point3d & p2);
+    ///
+    friend inline Point3d Center (const Point3d & p1, const Point3d & p2, const Point3d & p3);
+    ///
+    friend inline Point3d Center (const Point3d & p1, const Point3d & p2, 
+				  const Point3d & p3, const Point3d & p4);
+    ///
+    friend ostream & operator<<(ostream  & s, const Point3d & p);
+  
+    ///
+    friend class Vec3d;
+    ///
+    friend class Box3d;
+
+
+    operator Point<3> () const
+    {
+      return Point<3> (x[0], x[1], x[2]);
+    }
+  };
+
+
+  ///
+  class Vec3d
+  {
+  protected:
+    ///
+    double x[3];
+
+  public:
+    ///
+    inline Vec3d() { x[0] = x[1] = x[2] = 0; }
+    ///
+    inline Vec3d(double ax, double ay, double az)
+    { x[0] = ax; x[1] = ay; x[2] = az; }
+    ///
+    Vec3d(double ax[3])
+    { x[0] = ax[0]; x[1] = ax[1]; x[2] = ax[2]; }
+    ///
+    inline Vec3d(const Vec3d & v2) 
+    { x[0] = v2.x[0]; x[1] = v2.x[1]; x[2] = v2.x[2]; }
+    ///
+    inline Vec3d(const Point3d & p1, const Point3d & p2)
+    { 
+      x[0] = p2.x[0] - p1.x[0];
+      x[1] = p2.x[1] - p1.x[1];
+      x[2] = p2.x[2] - p1.x[2]; 
+    }
+    ///
+    inline Vec3d(const Point3d & p1)
+    { 
+      x[0] = p1.x[0];
+      x[1] = p1.x[1];
+      x[2] = p1.x[2];
+    }
+  
+    Vec3d (const Vec<3> & v2)
+    {
+      for (int i = 0; i < 3; i++)
+	x[i] = v2(i);
+    }
+
+    operator Vec<3> () const
+    {
+      return Vec<3> (x[0], x[1], x[2]);
+    }
+
+
+    Vec3d & operator= (const Vec3d & v2)
+    { x[0] = v2.x[0]; x[1] = v2.x[1]; x[2] = v2.x[2]; return *this; }
+    ///
+    Vec3d & operator= (double val)
+    { x[0] = x[1] = x[2] = val; return *this; }
+    ///
+    double & X() { return x[0]; }
+    ///
+    double & Y() { return x[1]; }
+    ///
+    double & Z() { return x[2]; }
+    ///
+    double & X(int i) { return x[i-1]; }
+
+    ///
+    double X() const { return x[0]; }
+    ///
+    double Y() const { return x[1]; }
+    ///
+    double Z() const { return x[2]; }
+    ///
+    double X(int i) const { return x[i-1]; }
+
+    ///
+    double Length() const 
+    { return sqrt (x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); }
+    ///
+    double Length2() const 
+    { return x[0] * x[0] + x[1] * x[1] + x[2] * x[2]; }
+
+    ///
+    inline friend double Dist (const Vec3d & v1, const Vec3d & v2)
+    { return sqrt (  (v1.x[0]-v2.x[0]) * (v1.x[0]-v2.x[0]) + 
+		     (v1.x[1]-v2.x[1]) * (v1.x[1]-v2.x[1]) +
+                     (v1.x[2]-v2.x[2]) * (v1.x[2]-v2.x[2])); }
+    ///
+    inline friend double Dist2 (const Vec3d & v1, const Vec3d & v2)
+    { return  (  (v1.x[0]-v2.x[0]) * (v1.x[0]-v2.x[0]) + 
+		 (v1.x[1]-v2.x[1]) * (v1.x[1]-v2.x[1]) +
+		 (v1.x[2]-v2.x[2]) * (v1.x[2]-v2.x[2])); }
+
+    ///
+    Vec3d & operator+= (const Vec3d & v2);
+    ///
+    Vec3d & operator-= (const Vec3d & v2);
+    ///
+    Vec3d & operator*= (double s);
+    ///
+    Vec3d & operator/= (double s);
+    ///
+    inline Vec3d & Add (double d, const Vec3d & v);
+    ///
+    inline Vec3d & Add2 (double d, const Vec3d & v,
+			 double d2, const Vec3d & v2);
+
+    ///
+    friend inline Vec3d operator- (const Point3d & p1, const Point3d & p2);
+    ///
+    friend inline Point3d operator- (const Point3d & p1, const Vec3d & v);
+    ///
+    friend inline Point3d operator+ (const Point3d & p1, const Vec3d & v);
+    ///
+    friend inline Vec3d operator- (const Vec3d & p1, const Vec3d & v);
+    ///
+    friend inline Vec3d operator+ (const Vec3d & p1, const Vec3d & v);
+    ///
+    friend inline Vec3d operator* (double scal, const Vec3d & v);
+
+    ///
+    friend inline double operator* (const Vec3d & v1, const Vec3d & v2);
+    ///
+    friend inline Vec3d Cross (const Vec3d & v1, const Vec3d & v2);
+    ///
+    friend inline void Cross (const Vec3d & v1, const Vec3d & v2, Vec3d & prod);
+
+    /// Returns one normal-vector to n
+    void GetNormal (Vec3d & n) const;
+    ///
+    friend double Angle (const Vec3d & v);
+    ///
+    friend double FastAngle (const Vec3d & v);
+    ///
+    friend double Angle (const Vec3d & v1, const Vec3d & v2);
+    ///
+    friend double FastAngle (const Vec3d & v1, const Vec3d & v2);
+
+    void Normalize() 
+    {
+      double len = (x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
+      if (len == 0) return;
+      len = sqrt (len);
+      x[0] /= len; x[1] /= len; x[2] /= len;
+    }
+
+    ///
+    friend ostream & operator<<(ostream  & s, const Vec3d & v);
+
+    ///
+    friend class Point3d;
+    friend void Transpose (Vec3d & v1, Vec3d & v2, Vec3d & v3);
+    friend int SolveLinearSystem (const Vec3d & col1,
+				  const Vec3d & col2,
+				  const Vec3d & col3,
+				  const Vec3d & rhs,
+				  Vec3d & sol);
+    friend int SolveLinearSystemLS (const Vec3d & col1,
+				    const Vec3d & col2,
+				    const Vec2d & rhs,
+				    Vec3d & sol);
+    friend int SolveLinearSystemLS2 (const Vec3d & col1,
+				     const Vec3d & col2,
+				     const Vec2d & rhs, 
+				     Vec3d & sol,
+				     double & x, double & y);
+    friend int PseudoInverse (const Vec3d & col1,
+			      const Vec3d & col2,
+			      Vec3d & inv1,
+			      Vec3d & inv2);
+    friend double Determinant (const Vec3d & col1,
+			       const Vec3d & col2,
+			       const Vec3d & col3);
+  };
+
+
+
+  class QuadraticFunction3d
+  {
+    double c0, cx, cy, cz;
+    double cxx, cyy, czz, cxy, cxz, cyz;
+
+  public:
+    QuadraticFunction3d (const Point3d & p, const Vec3d & v);
+    double Eval (const Point3d & p)
+    {
+      return 
+	c0 
+	+ p.X() * (cx + cxx * p.X() + cxy * p.Y() + cxz * p.Z())
+	+ p.Y() * (cy + cyy * p.Y() + cyz * p.Z())
+	+ p.Z() * (cz + czz * p.Z());
+    }
+  };
+
+
+
+  inline Point3d Center (const Point3d & p1, const Point3d & p2)
+  {
+    return Point3d (0.5 * (p1.x[0] + p2.x[0]),
+		    0.5 * (p1.x[1] + p2.x[1]),
+		    0.5 * (p1.x[2] + p2.x[2]));
+  }
+
+
+  inline Point3d Center (const Point3d & p1, const Point3d & p2,
+			 const Point3d & p3)
+  {
+    return Point3d (1.0/3.0 * (p1.x[0] + p2.x[0] + p3.x[0]),
+		    1.0/3.0 * (p1.x[1] + p2.x[1] + p3.x[1]),
+		    1.0/3.0 * (p1.x[2] + p2.x[2] + p3.x[2]));
+  }
+
+  inline Point3d Center (const Point3d & p1, const Point3d & p2,
+			 const Point3d & p3, const Point3d & p4)
+  {
+    return Point3d (0.25 * (p1.x[0] + p2.x[0] + p3.x[0] + p4.x[0]),
+		    0.25 * (p1.x[1] + p2.x[1] + p3.x[1] + p4.x[1]),
+		    0.25 * (p1.x[2] + p2.x[2] + p3.x[2] + p4.x[2]));
+  }
+
+
+
+  inline Vec3d & Vec3d :: operator+= (const Vec3d & v2)
+  {
+    x[0] += v2.x[0];
+    x[1] += v2.x[1];
+    x[2] += v2.x[2];
+    return *this;
+  }
+
+  inline Vec3d & Vec3d :: operator-= (const Vec3d & v2)
+  {
+    x[0] -= v2.x[0];
+    x[1] -= v2.x[1];
+    x[2] -= v2.x[2];
+    return *this;
+  }
+
+
+  inline Vec3d & Vec3d :: operator*= (double s)
+  {
+    x[0] *= s;
+    x[1] *= s;
+    x[2] *= s;
+    return *this;
+  }
+
+
+  inline Vec3d & Vec3d :: operator/= (double s)
+  {
+    if (s != 0)
+      {
+	x[0] /= s;
+	x[1] /= s;
+	x[2] /= s;
+      }
+#ifdef DEBUG
+    else
+      {
+	cerr << "Vec div by 0, v = " << (*this) << endl;
+	//      MyError ("Vec3d::operator /=: Divisioin by zero");
+      }
+#endif
+    return *this;
+  }
+
+  inline Vec3d & Vec3d::Add (double d, const Vec3d & v)
+  {
+    x[0] += d * v.x[0]; 
+    x[1] += d * v.x[1]; 
+    x[2] += d * v.x[2];
+    return *this;
+  }
+
+  inline Vec3d & Vec3d::Add2 (double d, const Vec3d & v,
+			      double d2, const Vec3d & v2)
+  {
+    x[0] += d * v.x[0] + d2 * v2.x[0]; 
+    x[1] += d * v.x[1] + d2 * v2.x[1]; 
+    x[2] += d * v.x[2] + d2 * v2.x[2]; 
+    return *this;
+  }
+
+
+
+
+
+
+
+
+  inline Vec3d operator- (const Point3d & p1, const Point3d & p2)
+  {
+    return Vec3d (p1.x[0] - p2.x[0], p1.x[1] - p2.x[1],p1.x[2] - p2.x[2]);
+  }
+
+
+  inline Point3d operator- (const Point3d & p1, const Vec3d & v)
+  {
+    return Point3d (p1.x[0] - v.x[0], p1.x[1] - v.x[1],p1.x[2] - v.x[2]);
+  }
+
+
+  inline Point3d operator+ (const Point3d & p1, const Vec3d & v)
+  {
+    return Point3d (p1.x[0] + v.x[0], p1.x[1] + v.x[1],p1.x[2] + v.x[2]);
+  }
+
+  inline Point3d & Point3d::operator+= (const Vec3d & v) 
+  {
+    x[0] += v.x[0]; 
+    x[1] += v.x[1]; 
+    x[2] += v.x[2];
+    return *this;
+  }
+
+  inline Point3d & Point3d::operator-= (const Vec3d & v) 
+  {
+    x[0] -= v.x[0]; 
+    x[1] -= v.x[1]; 
+    x[2] -= v.x[2];
+    return *this;
+  }
+
+  inline Point3d & Point3d::Add (double d, const Vec3d & v)
+  {
+    x[0] += d * v.x[0]; 
+    x[1] += d * v.x[1]; 
+    x[2] += d * v.x[2];
+    return *this;
+  }
+
+  inline Point3d & Point3d::Add2 (double d, const Vec3d & v,
+				  double d2, const Vec3d & v2)
+  {
+    x[0] += d * v.x[0] + d2 * v2.x[0]; 
+    x[1] += d * v.x[1] + d2 * v2.x[1]; 
+    x[2] += d * v.x[2] + d2 * v2.x[2]; 
+    return *this;
+  }
+
+
+  inline Vec3d operator- (const Vec3d & v1, const Vec3d & v2)
+  {
+    return Vec3d (v1.x[0] - v2.x[0], v1.x[1] - v2.x[1],v1.x[2] - v2.x[2]);
+  }
+
+
+  inline Vec3d operator+ (const Vec3d & v1, const Vec3d & v2)
+  {
+    return Vec3d (v1.x[0] + v2.x[0], v1.x[1] + v2.x[1],v1.x[2] + v2.x[2]);
+  }
+
+
+  inline Vec3d operator* (double scal, const Vec3d & v)
+  {
+    return Vec3d (scal * v.x[0], scal * v.x[1], scal * v.x[2]);
+  }
+
+
+
+  inline double operator* (const Vec3d & v1, const Vec3d & v2)
+  {
+    return v1.x[0] * v2.x[0] + v1.x[1] * v2.x[1] + v1.x[2] * v2.x[2];
+  }
+
+
+
+  inline Vec3d Cross (const Vec3d & v1, const Vec3d & v2)
+  {
+    return Vec3d
+      ( v1.x[1] * v2.x[2] - v1.x[2] * v2.x[1],
+	v1.x[2] * v2.x[0] - v1.x[0] * v2.x[2],
+	v1.x[0] * v2.x[1] - v1.x[1] * v2.x[0]);
+  }
+
+  inline void Cross (const Vec3d & v1, const Vec3d & v2, Vec3d & prod)
+  {
+    prod.x[0] = v1.x[1] * v2.x[2] - v1.x[2] * v2.x[1];
+    prod.x[1] = v1.x[2] * v2.x[0] - v1.x[0] * v2.x[2];
+    prod.x[2] = v1.x[0] * v2.x[1] - v1.x[1] * v2.x[0];
+  }
+
+  inline double Determinant (const Vec3d & col1,
+			     const Vec3d & col2,
+			     const Vec3d & col3)
+  {
+    return
+      col1.x[0] * ( col2.x[1] * col3.x[2] - col2.x[2] * col3.x[1]) +
+      col1.x[1] * ( col2.x[2] * col3.x[0] - col2.x[0] * col3.x[2]) +
+      col1.x[2] * ( col2.x[0] * col3.x[1] - col2.x[1] * col3.x[0]);
+  }
+
+
+  ///
+  class Box3d
+  {
+  protected:
+    ///
+    double minx[3], maxx[3];
+
+  public:
+    ///
+    Box3d () { };
+    ///
+    Box3d ( double aminx, double amaxx,
+	    double aminy, double amaxy,
+	    double aminz, double amaxz );
+    ///
+    Box3d ( const Box3d & b2 );
+    ///
+    Box3d (const Point3d& p1, const Point3d& p2);
+    ///
+    Box3d (const Box<3> & b2);
+    ///
+    double MinX () const { return minx[0]; }
+    ///
+    double MaxX () const { return maxx[0]; }
+    ///
+    double MinY () const { return minx[1]; }
+    ///
+    double MaxY () const { return maxx[1]; }
+    ///
+    double MinZ () const { return minx[2]; }
+    ///
+    double MaxZ () const { return maxx[2]; }
+
+    ///
+    double Mini (int i) const { return minx[i-1]; }
+    ///
+    double Maxi (int i) const { return maxx[i-1]; }
+
+    ///
+    Point3d PMin () const { return Point3d(minx[0], minx[1], minx[2]); }
+    ///
+    Point3d PMax () const { return Point3d(maxx[0], maxx[1], maxx[2]); }
+
+    ///
+    void GetPointNr (int i, Point3d & point) const;
+    /// increase Box at each side with dist 
+    void Increase (double dist);
+    /// increase Box by factor rel
+    void IncreaseRel (double rel);
+    /// return 1 if closures are intersecting
+    int Intersect (const Box3d & box2) const
+    {
+      if (minx[0] > box2.maxx[0] || maxx[0] < box2.minx[0] ||
+	  minx[1] > box2.maxx[1] || maxx[1] < box2.minx[1] ||
+	  minx[2] > box2.maxx[2] || maxx[2] < box2.minx[2])
+	return 0;
+      return 1;
+    }
+    /// return 1 if point p in closure
+    int IsIn (const Point3d & p) const
+    {
+      if (minx[0] <= p.x[0] && maxx[0] >= p.x[0] &&
+	  minx[1] <= p.x[1] && maxx[1] >= p.x[1] &&
+	  minx[2] <= p.x[2] && maxx[2] >= p.x[2])
+	return 1;
+      return 0;
+    }
+    ///
+    inline void SetPoint (const Point3d & p)
+    {
+      minx[0] = maxx[0] = p.X();
+      minx[1] = maxx[1] = p.Y();
+      minx[2] = maxx[2] = p.Z();    
+    }
+
+    ///
+    inline void AddPoint (const Point3d & p)
+    {
+      if (p.x[0] < minx[0]) minx[0] = p.x[0];
+      if (p.x[0] > maxx[0]) maxx[0] = p.x[0];
+      if (p.x[1] < minx[1]) minx[1] = p.x[1];
+      if (p.x[1] > maxx[1]) maxx[1] = p.x[1];
+      if (p.x[2] < minx[2]) minx[2] = p.x[2];
+      if (p.x[2] > maxx[2]) maxx[2] = p.x[2];
+    }
+
+    ///
+    const Box3d& operator+=(const Box3d& b);
+
+    ///
+    Point3d MaxCoords() const;
+    ///
+    Point3d MinCoords() const;
+
+    /// Make a negative sized box;
+    //  void CreateNegMinMaxBox();
+  
+    ///
+    Point3d CalcCenter () const { return Point3d(0.5*(minx[0] + maxx[0]),
+						 0.5*(minx[1] + maxx[1]),
+						 0.5*(minx[2] + maxx[2])); }
+    ///
+    double CalcDiam () const { return sqrt(sqr(maxx[0]-minx[0])+
+					   sqr(maxx[1]-minx[1])+
+					   sqr(maxx[2]-minx[2])); }
+
+    ///
+    void WriteData(ofstream& fout) const;
+    ///
+    void ReadData(ifstream& fin);
+  };
+
+
+  class Box3dSphere : public Box3d
+  {
+  protected:
+    ///
+    double diam, inner;
+    ///
+    Point3d c;
+  public:
+    ///
+    Box3dSphere () { };
+    ///
+    Box3dSphere ( double aminx, double amaxx,
+		  double aminy, double amaxy,
+		  double aminz, double amaxz);
+    ///
+    const Point3d & Center () const { return c; }
+
+    ///
+    double Diam () const { return diam; }
+    ///
+    double Inner () const { return inner; }
+    ///
+    void GetSubBox (int i, Box3dSphere & sbox) const;
+
+    // private:
+    ///
+    void CalcDiamCenter ();
+  };
+
+
+
+
+  ///
+  class referencetransform
+  {
+    ///
+    Vec3d ex, ey, ez;
+    ///
+    Vec3d exh, eyh, ezh;
+    ///
+    Vec3d ex_h, ey_h, ez_h;
+    ///
+    Point3d rp;
+    ///
+    double h;
+
+  public:
+
+    ///
+    void Set (const Point3d & p1, const Point3d & p2,
+	      const Point3d & p3, double ah);
+
+    ///
+    void ToPlain (const Point3d & p, Point3d & pp) const;
+    ///
+    void ToPlain (const Array<Point3d> & p, Array<Point3d> & pp) const;
+    ///
+    void FromPlain (const Point3d & pp, Point3d & p) const;
+  };
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/geomfuncs.cpp b/contrib/Netgen/libsrc/gprim/geomfuncs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b2ac83824a8908e1e2a5be3417ad5e79057f3f72
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomfuncs.cpp
@@ -0,0 +1,111 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+
+namespace netgen
+{
+
+  /*
+  // template <>
+inline void CalcInverse (const Mat<2,2> & m, Mat<2,2> & inv)
+{
+  double det = m(0,0) * m(1,1) - m(0,1) * m(1,0);
+  if (det == 0) 
+    {
+      inv = 0;
+      return;
+    }
+
+  double idet = 1.0 / det;
+  inv(0,0) =  idet * m(1,1);
+  inv(0,1) = -idet * m(0,1);
+  inv(1,0) = -idet * m(1,0);
+  inv(1,1) =  idet * m(0,0);
+}
+  */
+
+
+
+  // template <>
+void CalcInverse (const Mat<3,3> & m, Mat<3,3> & inv)
+{
+  double det = Det (m);
+  if (det == 0) 
+    {
+      inv = 0;
+      return;
+    }
+
+  double idet = 1.0 / det;
+  inv(0,0) =  idet * (m(1,1) * m(2,2) - m(1,2) * m(2,1));
+  inv(1,0) = -idet * (m(1,0) * m(2,2) - m(1,2) * m(2,0));
+  inv(2,0) =  idet * (m(1,0) * m(2,1) - m(1,1) * m(2,0));
+
+  inv(0,1) = -idet * (m(0,1) * m(2,2) - m(0,2) * m(2,1));
+  inv(1,1) =  idet * (m(0,0) * m(2,2) - m(0,2) * m(2,0));
+  inv(2,1) = -idet * (m(0,0) * m(2,1) - m(0,1) * m(2,0));
+
+  inv(0,2) =  idet * (m(0,1) * m(1,2) - m(0,2) * m(1,1));
+  inv(1,2) = -idet * (m(0,0) * m(1,2) - m(0,2) * m(1,0));
+  inv(2,2) =  idet * (m(0,0) * m(1,1) - m(0,1) * m(1,0));
+}
+
+/*
+// template <>
+void CalcInverse (const Mat<2,3> & m, Mat<3,2> & inv)
+{
+  Mat<2,2> a = m * Trans (m);
+  Mat<2,2> ainv;
+  CalcInverse (a, ainv);
+  inv = Trans (m) * ainv;
+}
+*/
+
+
+
+double Det (const Mat<2,2> & m) 
+{
+  return  m(0,0) * m(1,1) - m(0,1) * m(1,0);
+}
+
+double Det (const Mat<3,3> & m) 
+{
+  return 
+    m(0,0) * m(1,1) * m(2,2)
+    + m(1,0) * m(2,1) * m(0,2)
+    + m(2,0) * m(0,1) * m(1,2)
+    - m(0,0) * m(2,1) * m(1,2)
+    - m(1,0) * m(0,1) * m(2,2)
+    - m(2,0) * m(1,1) * m(0,2);
+}
+
+
+void EigenValues (const Mat<3,3> & m, Vec<3> & ev)
+{
+  const double pi = 3.141592;
+  double a, b, c, d;
+  double p, q;
+  double arg;
+
+  a = -1.;
+  b = m(0,0) + m(1,1) + m(2,2);
+  c = -( m(0,0)*m(2,2) + m(1,1)*m(2,2) + m(0,0)*m(1,1) - sqr(m(0,1)) - sqr(m(0,2)) - sqr(m(1,2)) );
+  d = Det (m);
+  p = 3.*a*c - sqr(b);
+  q = 27.*sqr(a)*d - 9.*a*b*c + 2.*sqr(b)*b;
+
+
+  arg = acos((-q/2)/sqrt(-(p*p*p)));
+
+
+  ev(0) = (2. * sqrt(-p) * cos(arg/3.) - b) / 3.*a;
+  ev(1) = (-2. * sqrt(-p) * cos(arg/3.+pi/3) - b) / 3.*a;
+  ev(2) = (-2. * sqrt(-p) * cos(arg/3.-pi/3)- b) / 3.*a;
+
+
+
+}
+
+
+}
diff --git a/contrib/Netgen/libsrc/gprim/geomfuncs.hpp b/contrib/Netgen/libsrc/gprim/geomfuncs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..66cbca81ef9a91003a07849ae8df9a34b2bb2447
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomfuncs.hpp
@@ -0,0 +1,170 @@
+#ifndef FILE_GEOMFUNCS
+#define FILE_GEOMFUNCS
+
+/* *************************************************************************/
+/* File:   geomfuncs.hpp                                                   */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+
+namespace netgen 
+{
+
+  template <int D>
+  inline double Abs (const Vec<D> & v)
+  {
+    double sum = 0;
+    for (int i = 0; i < D; i++)
+      sum += v(i) * v(i);
+    return sqrt (sum);
+  }
+
+
+  template <int D>
+  inline double Abs2 (const Vec<D> & v)
+  {
+    double sum = 0;
+    for (int i = 0; i < D; i++)
+      sum += v(i) * v(i);
+    return sum;
+  }
+
+
+
+  template <int D>
+  inline double Dist (const Point<D> & a, const Point<D> & b)
+  {
+    return Abs (a-b);
+  }
+
+  template <int D>
+  inline double Dist2 (const Point<D> & a, const Point<D> & b)
+  {
+    return Abs2 (a-b);
+  }
+
+
+  template <int D>
+  inline Point<D> Center (const Point<D> & a, const Point<D> & b)
+  {
+    Point<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = 0.5 * (a(i) + b(i));
+    return res;
+  }
+
+  template <int D>
+  inline Point<D> Center (const Point<D> & a, const Point<D> & b, const Point<D> & c)
+  {
+    Point<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = (1.0/3.0) * (a(i) + b(i) + c(i));
+    return res;
+  }
+
+  template <int D>
+  inline Point<D> Center (const Point<D> & a, const Point<D> & b, const Point<D> & c, const Point<D> & d)
+  {
+    Point<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = (1.0/4.0) * (a(i) + b(i) + c(i) + d(i));
+    return res;
+  }
+
+
+
+
+  /*
+    new wrong code problem with MSVC2010:
+    using Cross ( & , & ) computes wrong cross-product, problem arises in 
+    Surface::DefineTangentialPlane, e.g. with example boxcyl.geo
+   */
+  // inline Vec<3> Cross (const Vec<3> & v1, const Vec<3> & v2)
+
+  inline Vec<3> Cross (Vec<3> v1, Vec<3> v2)
+  {
+    return Vec<3> 
+      ( v1(1) * v2(2) - v1(2) * v2(1),
+	v1(2) * v2(0) - v1(0) * v2(2),
+	v1(0) * v2(1) - v1(1) * v2(0) );
+  }
+
+
+  inline double Determinant (const Vec<3> & col1,
+			     const Vec<3> & col2,
+			     const Vec<3> & col3)
+  {
+    return
+      col1(0) * ( col2(1) * col3(2) - col2(2) * col3(1)) +
+      col1(1) * ( col2(2) * col3(0) - col2(0) * col3(2)) +
+      col1(2) * ( col2(0) * col3(1) - col2(1) * col3(0));
+  }
+
+
+
+  template <>
+  inline  Vec<2> Vec<2> :: GetNormal () const
+  {
+    return Vec<2> (-x[1], x[0]);
+  }
+
+  template <>
+  inline  Vec<3> Vec<3> :: GetNormal () const
+  {
+    if (fabs (x[0]) > fabs (x[2]))
+      return Vec<3> (-x[1], x[0], 0);
+    else
+      return Vec<3> (0, x[2], -x[1]);
+  }
+
+
+
+  // template <int H, int W>
+  inline void CalcInverse (const Mat<2,2> & m, Mat<2,2> & inv)
+  {
+    double det = m(0,0) * m(1,1) - m(0,1) * m(1,0);
+    if (det == 0) 
+      {
+	inv = 0;
+	return;
+      }
+
+    double idet = 1.0 / det;
+    inv(0,0) =  idet * m(1,1);
+    inv(0,1) = -idet * m(0,1);
+    inv(1,0) = -idet * m(1,0);
+    inv(1,1) =  idet * m(0,0);
+  }
+
+  void CalcInverse (const Mat<3,3> & m, Mat<3,3> & inv);
+
+  inline void CalcInverse (const Mat<2,3> & m, Mat<3,2> & inv)
+  {
+    Mat<2,2> a = m * Trans (m);
+    Mat<2,2> ainv;
+    CalcInverse (a, ainv);
+    inv = Trans (m) * ainv;
+  }
+
+  void CalcInverse (const Mat<3,2> & m, Mat<2,3> & inv);
+
+  inline void CalcInverse (const Mat<3,2> & m, Mat<2,3> & inv)
+  {
+    Mat<2,2> a = Trans (m) * m;
+    Mat<2,2> ainv;
+    CalcInverse (a, ainv);
+    inv = ainv * Trans (m);
+  }
+
+
+  double Det (const Mat<2,2> & m);
+  double Det (const Mat<3,3> & m);
+
+  // eigenvalues of a symmetric matrix
+  void EigenValues (const Mat<3,3> & m, Vec<3> & ev);
+  void EigenValues (const Mat<2,2> & m, Vec<3> & ev);
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/geomobjects.hpp b/contrib/Netgen/libsrc/gprim/geomobjects.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..29b9bb1b53db4120e49dbbab0862e78dc7bde161
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomobjects.hpp
@@ -0,0 +1,370 @@
+#ifndef FILE_OBJECTS
+#define FILE_OBJECTS
+
+/* *************************************************************************/
+/* File:   geomobjects.hpp                                                 */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+
+namespace netgen
+{
+
+
+  template <int D> class Vec;
+  template <int D> class Point;
+
+
+  template <int D>
+  class Point
+  {
+
+  protected:
+    double x[D];
+
+  public:
+    Point () { ; }
+    Point (double ax) { for (int i = 0; i < D; i++) x[i] = ax; }
+    Point (double ax, double ay) { x[0] = ax; x[1] = ay; }
+    Point (double ax, double ay, double az)
+    { x[0] = ax; x[1] = ay; x[2] = az; }
+    Point (double ax, double ay, double az, double au)
+    { x[0] = ax; x[1] = ay; x[2] = az; x[3] = au;}
+
+    Point (const Point<D> & p2)
+    { for (int i = 0; i < D; i++) x[i] = p2.x[i]; }
+
+    explicit Point (const Vec<D> & v)
+    { for (int i = 0; i < D; i++) x[i] = v(i); }
+
+
+    Point & operator= (const Point<D> & p2)
+    {
+      for (int i = 0; i < D; i++) x[i] = p2.x[i]; 
+      return *this;
+    }
+
+    Point & operator= (double val)
+    {
+      for (int i = 0; i < D; i++) x[i] = val;
+      return *this;
+    }
+
+    double & operator() (int i) { return x[i]; }
+    const double & operator() (int i) const { return x[i]; }
+
+    operator const double* () const { return x; }
+  };
+
+
+
+
+
+  template <int D>
+  class Vec
+  {
+
+  protected:
+    double x[D];
+
+  public:
+    Vec () { ; } // for (int i = 0; i < D; i++) x[i] = 0; }
+    Vec (double ax) { for (int i = 0; i < D; i++) x[i] = ax; }
+    Vec (double ax, double ay) { x[0] = ax; x[1] = ay; }
+    Vec (double ax, double ay, double az)
+    { x[0] = ax; x[1] = ay; x[2] = az; }
+    Vec (double ax, double ay, double az, double au)
+    { x[0] = ax; x[1] = ay; x[2] = az; x[3] = au; }
+
+    Vec (const Vec<D> & p2)
+    { for (int i = 0; i < D; i++) x[i] = p2.x[i]; }
+
+    explicit Vec (const Point<D> & p)
+    { for (int i = 0; i < D; i++) x[i] = p(i); }
+
+    Vec (const Vec<D> & p1, const Vec<D> & p2)
+    { for(int i=0; i<D; i++) x[i] = p2(i)-p1(1); }
+  
+
+
+    Vec & operator= (const Vec<D> & p2)
+    {
+      for (int i = 0; i < D; i++) x[i] = p2.x[i]; 
+      return *this;
+    }
+
+    Vec & operator= (double s)
+    {
+      for (int i = 0; i < D; i++) x[i] = s;
+      return *this;
+    }
+
+    double & operator() (int i) { return x[i]; }
+    const double & operator() (int i) const { return x[i]; }
+
+    operator const double* () const { return x; }
+
+    double Length () const
+    {
+      double l = 0;
+      for (int i = 0; i < D; i++)
+	l += x[i] * x[i];
+      return sqrt (l);
+    }
+
+    double Length2 () const
+    {
+      double l = 0;
+      for (int i = 0; i < D; i++)
+	l += x[i] * x[i];
+      return l;
+    }
+
+    const Vec<D> & Normalize ()
+    {
+      double l = Length();
+      if (l != 0)
+	for (int i = 0; i < D; i++)
+	  x[i] /= l;
+      return *this;
+    }
+
+    Vec<D> GetNormal () const;
+  };
+
+
+
+
+
+  template <int H, int W=H>
+  class Mat
+  {
+
+  protected:
+    double x[H*W];
+
+  public:
+    Mat () { ; }
+    Mat (const Mat & b)
+    { for (int i = 0; i < H*W; i++) x[i] = b.x[i]; }
+  
+    Mat & operator= (double s)
+    {
+      for (int i = 0; i < H*W; i++) x[i] = s;
+      return *this;
+    }
+
+    Mat & operator= (const Mat & b)
+    {
+      for (int i = 0; i < H*W; i++) x[i] = b.x[i]; 
+      return *this;
+    }
+
+    double & operator() (int i, int j) { return x[i*W+j]; }
+    const double & operator() (int i, int j) const { return x[i*W+j]; }
+    double & operator() (int i) { return x[i]; }
+    const double & operator() (int i) const { return x[i]; }
+
+    Vec<H> Col (int i) const
+    {
+      Vec<H> hv; 
+      for (int j = 0; j < H; j++)
+	hv(j) = x[j*W+i];
+      return hv; 
+    }
+
+    Vec<W> Row (int i) const
+    {
+      Vec<W> hv; 
+      for (int j = 0; j < W; j++)
+	hv(j) = x[i*W+j];
+      return hv; 
+    }
+
+    void Solve (const Vec<H> & rhs, Vec<W> & sol) const
+    {
+      Mat<W,H> inv;
+      CalcInverse (*this, inv);
+      sol = inv * rhs;
+    }
+  };
+
+
+
+
+  template <int D>
+  class Box
+  {
+  protected:
+    Point<D> pmin, pmax;
+  public:
+    Box () { ; }
+
+    Box ( const Point<D> & p1)
+    {
+      for (int i = 0; i < D; i++)
+	pmin(i) = pmax(i) = p1(i);
+    }
+
+
+    Box ( const Point<D> & p1, const Point<D> & p2)
+    {
+      for (int i = 0; i < D; i++)
+	{
+	  pmin(i) = min2(p1(i), p2(i));
+	  pmax(i) = max2(p1(i), p2(i));
+	}
+    }
+
+    enum EB_TYPE { EMPTY_BOX = 1 };
+    Box ( EB_TYPE et ) 
+    {
+      pmin = Point<3> (1e99, 1e99, 1e99);
+      pmax = Point<3> (-1e99, -1e99, -1e99);
+    }
+
+    const Point<D> & PMin () const { return pmin; }
+    const Point<D> & PMax () const { return pmax; }
+  
+    void Set (const Point<D> & p)
+    { pmin = pmax = p; }
+
+    void Add (const Point<D> & p)
+    { 
+      for (int i = 0; i < D; i++)
+	{
+	  if (p(i) < pmin(i)) pmin(i) = p(i);
+	  else if (p(i) > pmax(i)) pmax(i) = p(i);
+	}
+    }
+
+    Point<D> Center () const 
+    { 
+      Point<D> c;
+      for (int i = 0; i < D; i++)
+	c(i) = 0.5 * (pmin(i)+pmax(i)); 
+      return c;
+    }
+    double Diam () const { return Abs (pmax-pmin); }
+
+    Point<D> GetPointNr (int nr) const
+    {
+      Point<D> p;
+      for (int i = 0; i < D; i++)
+	{
+	  p(i) = (nr & 1) ? pmax(i) : pmin(i);
+	  nr >>= 1;
+	}
+      return p;
+    }
+
+
+    bool Intersect (const Box<D> & box2) const
+    {
+      for (int i = 0; i < D; i++)
+	if (pmin(i) > box2.pmax(i) ||
+	    pmax(i) < box2.pmin(i)) return 0;
+      return 1;
+    }
+
+
+    bool IsIn (const Point<D> & p) const
+    {
+      for (int i = 0; i < D; i++)
+	if (p(i) < pmin(i) || p(i) > pmax(i)) return 0;
+      return 1;
+    }
+
+
+    void Increase (double dist)
+    {
+      for (int i = 0; i < D; i++)
+	{
+	  pmin(i) -= dist;
+	  pmax(i) += dist;
+	}
+    }
+  };
+
+
+
+
+  template <int D>
+  class BoxSphere : public Box<D>
+  {
+  protected:
+    ///
+    Point<D> c;
+    ///
+    double diam;
+    ///
+    double inner;
+  public:
+    ///
+    BoxSphere () { };
+    ///
+    BoxSphere (const Box<D> & box) 
+      : Box<D> (box) 
+    { 
+      CalcDiamCenter();
+    };
+
+    ///
+    BoxSphere ( Point<D> apmin, Point<D> apmax )
+      : Box<D> (apmin, apmax)
+    {
+      CalcDiamCenter();
+    }
+
+    ///
+    const Point<D> & Center () const { return c; }
+    ///
+    double Diam () const { return diam; }
+    ///
+    double Inner () const { return inner; }
+
+
+    ///
+    void GetSubBox (int nr, BoxSphere & sbox) const
+    {
+      for (int i = 0; i < D; i++)
+	{
+	  if (nr & 1)
+	    {
+	      sbox.pmin(i) = c(i);
+	      sbox.pmax(i) = this->pmax(i);
+	    }
+	  else
+	    {
+	      sbox.pmin(i) = this->pmin(i);
+	      sbox.pmax(i) = c(i);
+	    }
+	  sbox.c(i) = 0.5 * (sbox.pmin(i) + sbox.pmax(i));
+	  nr >>= 1;
+	}
+      sbox.diam = 0.5 * diam;
+      sbox.inner = 0.5 * inner;
+    }
+
+
+    ///
+    void CalcDiamCenter ()
+    {
+      c = Box<D>::Center ();
+      diam = Dist (this->pmin, this->pmax);
+
+      inner = this->pmax(0) - this->pmin(0);
+      for (int i = 1; i < D; i++)
+	if (this->pmax(i) - this->pmin(i) < inner)
+	  inner = this->pmax(i) - this->pmin(i);
+    }
+
+  };
+
+
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/geomobjects2.hpp b/contrib/Netgen/libsrc/gprim/geomobjects2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..014a38525a9dc65d4f63e9f1f970712591220f3b
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomobjects2.hpp
@@ -0,0 +1,366 @@
+#ifndef FILE_OBJECTS
+#define FILE_OBJECTS
+
+/* *************************************************************************/
+/* File:   geomobjects.hpp                                                 */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+template <typename T>
+class VecExpr
+{
+public:
+  VecExpr () { ; }
+  double operator() (int i) const { return static_cast<const T&> (*this) (i); }
+};
+
+
+template <int D> class Vec;
+template <int D> class Point;
+
+
+template <int D>
+class Point : public VecExpr<Point<D> >
+{
+
+protected:
+  double x[D];
+
+public:
+  Point () { ; }
+  Point (double ax) { x[0] = ax; }
+  Point (double ax, double ay) { x[0] = ax; x[1] = ay; }
+  Point (double ax, double ay, double az)
+  { x[0] = ax; x[1] = ay; x[2] = az; }
+  Point (double ax, double ay, double az, double au)
+  { x[0] = ax; x[1] = ay; x[2] = az; x[3] = au;}
+
+  Point (const Point<D> & p2)
+  { for (int i = 0; i < D; i++) x[i] = p2.x[i]; }
+
+  explicit Point (const Vec<D> & v)
+  { for (int i = 0; i < D; i++) x[i] = v(i); }
+
+
+  template <typename T>
+  explicit Point (const VecExpr<T> & expr)
+  {
+    for (int i = 0; i < D; i++) x[i] = expr(i); 
+  }
+
+  Point & operator= (const Point<D> & p2)
+  {
+    for (int i = 0; i < D; i++) x[i] = p2.x[i]; 
+    return *this;
+  }
+
+  template <typename T>
+  Point & operator= (const VecExpr<T> & expr)
+  {
+    for (int i = 0; i < D; i++) x[i] = expr(i);
+    return *this;
+  }
+
+  double & operator() (int i) { return x[i]; }
+  const double & operator() (int i) const { return x[i]; }
+
+  operator const double* () const { return x; }
+};
+
+
+
+
+
+template <int D>
+class Vec : public VecExpr<Vec<D> >
+{
+
+protected:
+  double x[D];
+
+public:
+  Vec () { ; }
+  Vec (double ax) { for (int i = 0; i < D; i++) x[i] = ax; }
+  Vec (double ax, double ay) { x[0] = ax; x[1] = ay; }
+  Vec (double ax, double ay, double az)
+  { x[0] = ax; x[1] = ay; x[2] = az; }
+  Vec (double ax, double ay, double az, double au)
+  { x[0] = ax; x[1] = ay; x[2] = az; x[3] = au; }
+
+  Vec (const Vec<D> & p2)
+  { for (int i = 0; i < D; i++) x[i] = p2.x[i]; }
+
+  explicit Vec (const Point<D> & p)
+  { for (int i = 0; i < D; i++) x[i] = p(i); }
+
+  template <typename T>
+  explicit Vec (const VecExpr<T> & expr)
+  {
+    for (int i = 0; i < D; i++) x[i] = expr(i); 
+  }
+
+
+  Vec & operator= (const Vec<D> & p2)
+  {
+    for (int i = 0; i < D; i++) x[i] = p2.x[i]; 
+    return *this;
+  }
+
+  template <typename T>
+  Vec & operator= (const VecExpr<T> & expr)
+  {
+    for (int i = 0; i < D; i++) x[i] = expr(i);
+    return *this;
+  }
+
+  Vec & operator= (double s)
+  {
+    for (int i = 0; i < D; i++) x[i] = s;
+    return *this;
+  }
+
+  double & operator() (int i) { return x[i]; }
+  const double & operator() (int i) const { return x[i]; }
+
+  operator const double* () const { return x; }
+
+  double Length () const
+  {
+    double l = 0;
+    for (int i = 0; i < D; i++)
+      l += x[i] * x[i];
+    return sqrt (l);
+  }
+
+  double Length2 () const
+  {
+    double l = 0;
+    for (int i = 0; i < D; i++)
+      l += x[i] * x[i];
+    return l;
+  }
+
+  const Vec<D> & Normalize ()
+  {
+    double l = Length();
+    if (l != 0)
+      for (int i = 0; i < D; i++)
+	x[i] /= l;
+    return *this;
+  }
+
+  Vec<D> GetNormal () const;
+};
+
+
+
+
+
+template <int H, int W=H>
+class Mat
+{
+
+protected:
+  double x[H*W];
+
+public:
+  Mat () { ; }
+  Mat (const Mat & b)
+  { for (int i = 0; i < H*W; i++) x[i] = b.x[i]; }
+  
+  Mat & operator= (double s)
+  {
+    for (int i = 0; i < H*W; i++) x[i] = s;
+    return *this;
+  }
+
+  Mat & operator= (const Mat & b)
+  {
+    for (int i = 0; i < H*W; i++) x[i] = b.x[i]; 
+    return *this;
+  }
+
+  double & operator() (int i, int j) { return x[i*W+j]; }
+  const double & operator() (int i, int j) const { return x[i*W+j]; }
+
+  Vec<H> Col (int i) const
+  {
+    Vec<H> hv; 
+    for (int j = 0; j < H; j++)
+      hv(j) = x[j*W+i];
+    return hv; 
+  }
+
+  Vec<W> Row (int i) const
+  {
+    Vec<W> hv; 
+    for (int j = 0; j < W; j++)
+      hv(j) = x[i*W+j];
+    return hv; 
+  }
+
+  void Solve (const Vec<H> & rhs, Vec<W> & sol) const
+  {
+    Mat<W,H> inv;
+    CalcInverse (*this, inv);
+    sol = inv * rhs;
+  }
+};
+
+
+
+
+template <int D>
+class Box
+{
+protected:
+  Point<D> pmin, pmax;
+public:
+  Box () { ; }
+  Box ( const Point<D> & p1, const Point<D> & p2)
+  {
+    for (int i = 0; i < D; i++)
+      {
+	pmin(i) = min2(p1(i), p2(i));
+	pmax(i) = max2(p1(i), p2(i));
+      }
+  }
+
+  const Point<D> & PMin () const { return pmin; }
+  const Point<D> & PMax () const { return pmax; }
+  
+  void Set (const Point<D> & p)
+  { pmin = pmax = p; }
+
+  void Add (const Point<D> & p)
+  { 
+    for (int i = 0; i < D; i++)
+      {
+	if (p(i) < pmin(i)) pmin(i) = p(i);
+	else if (p(i) > pmax(i)) pmax(i) = p(i);
+      }
+  }
+
+  Point<D> Center () const 
+  { 
+    Point<D> c;
+    for (int i = 0; i < D; i++)
+      c(i) = 0.5 * (pmin(i)+pmax(i)); 
+    return c;
+  }
+  double Diam () const { return Abs (pmax-pmin); }
+
+  Point<D> GetPointNr (int nr) const
+  {
+    Point<D> p;
+    for (int i = 0; i < D; i++)
+      {
+	p(i) = (nr & 1) ? pmax(i) : pmin(i);
+	nr >>= 1;
+      }
+    return p;
+  }
+
+
+  bool Intersect (const Box<D> & box2) const
+  {
+    for (int i = 0; i < D; i++)
+      if (pmin(i) > box2.pmax(i) ||
+	  pmax(i) < box2.pmin(i)) return 0;
+    return 1;
+  }
+
+
+  bool IsIn (const Point<D> & p) const
+  {
+    for (int i = 0; i < D; i++)
+      if (p(i) < pmin(i) || p(i) > pmax(i)) return 0;
+    return 1;
+  }
+
+
+  void Increase (double dist)
+  {
+    for (int i = 0; i < D; i++)
+      {
+	pmin(i) -= dist;
+	pmax(i) += dist;
+      }
+  }
+};
+
+
+
+
+template <int D>
+class BoxSphere : public Box<D>
+{
+protected:
+  ///
+  Point<D> c;
+  ///
+  double diam;
+  ///
+  double inner;
+public:
+  ///
+  BoxSphere () { };
+  ///
+  BoxSphere ( Point<D> pmin, Point<D> pmax )
+    : Box<D> (pmin, pmax)
+  {
+    CalcDiamCenter();
+  }
+
+  ///
+  const Point<D> & Center () const { return c; }
+  ///
+  double Diam () const { return diam; }
+  ///
+  double Inner () const { return inner; }
+
+
+  ///
+  void GetSubBox (int nr, BoxSphere & sbox) const
+  {
+    for (int i = 0; i < D; i++)
+      {
+	if (nr & 1)
+	  {
+	    sbox.pmin(i) = c(i);
+	    sbox.pmax(i) = this->pmax(i);
+	  }
+	else
+	  {
+	    sbox.pmin(i) = this->pmin(i);
+	    sbox.pmax(i) = c(i);
+	  }
+	sbox.c(i) = 0.5 * (sbox.pmin(i) + sbox.pmax(i));
+	nr >>= 1;
+      }
+    sbox.diam = 0.5 * diam;
+    sbox.inner = 0.5 * inner;
+  }
+
+
+  ///
+  void CalcDiamCenter ()
+  {
+    c = Box<D>::Center ();
+    diam = Dist (this->pmin, this->pmax);
+
+    inner = this->pmax(0) - this->pmin(0);
+    for (int i = 1; i < D; i++)
+      if (this->pmax(i) - this->pmin(i) < inner)
+	inner = this->pmax(i) - this->pmin(i);
+  }
+
+};
+
+
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/geomops.hpp b/contrib/Netgen/libsrc/gprim/geomops.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..90c3cb6bf540ce809542f6e50dd239ef5bd133f5
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomops.hpp
@@ -0,0 +1,394 @@
+#ifndef FILE_GEOMOPS
+#define FILE_GEOMOPS
+
+/* *************************************************************************/
+/* File:   geomops.hpp                                                     */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+
+namespace netgen
+{
+
+  /*
+
+  Point - Vector operations
+
+  */
+
+
+  template <int D>
+  inline Vec<D> operator+ (const Vec<D> & a, const Vec<D> & b)
+  {
+    Vec<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = a(i) + b(i);
+    return res;
+  }
+
+
+
+  template <int D>
+  inline Point<D> operator+ (const Point<D> & a, const Vec<D> & b)
+  {
+    Point<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = a(i) + b(i);
+    return res;
+  }
+
+
+
+  template <int D>
+  inline Vec<D> operator- (const Point<D> & a, const Point<D> & b)
+  {
+    Vec<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = a(i) - b(i);
+    return res;
+  }
+
+  template <int D>
+  inline Point<D> operator- (const Point<D> & a, const Vec<D> & b)
+  {
+    Point<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = a(i) - b(i);
+    return res;
+  }
+
+  template <int D>
+  inline Vec<D> operator- (const Vec<D> & a, const Vec<D> & b)
+  {
+    Vec<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = a(i) - b(i);
+    return res;
+  }
+
+
+
+  template <int D>
+  inline Vec<D> operator* (double s, const Vec<D> & b)
+  {
+    Vec<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = s * b(i);
+    return res;
+  }
+
+
+  template <int D>
+  inline double operator* (const Vec<D> & a, const Vec<D> & b)
+  {
+    double sum = 0;
+    for (int i = 0; i < D; i++)
+      sum += a(i) * b(i);
+    return sum;
+  }
+
+
+
+  template <int D>
+  inline Vec<D> operator- (const Vec<D> & b)
+  {
+    Vec<D> res;
+    for (int i = 0; i < D; i++)
+      res(i) = -b(i);
+    return res;
+  }
+
+
+  template <int D>
+  inline Point<D> & operator+= (Point<D> & a, const Vec<D> & b)
+  {
+    for (int i = 0; i < D; i++)
+      a(i) += b(i);
+    return a;
+  }
+
+  template <int D>
+  inline Vec<D> & operator+= (Vec<D> & a, const Vec<D> & b)
+  {
+    for (int i = 0; i < D; i++)
+      a(i) += b(i);
+    return a;
+  }
+
+
+  template <int D>
+  inline Point<D> & operator-= (Point<D> & a, const Vec<D> & b)
+  {
+    for (int i = 0; i < D; i++)
+      a(i) -= b(i);
+    return a;
+  }
+
+  template <int D>
+  inline Vec<D> & operator-= (Vec<D> & a, const Vec<D> & b)
+  {
+    for (int i = 0; i < D; i++)
+      a(i) -= b(i);
+    return a;
+  }
+
+
+
+  template <int D>
+  inline Vec<D> & operator*= (Vec<D> & a, double s)
+  {
+    for (int i = 0; i < D; i++)
+      a(i) *= s;
+    return a;
+  }
+
+
+  template <int D>
+  inline Vec<D> & operator/= (Vec<D> & a, double s)
+  {
+    for (int i = 0; i < D; i++)
+      a(i) /= s;
+    return a;
+  }
+
+
+
+
+  // Matrix - Vector operations
+
+  /*
+    template <int H, int W>
+    inline Vec<H> operator* (const Mat<H,W> & m, const Vec<W> & v)
+    {
+    Vec<H> res;
+    for (int i = 0; i < H; i++)
+    {
+    res(i) = 0;
+    for (int j = 0; j < W; j++)
+    res(i) += m(i,j) * v(j);
+    }
+    return res;
+    }
+  */
+
+  // thanks to VC60 partial template specialization features !!!
+
+  inline Vec<2> operator* (const Mat<2,2> & m, const Vec<2> & v)
+  {
+    Vec<2> res;
+    for (int i = 0; i < 2; i++)
+      {
+	res(i) = 0;
+	for (int j = 0; j < 2; j++)
+	  res(i) += m(i,j) * v(j);
+      }
+    return res;
+  }
+
+  inline Vec<2> operator* (const Mat<2,3> & m, const Vec<3> & v)
+  {
+    Vec<2> res;
+    for (int i = 0; i < 2; i++)
+      {
+	res(i) = 0;
+	for (int j = 0; j < 3; j++)
+	  res(i) += m(i,j) * v(j);
+      }
+    return res;
+  }
+
+
+  inline Vec<3> operator* (const Mat<3,2> & m, const Vec<2> & v)
+  {
+    Vec<3> res;
+    for (int i = 0; i < 3; i++)
+      {
+	res(i) = 0;
+	for (int j = 0; j < 2; j++)
+	  res(i) += m(i,j) * v(j);
+      }
+    return res;
+  }
+
+
+  inline Vec<3> operator* (const Mat<3,3> & m, const Vec<3> & v)
+  {
+    Vec<3> res;
+    for (int i = 0; i < 3; i++)
+      {
+	res(i) = 0;
+	for (int j = 0; j < 3; j++)
+	  res(i) += m(i,j) * v(j);
+      }
+    return res;
+  }
+
+
+
+
+
+
+
+  /*
+    template <int H1, int W1, int H2, int W2>
+    inline Mat<H1,W2> operator* (const Mat<H1,W1> & a, const Mat<H2,W2> & b)
+    {
+    Mat<H1,W2> m;
+    for (int i = 0; i < H1; i++)
+    for (int j = 0; j < W2; j++)
+    {
+    double sum = 0;
+    for (int k = 0; k < W1; k++)
+    sum += a(i,k) * b(k, j);
+    m(i,j) = sum; 
+    }
+    return m;
+    }
+  */
+
+  inline Mat<2,2> operator* (const Mat<2,2> & a, const Mat<2,2> & b)
+  {
+    Mat<2,2> m;
+    for (int i = 0; i < 2; i++)
+      for (int j = 0; j < 2; j++)
+	{
+	  double sum = 0;
+	  for (int k = 0; k < 2; k++)
+	    sum += a(i,k) * b(k, j);
+	  m(i,j) = sum; 
+	}
+    return m;
+  }
+
+  inline Mat<2,2> operator* (const Mat<2,3> & a, const Mat<3,2> & b)
+  {
+    Mat<2,2> m;
+    for (int i = 0; i < 2; i++)
+      for (int j = 0; j < 2; j++)
+	{
+	  double sum = 0;
+	  for (int k = 0; k < 3; k++)
+	    sum += a(i,k) * b(k, j);
+	  m(i,j) = sum; 
+	}
+    return m;
+  }
+
+
+  inline Mat<3,2> operator* (const Mat<3,2> & a, const Mat<2,2> & b)
+  {
+    Mat<3,2> m;
+    for (int i = 0; i < 3; i++)
+      for (int j = 0; j < 2; j++)
+	{
+	  double sum = 0;
+	  for (int k = 0; k < 2; k++)
+	    sum += a(i,k) * b(k, j);
+	  m(i,j) = sum; 
+	}
+    return m;
+  }
+
+
+
+  inline Mat<2,3> operator* (const Mat<2,2> & a, const Mat<2,3> & b)
+  {
+    Mat<2,3> m;
+    for (int i = 0; i < 2; i++)
+      for (int j = 0; j < 3; j++)
+	{
+	  double sum = 0;
+	  for (int k = 0; k < 2; k++)
+	    sum += a(i,k) * b(k, j);
+	  m(i,j) = sum; 
+	}
+    return m;
+  }
+
+
+  inline Mat<3,3> operator* (const Mat<3,3> & a, const Mat<3,3> & b)
+  {
+    Mat<3,3> m;
+    for (int i = 0; i < 3; i++)
+      for (int j = 0; j < 3; j++)
+	{
+	  double sum = 0;
+	  for (int k = 0; k < 3; k++)
+	    sum += a(i,k) * b(k, j);
+	  m(i,j) = sum; 
+	}
+    return m;
+  }
+
+
+
+
+
+
+
+
+  template <int H, int W>
+  inline Mat<W,H> Trans (const Mat<H,W> & m)
+  {
+    Mat<W,H> res;
+    for (int i = 0; i < H; i++)
+      for (int j = 0; j < W; j++)
+	res(j,i) = m(i,j);
+    return res;
+  }
+
+
+
+
+
+
+
+
+
+
+
+  template <int D>
+  inline ostream & operator<< (ostream & ost, const Vec<D> & a)
+  {
+    ost << "(";
+    for (int i = 0; i < D-1; i++)
+      ost << a(i) << ", ";
+    ost << a(D-1) << ")";
+    return ost;
+  }
+
+  template <int D>
+  inline ostream & operator<< (ostream & ost, const Point<D> & a)
+  {
+    ost << "(";
+    for (int i = 0; i < D-1; i++)
+      ost << a(i) << ", ";
+    ost << a(D-1) << ")";
+    return ost;
+  }
+
+  template <int D>
+  inline ostream & operator<< (ostream & ost, const Box<D> & b)
+  {
+    ost << b.PMin() << " - " << b.PMax();
+    return ost;
+  }
+
+  template <int H, int W>
+  inline ostream & operator<< (ostream & ost, const Mat<H,W> & m)
+  {
+    ost << "(";
+    for (int i = 0; i < H; i++)
+      {
+	for (int j = 0; j < W; j++)
+	  ost << m(i,j) << "   ";
+	ost << endl;
+      }
+    return ost;
+  }
+
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/geomops2.hpp b/contrib/Netgen/libsrc/gprim/geomops2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c615da14ecabbc8ae99598e4e04052085750a0b2
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomops2.hpp
@@ -0,0 +1,428 @@
+#ifndef FILE_GEOMOPS
+#define FILE_GEOMOPS
+
+/* *************************************************************************/
+/* File:   geomops.hpp                                                     */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+
+/*
+
+Point - Vector operations
+
+ */
+
+
+
+
+template <class TA, class TB>
+class SumExpr : public VecExpr<SumExpr<TA, TB> >
+{
+  const TA a;
+  const TB b;
+public:
+  SumExpr (const TA aa, const TB ab) : a(aa), b(ab) { ; }
+  double operator() (int i) const { return a(i) + b(i); }
+};
+
+template <typename TA, typename TB>
+inline SumExpr<TA,TB>
+operator+ (const VecExpr<TA> & a, const VecExpr<TB> & b)
+{
+  return SumExpr<TA,TB> (static_cast <const TA&> (a), static_cast <const TB&> (b));
+}
+
+/*
+template <int D1, int D2>
+inline SumExpr<const Vec<D1>&, const Vec<D2>&>
+operator+ (const Vec<D1> & a, const Vec<D2> & b)
+{
+  return SumExpr<const Vec<D1>&, const Vec<D2>&> (a, b);
+}
+*/
+
+
+
+
+
+/*
+template <int D>
+inline Vec<D> operator+ (const Vec<D> & a, const Vec<D> & b)
+{
+  Vec<D> res;
+  for (int i = 0; i < D; i++)
+    res(i) = a(i) + b(i);
+  return res;
+}
+*/
+
+template <int D>
+inline Point<D> operator+ (const Point<D> & a, const Vec<D> & b)
+{
+  Point<D> res;
+  for (int i = 0; i < D; i++)
+    res(i) = a(i) + b(i);
+  return res;
+}
+
+
+template <int D>
+inline Vec<D> operator- (const Point<D> & a, const Point<D> & b)
+{
+  Vec<D> res;
+  for (int i = 0; i < D; i++)
+    res(i) = a(i) - b(i);
+  return res;
+}
+
+template <int D>
+inline Point<D> operator- (const Point<D> & a, const Vec<D> & b)
+{
+  Point<D> res;
+  for (int i = 0; i < D; i++)
+    res(i) = a(i) - b(i);
+  return res;
+}
+
+template <int D>
+inline Vec<D> operator- (const Vec<D> & a, const Vec<D> & b)
+{
+  Vec<D> res;
+  for (int i = 0; i < D; i++)
+    res(i) = a(i) - b(i);
+  return res;
+}
+
+
+template <int D>
+inline Vec<D> operator* (double s, const Vec<D> & b)
+{
+  Vec<D> res;
+  for (int i = 0; i < D; i++)
+    res(i) = s * b(i);
+  return res;
+}
+
+
+template <int D>
+inline double operator* (const Vec<D> & a, const Vec<D> & b)
+{
+  double sum = 0;
+  for (int i = 0; i < D; i++)
+    sum += a(i) * b(i);
+  return sum;
+}
+
+
+
+template <int D>
+inline Vec<D> operator- (const Vec<D> & b)
+{
+  Vec<D> res;
+  for (int i = 0; i < D; i++)
+    res(i) = -b(i);
+  return res;
+}
+
+
+template <int D>
+inline Point<D> & operator+= (Point<D> & a, const Vec<D> & b)
+{
+  for (int i = 0; i < D; i++)
+    a(i) += b(i);
+  return a;
+}
+
+
+template <int D, typename T>
+inline Point<D> & operator+= (Point<D> & a, const VecExpr<T> & b)
+{
+  for (int i = 0; i < D; i++)
+    a(i) += b(i);
+  return a;
+}
+
+template <int D>
+inline Vec<D> & operator+= (Vec<D> & a, const Vec<D> & b)
+{
+  for (int i = 0; i < D; i++)
+    a(i) += b(i);
+  return a;
+}
+
+
+
+
+
+template <int D>
+inline Point<D> & operator-= (Point<D> & a, const Vec<D> & b)
+{
+  for (int i = 0; i < D; i++)
+    a(i) -= b(i);
+  return a;
+}
+
+template <int D, typename T>
+inline Point<D> & operator-= (Point<D> & a, const VecExpr<T> & b)
+{
+  for (int i = 0; i < D; i++)
+    a(i) -= b(i);
+  return a;
+}
+
+
+
+
+
+template <int D>
+inline Vec<D> & operator-= (Vec<D> & a, const Vec<D> & b)
+{
+  for (int i = 0; i < D; i++)
+    a(i) -= b(i);
+  return a;
+}
+
+
+
+template <int D>
+inline Vec<D> & operator*= (Vec<D> & a, double s)
+{
+  for (int i = 0; i < D; i++)
+    a(i) *= s;
+  return a;
+}
+
+
+template <int D>
+inline Vec<D> & operator/= (Vec<D> & a, double s)
+{
+  for (int i = 0; i < D; i++)
+    a(i) /= s;
+  return a;
+}
+
+
+
+
+// Matrix - Vector operations
+
+/*
+template <int H, int W>
+inline Vec<H> operator* (const Mat<H,W> & m, const Vec<W> & v)
+{
+  Vec<H> res;
+  for (int i = 0; i < H; i++)
+    {
+      res(i) = 0;
+      for (int j = 0; j < W; j++)
+	res(i) += m(i,j) * v(j);
+    }
+  return res;
+}
+*/
+
+// thanks to VC60 partial template specialization features !!!
+
+inline Vec<2> operator* (const Mat<2,2> & m, const Vec<2> & v)
+{
+  Vec<2> res;
+  for (int i = 0; i < 2; i++)
+    {
+      res(i) = 0;
+      for (int j = 0; j < 2; j++)
+	res(i) += m(i,j) * v(j);
+    }
+  return res;
+}
+
+inline Vec<2> operator* (const Mat<2,3> & m, const Vec<3> & v)
+{
+  Vec<2> res;
+  for (int i = 0; i < 2; i++)
+    {
+      res(i) = 0;
+      for (int j = 0; j < 3; j++)
+	res(i) += m(i,j) * v(j);
+    }
+  return res;
+}
+
+
+inline Vec<3> operator* (const Mat<3,2> & m, const Vec<2> & v)
+{
+  Vec<3> res;
+  for (int i = 0; i < 3; i++)
+    {
+      res(i) = 0;
+      for (int j = 0; j < 2; j++)
+	res(i) += m(i,j) * v(j);
+    }
+  return res;
+}
+
+
+inline Vec<3> operator* (const Mat<3,3> & m, const Vec<3> & v)
+{
+  Vec<3> res;
+  for (int i = 0; i < 3; i++)
+    {
+      res(i) = 0;
+      for (int j = 0; j < 3; j++)
+	res(i) += m(i,j) * v(j);
+    }
+  return res;
+}
+
+
+
+
+
+
+
+/*
+template <int H1, int W1, int H2, int W2>
+inline Mat<H1,W2> operator* (const Mat<H1,W1> & a, const Mat<H2,W2> & b)
+{
+  Mat<H1,W2> m;
+  for (int i = 0; i < H1; i++)
+    for (int j = 0; j < W2; j++)
+      {
+	double sum = 0;
+	for (int k = 0; k < W1; k++)
+	  sum += a(i,k) * b(k, j);
+	m(i,j) = sum; 
+      }
+  return m;
+}
+*/
+
+inline Mat<2,2> operator* (const Mat<2,2> & a, const Mat<2,2> & b)
+{
+  Mat<2,2> m;
+  for (int i = 0; i < 2; i++)
+    for (int j = 0; j < 2; j++)
+      {
+	double sum = 0;
+	for (int k = 0; k < 2; k++)
+	  sum += a(i,k) * b(k, j);
+	m(i,j) = sum; 
+      }
+  return m;
+}
+
+inline Mat<2,2> operator* (const Mat<2,3> & a, const Mat<3,2> & b)
+{
+  Mat<2,2> m;
+  for (int i = 0; i < 2; i++)
+    for (int j = 0; j < 2; j++)
+      {
+	double sum = 0;
+	for (int k = 0; k < 3; k++)
+	  sum += a(i,k) * b(k, j);
+	m(i,j) = sum; 
+      }
+  return m;
+}
+
+
+inline Mat<3,2> operator* (const Mat<3,2> & a, const Mat<2,2> & b)
+{
+  Mat<3,2> m;
+  for (int i = 0; i < 3; i++)
+    for (int j = 0; j < 2; j++)
+      {
+	double sum = 0;
+	for (int k = 0; k < 2; k++)
+	  sum += a(i,k) * b(k, j);
+	m(i,j) = sum; 
+      }
+  return m;
+}
+
+inline Mat<3,3> operator* (const Mat<3,3> & a, const Mat<3,3> & b)
+{
+  Mat<3,3> m;
+  for (int i = 0; i < 3; i++)
+    for (int j = 0; j < 3; j++)
+      {
+	double sum = 0;
+	for (int k = 0; k < 3; k++)
+	  sum += a(i,k) * b(k, j);
+	m(i,j) = sum; 
+      }
+  return m;
+}
+
+
+
+
+
+
+
+
+template <int H, int W>
+inline Mat<W,H> Trans (const Mat<H,W> & m)
+{
+  Mat<W,H> res;
+  for (int i = 0; i < H; i++)
+    for (int j = 0; j < W; j++)
+      res(j,i) = m(i,j);
+  return res;
+}
+
+
+
+
+
+
+
+
+
+
+
+template <int D>
+inline ostream & operator<< (ostream & ost, const Vec<D> & a)
+{
+  ost << "(";
+  for (int i = 0; i < D-1; i++)
+    ost << a(i) << ", ";
+  ost << a(D-1) << ")";
+  return ost;
+}
+
+template <int D>
+inline ostream & operator<< (ostream & ost, const Point<D> & a)
+{
+  ost << "(";
+  for (int i = 0; i < D-1; i++)
+    ost << a(i) << ", ";
+  ost << a(D-1) << ")";
+  return ost;
+}
+
+template <int D>
+inline ostream & operator<< (ostream & ost, const Box<D> & b)
+{
+  ost << b.PMin() << " - " << b.PMax();
+  return ost;
+}
+
+template <int H, int W>
+inline ostream & operator<< (ostream & ost, const Mat<H,W> & m)
+{
+  ost << "(";
+  for (int i = 0; i < H; i++)
+    {
+      for (int j = 0; j < W; j++)
+	ost << m(i,j) << "   ";
+      ost << endl;
+    }
+  return ost;
+}
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/geomtest3d.cpp b/contrib/Netgen/libsrc/gprim/geomtest3d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb4000b36d2cbc5d538bf65fbf6025a727ef7b0c
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomtest3d.cpp
@@ -0,0 +1,1150 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+namespace netgen
+{
+int
+IntersectTriangleLine (const Point<3> ** tri, const Point<3> ** line)
+{
+  Vec3d vl(*line[0], *line[1]);
+  Vec3d vt1(*tri[0], *tri[1]);
+  Vec3d vt2(*tri[0], *tri[2]);
+  Vec3d vrs(*tri[0], *line[0]);
+
+  static DenseMatrix a(3), ainv(3);
+  static Vector rs(3), lami(3);
+  int i;
+
+  /*
+  (*testout) << "Tri-Line inters: " << endl
+	     << "tri = " << *tri[0] << ", " << *tri[1] << ", " << *tri[2] << endl
+	     << "line = " << *line[0] << ", " << *line[1] << endl;
+  */
+  for (i = 0; i < 3; i++)
+    {
+      a(i, 0) = -vl.X(i+1);
+      a(i, 1) = vt1.X(i+1);
+      a(i, 2) = vt2.X(i+1);
+      rs(i) = vrs.X(i+1);
+    }
+
+  double det = a.Det();
+
+  double arel = vl.Length() * vt1.Length() * vt2.Length();
+  /*
+  double amax = 0;
+  for (i = 1; i <= 9; i++)
+    if (fabs (a.Get(i)) > amax)
+      amax = fabs(a.Get(i));
+  */
+  // new !!!!
+  if (fabs (det) <= 1e-10 * arel)
+    {
+#ifdef DEVELOP      
+      // line parallel to triangle !
+      // cout << "ERROR: IntersectTriangleLine degenerated" << endl;
+      //      (*testout) << "WARNING: IntersectTriangleLine degenerated\n";
+      /*
+      (*testout) << "lin-tri intersection: " << endl
+		 << "line = " << *line[0] << " - " << *line[1] << endl
+		 << "tri = " << *tri[0] << " - " << *tri[1] << " - " << *tri[2] << endl
+		 << "lami = " << lami << endl
+		 << "pc = " << ( *line[0] + lami.Get(1) * vl ) << endl
+		 << "   = " << ( *tri[0] + lami.Get(2) * vt1 + lami.Get(3) * vt2) << endl
+		 << " a = " << a << endl
+		 << " ainv = " << ainv << endl
+		 << " det(a) = " << det << endl
+		 << " rs = " << rs << endl;
+      */
+#endif
+      return 0;
+    }
+
+  CalcInverse (a, ainv);
+  ainv.Mult (rs, lami);
+
+  //  (*testout) << "lami = " << lami << endl;
+
+  double eps = 1e-6;
+  if (
+      (lami(0) >= -eps && lami(0) <= 1+eps && 
+       lami(1) >= -eps && lami(2) >= -eps && 
+       lami(1) + lami(2) <= 1+eps)  && !
+      (lami(0) >= eps && lami(0) <= 1-eps && 
+       lami(1) >= eps && lami(2) >= eps && 
+       lami(1) + lami(2) <= 1-eps) )
+
+
+     {
+#ifdef DEVELOP
+       //      cout << "WARNING: IntersectTriangleLine degenerated" << endl;
+      (*testout) << "WARNING: IntersectTriangleLine numerical inexact" << endl;
+
+      (*testout) << "lin-tri intersection: " << endl
+		 << "line = " << *line[0] << " - " << *line[1] << endl
+		 << "tri = " << *tri[0] << " - " << *tri[1] << " - " << *tri[2] << endl
+		 << "lami = " << lami << endl
+		 << "pc = " << ( *line[0] + lami.Get(1) * vl ) << endl
+		 << "   = " << ( *tri[0] + lami.Get(2) * vt1 + lami.Get(3) * vt2) << endl
+		 << " a = " << a << endl
+		 << " ainv = " << ainv << endl
+		 << " det(a) = " << det << endl
+		 << " rs = " << rs << endl;
+#endif
+    }
+      
+
+  if (lami(0) >= 0 && lami(0) <= 1 && 
+      lami(1) >= 0 && lami(2) >= 0 && lami(1) + lami(2) <= 1)
+    {
+
+      return 1;
+    }
+
+  return 0;
+}
+
+
+
+
+
+int IntersectTetTriangle (const Point<3> ** tet, const Point<3> ** tri,
+			  const int * tetpi, const int * tripi)
+{
+  int i, j;
+  double diam = Dist (*tri[0], *tri[1]);
+  double epsrel = 1e-8;
+  double eps = diam * epsrel;
+
+  double eps2 = eps * eps;
+  int cnt = 0;
+
+  int tetp1 = -1, tetp2 = -1;
+  int trip1 = -1, trip2 = -1;
+  int tetp3, tetp4, trip3;
+
+  /*
+  for (i = 0; i < 4; i++)
+    loctetpi[i] = -1;
+  */
+
+
+  if (!tetpi)
+    {
+      for (i = 0; i <= 2; i++)
+	{
+	  //	  loctripi[i] = -1;
+	  for (j = 0; j <= 3; j++)
+	    {
+	      if (Dist2 (*tet[j], *tri[i]) < eps2)
+		{
+		  //		  loctripi[i] = j;
+		  //		  loctetpi[j] = i;
+		  cnt++;
+		  tetp2 = tetp1;
+		  tetp1 = j;
+		  trip2 = trip1;
+		  trip1 = i;
+		  break;
+		}
+	    }
+	}
+    }
+  else
+    {
+      for (i = 0; i <= 2; i++)
+	{
+	  //	  loctripi[i] = -1;
+	  for (j = 0; j <= 3; j++)
+	    {
+	      if (tetpi[j] == tripi[i])
+		{
+		  //		  loctripi[i] = j;
+		  //		  loctetpi[j] = i;
+		  cnt++;
+		  tetp2 = tetp1;
+		  tetp1 = j;
+		  trip2 = trip1;
+		  trip1 = i;
+		  break;
+		}
+	    }
+	}
+    }  
+  
+  //  (*testout) << "cnt = " << cnt << endl;
+
+
+  //  (*testout) << "tet-trig inters, cnt = " << cnt << endl;
+  
+  // cnt .. number of common points
+  switch (cnt)
+    {
+    case 0:
+      {
+	Vec3d no, n;
+	int inpi[3];
+
+	// check, if some trigpoint is in tet:
+
+	for (j = 0; j < 3; j++)
+	  inpi[j] = 1;
+
+	for (i = 1; i <= 4; i++)
+	  {
+	    int pi1 = i % 4;
+	    int pi2 = (i+1) % 4;
+	    int pi3 = (i+2) % 4;
+	    int pi4 = (i+3) % 4;
+
+	    Vec3d v1 (*tet[pi1], *tet[pi2]);
+	    Vec3d v2 (*tet[pi1], *tet[pi3]);
+	    Vec3d v3 (*tet[pi1], *tet[pi4]);
+	    Cross (v1, v2, n);
+
+	    // n /= n.Length();
+	    double nl = n.Length();
+
+	    if (v3 * n > 0)
+	      n *= -1;
+
+	    int outeri = 1;
+	    for (j = 0; j < 3; j++)
+	      {
+		Vec3d v(*tet[pi1], *tri[j]);
+		if ( v * n < eps * nl)
+		  outeri = 0;
+		else
+		  inpi[j] = 0;
+	      }
+
+	    if (outeri)
+	      return 0;
+	  }
+
+	if (inpi[0] || inpi[1] || inpi[2])
+	  {
+	    return 1;
+	  }
+
+
+	// check, if some tet edge intersects triangle:
+	const Point<3> * line[2], *tetf[3];
+	for (i = 0; i <= 2; i++)
+	  for (j = i+1; j <= 3; j++)
+	    {
+	      line[0] = tet[i];
+	      line[1] = tet[j];
+
+	      if (IntersectTriangleLine (tri, &line[0]))
+		return 1;
+	    }
+
+	// check, if triangle line intersects tet face:
+	for (i = 0; i <= 3; i++)
+	  {
+	    for (j = 0; j <= 2; j++)
+	      tetf[j] = tet[(i+j) % 4];
+	    
+	    for (j = 0; j <= 2; j++)
+	      {
+		line[0] = tri[j];
+		line[1] = tri[(j+1) % 3];
+		
+		if (IntersectTriangleLine (&tetf[0], &line[0]))
+		  return 1;
+	      }
+	  }
+
+
+	return 0;
+//GH	break;
+      }
+    case 1:
+      {
+	trip2 = 0;
+	while (trip2 == trip1)
+	  trip2++;
+	trip3 = 3 - trip1 - trip2;
+
+	tetp2 = 0;
+	while (tetp2 == tetp1)
+	  tetp2++;
+	tetp3 = 0;
+	while (tetp3 == tetp1 || tetp3 == tetp2)
+	  tetp3++;
+	tetp4 = 6 - tetp1 - tetp2 - tetp3;
+
+	Vec3d vtri1 = *tri[trip2] - *tri[trip1];
+	Vec3d vtri2 = *tri[trip3] - *tri[trip1];
+	Vec3d ntri;
+	Cross (vtri1, vtri2, ntri);
+
+	// tri durch tet ?
+	// fehlt noch
+
+
+	// test 3 tet-faces:
+	for (i = 1; i <= 3; i++)
+	  {
+	    Vec3d vtet1, vtet2;
+	    switch (i)
+	      {
+	      case 1:
+		{
+		  vtet1 = *tet[tetp2] - *tet[tetp1];
+		  vtet2 = *tet[tetp3] - *tet[tetp1];
+		  break;
+		}
+	      case 2:
+		{
+		  vtet1 = *tet[tetp3] - *tet[tetp1];
+		  vtet2 = *tet[tetp4] - *tet[tetp1];
+		  break;
+		}
+	      case 3:
+		{
+		  vtet1 = *tet[tetp4] - *tet[tetp1];
+		  vtet2 = *tet[tetp2] - *tet[tetp1];
+		  break;
+		}
+	      }
+	    
+	    Vec3d ntet;
+	    Cross (vtet1, vtet2, ntet);
+	    
+	    Vec3d crline = Cross (ntri, ntet);
+
+	    double lcrline = crline.Length();
+
+	    if (lcrline < eps * eps * eps * eps)  // new change !
+	      continue;
+
+	    if (vtri1 * crline + vtri2 * crline < 0)
+	      crline *= -1;
+
+	    crline /= lcrline;
+
+	    double lam1, lam2, lam3, lam4;
+	    LocalCoordinates (vtri1, vtri2, crline, lam1, lam2);
+	    LocalCoordinates (vtet1, vtet2, crline, lam3, lam4);
+	    
+	    if (lam1 > -epsrel && lam2 > -epsrel &&
+		lam3 > -epsrel && lam4 > -epsrel)
+	      {
+		
+		/*
+		(*testout) << "lcrline = " << lcrline 
+			   << " eps = " << eps << " diam = " << diam << endl;
+		 
+		(*testout) << "hit, cnt == 1 " 
+			   << "lam1,2,3,4 = " << lam1 << ", " 
+			   << lam2 << ", " << lam3 << ", " << lam4
+			   << "\n";
+		*/
+		return 1;
+	      }
+	  }
+	return 0;
+//GH	break;
+      }
+    case 2:
+      {
+	// common edge
+	tetp3 = 0;
+	while (tetp3 == tetp1 || tetp3 == tetp2)
+	  tetp3++;
+	tetp4 = 6 - tetp1 - tetp2 - tetp3;
+	trip3 = 3 - trip1 - trip2;
+
+	//	(*testout) << "trip1,2,3 = " << trip1 << ", " << trip2 << ", " << trip3 << endl;
+	//	(*testout) << "tetp1,2,3,4 = " << tetp1 << ", " << tetp2 
+	//		   << ", " << tetp3 << ", " << tetp4 << endl;
+
+	Vec3d vtri = *tri[trip3] - *tri[trip1];
+	Vec3d vtet1 = *tet[tetp3] - *tri[trip1];
+	Vec3d vtet2 = *tet[tetp4] - *tri[trip1];
+
+	Vec3d n = *tri[trip2] - *tri[trip1];
+	n /= n.Length();
+
+	vtet1 -= (n * vtet1) * n;
+	vtet2 -= (n * vtet2) * n;
+
+
+	double lam1, lam2;
+	LocalCoordinates (vtet1, vtet2, vtri, lam1, lam2);
+	
+	if (lam1 < -epsrel || lam2 < -epsrel)
+	  return 0;
+	else
+	  {
+	    /*
+
+	    (*testout) << "vtet1 = " << vtet1 << endl;
+	    (*testout) << "vtet2 = " << vtet2 << endl;
+	    (*testout) << "vtri = " << vtri << endl;
+	    (*testout) << "lam1 = " << lam1 << " lam2 = " << lam2 << endl;
+	    (*testout) << (lam1 * (vtet1 * vtet1) + lam2 * (vtet1 * vtet2))
+		       << " = " << (vtet1 * vtri) << endl;
+	    (*testout) << (lam1 * (vtet1 * vtet2) + lam2 * (vtet2 * vtet2))
+		       << " = " << (vtet2 * vtri) << endl;
+	    
+	    (*testout) << "tet = ";
+	    for (j = 0; j < 4; j++)
+	      (*testout) << (*tet[j]) << " ";
+	    (*testout) << endl;
+	    (*testout) << "tri = ";
+	    for (j = 0; j < 3; j++)
+	      (*testout) << (*tri[j]) << " ";
+	    (*testout) << endl;
+
+	    (*testout) << "hit, cnt == 2" << endl;
+	    */
+	    
+	    return 1;
+	  }
+	  
+	break;
+      }
+    case 3:
+      {
+	// common face
+	return 0;
+      }
+    }
+
+  (*testout) << "hit, cnt = " << cnt << endl;
+  return 1;
+}
+
+
+
+
+
+int IntersectTetTriangleRef (const Point<3> ** tri, const int * tripi)
+{
+  int i, j;
+  double eps = 1e-8;
+  // double eps2 = eps * eps;
+
+  static Point<3> rtetp1(0, 0, 0);
+  static Point<3> rtetp2(1, 0, 0);  
+  static Point<3> rtetp3(0, 1, 0); 
+  static Point<3> rtetp4(0, 0, 1);
+
+  static const Point<3> * tet[] = { &rtetp1, &rtetp2, &rtetp3, &rtetp4 };
+  static int tetpi[] = { 1, 2, 3, 4 };
+
+
+  //  return IntersectTetTriangle (tet, tri, tetpi, tripi);
+
+  
+  int cnt = 0;
+
+  int tetp1 = -1, tetp2 = -1;
+  int trip1 = -1, trip2 = -1;
+  int tetp3, tetp4, trip3;
+
+  /*
+  if (!tetpi)
+    {
+      for (i = 0; i <= 2; i++)
+	{
+	  for (j = 0; j <= 3; j++)
+	    {
+	      if (Dist2 (*tet[j], *tri[i]) < eps2)
+		{
+		  cnt++;
+		  tetp2 = tetp1;
+		  tetp1 = j;
+		  trip2 = trip1;
+		  trip1 = i;
+		  break;
+		}
+	    }
+	}
+    }
+  else
+  */
+    {
+      for (i = 0; i <= 2; i++)
+	{
+	  for (j = 0; j <= 3; j++)
+	    {
+	      if (tetpi[j] == tripi[i])
+		{
+		  cnt++;
+		  tetp2 = tetp1;
+		  tetp1 = j;
+		  trip2 = trip1;
+		  trip1 = i;
+		  break;
+		}
+	    }
+	}
+    }  
+  
+  //  (*testout) << "cnt = " << cnt << endl;
+
+
+  switch (cnt)
+    {
+    case 0:
+      {
+	Vec3d no, n;
+	//	int inpi[3];
+	int pside[3][4];
+
+	for (j = 0; j < 3; j++)
+	  {
+	    pside[j][0] = (*tri[j])(0) > -eps;
+	    pside[j][1] = (*tri[j])(1) > -eps;
+	    pside[j][2] = (*tri[j])(2) > -eps;
+	    pside[j][3] = (*tri[j])(0) + (*tri[j])(1) + (*tri[j])(2) < 1+eps;
+	  }
+
+	
+	for (j = 0; j < 4; j++)
+	  {
+	    if (!pside[0][j] && !pside[1][j] && !pside[2][j])
+	      return 0;
+	  }
+
+	for (j = 0; j < 3; j++)
+	  {
+	    if (pside[j][0] && pside[j][1] && pside[j][2] && pside[j][3])
+	      return 1;
+	  }
+
+
+	const Point<3> * line[2], *tetf[3];
+	for (i = 0; i <= 2; i++)
+	  for (j = i+1; j <= 3; j++)
+	    {
+	      line[0] = tet[i];
+	      line[1] = tet[j];
+
+	      if (IntersectTriangleLine (tri, &line[0]))
+		return 1;
+	    }
+
+	for (i = 0; i <= 3; i++)
+	  {
+	    for (j = 0; j <= 2; j++)
+	      tetf[j] = tet[(i+j) % 4];
+	    
+	    for (j = 0; j <= 2; j++)
+	      {
+		line[0] = tri[j];
+		line[1] = tri[(j+1) % 3];
+
+	      if (IntersectTriangleLine (&tetf[0], &line[0]))
+		return 1;
+	      }
+	  }
+
+
+	return 0;
+	break;
+      }
+    case 1:
+      {
+	trip2 = 0;
+	if (trip2 == trip1)
+	  trip2++;
+	trip3 = 3 - trip1 - trip2;
+
+	tetp2 = 0;
+	while (tetp2 == tetp1)
+	  tetp2++;
+	tetp3 = 0;
+	while (tetp3 == tetp1 || tetp3 == tetp2)
+	  tetp3++;
+	tetp4 = 6 - tetp1 - tetp2 - tetp3;
+
+	Vec3d vtri1 = *tri[trip2] - *tri[trip1];
+	Vec3d vtri2 = *tri[trip3] - *tri[trip1];
+	Vec3d ntri;
+	Cross (vtri1, vtri2, ntri);
+
+	// tri durch tet ?
+
+	/*
+	Vec3d vtet1(*tet[tetp1], *tet[tetp2]);
+	Vec3d vtet2(*tet[tetp1], *tet[tetp3]);
+	Vec3d vtet3(*tet[tetp1], *tet[tetp4]);
+	Vec3d sol;
+	
+	SolveLinearSystem (vtet1, vtet2, vtet3, vtri1, sol);
+	if (sol.X() > 0 && sol.Y() > 0 && sol.Z() > 0)
+	  return 1;
+
+	SolveLinearSystem (vtet1, vtet2, vtet3, vtri2, sol);
+	if (sol.X() > 0 && sol.Y() > 0 && sol.Z() > 0)
+	  return 1;
+	*/
+
+	// test 3 tet-faces:
+	for (i = 1; i <= 3; i++)
+	  {
+	    Vec3d vtet1, vtet2;
+	    switch (i)
+	      {
+	      case 1:
+		{
+		  vtet1 = *tet[tetp2] - *tet[tetp1];
+		  vtet2 = *tet[tetp3] - *tet[tetp1];
+		  break;
+		}
+	      case 2:
+		{
+		  vtet1 = *tet[tetp3] - *tet[tetp1];
+		  vtet2 = *tet[tetp4] - *tet[tetp1];
+		  break;
+		}
+	      case 3:
+		{
+		  vtet1 = *tet[tetp4] - *tet[tetp1];
+		  vtet2 = *tet[tetp2] - *tet[tetp1];
+		  break;
+		}
+	      }
+	    
+	    Vec3d ntet;
+	    Cross (vtet1, vtet2, ntet);
+	    
+	    Vec3d crline = Cross (ntri, ntet);
+
+	    double lcrline = crline.Length();
+	    if (lcrline < eps * eps)
+	      continue;
+
+
+	    if (vtri1 * crline + vtri2 * crline < 0)
+	      crline *= -1;
+
+	    double lam1, lam2, lam3, lam4;
+	    LocalCoordinates (vtri1, vtri2, crline, lam1, lam2);
+	    LocalCoordinates (vtet1, vtet2, crline, lam3, lam4);
+	    
+	    if (lam1 > -eps && lam2 > -eps &&
+		lam3 > -eps && lam4 > -eps)
+	      {
+		//		(*testout) << "hit, cnt == 1" << "\n";
+		return 1;
+	      }
+	  }
+
+	return 0;
+	break;
+      }
+    case 2:
+      {
+	// common edge
+	tetp3 = 0;
+	while (tetp3 == tetp1 || tetp3 == tetp2)
+	  tetp3++;
+	tetp4 = 6 - tetp1 - tetp2 - tetp3;
+	trip3 = 3 - trip1 - trip2;
+
+	//	(*testout) << "trip1,2,3 = " << trip1 << ", " << trip2 << ", " << trip3 << endl;
+	//	(*testout) << "tetp1,2,3,4 = " << tetp1 << ", " << tetp2 
+	//		   << ", " << tetp3 << ", " << tetp4 << endl;
+
+	Vec3d vtri = *tri[trip3] - *tri[trip1];
+	Vec3d vtet1 = *tet[tetp3] - *tri[trip1];
+	Vec3d vtet2 = *tet[tetp4] - *tri[trip1];
+
+	Vec3d n = *tri[trip2] - *tri[trip1];
+	n /= n.Length();
+
+	vtet1 -= (n * vtet1) * n;
+	vtet2 -= (n * vtet2) * n;
+
+
+	double lam1, lam2;
+	LocalCoordinates (vtet1, vtet2, vtri, lam1, lam2);
+	
+	if (lam1 < -eps || lam2 < -eps)
+	  return 0;
+	else
+	  {
+
+// 	    (*testout) << "vtet1 = " << vtet1 << endl;
+// 	    (*testout) << "vtet2 = " << vtet2 << endl;
+// 	    (*testout) << "vtri = " << vtri << endl;
+// 	    (*testout) << "lam1 = " << lam1 << " lam2 = " << lam2 << endl;
+
+// 	    (*testout) << (lam1 * (vtet1 * vtet1) + lam2 * (vtet1 * vtet2))
+// 		       << " = " << (vtet1 * vtri) << endl;
+// 	    (*testout) << (lam1 * (vtet1 * vtet2) + lam2 * (vtet2 * vtet2))
+// 		       << " = " << (vtet2 * vtri) << endl;
+	    
+// 	    (*testout) << "tet = ";
+// 	    for (j = 0; j < 4; j++)
+// 	      (*testout) << (*tet[j]) << " ";
+// 	    (*testout) << endl;
+// 	    (*testout) << "tri = ";
+// 	    for (j = 0; j < 3; j++)
+// 	      (*testout) << (*tri[j]) << " ";
+// 	    (*testout) << endl;
+
+// 	    (*testout) << "hit, cnt == 2" << endl;
+
+	    return 1;
+	  }
+	  
+	break;
+      }
+    case 3:
+      {
+	// common face
+	return 0;
+      }
+    }
+
+  (*testout) << "hit, cnt = " << cnt << endl;
+  return 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+int IntersectTriangleTriangle (const Point<3> ** tri1, const Point<3> ** tri2)
+{
+  int i, j;
+  double diam = Dist (*tri1[0], *tri1[1]);
+  double epsrel = 1e-8;
+  double eps = diam * epsrel;
+  double eps2 = eps * eps;
+
+
+
+  int cnt = 0;
+  /*
+  int tri1pi[3];
+  int tri2pi[3];
+  */
+
+  //  int tri1p1 = -1; 
+  /// int tri1p2 = -1;
+  //  int tri2p1 = -1;
+  //   int tri2p2 = -1;
+  //  int tri1p3, tri2p3;
+
+  /*
+  for (i = 0; i < 3; i++)
+    tri1pi[i] = -1;
+  */
+  for (i = 0; i <= 2; i++)
+    {
+      //      tri2pi[i] = -1;
+      for (j = 0; j <= 2; j++)
+	{
+	  if (Dist2 (*tri1[j], *tri2[i]) < eps2)
+	    {
+	      //	      tri2pi[i] = j;
+	      //	      tri1pi[j] = i;
+	      cnt++;
+	      //	      tri1p2 = tri1p1;
+	      //	      tri1p1 = j;
+	      //	      tri2p2 = tri2p1;
+	      //	      tri2p1 = i;
+	      break;
+	    }
+	}
+    }
+  
+  switch (cnt)
+    {
+    case 0:
+      {
+	const Point<3> * line[2];
+	
+	for (i = 0; i <= 2; i++)
+	  {
+	    line[0] = tri2[i];
+	    line[1] = tri2[(i+1)%3];
+
+	    if (IntersectTriangleLine (tri1, &line[0]))
+	      {
+		(*testout) << "int1, line = " << *line[0] << " - " << *line[1] << endl;
+		return 1;
+	      }
+	  }	
+
+	for (i = 0; i <= 2; i++)
+	  {
+	    line[0] = tri1[i];
+	    line[1] = tri1[(i+1)%3];
+
+	    if (IntersectTriangleLine (tri2, &line[0]))
+	      {
+		(*testout) << "int2, line = " << *line[0] << " - " << *line[1] << endl;
+		return 1;
+	      }
+	  }	
+	break;
+      }
+    default:
+      return 0;
+    }
+
+  return 0;
+}
+
+
+
+void
+LocalCoordinates (const Vec3d & e1, const Vec3d & e2,
+		  const Vec3d & v, double & lam1, double & lam2)
+{
+  double m11 = e1 * e1;
+  double m12 = e1 * e2;
+  double m22 = e2 * e2;
+  double rs1 = v * e1;
+  double rs2 = v * e2;
+  
+  double det = m11 * m22 - m12 * m12;
+  lam1 = (rs1 * m22 - rs2 * m12)/det;
+  lam2 = (m11 * rs2 - m12 * rs1)/det;
+}
+
+
+
+
+
+int CalcSphereCenter (const Point<3> ** pts, Point<3> & c)
+{
+  Vec3d row1 (*pts[0], *pts[1]);
+  Vec3d row2 (*pts[0], *pts[2]);
+  Vec3d row3 (*pts[0], *pts[3]);
+
+  Vec3d rhs(0.5 * (row1*row1),
+	    0.5 * (row2*row2),
+	    0.5 * (row3*row3));
+  Transpose (row1, row2, row3);
+  
+  Vec3d sol;
+  if (SolveLinearSystem (row1, row2, row3, rhs, sol))
+    {
+      (*testout) << "CalcSphereCenter: degenerated" << endl;
+      return 1;
+    }
+
+  c = *pts[0] + sol;
+  return 0;
+}
+
+
+
+
+
+int CalcTriangleCenter (const Point3d ** pts, Point3d & c)
+{
+  static DenseMatrix a(2), inva(2);
+  static Vector rs(2), sol(2);
+  double h = Dist(*pts[0], *pts[1]);
+
+  Vec3d v1(*pts[0], *pts[1]);
+  Vec3d v2(*pts[0], *pts[2]);
+
+  rs(0) = v1 * v1;
+  rs(1) = v2 * v2;
+
+  a(0,0) = 2 * rs(0);
+  a(0,1) = a(1,0) = 2 * (v1 * v2);
+  a(1,1) = 2 * rs(1);
+
+  if (fabs (a.Det()) <= 1e-12 * h * h)
+    {
+      (*testout) << "CalcTriangleCenter: degenerated" << endl;
+      return 1;
+    }
+
+  CalcInverse (a, inva);
+  inva.Mult (rs, sol);
+
+  c = *pts[0];
+  v1 *= sol(0);
+  v2 *= sol(1);
+
+  c += v1;
+  c += v2;
+
+  return 0;
+}
+
+
+
+double ComputeCylinderRadius (const Point3d & p1, 
+			      const Point3d & p2,
+			      const Point3d & p3, 
+			      const Point3d & p4)
+{
+  Vec3d v12(p1, p2);
+  Vec3d v13(p1, p3);
+  Vec3d v14(p1, p4);
+
+  Vec3d n1 = Cross (v12, v13);
+  Vec3d n2 = Cross (v14, v12);
+		
+  double n1l = n1.Length();
+  double n2l = n2.Length();
+  n1 /= n1l;
+  n2 /= n2l;
+
+  double v12len = v12.Length();
+  double h1 = n1l / v12len;
+  double h2 = n2l / v12len;
+  
+  /*
+  (*testout) << "n1 = " << n1 << " n2 = " << n2 
+	     << "h1 = " << h1 << " h2 = " << h2 << endl;
+  */
+  return ComputeCylinderRadius (n1, n2, h1, h2);
+}
+
+
+
+
+/*
+  Two triangles T1 and T2 have normals n1 and n2.
+  The height over the common edge is h1, and h2.
+ */
+double ComputeCylinderRadius (const Vec3d & n1, const Vec3d & n2,
+				     double h1, double h2)
+{
+  Vec3d t1, t2;
+  double n11 = n1 * n1;
+  double n12 = n1 * n2;
+  double n22 = n2 * n2;
+  double det = n11 * n22 - n12 * n12;
+  
+  if (fabs (det) < 1e-14 * n11 * n22)
+    return 1e20;
+
+  // a biorthogonal bases   (ti * nj) = delta_ij:
+  t1 = (n22/det) * n1 + (-n12/det) * n2;
+  t2 = (-n12/det) * n1 + (n11/det) * n2;
+
+  // normalize:
+  t1 /= t1.Length();
+  t2 /= t2.Length();
+
+  /*
+    vector to center point has form
+    v = lam1 n1 + lam2 n2
+    and fulfills
+    t2 v = h1/2
+    t1 v = h2/2
+  */
+
+  double lam1 = 0.5 * h2 / (n1 * t1);
+  double lam2 = 0.5 * h1 / (n2 * t2);
+  
+  double rad = (lam1 * n1 + lam2 * n2).Length();
+  /*
+  (*testout) << "n1 = " << n1
+	     << " n2 = " << n2
+	     << " t1 = " << t1
+	     << " t2 = " << t2
+	     << " rad = " << rad << endl;
+  */
+  return rad;
+}
+    
+
+
+
+
+
+double MinDistLP2 (const Point2d & lp1, const Point2d & lp2, const Point2d & p)
+{
+  Vec2d v(lp1, lp2);
+  Vec2d vlp(lp1, p);
+
+  // dist(lam) = \| vlp \|^2 - 2 lam (v1p, v) + lam^2 \| v \|^2
+
+  // lam = (v * vlp) / (v * v);
+  // if (lam < 0) lam = 0;
+  // if (lam > 1) lam = 1;
+
+  double num = v*vlp;
+  double den = v*v;
+
+  if (num <= 0) 
+    return Dist2 (lp1, p);
+
+  if (num >= den) 
+    return Dist2 (lp2, p);
+  
+  if (den > 0)
+    {
+      return vlp.Length2() - num * num /den;
+    }
+  else
+    return vlp.Length2();
+}
+
+
+
+
+double MinDistLP2 (const Point3d & lp1, const Point3d & lp2, const Point3d & p)
+{
+  Vec3d v(lp1, lp2);
+  Vec3d vlp(lp1, p);
+
+  // dist(lam) = \| vlp \|^2 - 2 lam (v1p, v) + lam^2 \| v \|^2
+
+  // lam = (v * vlp) / (v * v);
+  // if (lam < 0) lam = 0;
+  // if (lam > 1) lam = 1;
+
+  double num = v*vlp;
+  double den = v*v;
+
+  if (num <= 0) 
+    return Dist2 (lp1, p);
+
+  if (num >= den) 
+    return Dist2 (lp2, p);
+  
+  if (den > 0)
+    {
+      return vlp.Length2() - num * num /den;
+    }
+  else
+    return vlp.Length2();
+}
+
+
+
+double MinDistTP2 (const Point3d & tp1, const Point3d & tp2, 
+		   const Point3d & tp3, const Point3d & p)
+{
+  double lam1, lam2;
+  double res;
+
+  LocalCoordinates (Vec3d (tp1, tp2), Vec3d (tp1, tp3),
+		    Vec3d (tp1, p), lam1, lam2);
+  int in1 = lam1 >= 0;
+  int in2 = lam2 >= 0;
+  int in3 = lam1+lam2 <= 1;
+  
+  if (in1 && in2 && in3)
+    {
+      Point3d pp = tp1 + lam1 * Vec3d(tp1, tp2) + lam2 *  Vec3d (tp1, tp3);
+      res = Dist2 (p, pp);
+    }
+  else
+    {
+      res = Dist2 (tp1, p);
+      if (!in1)
+	{
+	  double hv = MinDistLP2 (tp1, tp3, p);
+	  if (hv < res) res = hv; 
+	}
+      if (!in2)
+	{
+	  double hv = MinDistLP2 (tp1, tp2, p);
+	  if (hv < res) res = hv; 
+	}
+      if (!in3)
+	{
+	  double hv = MinDistLP2 (tp2, tp3, p);
+	  if (hv < res) res = hv; 
+	}
+      /*
+      double d1 = MinDistLP2 (tp1, tp2, p);
+      double d2 = MinDistLP2 (tp1, tp3, p);
+      double d3 = MinDistLP2 (tp2, tp3, p);
+      res = min3 (d1, d2, d3);
+      */
+    }
+
+  return res;
+
+  Vec3d pp1(tp1, p);
+  Vec3d v1(tp1, tp2), v2(tp1, tp3);
+
+  double c = pp1.Length2();
+  double cx = -2 * (pp1 * v1);
+  double cy = -2 * (pp1 * v2);
+  double cxx = v1.Length2();
+  double cxy = 2 * (v1 * v2);
+  double cyy = v2.Length2();
+
+  QuadraticPolynomial2V pol (-c, -cx, -cy, -cxx, -cxy, -cyy);
+  double res2 =  - pol.MaxUnitTriangle ();
+
+  if (fabs (res - res2) > 1e-8)
+    cout << "res and res2 differ: " << res << " != " << res2 << endl;
+  return res2;
+}
+
+
+// 0 checks !!!
+double MinDistLL2 (const Point3d & l1p1, const Point3d & l1p2,
+		  const Point3d & l2p1, const Point3d & l2p2)
+{
+  // dist(lam1,lam2) = \| l2p1+lam2v2 - (l1p1+lam1 v1) \|
+  // min !
+
+  Vec3d l1l2 (l1p1, l2p1);
+  Vec3d v1 (l1p1, l1p2);
+  Vec3d v2 (l2p1, l2p2);
+
+  double a11, a12, a22, rs1, rs2;
+  double lam1, lam2, det;
+
+  a11 = v1*v1;
+  a12 = -(v1*v2);
+  a22 = v2*v2;
+  rs1 = l1l2 * v1;
+  rs2 = - (l1l2 * v2);
+  
+  det = a11 * a22 - a12 * a12;
+  if (det < 1e-14 * a11 * a22) 
+    det = 1e-14 * a11 * a22;  // regularization should be stable
+
+  if (det < 1e-20)
+    det = 1e-20;
+
+
+  lam1 = (a22 * rs1 - a12 * rs2) / det;
+  lam2 = (-a12 * rs1 + a11 * rs2) / det;
+
+  if (lam1 >= 0 && lam2 >= 0 && lam1 <= 1 && lam2 <= 1)
+    {
+      Vec3d v = l1l2 + (-lam1) * v1 + lam2 * v2;
+      return v.Length2();
+    }
+
+  double minv, hv;
+  minv = MinDistLP2 (l1p1, l1p2, l2p1);
+  hv =  MinDistLP2 (l1p1, l1p2, l2p2);
+  if (hv < minv) minv = hv;
+
+  hv =  MinDistLP2 (l2p1, l2p2, l1p1);
+  if (hv < minv) minv = hv;
+  hv =  MinDistLP2 (l2p1, l2p2, l1p2);
+  if (hv < minv) minv = hv;
+
+  return minv;
+}
+			 
+}
+
+
diff --git a/contrib/Netgen/libsrc/gprim/geomtest3d.hpp b/contrib/Netgen/libsrc/gprim/geomtest3d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..05387da00f4b0e31389407f4c063cd14bef4e6fa
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomtest3d.hpp
@@ -0,0 +1,87 @@
+#ifndef FILE_GEOMTEST3D
+#define FILE_GEOMTEST3D
+
+/* *************************************************************************/
+/* File:   geomtest3d.hh                                                   */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   13. Feb. 98                                                     */
+/* *************************************************************************/
+
+
+namespace netgen
+{
+
+
+extern int
+IntersectTriangleLine (const Point<3> ** tri, const Point<3> ** line);
+
+
+
+/**
+  Returns 0, iff
+  closure (tet)  cup  closure (tri)  is empty, one corner point of tet,
+  one edge of tet or one face of tet
+ */
+extern int 
+IntersectTetTriangle (const Point<3> ** tet, const Point<3> ** tri,
+		      const int * tetpi = NULL, const int * tripi = NULL);
+
+/**
+  Same test as above, but tet int reference position (0, ex, ey, ez),
+  tetpi = 1, 2, 4, 5
+ */
+extern int 
+IntersectTetTriangleRef (const Point3d ** tri, const int * tripi = NULL);
+
+
+// 1, iff not regular triangulation
+extern int 
+IntersectTriangleTriangle (const Point<3> ** tri1, const Point<3> ** tri2);
+
+
+extern void
+LocalCoordinates (const Vec3d & e1, const Vec3d & e2,
+		  const Vec3d & v, double & lam1, double & lam2);
+
+/// return 1 = degenerated sphere
+extern int
+CalcSphereCenter (const Point<3> ** pts, Point<3> & c);
+
+/// return 1 = degenerated triangle
+extern int
+CalcTriangleCenter (const Point3d ** pts, Point3d & c);
+
+
+
+/*
+  Compute radius of cylinder fitting 4 points.
+  cylinder axis is in the direction of p1-p2
+*/
+extern double ComputeCylinderRadius (const Point3d & p1, const Point3d & p2,
+				     const Point3d & p3, const Point3d & p4);
+
+/*
+  Two triangles T1 and T2 have normals n1 and n2.
+  The height over the common edge is h1, and h2.
+  Radius of cylinder fitting both triangles
+*/
+extern double ComputeCylinderRadius (const Vec3d & n1, const Vec3d & n2,
+				     double h1, double h2);
+
+/// Minimal distance of point p to the line segment [lp1,lp2]
+extern double MinDistLP2 (const Point2d & lp1, const Point2d & lp2, const Point2d & p);
+
+/// Minimal distance of point p to the line segment [lp1,lp2]
+extern double MinDistLP2 (const Point3d & lp1, const Point3d & lp2, const Point3d & p);
+
+/// Minimal distance of point p to the triangle segment [tp1,tp2,pt3]
+extern double MinDistTP2 (const Point3d & tp1, const Point3d & tp2, 
+			  const Point3d & tp3, const Point3d & p);
+
+/// Minimal distance of the 2 lines [l1p1,l1p2] and [l2p1,l2p2]
+extern double MinDistLL2 (const Point3d & l1p1, const Point3d & l1p2,
+			  const Point3d & l2p1, const Point3d & l2p2);
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/gprim.hpp b/contrib/Netgen/libsrc/gprim/gprim.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..016b1280aa8788456646ff086ce538373d800855
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/gprim.hpp
@@ -0,0 +1,33 @@
+#ifndef FILE_GPRIM
+#define FILE_GPRIM
+
+/* *************************************************************************/
+/* File:   gprim.hpp                                                        */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   14. Aug. 97                                                     */
+/* *************************************************************************/
+
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+
+
+
+
+#include "geomobjects.hpp"
+#include "geomops.hpp"
+#include "geomfuncs.hpp"
+
+#include "geom2d.hpp"
+#include "geom3d.hpp"
+
+#include "geomtest3d.hpp"
+#include "transform3d.hpp"
+
+#include "adtree.hpp"
+
+#include "spline.hpp"
+#include "splinegeometry.hpp"
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/spline.cpp b/contrib/Netgen/libsrc/gprim/spline.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b6789913e46e80550d86cd6d16193703514c5d0b
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/spline.cpp
@@ -0,0 +1,497 @@
+/*
+
+Spline curve for Mesh generator
+
+*/
+
+#include <mystdlib.h>
+#include <linalg.hpp>
+#include <gprim.hpp>
+#include "spline.hpp"
+
+namespace netgen
+{
+
+  // just for testing (JS)
+  template <int D>
+  void ProjectTrivial (const SplineSeg3<D> & seg, 
+                       const Point<D> point, Point<D> & point_on_curve, double & t)
+  {
+    double mindist = -1;
+    for (int i = 0; i <= 1000; i++)
+      {
+        double ht = double(i)/1000;
+        Point<D> p = seg.GetPoint(ht);
+        double dist = Dist2 (p, point);
+        if (i == 0 || dist < mindist)
+          {
+            mindist = dist;
+            t = ht;
+          }
+      }
+    point_on_curve = seg.GetPoint(t);
+  }
+
+
+  template <> 
+  void CircleSeg<3> :: LineIntersections (const double a, const double b, const double c,
+					  Array < Point<3> > & points, const double eps) const
+  {
+    cerr << "CircleSeg<3>::LineIntersections not implemented" << endl;
+  }
+  
+  template <> 
+  void CircleSeg<2> :: LineIntersections (const double a, const double b, const double c,
+					  Array < Point<2> > & points, const double eps) const
+  {
+    points.SetSize(0);
+
+    double px=0,py=0;
+
+    if(fabs(b) > 1e-20)
+      py = -c/b;
+    else
+      px = -c/a;
+
+    const double c1 = a*a + b*b;
+    const double c2 = 2. * ( a*(py-pm(1)) - b*(px-pm(0)));
+    const double c3 = pow(px-pm(0),2) + pow(py-pm(1),2) - pow(Radius(),2);
+    
+    const double discr = c2*c2 - 4*c1*c3;
+
+    if(discr < 0)
+      return;
+
+    Array<double> t;
+
+    if(fabs(discr) < 1e-20)
+      t.Append(-0.5*c2/c1);
+    else
+      {
+	t.Append((-c2+sqrt(discr))/(2.*c1));
+	t.Append((-c2-sqrt(discr))/(2.*c1));
+      }
+
+    for(int i=0; i<t.Size(); i++)
+      {
+	Point<2> p (px-t[i]*b,py+t[i]*a);
+
+	double angle = atan2(p(1),p(0))+M_PI;
+
+	if(angle > StartAngle()-eps && angle < EndAngle()+eps)
+	  points.Append(p);
+      }
+  }
+
+
+
+
+  template<int D>
+  SplineSeg3<D> :: SplineSeg3 (const GeomPoint<D> & ap1, 
+			       const GeomPoint<D> & ap2,
+			       const GeomPoint<D> & ap3)
+    : p1(ap1), p2(ap2), p3(ap3)
+  {
+    weight = Dist (p1, p3) / sqrt (0.5 * (Dist2 (p1, p2) + Dist2 (p2, p3)));
+    // weight = sqrt(2);
+    // cout << "weight = " << weight << endl;
+    proj_latest_t = 0.5;
+  }
+
+  template<int D>
+  inline Point<D> SplineSeg3<D> :: GetPoint (double t) const
+  {
+    double x, y, w;
+    double b1, b2, b3;
+
+    b1 = (1-t)*(1-t);
+    b2 = weight * t * (1-t);
+    b3 = t * t;
+
+    x = p1(0) * b1 + p2(0) * b2 + p3(0) * b3;
+    y = p1(1) * b1 + p2(1) * b2 + p3(1) * b3;
+    w = b1 + b2 + b3;
+
+    if(D==3)
+      {
+	double z = p1(2) * b1 + p2(2) * b2 + p3(2) * b3;
+	return Point<D> (x/w, y/w, z/w);
+      }
+    else
+      return Point<D> (x/w, y/w);
+  }
+
+
+
+
+  template<int D>
+  Vec<D> SplineSeg3<D> :: GetTangent (const double t) const
+  {
+    const double b1 = (1.-t)*((weight-2.)*t-weight);
+    const double b2 = weight*(1.-2.*t);
+    const double b3 = t*((weight-2)*t+2.);
+
+
+    Vec<D> retval;
+    for(int i=0; i<D; i++) 
+      retval(i) = b1*p1(i) + b2*p2(i) + b3*p3(i);
+
+    return retval;
+
+  }
+
+
+  template<int D>
+  void SplineSeg3<D> :: GetCoeff (Vector & u) const
+  {
+    DenseMatrix a(6, 6);
+    DenseMatrix ata(6, 6);
+    Vector f(6);
+
+    u.SetSize(6);
+
+    //  ata.SetSymmetric(1);
+
+    double t = 0;
+    for (int i = 0; i < 5; i++, t += 0.25)
+      {
+	Point<D> p = GetPoint (t);
+	a(i, 0) = p(0) * p(0);
+	a(i, 1) = p(1) * p(1);
+	a(i, 2) = p(0) * p(1);
+	a(i, 3) = p(0);
+	a(i, 4) = p(1);
+	a(i, 5) = 1;
+      }
+    a(5, 0) = 1;
+
+    CalcAtA (a, ata);
+
+    u = 0;
+    u(5) = 1;
+    a.MultTrans (u, f);
+    ata.Solve (f, u);
+
+    // the sign
+    Point<D> p0 = GetPoint(0);
+    Vec<D> ht = GetTangent(0);
+    Vec<2> tang(ht(0), ht(1));
+
+    double gradx = 2.*u(0)*p0(0) + u(2)*p0(1) + u(3);
+    double grady = 2.*u(1)*p0(1) + u(2)*p0(0) + u(4);
+    Vec<2> gradn (grady, -gradx);
+  
+    if (tang * gradn < 0) u *= -1;
+  }
+
+  
+
+
+  template<int D>
+  void SplineSeg3<D> :: Project (const Point<D> point, Point<D> & point_on_curve, double & t) const
+  {
+    double t_old = -1;
+
+    if(proj_latest_t > 0. && proj_latest_t < 1.)
+      t = proj_latest_t;
+    else
+      t = 0.5;
+	
+    Point<D> phi;
+    Vec<D> phip,phipp,phimp;
+    
+    int i=0;
+
+    while(t > -0.5 && t < 1.5 && i<20 && fabs(t-t_old) > 1e-15 )
+      {
+        GetDerivatives(t,phi,phip,phipp);
+	
+        t_old = t;
+
+        phimp = phi-point;
+
+        //t = min2(max2(t-(phip*phimp)/(phipp*phimp + phip*phip),0.),1.);
+        t -= (phip*phimp)/(phipp*phimp + phip*phip);
+
+        i++;
+      }
+    
+    //if(i<10 && t > 0. && t < 1.)
+    if(i<20 && t > -0.4 && t < 1.4)
+      {
+        if(t < 0)
+          {
+            t = 0.;
+          }
+        if(t > 1)
+          {
+            t = 1.;
+          }
+
+        point_on_curve = SplineSeg3<D>::GetPoint(t);
+	
+        double dist = Dist(point,point_on_curve);
+	
+        phi =  SplineSeg3<D> ::GetPoint(0);
+        double auxdist = Dist(phi,point);
+        if(auxdist < dist)
+          {
+            t = 0.;
+            point_on_curve = phi;
+            dist = auxdist;
+          }
+        phi =  SplineSeg3<D> ::GetPoint(1);
+        auxdist = Dist(phi,point);
+        if(auxdist < dist)
+          {
+            t = 1.;
+            point_on_curve = phi;
+            dist = auxdist;
+          }
+      }
+    else
+      {
+        double t0 = 0;
+        double t1 = 0.5;
+        double t2 = 1.;
+
+        double d0,d1,d2;
+
+	
+        //(*testout) << "newtonersatz" << endl;
+        while(t2-t0 > 1e-8)
+          {
+	    
+            phi =  SplineSeg3<D> ::GetPoint(t0); d0 = Dist(phi,point);
+            phi =  SplineSeg3<D> ::GetPoint(t1); d1 = Dist(phi,point);
+            phi =  SplineSeg3<D> ::GetPoint(t2); d2 = Dist(phi,point);
+
+            double a = (2.*d0 - 4.*d1 +2.*d2)/pow(t2-t0,2);
+
+            if(a <= 0)
+              {
+                if(d0 < d2)
+                  t2 -= 0.3*(t2-t0);
+                else
+                  t0 += 0.3*(t2-t0);
+
+                t1 = 0.5*(t2+t0);
+              }
+            else
+              {
+                double b = (d1-d0-a*(t1*t1-t0*t0))/(t1-t0);
+
+                double auxt1 = -0.5*b/a;
+
+                if(auxt1 < t0)
+                  {
+                    t2 -= 0.4*(t2-t0);
+                    t0 = max2(0.,t0-0.1*(t2-t0));
+                  }
+                else if (auxt1 > t2)
+                  {
+                    t0 += 0.4*(t2-t0);
+                    t2 = min2(1.,t2+0.1*(t2-t0));
+                  }
+                else
+                  {
+                    t1 = auxt1;
+                    auxt1 = 0.25*(t2-t0);
+                    t0 = max2(0.,t1-auxt1);
+                    t2 = min2(1.,t1+auxt1);
+                  }
+		
+                t1 = 0.5*(t2+t0);
+              }  
+
+          }
+
+	
+        phi =  SplineSeg3<D> ::GetPoint(t0); d0 = Dist(phi,point);
+        phi =  SplineSeg3<D> ::GetPoint(t1); d1 = Dist(phi,point);
+        phi =  SplineSeg3<D> ::GetPoint(t2); d2 = Dist(phi,point);
+
+        double mind = d0;
+        t = t0;
+        if(d1 < mind)
+          {
+            t = t1;
+            mind = d1;
+          }
+        if(d2 < mind)
+          {
+            t = t2;
+            mind = d2;
+          }
+
+        point_on_curve =  SplineSeg3<D> ::GetPoint(t);
+      }
+    //(*testout) << " latest_t " << proj_latest_t << " t " << t << endl;
+
+    proj_latest_t = t;
+
+    /*
+    // test it by trivial sampling
+    double ht;
+    Point<D> hp;
+    ProjectTrivial (*this, point, hp, ht);
+    if (fabs (t-ht) > 1e-3)
+    {
+    // if (Dist2 (point, hp) < Dist2 (point, point_on_curve))
+    cout << "project is wrong" << endl;
+    cout << "t = " << t << ", ht = " << ht << endl;
+    cout << "dist org = " << Dist(point, point_on_curve) << endl;
+    cout << "dist trivial = " << Dist(point, hp) << endl;
+    }
+    */
+  }
+
+
+
+
+
+
+  template<int D>
+  void SplineSeg3<D> :: GetDerivatives (const double t, 
+                                        Point<D> & point,
+                                        Vec<D> & first,
+                                        Vec<D> & second) const
+  {
+    Vec<D> v1(p1), v2(p2), v3(p3);
+
+    double b1 = (1.-t)*(1.-t);
+    double b2 = weight*t*(1.-t);
+    double b3 = t*t;
+    double w = b1+b2+b3;
+    b1 *= 1./w; b2 *= 1./w; b3 *= 1./w;
+
+    double b1p = 2.*(t-1.);
+    double b2p = weight*(1.-2.*t);
+    double b3p = 2.*t;
+    const double wp = b1p+b2p+b3p;
+    const double fac1 = wp/w;
+    b1p *= 1./w; b2p *= 1./w; b3p *= 1./w;
+
+    const double b1pp = 2.;
+    const double b2pp = -2.*weight;
+    const double b3pp = 2.;
+    const double wpp = b1pp+b2pp+b3pp;
+    const double fac2 = (wpp*w-2.*wp*wp)/(w*w);
+
+    for(int i=0; i<D; i++)
+      point(i) = b1*p1(i) + b2*p2(i) + b3*p3(i);
+    
+ 
+    first = (b1p - b1*fac1) * v1 +
+      (b2p - b2*fac1) * v2 +
+      (b3p - b3*fac1) * v3;
+
+    second = (b1pp/w - 2*b1p*fac1 - b1*fac2) * v1 +
+      (b2pp/w - 2*b2p*fac1 - b2*fac2) * v2 +
+      (b3pp/w - 2*b3p*fac1 - b3*fac2) * v3;
+  }
+
+
+
+  template<>
+  double SplineSeg3<2> :: MaxCurvature(void) const
+  {
+    Vec<2> v1 = p1-p2;
+    Vec<2> v2 = p3-p2;
+    double l1 = v1.Length();
+    double l2 = v2.Length();
+        
+    double cosalpha = (v1*v2)/(l1*l2);
+    
+            
+    return sqrt(cosalpha + 1.)/(min2(l1,l2)*(1.-cosalpha));
+  }
+
+  template<>
+  double SplineSeg3<3> :: MaxCurvature(void) const
+  {
+    Vec<3> v1 = p1-p2;
+    Vec<3> v2 = p3-p2;
+    double l1 = v1.Length();
+    double l2 = v2.Length();
+        
+    double cosalpha = v1*v2/(l1*l2);
+    
+        
+    return sqrt(cosalpha + 1.)/(min2(l1,l2)*(1.-cosalpha));
+  }
+
+
+  template<int D>
+  void SplineSeg3<D> :: LineIntersections (const double a, const double b, const double c,
+					   Array < Point<D> > & points, const double eps) const
+  {
+    points.SetSize(0);
+
+    double t;
+
+    const double c1 = a*p1(0) - weight*a*p2(0) + a*p3(0) 
+      + b*p1(1) - weight*b*p2(1) + b*p3(1) 
+      + (2.-weight)*c;
+    const double c2 = -2.*a*p1(0) + weight*a*p2(0) -2.*b*p1(1) + weight*b*p2(1) + (weight-2.)*c;
+    const double c3 = a*p1(0) + b*p1(1) + c;
+
+    if(fabs(c1) < 1e-20)
+      {
+	if(fabs(c2) < 1e-20)
+	  return;
+
+	t = -c3/c2;
+	if((t > -eps) && (t < 1.+eps))
+	  points.Append(GetPoint(t));
+	return;
+      }
+
+    const double discr = c2*c2-4.*c1*c3;
+
+    if(discr < 0)
+      return;
+
+    if(fabs(discr/(c1*c1)) < 1e-14)
+      {
+	t = -0.5*c2/c1;
+	if((t > -eps) && (t < 1.+eps))
+	  points.Append(GetPoint(t));
+	return;
+      }
+
+    t = (-c2 + sqrt(discr))/(2.*c1);
+    if((t > -eps) && (t < 1.+eps))
+      points.Append(GetPoint(t));
+
+    t = (-c2 - sqrt(discr))/(2.*c1);
+    if((t > -eps) && (t < 1.+eps))
+      points.Append(GetPoint(t));
+  }
+
+
+  template < int D >
+  void SplineSeg3<D> :: GetRawData (Array<double> & data) const
+  {
+    data.Append(3);
+    for(int i=0; i<D; i++)
+      data.Append(p1[i]);
+    for(int i=0; i<D; i++)
+      data.Append(p2[i]);
+    for(int i=0; i<D; i++)
+      data.Append(p3[i]);
+  }
+
+
+
+  template class  SplineSeg3<2>;
+  template class  SplineSeg3<3>;
+
+
+
+
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/gprim/spline.hpp b/contrib/Netgen/libsrc/gprim/spline.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f2701c3e1a9cc3510a88eeb0abd61a0a387efd6a
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/spline.hpp
@@ -0,0 +1,647 @@
+#ifndef FILE_SPLINE_HPP
+#define FILE_SPLINE_HPP
+
+/**************************************************************************/
+/* File:   spline.hpp                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   24. Jul. 96                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+
+
+  /*
+    Spline curves for 2D mesh generation
+  */
+
+
+  /// Geometry point
+  template < int D >
+  class GeomPoint : public Point<D>
+  {
+  public:
+    /// refinement factor at point
+    double refatpoint;
+    /// max mesh-size at point
+    double hmax;
+    /// hp-refinement
+    bool hpref;
+
+    ///
+    GeomPoint () { ; }
+
+    ///
+    GeomPoint (const Point<D> & ap, double aref = 1, bool ahpref=false)
+      : Point<D>(ap), refatpoint(aref), hmax(1e99), hpref(ahpref) { ; }
+  };
+
+
+
+
+  /// base class for 2d - segment
+  template < int D >
+  class SplineSeg
+  {
+  public:
+    SplineSeg () { ; }
+    /// calculates length of curve
+    virtual double Length () const;
+    /// returns point at curve, 0 <= t <= 1
+    virtual Point<D> GetPoint (double t) const = 0;
+    /// returns a (not necessarily unit-length) tangent vector for 0 <= t <= 1
+    virtual Vec<D> GetTangent (const double t) const
+    { cerr << "GetTangent not implemented for spline base-class"  << endl; Vec<D> dummy; return dummy;}
+    virtual void GetDerivatives (const double t, 
+				 Point<D> & point,
+				 Vec<D> & first,
+				 Vec<D> & second) const {;}
+
+
+    /// returns initial point on curve
+    virtual const GeomPoint<D> & StartPI () const = 0;
+    /// returns terminal point on curve
+    virtual const GeomPoint<D> & EndPI () const = 0;
+    /** writes curve description for fepp:
+	for implicitly given quadratic curves, the 6 coefficients of
+	the polynomial
+	$$ a x^2 + b y^2 + c x y + d x + e y + f = 0 $$
+	are written to ost */
+    void PrintCoeff (ostream & ost) const;
+
+    virtual void GetCoeff (Vector & coeffs) const = 0;
+
+    virtual void GetPoints (int n, Array<Point<D> > & points) const;
+
+    /** calculates (2D) lineintersections:
+	for lines $$ a x + b y + c = 0 $$ the interecting points are calculated
+	and stored in points */
+    virtual void LineIntersections (const double a, const double b, const double c,
+				    Array < Point<D> > & points, const double eps) const
+    {points.SetSize(0);}
+
+    virtual double MaxCurvature(void) const = 0;
+
+    virtual string GetType(void) const {return "splinebase";}
+
+    virtual void Project (const Point<D> point, Point<D> & point_on_curve, double & t) const
+    { cerr << "Project not implemented for spline base-class" << endl;}
+
+    virtual void GetRawData (Array<double> & data) const
+    { cerr << "GetRawData not implemented for spline base-class" << endl;}
+
+  };
+
+
+  /// Straight line form p1 to p2
+  template< int D >
+  class LineSeg : public SplineSeg<D>
+  {
+    ///
+    GeomPoint<D> p1, p2;
+  public:
+    ///
+    LineSeg (const GeomPoint<D> & ap1, const GeomPoint<D> & ap2);
+    ///
+    virtual double Length () const;
+    ///
+    inline virtual Point<D> GetPoint (double t) const;
+    ///
+    virtual Vec<D> GetTangent (const double t) const;
+
+  
+    virtual void GetDerivatives (const double t, 
+				 Point<D> & point,
+				 Vec<D> & first,
+				 Vec<D> & second) const;
+    ///
+    virtual const GeomPoint<D> & StartPI () const { return p1; };
+    ///
+    virtual const GeomPoint<D> & EndPI () const { return p2; }
+    ///
+    virtual void GetCoeff (Vector & coeffs) const;
+
+    virtual string GetType(void) const {return "line";}
+
+    virtual void LineIntersections (const double a, const double b, const double c,
+				    Array < Point<D> > & points, const double eps) const;
+
+    virtual double MaxCurvature(void) const {return 0;}
+
+    virtual void Project (const Point<D> point, Point<D> & point_on_curve, double & t) const;
+
+    virtual void GetRawData (Array<double> & data) const;
+  };
+
+
+  /// curve given by a rational, quadratic spline (including ellipses)
+  template< int D >
+  class SplineSeg3 : public SplineSeg<D>
+  {
+    ///
+    GeomPoint<D> p1, p2, p3;
+    double weight;
+    mutable double proj_latest_t;
+  public:
+    ///
+    SplineSeg3 (const GeomPoint<D> & ap1, 
+		const GeomPoint<D> & ap2, 
+		const GeomPoint<D> & ap3);
+    ///
+    inline virtual Point<D> GetPoint (double t) const;
+    ///
+    virtual Vec<D> GetTangent (const double t) const;
+
+  
+    DLL_HEADER virtual void GetDerivatives (const double t, 
+				 Point<D> & point,
+				 Vec<D> & first,
+				 Vec<D> & second) const;
+    ///
+    virtual const GeomPoint<D> & StartPI () const { return p1; };
+    ///
+    virtual const GeomPoint<D> & EndPI () const { return p3; }
+    ///
+    virtual void GetCoeff (Vector & coeffs) const;
+
+    virtual string GetType(void) const {return "spline3";}
+
+    const GeomPoint<D> & TangentPoint (void) const { return p2; }
+
+    DLL_HEADER virtual void LineIntersections (const double a, const double b, const double c,
+				    Array < Point<D> > & points, const double eps) const;
+
+    DLL_HEADER virtual double MaxCurvature(void) const;
+
+    DLL_HEADER virtual void Project (const Point<D> point, Point<D> & point_on_curve, double & t) const;
+
+    DLL_HEADER virtual void GetRawData (Array<double> & data) const;
+  };
+
+
+  // Gundolf Haase  8/26/97
+  /// A circle
+  template < int D >
+  class CircleSeg : public SplineSeg<D>
+  {
+    ///
+  private:
+    GeomPoint<D>	p1, p2, p3;
+    //const GeomPoint<D>	&p1, &p2, &p3;
+    Point<D>		pm;
+    double		radius, w1,w3;
+  public:
+    ///
+    CircleSeg (const GeomPoint<D> & ap1, 
+	       const GeomPoint<D> & ap2, 
+	       const GeomPoint<D> & ap3);
+    ///
+    virtual Point<D> GetPoint (double t) const;
+    ///
+    virtual const GeomPoint<D> & StartPI () const { return p1; }
+    ///
+    virtual const GeomPoint<D> & EndPI () const { return p3; }
+    ///
+    virtual void GetCoeff (Vector & coeffs) const;
+    ///
+    double Radius() const { return radius; }
+    ///
+    double StartAngle() const { return w1; }
+    ///
+    double EndAngle() const { return w3; }
+    ///
+    const Point<D> & MidPoint(void) const {return pm; }
+
+    virtual string GetType(void) const {return "circle";}
+
+    virtual void LineIntersections (const double a, const double b, const double c,
+				    Array < Point<D> > & points, const double eps) const;
+
+    virtual double MaxCurvature(void) const {return 1./radius;}
+  };
+
+
+
+
+
+
+  /// 
+  template<int D>
+  class DiscretePointsSeg : public SplineSeg<D>
+  {
+    Array<Point<D> > pts;
+    GeomPoint<D> p1n, p2n;
+  public:
+    ///
+    DiscretePointsSeg (const Array<Point<D> > & apts);
+    ///
+    virtual ~DiscretePointsSeg ();
+    ///
+    virtual Point<D> GetPoint (double t) const;
+    ///
+    virtual const GeomPoint<D> & StartPI () const { return p1n; };
+    ///
+    virtual const GeomPoint<D> & EndPI () const { return p2n; }
+    ///
+    virtual void GetCoeff (Vector & coeffs) const {;}
+
+    virtual double MaxCurvature(void) const {return 1;}
+  };
+
+
+
+
+
+
+  // calculates length of spline-curve
+  template<int D>
+  double SplineSeg<D> :: Length () const
+  {
+    int n = 100;
+    double dt = 1.0 / n;
+
+    Point<D> pold = GetPoint (0);
+
+    double l = 0;
+    for (int i = 1; i <= n; i++)
+      {
+	Point<D> p = GetPoint (i * dt);
+	l += Dist (p, pold);
+	pold = p;
+      }
+
+    return l;
+  }
+
+
+  template<int D>
+  void SplineSeg<D> :: GetPoints (int n, Array<Point<D> > & points) const
+  {
+    points.SetSize (n);
+    if (n >= 2)
+      for (int i = 0; i < n; i++)
+	points[i] = GetPoint(double(i) / (n-1));
+  }
+
+
+  template<int D>
+  void SplineSeg<D> :: PrintCoeff (ostream & ost) const
+  {
+    Vector u(6);
+
+    GetCoeff(u);
+
+    for ( int i=0; i<6; i++)
+      ost << u[i] << "  ";
+    ost << endl;
+  }
+
+
+
+  /* 
+     Implementation of line-segment from p1 to p2
+  */
+
+
+  template<int D>
+  LineSeg<D> :: LineSeg (const GeomPoint<D> & ap1, 
+			 const GeomPoint<D> & ap2)
+    : p1(ap1), p2(ap2)
+  {
+    ;
+  }
+
+
+  template<int D>
+  inline Point<D> LineSeg<D> :: GetPoint (double t) const
+  {
+    return p1 + t * (p2 - p1);
+  }
+
+  template<int D>
+  Vec<D> LineSeg<D> :: GetTangent (const double t) const
+  {
+    return p2-p1;
+  }
+
+  template<int D>
+  void LineSeg<D> :: GetDerivatives (const double t, 
+				     Point<D> & point,
+				     Vec<D> & first,
+				     Vec<D> & second) const
+  {
+    first = p2 - p1;
+    point = p1 + t * first;
+    second = 0;
+  }
+
+
+  template<int D>
+  double LineSeg<D> :: Length () const
+  {
+    return Dist (p1, p2);
+  }
+
+
+  template<int D>
+  void LineSeg<D> :: GetCoeff (Vector & coeffs) const
+  {
+    coeffs.SetSize(6);
+
+    double dx = p2(0) - p1(0);
+    double dy = p2(1) - p1(1);
+
+    coeffs[0] = coeffs[1] = coeffs[2] = 0;
+    coeffs[3] = -dy;
+    coeffs[4] = dx;
+    coeffs[5] = -dx * p1(1) + dy * p1(0);
+  }
+
+
+
+  template<int D>
+  void LineSeg<D> :: LineIntersections (const double a, const double b, const double c,
+					Array < Point<D> > & points, const double eps) const
+  {
+    points.SetSize(0);
+
+    double denom = -a*p2(0)+a*p1(0)-b*p2(1)+b*p1(1);
+    if(fabs(denom) < 1e-20)
+      return;
+
+    double t = (a*p1(0)+b*p1(1)+c)/denom;
+    if((t > -eps) && (t <  1.+eps))
+      points.Append(GetPoint(t));
+  }
+
+
+
+  template<int D>
+  void LineSeg<D> :: Project (const Point<D> point, Point<D> & point_on_curve, double & t) const
+  {
+    Vec<D> v = p2-p1;
+    double l = v.Length();
+    v *= 1./l;
+    t = (point-p1)*v;
+
+    if(t<0) t = 0;
+    if(t>l) t = l;
+
+    point_on_curve = p1+t*v;
+
+    t *= 1./l;
+  }
+
+
+  template<int D>
+  void LineSeg<D> :: GetRawData (Array<double> & data) const
+  {
+    data.Append(2);
+    for(int i=0; i<D; i++)
+      data.Append(p1[i]);
+    for(int i=0; i<D; i++)
+      data.Append(p2[i]);
+  }
+
+
+
+
+
+  /*
+    template<int D>
+    double SplineSeg3<D> :: MaxCurvature(void) const
+    {
+    Vec<D> v1 = p1-p2;
+    Vec<D> v2 = p3-p2;
+    double l1 = v1.Length();
+    double l2 = v2.Length();
+    (*testout) << "v1 " << v1 << " v2 " << v2 << endl;
+
+    double cosalpha = v1*v2/(l1*l2);
+
+    (*testout) << "cosalpha " << cosalpha << endl;
+
+    return sqrt(cosalpha + 1.)/(min2(l1,l2)*(1.-cosalpha));
+    }
+  */
+  
+
+
+  //########################################################################
+  //		circlesegment
+
+  template<int D>
+  CircleSeg<D> :: CircleSeg (const GeomPoint<D> & ap1, 
+			     const GeomPoint<D> & ap2,
+			     const GeomPoint<D> & ap3)
+    : p1(ap1), p2(ap2), p3(ap3)
+  {
+    Vec<D> v1,v2;
+  
+    v1 = p1 - p2;
+    v2 = p3 - p2;
+  
+    Point<D> p1t(p1+v1);
+    Point<D> p2t(p3+v2);
+
+    // works only in 2D!!!!!!!!!
+    
+    Line2d g1t,g2t;
+
+    g1t.P1() = Point<2>(p1(0),p1(1));
+    g1t.P2() = Point<2>(p1t(0),p1t(1));
+    g2t.P1() = Point<2>(p3(0),p3(1));
+    g2t.P2() = Point<2>(p2t(0),p2t(1));
+
+    Point<2> mp = CrossPoint (g1t,g2t);
+
+    pm(0) = mp(0); pm(1) = mp(1);
+    radius  = Dist(pm,StartPI());
+    Vec2d auxv;
+    auxv.X() = p1(0)-pm(0); auxv.Y() = p1(1)-pm(1);
+    w1      = Angle(auxv);
+    auxv.X() = p3(0)-pm(0); auxv.Y() = p3(1)-pm(1);
+    w3      = Angle(auxv);
+    if ( fabs(w3-w1) > M_PI )
+      {  
+	if ( w3>M_PI )   w3 -= 2*M_PI;
+	if ( w1>M_PI )   w1 -= 2*M_PI;
+      }
+  }
+ 
+
+  template<int D>
+  Point<D> CircleSeg<D> :: GetPoint (double t) const
+  {
+    if (t >= 1.0)  { return p3; }
+     
+    double phi = StartAngle() + t*(EndAngle()-StartAngle());
+    Vec<D>  tmp(cos(phi),sin(phi));
+     
+    return pm + Radius()*tmp;
+  }
+  
+  template<int D>
+  void CircleSeg<D> :: GetCoeff (Vector & coeff) const
+  { 
+    coeff[0] = coeff[1] = 1.0;
+    coeff[2] = 0.0;
+    coeff[3] = -2.0 * pm[0];
+    coeff[4] = -2.0 * pm[1];
+    coeff[5] = sqr(pm[0]) + sqr(pm[1]) - sqr(Radius());
+  }
+
+  
+
+
+
+  template<int D>
+  DiscretePointsSeg<D> ::   DiscretePointsSeg (const Array<Point<D> > & apts)
+    : pts (apts)
+  { 
+    for(int i=0; i<D; i++)
+      {
+	p1n(i) = apts[0](i);
+	p2n(i) = apts.Last()(i);
+      }
+    p1n.refatpoint = 1;
+    p2n.refatpoint = 1;
+    p1n.hmax = 1e99;
+    p2n.hmax = 1e99;
+  }
+
+
+  template<int D>
+  DiscretePointsSeg<D> :: ~DiscretePointsSeg ()
+  { ; }
+
+  template<int D>
+  Point<D> DiscretePointsSeg<D> :: GetPoint (double t) const
+  {
+    double t1 = t * (pts.Size()-1);
+    int segnr = int(t1);
+    if (segnr < 0) segnr = 0;
+    if (segnr >= pts.Size()) segnr = pts.Size()-1;
+
+    double rest = t1 - segnr;
+    
+    return pts[segnr] + rest*Vec<D>(pts[segnr+1]-pts[segnr]);
+  }
+
+
+
+
+
+
+
+  
+
+  // *************************************
+  // Template for B-Splines of order ORDER
+  // thx to Gerhard Kitzler
+  // *************************************
+  
+  template<int D, int ORDER>
+  class BSplineSeg : public SplineSeg<D>
+  {
+    Array<Point<D> > pts;
+    GeomPoint<D> p1n, p2n;    
+    Array<int> ti; 
+
+  public:
+    ///
+    BSplineSeg (const Array<Point<D> > & apts);
+    ///
+    virtual ~BSplineSeg();
+    ///
+    virtual Point<D> GetPoint (double t) const;
+    ///
+    virtual const GeomPoint<D> & StartPI () const { return p1n; };
+    ///
+    virtual const GeomPoint<D> & EndPI () const { return p2n; }
+    ///
+    virtual void GetCoeff (Vector & coeffs) const {;}
+
+    virtual double MaxCurvature(void) const {return 1;}
+  };
+
+  // Constructor
+  template<int D,int ORDER>
+  BSplineSeg<D,ORDER> :: BSplineSeg (const Array<Point<D> > & apts)
+    : pts (apts)
+  { 
+    /*
+    for(int i=0; i<D; i++)
+      {
+	p1n(i) = apts[0](i);
+	p2n(i) = apts.Last()(i);
+      }
+    */
+    p1n = apts[0];
+    p2n = apts.Last();
+
+    /*
+    p1n.refatpoint = 1;
+    p2n.refatpoint = 1;
+    p1n.hmax = 1e99;
+    p2n.hmax = 1e99;
+    */
+
+    int m=pts.Size()+ORDER;
+    ti.SetSize(m);
+    // b.SetSize(m-1);
+    ti=0;    
+    //    b=0.0;
+    for(int i=ORDER;i<m-ORDER+1;i++)
+      ti[i]=i-ORDER+1;   
+    for(int i=m-ORDER+1;i<m;i++)
+      ti[i]=m-2*ORDER+1;
+  }
+  // Destructor
+  template<int D,int ORDER>
+  BSplineSeg<D, ORDER> :: ~BSplineSeg ()
+  { ; }
+
+
+  // GetPoint Method...(evaluation of BSpline Curve)
+  template<int D,int ORDER>
+  Point<D> BSplineSeg<D,ORDER> :: GetPoint (double t_in) const
+  {    
+    int m=pts.Size()+ORDER;           
+
+    double t = t_in * (m-2*ORDER+1);    
+
+    double b[ORDER];
+    
+    int interval_nr = int(t)+ORDER-1;    
+    if (interval_nr < ORDER-1) interval_nr = ORDER-1;
+    if (interval_nr > m-ORDER-1) interval_nr = m-ORDER-1;
+
+    b[ORDER-1] = 1.0;
+    
+    for(int degree=1;degree<ORDER;degree++)
+      for (int k = 0; k <= degree; k++)
+	{
+	  int j = interval_nr-degree+k;
+	  double bnew = 0;
+
+	  if (k != 0) 
+	    bnew += (t-ti[j]) / ( ti[j+degree]-ti[j] ) * b[k-degree+ORDER-1];
+	  if (k != degree)
+	    bnew += (ti[j+degree+1]-t) / ( ti[j+degree+1]-ti[j+1] ) * b[k-degree+ORDER];
+	  b[k-degree+ORDER-1] = bnew;
+	}
+
+    Point<D> p = 0.0;
+    for(int i=0; i < ORDER; i++) 
+      p += b[i] * Vec<D> (pts[i+interval_nr-ORDER+1]);
+    return p;
+  }
+
+
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/splinegeometry.cpp b/contrib/Netgen/libsrc/gprim/splinegeometry.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..580e61abafd2fa2c38e98179ae53b05493dfc3e7
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/splinegeometry.cpp
@@ -0,0 +1,134 @@
+/*
+
+2d Spline curve for Mesh generator
+
+*/
+
+
+#include <mystdlib.h>
+#include <linalg.hpp>
+#include <gprim.hpp>
+#include "splinegeometry.hpp"
+ 
+namespace netgen
+{
+
+
+  template<int D>
+  SplineGeometry<D> :: ~SplineGeometry()
+  {
+    for(int i = 0; i < splines.Size(); i++)
+      delete splines[i];
+  }
+
+
+  template<int D>
+  void SplineGeometry<D> :: GetRawData (Array<double> & raw_data) const
+  {
+    raw_data.Append(D);
+    // raw_data.Append(elto0);
+
+    raw_data.Append(splines.Size());
+    for(int i=0; i<splines.Size(); i++)
+      splines[i]->GetRawData(raw_data);
+  }
+
+
+
+  template<int D>
+  int SplineGeometry<D> :: Load (const Array<double> & raw_data, const int startpos)
+  {
+    int pos = startpos;
+    if(raw_data[pos] != D)
+      throw NgException("wrong dimension of spline raw_data");
+
+    pos++;
+
+    // elto0 = raw_data[pos]; pos++;
+
+    splines.SetSize(int(raw_data[pos]));
+    pos++;
+
+    Array< Point<D> > pts(3);
+
+    for(int i=0; i<splines.Size(); i++)
+      {
+	int type = int(raw_data[pos]);
+	pos++;
+      
+	for(int j=0; j<type; j++)
+	  for(int k=0; k<D; k++)
+	    {
+	      pts[j](k) = raw_data[pos];
+	      pos++;
+	    }
+
+	if (type == 2)
+	  {
+	    splines[i] = new LineSeg<D>(GeomPoint<D>(pts[0],1),
+					GeomPoint<D>(pts[1],1));
+	  }
+	else if (type == 3)
+	  {
+	    splines[i] = new SplineSeg3<D>(GeomPoint<D>(pts[0],1),
+					   GeomPoint<D>(pts[1],1),
+					   GeomPoint<D>(pts[2],1));
+	  }
+	else
+	  throw NgException("something wrong with spline raw data");
+
+      }
+    return pos;
+  }
+
+
+
+
+
+
+
+  
+
+  template<int D>
+  void SplineGeometry<D> :: GetBoundingBox (Box<D> & box) const
+  {
+    if (!splines.Size())
+      {
+	Point<D> auxp = 0.;
+	box.Set (auxp);
+	return;
+      }
+
+    Array<Point<D> > points;
+    for (int i = 0; i < splines.Size(); i++)
+      {
+	splines[i]->GetPoints (20, points);
+
+	if (i == 0) box.Set(points[0]);
+	for (int j = 0; j < points.Size(); j++)
+	  box.Add (points[j]);
+      }
+  }
+
+  /*
+  template<int D>
+  void SplineGeometry<D> :: SetGrading (const double grading)
+  { 
+    elto0 = grading;
+  }
+  */
+
+  template<int D>
+  void SplineGeometry<D> :: AppendPoint (const Point<D> & p, const double reffac, const bool hpref)
+  {
+    geompoints.Append (GeomPoint<D>(p, reffac));
+    geompoints.Last().hpref = hpref;
+  }
+
+
+
+  template class SplineGeometry<2>;
+  template class SplineGeometry<3>;
+}
+
+
diff --git a/contrib/Netgen/libsrc/gprim/splinegeometry.hpp b/contrib/Netgen/libsrc/gprim/splinegeometry.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d29adbc42e20f3edd54c91fe0d478ae5935ed700
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/splinegeometry.hpp
@@ -0,0 +1,68 @@
+/*
+
+
+JS, Nov 2007
+
+
+The 2D/3D template-base classes should go into the libsrc/gprim directory
+
+in geom2d only 2D - Geometry classes (with material properties etc.)
+
+
+*/
+
+#include "spline.hpp"
+
+
+#ifndef _FILE_SPLINEGEOMETRY
+#define _FILE_SPLINEGEOMETRY
+
+namespace netgen
+{
+
+
+  template < int D >
+  class SplineGeometry 
+  {
+    // protected:
+  public:  
+    Array < GeomPoint<D> > geompoints;
+    Array < SplineSeg<D>* > splines;
+
+    DLL_HEADER ~SplineGeometry();
+
+    DLL_HEADER int Load (const Array<double> & raw_data, const int startpos = 0);
+
+    DLL_HEADER void GetRawData (Array<double> & raw_data) const;
+
+
+    const Array<SplineSeg<D>*> & GetSplines () const
+    { return splines; }
+
+    int GetNSplines (void) const { return splines.Size(); }
+    string GetSplineType (const int i) const { return splines[i]->GetType(); }
+    SplineSeg<D> & GetSpline (const int i) {return *splines[i];}
+    const SplineSeg<D> & GetSpline (const int i) const {return *splines[i];}
+
+    DLL_HEADER void GetBoundingBox (Box<D> & box) const;
+    Box<D> GetBoundingBox () const 
+    { Box<D> box; GetBoundingBox (box); return box; }
+
+    int GetNP () const { return geompoints.Size(); }
+    const GeomPoint<D> & GetPoint(int i) const { return geompoints[i]; }
+
+    // void SetGrading (const double grading);
+    DLL_HEADER void AppendPoint (const Point<D> & p, const double reffac = 1., const bool hpref = false);
+
+
+    void AppendSegment(SplineSeg<D> * spline)
+    {
+      splines.Append (spline);
+    }
+  };
+
+
+
+}
+
+#endif // _FILE_SPLINEGEOMETRY
diff --git a/contrib/Netgen/libsrc/gprim/transform3d.cpp b/contrib/Netgen/libsrc/gprim/transform3d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dcc4cef116002acf848de323377b04691dbf1967
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/transform3d.cpp
@@ -0,0 +1,165 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+#include <linalg.hpp>
+
+namespace netgen
+{
+
+Transformation3d :: Transformation3d ()
+{
+  for (int i = 0; i < 3; i++)
+    {
+      offset[i] = 0;
+      for (int j = 0; j < 3; j++)
+	lin[i][j] = 0;
+    }
+}
+
+Transformation3d :: Transformation3d (const Vec3d & translate)
+{
+  for (int i = 0; i < 3; i++)
+    for (int j = 0; j < 3; j++)
+      lin[i][j] = 0;
+  for (int i = 0; i < 3; i++)
+    {
+      offset[i] = translate.X(i+1);
+      lin[i][i] = 1;
+    }
+}
+
+
+Transformation3d :: 
+Transformation3d (const Point3d & c, double alpha, 
+		  double beta, double gamma)
+{
+  // total = T_c x Rot_0 x T_c^{-1}
+  // Use Euler angles, see many books from tech mech, e.g. 
+  // Shabana "multibody systems"
+
+  Transformation3d tc(c);
+  Transformation3d tcinv;
+  tc.CalcInverse (tcinv);
+
+  Transformation3d r1, r2, r3, ht, ht2;
+  r1.SetAxisRotation (3, alpha);
+  r2.SetAxisRotation (1, beta);
+  r3.SetAxisRotation (3, gamma);
+
+  ht.Combine (tc, r3);
+  ht2.Combine (ht, r2);
+  ht.Combine (ht2, r1);
+  Combine (ht, tcinv);
+
+  // cout << "Rotation - Transformation:" << (*this) << endl;
+  //  (*testout) << "Rotation - Transformation:" << (*this) << endl;
+}
+
+
+
+
+Transformation3d :: Transformation3d (const Point3d ** pp)
+{
+  for (int i = 1; i <= 3; i++)
+    {
+      offset[i-1] = (*pp[0]).X(i);
+      for (int j = 1; j <= 3; j++)
+	lin[i-1][j-1] = (*pp[j]).X(i) - (*pp[0]).X(i);
+    }
+}
+
+Transformation3d :: Transformation3d (const Point3d pp[])
+{
+  for (int i = 1; i <= 3; i++)
+    {
+      offset[i-1] = pp[0].X(i);
+      for (int j = 1; j <= 3; j++)
+	lin[i-1][j-1] = pp[j].X(i) - pp[0].X(i);
+    }
+}
+
+
+void Transformation3d :: CalcInverse (Transformation3d & inv) const
+{
+  static DenseMatrix a(3), inva(3);
+  static Vector b(3), sol(3);
+  
+  for (int i = 0; i < 3; i++)
+    {
+      b(i) = offset[i];
+      for (int j = 0; j < 3; j++)
+	a(i, j) = lin[i][j];
+    }
+
+  ::netgen::CalcInverse (a, inva);
+  inva.Mult (b, sol);
+
+  for (int i = 0; i < 3; i++)
+    {
+      inv.offset[i] = -sol(i);
+      for (int j = 0; j < 3; j++)
+	inv.lin[i][j] = inva(i, j);
+    }
+}
+
+
+void  Transformation3d:: 
+Combine (const Transformation3d & ta, const Transformation3d & tb)
+{
+  // o = o_a+ m_a o_b
+  // m = m_a m_b
+
+  for (int i = 0; i <= 2; i++)
+    {
+      offset[i] = ta.offset[i];
+      for (int j = 0; j <= 2; j++)
+	offset[i] += ta.lin[i][j] * tb.offset[j];
+    }
+  
+  for (int i = 0; i <= 2; i++)
+    for (int j = 0; j <= 2; j++)
+      {
+	lin[i][j] = 0;
+	for (int k = 0; k <= 2; k++)
+	  lin[i][j] += ta.lin[i][k] * tb.lin[k][j];
+      }
+}
+void Transformation3d :: SetAxisRotation (int dir, double alpha)
+{
+  double co = cos(alpha);
+  double si = sin(alpha);
+  dir--;
+  int pos1 = (dir+1) % 3;
+  int pos2 = (dir+2) % 3;
+
+  int i, j;
+  for (i = 0; i <= 2; i++)
+    {
+      offset[i] = 0;
+      for (j = 0; j <= 2; j++)
+	lin[i][j] = 0;
+    }
+
+  lin[dir][dir] = 1;
+  lin[pos1][pos1] = co;
+  lin[pos2][pos2] = co;
+  lin[pos1][pos2] = si;
+  lin[pos2][pos1] = -si;
+}
+
+ostream & operator<< (ostream & ost, Transformation3d & trans)
+{
+  ost << "offset = ";
+  for (int i = 0; i <= 2; i++)
+    ost << trans.offset[i] << " ";
+  ost << endl << "linear = " << endl;
+  for (int i = 0; i <= 2; i++)
+    {
+      for (int j = 0; j <= 2; j++)
+	ost << trans.lin[i][j] << " ";
+      ost << endl;
+    }
+  return ost;
+}
+}
diff --git a/contrib/Netgen/libsrc/gprim/transform3d.hpp b/contrib/Netgen/libsrc/gprim/transform3d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cd4124335caff0a827ee1af0df5bec36f1f0d1ae
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/transform3d.hpp
@@ -0,0 +1,193 @@
+#ifndef FILE_TRANSFORM3D
+#define FILE_TRANSFORM3D
+
+/* *************************************************************************/
+/* File:   transform3d.hh                                                  */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   22. Mar. 98                                                     */
+/* *************************************************************************/
+
+/*
+  Affine - Linear mapping in 3D space
+ */
+
+namespace netgen
+{
+
+class Transformation3d;
+ostream & operator<< (ostream & ost, Transformation3d & trans);
+
+class Transformation3d
+{
+  double lin[3][3];
+  double offset[3];
+public:
+  ///
+  Transformation3d ();
+  /// Unit tet is mapped to tet descibed by pp
+  Transformation3d (const Point3d ** pp);
+  /// Unit tet is mapped to tet descibed by pp
+  Transformation3d (const Point3d pp[]);
+  /// translation
+  Transformation3d (const Vec3d & translate);
+  /// rotation with ...
+  Transformation3d (const Point3d & c, double alpha, double beta, double gamma);
+  /// 
+  void CalcInverse (Transformation3d & inv) const;
+  /// this = ta x tb
+  void Combine (const Transformation3d & ta, const Transformation3d & tb);
+  /// dir = 1..3 (== x..z)
+  void SetAxisRotation (int dir, double alpha);
+  ///
+  void Transform (const Point3d & from, Point3d & to) const
+    {
+      for (int i = 1; i <= 3; i++)
+	{
+	  to.X(i) = offset[i-1] + lin[i-1][0] * from.X(1) + 
+	    lin[i-1][1] * from.X(2) + lin[i-1][2] * from.X(3);
+	}
+    }
+
+  ///
+  void Transform (Point3d & p) const
+  {
+    Point3d hp;
+    Transform (p, hp);
+    p = hp;
+  }
+
+  /// transform vector, apply only linear part, not offset
+  void Transform (const Vec3d & from, Vec3d & to) const
+    {
+      for (int i = 1; i <= 3; i++)
+	{
+	  to.X(i) = lin[i-1][0] * from.X(1) + 
+	    lin[i-1][1] * from.X(2) + lin[i-1][2] * from.X(3);
+	}
+    }
+  friend ostream & operator<< (ostream & ost, Transformation3d & trans);
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+template <int D>
+class Transformation
+{
+  Mat<D> m;
+  Vec<D> v;
+public:
+  ///
+  Transformation () { m = 0; v = 0; }
+
+  /// Unit tet is mapped to tet descibed by pp
+  Transformation (const Point<D> * pp);
+
+  /// translation
+  Transformation (const Vec<D> & translate)
+  {
+    v = translate;
+    m = 0;
+    for (int i = 0; i < D; i++)
+      m(i,i) = 1;
+  }
+
+  // rotation with ...
+  Transformation (const Point<D> & c, double alpha, double beta, double gamma)
+  {
+    // total = T_c x Rot_0 x T_c^{-1}
+    // Use Euler angles, see many books from tech mech, e.g. 
+    // Shabana "multibody systems"
+    
+    Vec<D> vc(c);
+    Transformation<D> tc(vc);
+    Transformation<D> tcinv(-vc);
+    // tc.CalcInverse (tcinv);
+    
+    Transformation<D> r1, r2, r3, ht, ht2;
+    r1.SetAxisRotation (3, alpha);
+    r2.SetAxisRotation (1, beta);
+    r3.SetAxisRotation (3, gamma);
+    
+    ht.Combine (tc, r3);
+    ht2.Combine (ht, r2);
+    ht.Combine (ht2, r1);
+    Combine (ht, tcinv);
+    
+    // cout << "Rotation - Transformation:" << (*this) << endl;
+    //  (*testout) << "Rotation - Transformation:" << (*this) << endl;
+  }
+
+  /// 
+  void CalcInverse (Transformation & inv) const;
+
+  /// this = ta x tb
+  void Combine (const Transformation & ta, const Transformation & tb)
+  {
+    v = ta.v + ta.m * tb.v;
+    m = ta.m * tb.m;
+  }
+
+
+
+  /// dir = 1..3 (== x..z)
+  void SetAxisRotation (int dir, double alpha)
+  {
+    double co = cos(alpha);
+    double si = sin(alpha);
+    dir--;
+    int pos1 = (dir+1) % 3;
+    int pos2 = (dir+2) % 3;
+    
+    int i, j;
+    for (i = 0; i <= 2; i++)
+    {
+      v(i) = 0;
+      for (j = 0; j <= 2; j++)
+	m(i,j) = 0;
+    }
+    
+    m(dir,dir) = 1;
+    m(pos1, pos1) = co;
+    m(pos2, pos2) = co;
+    m(pos1, pos2) = si;
+    m(pos2, pos1) = -si;
+  }
+
+  ///
+  void Transform (const Point<D> & from, Point<D> & to) const
+  {
+    to = Point<D> (v + m * Vec<D>(from));
+  }
+
+  void Transform (Point<D> & p) const
+  {
+    p = Point<D> (v + m * Vec<D>(p));
+  }
+
+
+
+  /// transform vector, apply only linear part, not offset
+  void Transform (const Vec<D> & from, Vec<D> & to) const
+  {
+    to = m * from;
+  }
+};
+
+template <int D>
+ostream & operator<< (ostream & ost, Transformation<D> & trans);
+
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/include/Makefile.am b/contrib/Netgen/libsrc/include/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..f84db193a37346bab9d14af54eeca71f9194a2fe
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/Makefile.am
@@ -0,0 +1,8 @@
+noinst_HEADERS = acisgeom.hpp gprim.hpp meshing.hpp occgeom.hpp	\
+visual.hpp csg.hpp incvis.hpp myadt.hpp opti.hpp geometry2d.hpp	\
+linalg.hpp mydefs.hpp parallel.hpp stlgeom.hpp mystdlib.h
+
+include_HEADERS = nginterface.h nginterface_v2.hpp 
+
+AM_CPPFLAGS = 
+METASOURCES = AUTO
diff --git a/contrib/Netgen/libsrc/include/acisgeom.hpp b/contrib/Netgen/libsrc/include/acisgeom.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..491b7720dbdb18f49b2fce4530708f0c890170f0
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/acisgeom.hpp
@@ -0,0 +1,3 @@
+#ifdef ACIS
+#include "../acisgeom/acisgeom.hpp"
+#endif
diff --git a/contrib/Netgen/libsrc/include/csg.hpp b/contrib/Netgen/libsrc/include/csg.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ffd45ef0bf4fb948a3efd2432eb8e7b6c111900c
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/csg.hpp
@@ -0,0 +1 @@
+#include "../csg/csg.hpp"
diff --git a/contrib/Netgen/libsrc/include/geometry2d.hpp b/contrib/Netgen/libsrc/include/geometry2d.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bf0965c228fc36935a9c2a539aa8d1ea133ad158
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/geometry2d.hpp
@@ -0,0 +1 @@
+#include "../geom2d/geometry2d.hpp"
diff --git a/contrib/Netgen/libsrc/include/gprim.hpp b/contrib/Netgen/libsrc/include/gprim.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e827aaf8c01c9b7296876c16309c3462e772ac8
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/gprim.hpp
@@ -0,0 +1 @@
+#include "../gprim/gprim.hpp"
diff --git a/contrib/Netgen/libsrc/include/incvis.hpp b/contrib/Netgen/libsrc/include/incvis.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bd31bfccd63ea040a3590e6f37acac1e58472daa
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/incvis.hpp
@@ -0,0 +1,39 @@
+// libraries for User interface:
+
+
+#include <tcl.h>
+#include <tk.h>
+
+#if TK_MAJOR_VERSION==8 && TK_MINOR_VERSION>=4
+#define tcl_const const
+#else
+#define tcl_const
+#endif
+
+
+#  if defined(TOGL_AGL) || defined(TOGL_AGL_CLASSIC)
+#    include <OpenGL/gl.h>
+#    include <OpenGL/glu.h>
+#  else
+#    include <GL/gl.h>
+#    include <GL/glu.h>
+#  endif
+
+
+#ifdef TOGL_X11
+// parallel
+#define GLX_GLXEXT_PROTOTYPES
+#include <GL/glx.h>
+#include <GL/glxext.h>
+#endif
+
+
+
+// part of OpenGL 1.2, but not in Microsoft's OpenGL 1.1 header:
+// GL version sould be checked at runtime
+#ifndef GL_CLAMP_TO_EDGE
+#define GL_CLAMP_TO_EDGE 0x812F
+#endif
+
+
+
diff --git a/contrib/Netgen/libsrc/include/linalg.hpp b/contrib/Netgen/libsrc/include/linalg.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e96bd036c3ed3606d458b2db3275fb273d27d138
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/linalg.hpp
@@ -0,0 +1 @@
+#include "../linalg/linalg.hpp"
diff --git a/contrib/Netgen/libsrc/include/meshing.hpp b/contrib/Netgen/libsrc/include/meshing.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e41a88f9f221c969e56923d95d5dc9c5214b98f3
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/meshing.hpp
@@ -0,0 +1 @@
+#include <../meshing/meshing.hpp>
diff --git a/contrib/Netgen/libsrc/include/myadt.hpp b/contrib/Netgen/libsrc/include/myadt.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d36bef05c12d28b9aa35e9c1de6fcd8df749dcc9
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/myadt.hpp
@@ -0,0 +1 @@
+#include <../general/myadt.hpp>
diff --git a/contrib/Netgen/libsrc/include/mydefs.hpp b/contrib/Netgen/libsrc/include/mydefs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a821d381f27862949346491e560839aa99135cca
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/mydefs.hpp
@@ -0,0 +1,49 @@
+#ifndef FILE_MYDEFS
+#define FILE_MYDEFS
+
+/**************************************************************************/
+/* File:   mydefs.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   10. Mar. 98                                                    */
+/**************************************************************************/
+
+/*
+  defines for graphics, testmodes, ...
+*/
+
+
+// #define DEBUG
+
+// Philippose - 31/01/2009
+// Hack for the Windows Version
+// in Linux, "PACKAGE_VERSION" is replaced 
+// in the configure/make phases, with the 
+// right version number
+#ifdef WIN32
+#define PACKAGE_VERSION "4.9.14"
+#endif
+
+
+#if 0 // GMSH #ifdef WIN32
+   #if NGINTERFACE_EXPORTS || NGLIB_EXPORTS || nglib_EXPORTS
+      #define DLL_HEADER   __declspec(dllexport)
+   #else
+      #define DLL_HEADER   __declspec(dllimport)
+   #endif
+#else
+   #define DLL_HEADER 
+#endif
+
+
+#define noDEMOVERSION
+#define noDEVELOP
+#define noSTEP
+#define noSOLIDGEOM
+
+#define noDEMOAPP
+#define noMODELLER
+
+#define noSTAT_STREAM
+#define noLOG_STREAM
+
+#endif
diff --git a/contrib/Netgen/libsrc/include/mystdlib.h b/contrib/Netgen/libsrc/include/mystdlib.h
new file mode 100644
index 0000000000000000000000000000000000000000..c1ac8b7f1d5bd72ad375158d0a5ab097f0412afa
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/mystdlib.h
@@ -0,0 +1,88 @@
+#ifndef FILE_MYSTDLIB
+#define FILE_MYSTDLIB
+
+#ifndef WIN32
+//GMSH #include <config.h>
+#endif
+
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+
+
+#include <cstdlib>
+#include <cstdio>
+#include <cmath>
+#include <cctype>
+#include <ctime>
+#include <cstring>
+#include <climits>
+#include <algorithm>
+
+
+#include <new>
+#include <string>
+#include <typeinfo>
+
+#ifdef PARALLEL
+// #undef SEEK_SET
+// #undef SEEK_CUR
+// #undef SEEK_END
+#include <mpi.h>
+#endif
+
+#ifdef _OPENMP
+#include <omp.h>
+#endif
+
+
+/*
+#ifdef METIS
+namespace metis { extern "C" {
+#include <metis.h>
+} }
+#endif
+*/
+
+
+#ifndef NO_PARALLEL_THREADS
+#ifndef WIN32
+#include <pthread.h>
+#endif
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+
+/*** Windows headers ***/
+#ifdef _MSC_VER
+# define WIN32_LEAN_AND_MEAN
+# ifndef NO_PARALLEL_THREADS
+#  ifdef MSVC_EXPRESS
+#   include <pthread.h>
+#  else
+#   include <afxwin.h>
+#   include <afxmt.h>
+#  endif // MSVC_EXPRESS
+# endif
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+# include <winnt.h>
+
+#else // Not using MC VC++
+
+# ifndef NO_PARALLEL_THREADS
+#  include <pthread.h>
+# endif
+
+#endif
+
+
+using namespace std;
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/include/nginterface.h b/contrib/Netgen/libsrc/include/nginterface.h
new file mode 100644
index 0000000000000000000000000000000000000000..f15c930e146a78ae36e47feb796c6d33c38abf2f
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/nginterface.h
@@ -0,0 +1,510 @@
+#ifndef NGINTERFACE
+#define NGINTERFACE
+
+
+
+
+
+/**************************************************************************/
+/* File:   nginterface.h                                                  */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   20. Nov. 99                                                    */
+/**************************************************************************/
+
+/*
+  Application program interface to Netgen
+
+*/
+
+#if 0 // GMSH #ifdef WIN32
+   #if NGINTERFACE_EXPORTS || NGLIB_EXPORTS || nglib_EXPORTS
+      #define DLL_HEADER   __declspec(dllexport)
+   #else
+      #define DLL_HEADER   __declspec(dllimport)
+   #endif
+#else
+   #define DLL_HEADER 
+#endif
+
+
+// max number of nodes per element
+#define NG_ELEMENT_MAXPOINTS 12
+
+// max number of nodes per surface element
+#define NG_SURFACE_ELEMENT_MAXPOINTS 8
+
+
+
+// implemented element types:
+enum NG_ELEMENT_TYPE { 
+  NG_SEGM = 1, NG_SEGM3 = 2,
+  NG_TRIG = 10, NG_QUAD=11, NG_TRIG6 = 12, NG_QUAD6 = 13,
+  NG_TET = 20, NG_TET10 = 21, 
+  NG_PYRAMID = 22, NG_PRISM = 23, NG_PRISM12 = 24,
+  NG_HEX = 25
+};
+
+typedef double NG_POINT[3];  // coordinates
+typedef int NG_EDGE[2];      // initial point, end point
+typedef int NG_FACE[4];      // points, last one is 0 for trig
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  
+  // load geomtry from file 
+  DLL_HEADER void Ng_LoadGeometry (const char * filename);
+  
+  // load netgen mesh
+  DLL_HEADER void Ng_LoadMesh (const char * filename);
+
+  // load netgen mesh
+  DLL_HEADER void Ng_LoadMeshFromString (const char * mesh_as_string);
+
+  // space dimension (2 or 3)
+  DLL_HEADER int Ng_GetDimension ();
+
+  // number of mesh points
+  DLL_HEADER int Ng_GetNP ();
+  
+  // number of mesh vertices (differs from GetNP for 2nd order elements)
+  DLL_HEADER int Ng_GetNV ();
+  
+  // number of mesh elements
+  DLL_HEADER int Ng_GetNE ();
+  
+  // number of surface triangles
+  DLL_HEADER int Ng_GetNSE ();
+  
+  // Get Point coordintes, index from 1 .. np
+  DLL_HEADER void Ng_GetPoint (int pi, double * p);
+  
+  // Get Element Points
+  DLL_HEADER NG_ELEMENT_TYPE Ng_GetElement (int ei, int * epi, int * np = 0);
+
+  // Get Element Type
+  DLL_HEADER NG_ELEMENT_TYPE Ng_GetElementType (int ei);
+
+  // Get sub-domain of element ei
+  DLL_HEADER int Ng_GetElementIndex (int ei);
+
+  DLL_HEADER void Ng_SetElementIndex(const int ei, const int index);
+
+  // Get Material of element ei
+  DLL_HEADER char * Ng_GetElementMaterial (int ei);
+
+  // Get Material of domain dom
+  DLL_HEADER char * Ng_GetDomainMaterial (int dom);
+  
+  // Get User Data
+  DLL_HEADER int Ng_GetUserDataSize (char * id);
+  DLL_HEADER void Ng_GetUserData (char * id, double * data);
+
+  // Get Surface Element Points
+  DLL_HEADER NG_ELEMENT_TYPE Ng_GetSurfaceElement (int ei, int * epi, int * np = 0);
+
+  // Get Surface Element Type
+  DLL_HEADER NG_ELEMENT_TYPE Ng_GetSurfaceElementType (int ei);
+
+  // Get Surface Element Index
+  DLL_HEADER int Ng_GetSurfaceElementIndex (int ei);
+
+  // Get Surface Element Surface Number
+  DLL_HEADER int Ng_GetSurfaceElementSurfaceNumber (int ei);
+  
+  // Get Surface Element Number
+  DLL_HEADER int Ng_GetSurfaceElementFDNumber (int ei);
+
+  // Get BCName for Surface Element  
+  DLL_HEADER char * Ng_GetSurfaceElementBCName (int ei);
+  //void Ng_GetSurfaceElementBCName (int ei, char * name);
+
+  // Get BCName for bc-number
+  DLL_HEADER char * Ng_GetBCNumBCName (int bcnr);
+  //void Ng_GetBCNumBCName (int bcnr, char * name);
+
+  // Get normal vector of surface element node
+  DLL_HEADER void Ng_GetNormalVector (int sei, int locpi, double * nv);     
+  
+
+  DLL_HEADER void Ng_SetPointSearchStartElement(int el);
+  
+  // Find element of point, returns local coordinates
+  DLL_HEADER int Ng_FindElementOfPoint (double * p, double * lami,
+                                        int build_searchtrees = 0, 
+                                        const int * const indices = NULL, const int numind = 0);
+  
+  // Find surface element of point, returns local coordinates
+  DLL_HEADER int Ng_FindSurfaceElementOfPoint (double * p, double * lami,
+                                               int build_searchtrees = 0, 
+                                               const int * const indices = NULL, const int numind = 0);
+  
+
+  // is elment ei curved ?
+  DLL_HEADER int Ng_IsElementCurved (int ei);
+  // is elment sei curved ?
+  DLL_HEADER int Ng_IsSurfaceElementCurved (int sei);
+
+  /// Curved Elemens:
+  /// xi..local coordinates
+  /// x ..global coordinates
+  /// dxdxi...D x D Jacobian matrix (row major storage)
+  DLL_HEADER void Ng_GetElementTransformation (int ei, const double * xi, 
+                                               double * x, double * dxdxi);
+
+  
+  /// buffer must be at least 100 doubles, alignment of double
+  DLL_HEADER void Ng_GetBufferedElementTransformation (int ei, const double * xi, 
+                                                       double * x, double * dxdxi,
+                                                       void * buffer, int buffervalid);
+  
+
+
+  /// Curved Elemens:
+  /// xi..local coordinates
+  /// x ..global coordinates
+  /// dxdxi...D x D-1 Jacobian matrix (row major storage)
+  /// curved ...is element curved ?
+  DLL_HEADER void Ng_GetSurfaceElementTransformation (int sei, const double * xi, 
+                                                      double * x, double * dxdxi);
+  
+  /// Curved Elemens:
+  /// xi..local coordinates
+  /// sxi..step xi
+  /// x ..global coordinates
+  /// dxdxi...D x D Jacobian matrix (row major storage)
+  DLL_HEADER void Ng_GetMultiElementTransformation (int ei, int n,
+                                                    const double * xi, size_t sxi,
+                                                    double * x, size_t sx,
+                                                    double * dxdxi, size_t sdxdxi);
+
+  
+  
+  DLL_HEADER int Ng_GetSegmentIndex (int elnr);
+  DLL_HEADER NG_ELEMENT_TYPE Ng_GetSegment (int elnr, int * epi, int * np = 0);
+
+
+
+
+  // Mark element for refinement
+  DLL_HEADER void Ng_SetRefinementFlag (int ei, int flag);
+  DLL_HEADER void Ng_SetSurfaceRefinementFlag (int sei, int flag);
+
+  // Do local refinement
+  enum NG_REFINEMENT_TYPE { NG_REFINE_H = 0, NG_REFINE_P = 1, NG_REFINE_HP = 2 };
+  DLL_HEADER void Ng_Refine (NG_REFINEMENT_TYPE reftype);
+
+  // Use second order elements
+  DLL_HEADER void Ng_SecondOrder ();
+  DLL_HEADER void Ng_HighOrder (int order, bool rational = false);
+  //void Ng_HPRefinement (int levels, double parameter = 0.125);
+  DLL_HEADER void Ng_HPRefinement (int levels, double parameter = 0.125,
+                                   bool setorders = true,bool ref_level = false);
+  // void Ng_HPRefinement (int levels);
+  // void Ng_HPRefinement (int levels, double parameter);
+
+
+  // Topology and coordinate information of master element:
+
+  DLL_HEADER int Ng_ME_GetNVertices (NG_ELEMENT_TYPE et);
+  DLL_HEADER int Ng_ME_GetNEdges (NG_ELEMENT_TYPE et);
+  DLL_HEADER int Ng_ME_GetNFaces (NG_ELEMENT_TYPE et);
+
+  DLL_HEADER const NG_POINT * Ng_ME_GetVertices (NG_ELEMENT_TYPE et);
+  DLL_HEADER const NG_EDGE * Ng_ME_GetEdges (NG_ELEMENT_TYPE et);
+  DLL_HEADER const NG_FACE * Ng_ME_GetFaces (NG_ELEMENT_TYPE et);
+
+  DLL_HEADER void Ng_UpdateTopology();
+
+  DLL_HEADER int Ng_GetNEdges();
+  DLL_HEADER int Ng_GetNFaces();
+
+  
+  DLL_HEADER int Ng_GetElement_Edges (int elnr, int * edges, int * orient = 0);
+  DLL_HEADER int Ng_GetElement_Faces (int elnr, int * faces, int * orient = 0);
+
+  DLL_HEADER int Ng_GetSurfaceElement_Edges (int selnr, int * edges, int * orient = 0);
+  DLL_HEADER int Ng_GetSurfaceElement_Face (int selnr, int * orient = 0);
+
+  DLL_HEADER void Ng_GetSurfaceElementNeighbouringDomains(const int selnr, int & in, int & out);
+       
+  DLL_HEADER int Ng_GetFace_Vertices (int fnr, int * vert);
+  DLL_HEADER void Ng_GetEdge_Vertices (int ednr, int * vert);
+  DLL_HEADER int Ng_GetFace_Edges (int fnr, int * edge);
+
+  DLL_HEADER int Ng_GetNVertexElements (int vnr);
+  DLL_HEADER void Ng_GetVertexElements (int vnr, int * els);
+
+  DLL_HEADER int Ng_GetElementOrder (int enr);
+  DLL_HEADER void Ng_GetElementOrders (int enr, int * ox, int * oy, int * oz);
+
+  DLL_HEADER void Ng_SetElementOrder (int enr, int order);
+  DLL_HEADER void Ng_SetElementOrders (int enr, int ox, int oy, int oz);
+
+  DLL_HEADER int Ng_GetSurfaceElementOrder (int enr);
+  DLL_HEADER void Ng_GetSurfaceElementOrders (int enr, int * ox, int * oy);
+
+  DLL_HEADER void Ng_SetSurfaceElementOrder (int enr, int order);
+  DLL_HEADER void Ng_SetSurfaceElementOrders (int enr, int ox, int oy);
+
+  // Multilevel functions:
+
+  // number of levels:
+  DLL_HEADER int Ng_GetNLevels ();
+  // get two parent nodes (indeed vertices !) of node ni
+  DLL_HEADER void Ng_GetParentNodes (int ni, int * parents);
+
+  // get parent element (first child has always same number)
+  DLL_HEADER int Ng_GetParentElement (int ei);
+
+  // get parent surface element (first child has always same number)
+  DLL_HEADER int Ng_GetParentSElement (int ei);
+
+  // representant of anisotropic cluster
+  DLL_HEADER int Ng_GetClusterRepVertex (int vi);
+  DLL_HEADER int Ng_GetClusterRepEdge (int edi);
+  DLL_HEADER int Ng_GetClusterRepFace (int fai);
+  DLL_HEADER int Ng_GetClusterRepElement (int eli);
+
+
+  void Ng_SurfaceElementTransformation (int eli, double x, double y, 
+					double * p3d, double * jacobian);
+
+#ifdef PARALLEL
+  // Is Element ei an element of this processor ??
+  bool Ng_IsGhostEl (int ei);
+
+  void Ng_SetGhostEl(const int ei, const bool aisghost );
+
+  bool Ng_IsGhostSEl (int ei);
+
+  void Ng_SetGhostSEl(const int ei, const bool aisghost );
+
+  bool Ng_IsGhostVert ( int pnum );
+  bool Ng_IsGhostEdge ( int ednum );
+  bool Ng_IsGhostFace ( int fanum );
+
+  bool Ng_IsExchangeEl ( int elnum );
+  bool Ng_IsExchangeSEl ( int selnr );
+
+  void Ng_UpdateOverlap ();
+  int Ng_Overlap();
+/*   void Ng_SetGhostVert ( const int pnum, const bool aisghost ); */
+/*   void Ng_SetGhostEdge ( const int ednum, const bool aisghost ); */
+/*   void Ng_SetGhostFace ( const int fanum, const bool aisghost ); */
+
+
+
+
+  // the folling functions are 0-base  !!
+  int NgPar_GetLoc2Glob_VolEl ( int locnum );
+
+  // int NgPar_GetDistantNodeNums ( int nt, int locnum, int * procs, int * distnum);
+
+  // number on distant processor 
+
+  // gibt anzahl an distant pnums zurueck
+  // * pnums entspricht ARRAY<int[2] >
+  int NgPar_GetDistantNodeNums ( int nodetype, int locnum, int * pnums );
+  int NgPar_GetNDistantNodeNums ( int nodetype, int locnum );
+
+  int NgPar_GetDistantPNum ( int proc, int locnum ) ;
+  int NgPar_GetDistantEdgeNum ( int proc, int locnum ) ;
+  int NgPar_GetDistantFaceNum ( int proc, int locnum ) ;
+  int NgPar_GetDistantElNum ( int proc, int locnum );
+
+  bool NgPar_IsExchangeFace ( int fnr ) ;
+  bool NgPar_IsExchangeVert ( int vnum );
+  bool NgPar_IsExchangeEdge ( int ednum );
+  bool NgPar_IsExchangeElement ( int elnum );
+
+  void NgPar_PrintParallelMeshTopology ();
+  bool NgPar_IsElementInPartition ( int elnum, int dest );
+
+  bool NgPar_IsGhostFace ( int facenum );
+  bool NgPar_IsGhostEdge ( int edgenum );
+
+
+#endif
+  
+  namespace netgen {
+  // #include "../visualization/soldata.hpp"
+    class SolutionData;
+  }
+
+  enum Ng_SolutionType
+  { NG_SOLUTION_NODAL = 1, 
+    NG_SOLUTION_ELEMENT = 2, 
+    NG_SOLUTION_SURFACE_ELEMENT = 3, 
+    NG_SOLUTION_NONCONTINUOUS = 4,
+    NG_SOLUTION_SURFACE_NONCONTINUOUS = 5,
+    NG_SOLUTION_VIRTUAL_FUNCTION = 6,
+    NG_SOLUTION_MARKED_ELEMENTS = 10,
+    NG_SOLUTION_ELEMENT_ORDER = 11
+  };
+  
+  struct Ng_SolutionData
+  {
+    const char * name; // name of gridfunction
+    double * data;    // solution values
+    int components;   // relevant (double) components in solution vector
+    int dist;         // # doubles per entry alignment! 
+    int iscomplex;    // complex vector ? 
+    bool draw_surface;
+    bool draw_volume;
+    int order;        // order of elements, only partially supported 
+    Ng_SolutionType soltype;  // type of solution function
+    netgen::SolutionData * solclass;
+  };
+  
+  // initialize solution data with default arguments
+  DLL_HEADER void Ng_InitSolutionData (Ng_SolutionData * soldata);
+  // set solution data
+  DLL_HEADER void Ng_SetSolutionData (Ng_SolutionData * soldata);
+  /// delete gridfunctions
+  DLL_HEADER void Ng_ClearSolutionData();
+  // redraw 
+  DLL_HEADER void Ng_Redraw();
+  //
+  DLL_HEADER void Ng_SetVisualizationParameter (const char * name, 
+                                                const char * value);
+  
+
+  // number of periodic vertices  
+  DLL_HEADER int Ng_GetNPeriodicVertices (int idnr);
+  // pairs should be an integer array of 2*npairs
+  DLL_HEADER void Ng_GetPeriodicVertices (int idnr, int * pairs); 
+
+  // number of periodic edges  
+  DLL_HEADER int Ng_GetNPeriodicEdges (int idnr);
+  // pairs should be an integer array of 2*npairs
+  DLL_HEADER void Ng_GetPeriodicEdges (int idnr, int * pairs); 
+
+  DLL_HEADER void RunParallel ( void * (*fun)(void *), void * in);
+
+  DLL_HEADER void Ng_PushStatus (const char * str);
+  DLL_HEADER void Ng_PopStatus ();
+  DLL_HEADER void Ng_SetThreadPercentage (double percent);
+  DLL_HEADER void Ng_GetStatus (char ** str, double & percent);
+
+  DLL_HEADER void Ng_SetTerminate(void);
+  DLL_HEADER void Ng_UnSetTerminate(void);
+  DLL_HEADER int Ng_ShouldTerminate(void);
+  DLL_HEADER void Ng_SetRunning(int flag);
+  DLL_HEADER int Ng_IsRunning();
+  
+  //// added by Roman Stainko ....
+  DLL_HEADER int Ng_GetVertex_Elements( int vnr, int* elems);
+  DLL_HEADER int Ng_GetVertex_SurfaceElements( int vnr, int* elems );
+  DLL_HEADER int Ng_GetVertex_NElements( int vnr );
+  DLL_HEADER int Ng_GetVertex_NSurfaceElements( int vnr );
+
+
+#ifdef SOCKETS
+  int Ng_SocketClientOpen( const int port, const char * host );
+  void Ng_SocketClientWrite( const char * write, char ** reply);
+  void Ng_SocketClientClose ( void );
+  void Ng_SocketClientGetServerHost ( const int number, char ** host );
+  void Ng_SocketClientGetServerPort ( const int number, int * port );
+  void Ng_SocketClientGetServerClientID ( const int number, int * id );
+#endif
+
+  DLL_HEADER void Ng_InitPointCurve(double red, double green, double blue);
+  DLL_HEADER void Ng_AddPointCurvePoint(const double * point);
+
+
+#ifdef PARALLEL
+  void Ng_SetElementPartition ( int elnr, int part );
+  int  Ng_GetElementPartition ( int elnr );
+#endif
+
+  DLL_HEADER void Ng_SaveMesh ( const char * meshfile );
+  DLL_HEADER void Ng_Bisect ( const char * refinementfile );
+
+  // if qualityloss is not equal to NULL at input, a (1-based) list of qualitylosses (due to projection)
+  // is saved in *qualityloss, its size is the return value
+  DLL_HEADER int Ng_Bisect_WithInfo ( const char * refinementfile, double ** qualityloss);
+
+  typedef void * Ng_Mesh;
+  DLL_HEADER Ng_Mesh Ng_SelectMesh (Ng_Mesh mesh);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
+
+
+
+
+/*
+  The new node interface ...
+  it is 0-based !
+ */
+
+extern "C" {
+  
+  /*
+    number of nodes of type nt
+    nt = 0 is Vertex
+    nt = 1 is Edge
+    nt = 2 is Face
+    nt = 3 is Cell
+   */
+  DLL_HEADER int Ng_GetNNodes (int nt);
+
+  /*
+    closure nodes of node (nt, nodenr):
+    nodeset is bit-coded, bit 0 includes Vertices, bit 1 edges, etc
+    E.g., nodeset = 6 includes edge and face nodes
+    nodes consists of pairs of integers (nodetype, nodenr) 
+    return value is number of nodes
+   */
+  DLL_HEADER int Ng_GetClosureNodes (int nt, int nodenr, int nodeset, int * nodes);
+
+  
+  /*
+    number of dim-dimensional elements 
+    dim = 3 ... volume elements
+    dim = 2 ... surface elements
+    dim = 1 ... segments
+    dim = 0 ... not available
+  */
+  DLL_HEADER int Ng_GetNElements (int dim);
+
+  /*
+    closure nodes of dim-dimensional element elmentnr:
+    nodeset is bit-coded, bit 0 includes Vertices, bit 1 edges, etc
+    E.g., nodeset = 6 includes edge and face nodes
+    nodes consists of pairs of integers (nodetype, nodenr) 
+    return value is number of nodes
+   */
+  DLL_HEADER int Ng_GetElementClosureNodes (int dim, int elementnr, int nodeset, int * nodes);
+
+
+  struct Ng_Tcl_Interp;
+  typedef int (Ng_Tcl_CmdProc) (Ng_Tcl_Interp *interp, int argc, const char *argv[]);
+
+  DLL_HEADER void Ng_Tcl_CreateCommand (Ng_Tcl_Interp * interp, 
+                                        const char * cmdName, Ng_Tcl_CmdProc * proc);
+
+  void Ng_Tcl_SetResult (Ng_Tcl_Interp * interp, const char * result);
+}
+
+
+
+
+
+#ifdef __cplusplus
+#include <iostream>
+namespace netgen 
+{
+  DLL_HEADER extern std::ostream * testout;
+  DLL_HEADER extern int printmessage_importance;
+}
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/include/nginterface_v2.hpp b/contrib/Netgen/libsrc/include/nginterface_v2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..957b7b4dceb8db10a048b236b5dcf0f68f31eb66
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/nginterface_v2.hpp
@@ -0,0 +1,175 @@
+#ifndef NGINTERFACE_V2
+#define NGINTERFACE_V2
+
+
+/**************************************************************************/
+/* File:   nginterface_v2.hpp                                             */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   May  09                                                        */
+/**************************************************************************/
+
+/*
+  C++ interface to Netgen
+*/
+
+
+namespace netgen
+{
+  class Ng_Element
+  {
+
+    class Ng_Points
+    {
+    public:
+      int num;
+      const int * ptr;
+  
+      int Size() const { return num; }
+      int operator[] (int i) const { return ptr[i]-1; }
+    };
+
+
+    class Ng_Vertices
+    {
+    public:
+      int num;
+      const int * ptr;
+  
+      int Size() const { return num; }
+      int operator[] (int i) const { return ptr[i]-1; }
+    };
+
+    class Ng_Edges
+    {
+    public:
+      int num;
+      const int * ptr;
+  
+      int Size() const { return num; }
+      int operator[] (int i) const { return abs (ptr[i])-1; }
+    };
+
+    class Ng_Faces
+    {
+    public:
+      int num;
+      const int * ptr;
+  
+      int Size() const { return num; }
+      int operator[] (int i) const { return (ptr[i]-1) / 8; }
+    };
+
+  public:
+    NG_ELEMENT_TYPE type;
+    NG_ELEMENT_TYPE GetType() const { return type; }
+    
+    Ng_Points points;      // all points
+    Ng_Vertices vertices;
+    Ng_Edges edges;
+    Ng_Faces faces;
+  };
+
+
+
+
+  template <int DIM> 
+  DLL_HEADER int Ng_GetNElements ();
+
+  template <int DIM> 
+  DLL_HEADER Ng_Element Ng_GetElement (int nr);
+
+
+  
+  class Ng_Point
+  {
+  public:
+    double * pt;
+    double operator[] (int i)
+    { return pt[i]; }
+  };
+
+  DLL_HEADER Ng_Point Ng_GetPoint (int nr);
+
+
+
+
+  template <int DIM> class Ng_Node;
+
+  template <>
+  class Ng_Node<1>
+  {
+    class Ng_Vertices
+    {
+    public:
+      const int * ptr;
+  
+      int Size() const { return 2; }
+      int operator[] (int i) const { return ptr[i]-1; }
+    };
+
+
+  public:
+    Ng_Vertices vertices;
+  };
+
+
+
+  template <>
+  class Ng_Node<2>
+  {
+    class Ng_Vertices
+    {
+    public:
+      int nv;
+      const int * ptr;
+  
+      int Size() const { return nv; }
+      int operator[] (int i) const { return ptr[i]-1; }
+    };
+
+    class Ng_Edges
+    {
+    public:
+      int ned;
+      const int * ptr;
+  
+      int Size() const { return ned; }
+      int operator[] (int i) const { return ptr[i]-1; }
+    };
+
+
+  public:
+    Ng_Vertices vertices;
+    Ng_Edges edges;
+  };
+
+
+
+    
+  template <int DIM>
+  DLL_HEADER Ng_Node<DIM> Ng_GetNode (int nr);
+  
+
+  template <int DIM>
+  DLL_HEADER int Ng_GetNNodes ();
+
+
+
+
+
+  /// Curved Elements:
+  /// xi..... DIM_EL local coordinates
+  /// sxi ... step xi
+  /// x ..... DIM_SPACE global coordinates
+  /// dxdxi...DIM_SPACE x DIM_EL Jacobian matrix (row major storage)
+  template <int DIM_EL, int DIM_SPACE> 
+  DLL_HEADER void Ng_MultiElementTransformation (int elnr, int npts,
+                                                 const double * xi, size_t sxi,
+                                                 double * x, size_t sx,
+                                                 double * dxdxi, size_t sdxdxi);
+  
+  template <int DIM> 
+  DLL_HEADER int Ng_GetElementIndex (int nr);
+}
+#endif
+
diff --git a/contrib/Netgen/libsrc/include/occgeom.hpp b/contrib/Netgen/libsrc/include/occgeom.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..af258e0df032193ef448a72102d8c7215ec9d9b0
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/occgeom.hpp
@@ -0,0 +1 @@
+#include "../occ/occgeom.hpp"
diff --git a/contrib/Netgen/libsrc/include/opti.hpp b/contrib/Netgen/libsrc/include/opti.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5792d70f2a45cf9efe0c644ac390b4c654069b7c
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/opti.hpp
@@ -0,0 +1 @@
+#include "../linalg/opti.hpp"
diff --git a/contrib/Netgen/libsrc/include/parallel.hpp b/contrib/Netgen/libsrc/include/parallel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ba662f810ea4c6240c57cf64608803abc18f602
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/parallel.hpp
@@ -0,0 +1 @@
+#include "../parallel/parallel.hpp"
diff --git a/contrib/Netgen/libsrc/include/parallelinterface.hpp b/contrib/Netgen/libsrc/include/parallelinterface.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4fb8b8e2ec0290f9fcdce7517a5084a58e2f07a9
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/parallelinterface.hpp
@@ -0,0 +1,49 @@
+gibt's nicht mehr
+
+
+#ifndef FILE_PARALLELINTERFACE
+#define FILE_PARALLELINTERFACE
+
+#ifdef PARALLEL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  // this interface is 0-base  !!
+
+
+  int NgPar_GetLoc2Glob_VolEl ( int locnum );
+
+  // int NgPar_GetDistantNodeNums ( int nt, int locnum, int * procs, int * distnum);
+
+  // number on distant processor 
+
+  // gibt anzahl an distant pnums zurueck
+  // * pnums entspricht ARRAY<int[2] >
+  int NgPar_GetDistantNodeNums ( int nodetype, int locnum, int * pnums );
+  int NgPar_GetNDistantNodeNums ( int nodetype, int locnum );
+
+  int NgPar_GetDistantPNum ( int proc, int locnum ) ;
+  int NgPar_GetDistantEdgeNum ( int proc, int locnum ) ;
+  int NgPar_GetDistantFaceNum ( int proc, int locnum ) ;
+  int NgPar_GetDistantElNum ( int proc, int locnum );
+
+  bool NgPar_IsExchangeFace ( int fnr ) ;
+  bool NgPar_IsExchangeVert ( int vnum );
+  bool NgPar_IsExchangeEdge ( int ednum );
+  bool NgPar_IsExchangeElement ( int elnum );
+
+  void NgPar_PrintParallelMeshTopology ();
+  bool NgPar_IsElementInPartition ( int elnum, int dest );
+
+  bool NgPar_IsGhostFace ( int facenum );
+  bool NgPar_IsGhostEdge ( int edgenum );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/include/stlgeom.hpp b/contrib/Netgen/libsrc/include/stlgeom.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f1eea264e11281071ae2ae1235bc098ff3352ce6
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/stlgeom.hpp
@@ -0,0 +1 @@
+#include <../stlgeom/stlgeom.hpp>
diff --git a/contrib/Netgen/libsrc/include/visual.hpp b/contrib/Netgen/libsrc/include/visual.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f026f5a458c6c131b65b0b1d616d42eefae015a5
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/visual.hpp
@@ -0,0 +1 @@
+#include "../visualization/visual.hpp"
diff --git a/contrib/Netgen/libsrc/interface/Makefile.am b/contrib/Netgen/libsrc/interface/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..013797957cccae57147c3305a937d11a36f3eaae
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/Makefile.am
@@ -0,0 +1,12 @@
+noinst_HEADERS = writeuser.hpp
+
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc/include -I$(top_srcdir)/libsrc/interface  $(MPI_INCLUDES) $(TCL_INCLUDES) -DOPENGL
+METASOURCES = AUTO
+lib_LTLIBRARIES = libinterface.la
+libinterface_la_SOURCES = nginterface.cpp nginterface_v2.cpp \
+	read_fnf_mesh.cpp readtetmesh.cpp readuser.cpp writeabaqus.cpp writediffpack.cpp \
+	writedolfin.cpp writeelmer.cpp writefeap.cpp writefluent.cpp writegmsh.cpp writejcm.cpp \
+	writepermas.cpp writetecplot.cpp writetet.cpp writetochnog.cpp writeuser.cpp \
+	wuchemnitz.cpp writegmsh2.cpp writeOpenFOAM15x.cpp 
+
+
diff --git a/contrib/Netgen/libsrc/interface/nginterface.cpp b/contrib/Netgen/libsrc/interface/nginterface.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a5f7e7a145483f2569317d4e511f7f0e44d842c1
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/nginterface.cpp
@@ -0,0 +1,2448 @@
+#include <mystdlib.h>
+
+#include <meshing.hpp>
+#include <csg.hpp>
+
+
+#ifdef SOCKETS
+#include "../sockets/sockets.hpp"
+#endif
+
+#ifndef NOTCL
+#include <visual.hpp>
+#endif
+
+
+#include "nginterface.h"
+#include "../visualization/soldata.hpp"
+
+
+#ifdef _MSC_VER
+// Philippose - 30/01/2009
+// MSVC Express Edition Support
+#ifdef MSVC_EXPRESS
+
+// #include <pthread.h>
+
+  static pthread_t meshingthread;
+  void RunParallel ( void * (*fun)(void *), void * in)
+  {
+    if (netgen::mparam.parthread) //  && (ntasks == 1) )
+     {
+	     pthread_attr_t attr;
+	     pthread_attr_init (&attr);
+	     // the following call can be removed if not available:
+	     pthread_attr_setstacksize(&attr, 1000000);
+	     //pthread_create (&meshingthread, &attr, fun, NULL);
+	     pthread_create (&meshingthread, &attr, fun, in);
+     }
+     else
+       fun (in);
+  }
+
+#else // Using MS VC++ Standard / Enterprise / Professional edition
+
+  // Afx - Threads need different return - value:
+
+  static void* (*sfun)(void *);
+  unsigned int fun2 (void * val)
+  {
+    sfun (val);
+    return 0;
+  }
+
+  void RunParallel ( void* (*fun)(void *), void * in)
+  {
+    sfun = fun;
+    if (netgen::mparam.parthread)
+      AfxBeginThread (fun2, in);
+    //AfxBeginThread (fun2, NULL);
+    else
+      fun (in);
+  }
+
+#endif // #ifdef MSVC_EXPRESS
+
+#else  // For #ifdef _MSC_VER
+
+// #include <pthread.h>
+ 
+  static pthread_t meshingthread;
+  void RunParallel ( void * (*fun)(void *), void * in)
+  {
+    bool parthread = netgen::mparam.parthread;
+
+#ifdef PARALLEL
+    int provided;
+    MPI_Query_thread(&provided);
+    if (provided < 3)
+      if (netgen::ntasks > 1) parthread = false;
+    // cout << "runparallel = " << parthread << endl;
+#endif
+
+    if (parthread)
+      {
+	pthread_attr_t attr;
+	pthread_attr_init (&attr);
+	// the following call can be removed if not available:
+	pthread_attr_setstacksize(&attr, 1000000);
+	//pthread_create (&meshingthread, &attr, fun, NULL);
+	pthread_create (&meshingthread, &attr, fun, in);
+      }
+    else
+      fun (in);
+  }
+
+#endif // #ifdef _MSC_VER
+
+
+
+
+
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+  MeshingParameters mparam;
+
+  // global variable mesh (should not be used in libraries)
+  AutoPtr<Mesh> mesh;
+  NetgenGeometry * ng_geometry = new NetgenGeometry;
+
+  // extern NetgenGeometry * ng_geometry;
+  // extern AutoPtr<Mesh> mesh;
+
+#ifndef NOTCL
+  extern Tcl_Interp * tcl_interp;
+#endif
+
+
+#ifdef OPENGL 
+  extern VisualSceneSolution vssolution;
+#endif
+  extern CSGeometry * ParseCSG (istream & istr);
+
+#ifdef SOCKETS
+  extern AutoPtr<ClientSocket> clientsocket;
+  //extern Array< AutoPtr < ServerInfo > > servers;
+  extern Array< ServerInfo* > servers;
+#endif
+
+  
+}
+
+
+using namespace netgen;
+
+
+void Ng_LoadGeometry (const char * filename)
+{
+
+  for (int i = 0; i < geometryregister.Size(); i++)
+    {
+      NetgenGeometry * hgeom = geometryregister[i]->Load (filename);
+      if (hgeom)
+	{
+	  delete ng_geometry;
+	  ng_geometry = hgeom;
+	  
+	  mesh.Reset();
+	  return;
+	}
+    }
+
+  // he: if filename is empty, return
+  // can be used to reset geometry
+  if (strcmp(filename,"")==0) 
+    {
+      delete ng_geometry;
+      ng_geometry = new NetgenGeometry();
+      return;
+    }
+
+  cerr << "cannot load geometry '" << filename << "'" << endl;
+}                          
+
+
+void Ng_LoadMeshFromStream ( istream & input )
+{
+  mesh.Reset (new Mesh());
+  mesh -> Load(input);
+  
+  for (int i = 0; i < geometryregister.Size(); i++)
+    {
+      NetgenGeometry * hgeom = geometryregister[i]->LoadFromMeshFile (input);
+      if (hgeom)
+	{
+	  delete ng_geometry;
+	  ng_geometry = hgeom;
+	  break;
+	}
+    }
+
+
+#ifdef LOADOLD
+  if(input.good())
+    {
+      string auxstring;
+      input >> auxstring;
+      if(auxstring == "csgsurfaces")
+	{
+	  /*
+	    if (geometry)
+            {
+	    geometry.Reset (new CSGeometry (""));
+            }
+	    if (stlgeometry)
+	    {
+	    delete stlgeometry;
+	    stlgeometry = NULL;
+	    }
+	    #ifdef OCCGEOMETRY
+	    if (occgeometry)
+	    {
+	    delete occgeometry;
+	    occgeometry = NULL;
+	    }
+	    #endif
+	    #ifdef ACIS
+	    if (acisgeometry)
+	    {
+	    delete acisgeometry;
+	    acisgeometry = NULL;
+	    }
+	    #endif
+	    geometry2d.Reset (0);
+	  */
+ 	  // geometry -> LoadSurfaces(input);
+	  CSGeometry * geometry = new CSGeometry ("");
+	  geometry -> LoadSurfaces(input);
+
+	  delete ng_geometry;
+	  ng_geometry = geometry;
+	}
+    }
+#endif 
+}
+
+
+
+
+void Ng_LoadMesh (const char * filename)
+{
+  if ( (strlen (filename) > 4) &&
+       strcmp (filename + (strlen (filename)-4), ".vol") != 0 )
+    {
+      mesh.Reset (new Mesh());
+      ReadFile(*mesh,filename);
+
+      //mesh->SetGlobalH (mparam.maxh);
+      //mesh->CalcLocalH();
+      return;
+    }
+
+  ifstream infile(filename);
+  Ng_LoadMeshFromStream(infile);
+}
+
+void Ng_LoadMeshFromString (const char * mesh_as_string)
+{
+  istringstream instream(mesh_as_string);
+  Ng_LoadMeshFromStream(instream);
+}
+  
+
+
+
+int Ng_GetDimension ()
+{
+  return (mesh) ? mesh->GetDimension() : -1;
+}
+
+int Ng_GetNP ()
+{
+  return (mesh) ? mesh->GetNP() : 0;
+}
+
+int Ng_GetNV ()
+{
+  return (mesh) ? mesh->GetNV() : 0;
+}
+
+int Ng_GetNE ()
+{
+  if(!mesh) return 0;
+  if (mesh->GetDimension() == 3)
+    return mesh->GetNE();
+  else
+    return mesh->GetNSE();
+}
+
+int Ng_GetNSE ()
+{
+  if(!mesh) return 0;
+  if (mesh->GetDimension() == 3)
+    return mesh->GetNSE();
+  else
+    return mesh->GetNSeg();
+}
+
+void Ng_GetPoint (int pi, double * p)
+{
+  if (pi < 1 || pi > mesh->GetNP())
+    {
+      if (printmessage_importance>0)
+        cout << "Ng_GetPoint: illegal point " << pi << endl;
+      return;
+    }
+
+  const Point3d & hp = mesh->Point (pi);
+  p[0] = hp.X();
+  p[1] = hp.Y();
+  if (mesh->GetDimension() == 3)
+    p[2] = hp.Z();
+}
+
+
+NG_ELEMENT_TYPE Ng_GetElement (int ei, int * epi, int * np)
+{
+  if (mesh->GetDimension() == 3)
+    {
+      int i;
+      const Element & el = mesh->VolumeElement (ei);
+      for (i = 0; i < el.GetNP(); i++)
+	epi[i] = el.PNum(i+1);
+      
+      if (np)
+	*np = el.GetNP();
+
+      if (el.GetType() == PRISM)
+	{
+	  // degenerated prism, (should be obsolete)
+	  const int map1[] = { 3, 2, 5, 6, 1 };
+	  const int map2[] = { 1, 3, 6, 4, 2 };
+	  const int map3[] = { 2, 1, 4, 5, 3 };
+	  
+	  const int * map = NULL;
+	  int deg1 = 0, deg2 = 0, deg3 = 0;
+	  //int deg = 0;
+	  if (el.PNum(1) == el.PNum(4)) { map = map1; deg1 = 1; }
+	  if (el.PNum(2) == el.PNum(5)) { map = map2; deg2 = 1; }
+	  if (el.PNum(3) == el.PNum(6)) { map = map3; deg3 = 1; }
+	  
+	  switch (deg1+deg2+deg3)
+	    {
+	      {
+	      case 1:
+                if (printmessage_importance>0)
+                  cout << "degenerated prism found, deg = 1" << endl;
+		for (i = 0; i < 5; i++)
+		  epi[i] = el.PNum (map[i]);
+		
+		if (np) *np = 5;
+		return NG_PYRAMID;
+		break;
+	      }
+	    case 2:
+	      {
+                if (printmessage_importance>0)
+                  cout << "degenerated prism found, deg = 2" << endl;
+		if (!deg1) epi[3] = el.PNum(4);
+		if (!deg2) epi[3] = el.PNum(5);
+		if (!deg3) epi[3] = el.PNum(6);
+		
+		if (np) *np = 4;
+		return NG_TET;
+		break;
+	      }
+	    default:
+	      ;
+	    }
+	  
+	}
+
+      return NG_ELEMENT_TYPE (el.GetType());
+    }
+  else
+    {
+      int i;
+      const Element2d & el = mesh->SurfaceElement (ei);
+      for (i = 0; i < el.GetNP(); i++)
+	epi[i] = el.PNum(i+1);      
+
+      if (np) *np = el.GetNP();
+      return NG_ELEMENT_TYPE (el.GetType());
+      /*
+	switch (el.GetNP())
+	{
+	case 3: return NG_TRIG; 
+	case 4: return NG_QUAD; 
+	case 6: return NG_TRIG6; 
+	}
+      */
+    }
+
+  // should not occur
+  return NG_TET;
+}
+
+
+NG_ELEMENT_TYPE Ng_GetElementType (int ei)
+{
+  if (mesh->GetDimension() == 3)
+    {
+      return NG_ELEMENT_TYPE (mesh->VolumeElement (ei).GetType());
+    }
+  else
+    {
+      const Element2d & el = mesh->SurfaceElement (ei);
+      switch (el.GetNP())
+	{
+	case 3: return NG_TRIG; 
+	case 4: return NG_QUAD; 
+	case 6: return NG_TRIG6; 
+	}
+    }
+
+  // should not occur
+  return NG_TET;
+}
+
+
+
+int Ng_GetElementIndex (int ei)
+{
+  if (mesh->GetDimension() == 3)
+    return mesh->VolumeElement(ei).GetIndex();
+  else
+    {
+      int ind = mesh->SurfaceElement(ei).GetIndex(); 
+      ind = mesh->GetFaceDescriptor(ind).BCProperty();
+      return ind;
+    }
+}
+
+void Ng_SetElementIndex(const int ei, const int index)
+{
+  mesh->VolumeElement(ei).SetIndex(index);
+}
+
+char * Ng_GetElementMaterial (int ei)
+{
+  static char empty[] = "";
+  if (mesh->GetDimension() == 3)
+    {
+      int ind = mesh->VolumeElement(ei).GetIndex();
+      // cout << "ind = " << ind << endl;
+      const char * mat = mesh->GetMaterial (ind);
+      if (mat)
+	return const_cast<char*> (mat);
+      else 
+	return empty;
+    }
+  // add astrid
+  else
+    {
+      int ind = mesh->SurfaceElement(ei).GetIndex();
+      ind = mesh->GetFaceDescriptor(ind).BCProperty();
+      const char * mat = mesh->GetMaterial ( ind );
+      if (mat)
+	return const_cast<char*> (mat);
+      else
+	return empty;
+    }
+  return 0;
+}
+
+char * Ng_GetDomainMaterial (int dom)
+{
+  static char empty[] = "";
+  // astrid
+  if ( 1 ) // mesh->GetDimension() == 3)
+    {
+      const char * mat = mesh->GetMaterial(dom);
+      if (mat)
+	return const_cast<char*> (mat);
+      else 
+	return empty;      
+    }
+
+  return 0;
+}
+
+int Ng_GetUserDataSize (char * id)
+{
+  Array<double> da;
+  mesh->GetUserData (id, da);
+  return da.Size();
+}
+
+void Ng_GetUserData (char * id, double * data)
+{
+  Array<double> da;
+  mesh->GetUserData (id, da);
+  for (int i = 0; i < da.Size(); i++)
+    data[i] = da[i];
+}
+
+
+NG_ELEMENT_TYPE Ng_GetSurfaceElement (int ei, int * epi, int * np)
+{
+  if (mesh->GetDimension() == 3)
+    {
+      const Element2d & el = mesh->SurfaceElement (ei);
+      for (int i = 0; i < el.GetNP(); i++)
+	epi[i] = el[i];
+      
+      if (np) *np = el.GetNP();
+      
+      return NG_ELEMENT_TYPE (el.GetType());
+    }
+  else
+    {
+      const Segment & seg = mesh->LineSegment (ei);
+
+      if (seg[2] < 0)
+	{
+	  epi[0] = seg[0];
+	  epi[1] = seg[1];
+	  
+	  if (np) *np = 2;
+	  return NG_SEGM;
+	}
+      else
+	{
+	  epi[0] = seg[0];
+	  epi[1] = seg[1];
+	  epi[2] = seg[2];
+
+	  if (np) *np = 3;
+	  return NG_SEGM3;
+	}
+    }
+
+  return NG_TRIG;
+}
+
+int Ng_GetSurfaceElementIndex (int ei)
+{
+  if (mesh->GetDimension() == 3)
+    return mesh->GetFaceDescriptor(mesh->SurfaceElement(ei).GetIndex()).BCProperty();
+  else
+    return mesh->LineSegment(ei).si;
+}
+
+int Ng_GetSurfaceElementSurfaceNumber (int ei)
+{
+  if (mesh->GetDimension() == 3)
+    return mesh->GetFaceDescriptor(mesh->SurfaceElement(ei).GetIndex()).SurfNr();
+  else
+    return mesh->LineSegment(ei).si;
+}
+int Ng_GetSurfaceElementFDNumber (int ei)
+{
+  if (mesh->GetDimension() == 3)
+    return mesh->SurfaceElement(ei).GetIndex();
+  else
+    return -1;
+}
+
+
+char * Ng_GetSurfaceElementBCName (int ei)
+{
+  if ( mesh->GetDimension() == 3 )
+    return const_cast<char *>(mesh->GetFaceDescriptor(mesh->SurfaceElement(ei).GetIndex()).GetBCName().c_str());
+  else
+    return const_cast<char *>(mesh->LineSegment(ei).GetBCName().c_str());
+}
+
+
+// Inefficient (but maybe safer) version:
+//void Ng_GetSurfaceElementBCName (int ei, char * name)
+//{
+//  if ( mesh->GetDimension() == 3 )
+//      strcpy(name,mesh->GetFaceDescriptor(mesh->SurfaceElement(ei).GetIndex()).GetBCName().c_str());
+//  else
+//      strcpy(name,mesh->LineSegment(ei).GetBCName().c_str());
+//}
+
+char * Ng_GetBCNumBCName (int bcnr)
+{
+  return const_cast<char *>(mesh->GetBCName(bcnr).c_str());
+}
+
+
+// Inefficient (but maybe safer) version:
+//void Ng_GetBCNumBCName (int bcnr, char * name)
+//{
+//    strcpy(name,mesh->GetBCName(bcnr).c_str());
+//}
+
+void Ng_GetNormalVector (int sei, int locpi, double * nv)
+{
+  nv[0] = 0; 
+  nv[1] = 0;
+  nv[2] = 1;
+  
+  if (mesh->GetDimension() == 3)
+    {
+      Vec<3> n;
+      Point<3> p;
+      p = mesh->Point (mesh->SurfaceElement(sei).PNum(locpi));
+
+      int surfi = mesh->GetFaceDescriptor(mesh->SurfaceElement(sei).GetIndex()).SurfNr();
+      
+      (*testout) << "surfi = " << surfi << endl;
+#ifdef OCCGEOMETRYxxx
+      OCCGeometry * occgeometry = dynamic_cast<OCCGeometry*> (ng_geometry);
+      if (occgeometry)
+	{
+	  PointGeomInfo gi = mesh->SurfaceElement(sei).GeomInfoPi(locpi);
+	  occgeometry->GetSurface (surfi).GetNormalVector(p, gi, n);
+	  nv[0] = n(0);
+	  nv[1] = n(1);
+	  nv[2] = n(2);
+	}
+#endif
+      CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+      if (geometry)
+	{
+	  n = geometry->GetSurface (surfi) -> GetNormalVector(p);
+	  nv[0] = n(0);
+	  nv[1] = n(1);
+	  nv[2] = n(2);
+	}
+    }
+}
+
+
+
+void Ng_SetPointSearchStartElement(const int el)
+{
+  mesh->SetPointSearchStartElement(el);
+}
+
+
+int Ng_FindElementOfPoint (double * p, double * lami, int build_searchtree, 
+			   const int * const indices, const int numind)
+  
+{
+  Array<int> * dummy(NULL);
+  int ind = -1;
+
+  if(indices != NULL)
+    {
+      dummy = new Array<int>(numind);
+      for(int i=0; i<numind; i++) (*dummy)[i] = indices[i];
+    }
+
+  if (mesh->GetDimension() == 3)
+    {
+      Point3d p3d(p[0], p[1], p[2]);
+      ind = 
+	mesh->GetElementOfPoint(p3d, lami, dummy, build_searchtree != 0);
+    }
+  else
+    {
+      double lam3[3];
+      Point3d p2d(p[0], p[1], 0);
+      ind = 
+	mesh->GetElementOfPoint(p2d, lam3, dummy, build_searchtree != 0);
+
+      if (ind > 0)
+	{
+	  if(mesh->SurfaceElement(ind).GetType()==QUAD)
+	    {
+	      lami[0] = lam3[0];
+	      lami[1] = lam3[1];
+	    }
+	  else 
+	    {
+	      lami[0] = 1-lam3[0]-lam3[1];
+	      lami[1] = lam3[0];
+	    }
+	}
+    }
+
+  delete dummy;
+
+  return ind;
+}
+
+int Ng_FindSurfaceElementOfPoint (double * p, double * lami, int build_searchtree, 
+				  const int * const indices, const int numind)
+  
+{
+  Array<int> * dummy(NULL);
+  int ind = -1;
+
+  if(indices != NULL)
+    {
+      dummy = new Array<int>(numind);
+      for(int i=0; i<numind; i++) (*dummy)[i] = indices[i];
+    }
+
+  if (mesh->GetDimension() == 3)
+    {
+      Point3d p3d(p[0], p[1], p[2]);
+      ind = 
+	mesh->GetSurfaceElementOfPoint(p3d, lami, dummy, build_searchtree != 0);
+    }
+  else
+    {
+      //throw NgException("FindSurfaceElementOfPoint for 2D meshes not yet implemented");
+      cerr << "FindSurfaceElementOfPoint for 2D meshes not yet implemented" << endl;
+    }
+
+  delete dummy;
+
+  return ind;
+}
+
+
+int Ng_IsElementCurved (int ei)
+{
+  if (mesh->GetDimension() == 2)
+    return mesh->GetCurvedElements().IsSurfaceElementCurved (ei-1);
+  else
+    return mesh->GetCurvedElements().IsElementCurved (ei-1);
+}
+
+
+int Ng_IsSurfaceElementCurved (int sei)
+{
+  if (mesh->GetDimension() == 2)
+    return mesh->GetCurvedElements().IsSegmentCurved (sei-1);
+  else
+    return mesh->GetCurvedElements().IsSurfaceElementCurved (sei-1);
+}
+
+
+
+
+void Ng_GetElementTransformation (int ei, const double * xi, 
+				  double * x, double * dxdxi)
+{
+  if (mesh->GetDimension() == 2)
+    {
+      Point<2> xl(xi[0], xi[1]);
+      Point<3> xg;
+      Mat<3,2> dx;
+
+      mesh->GetCurvedElements().CalcSurfaceTransformation (xl, ei-1, xg, dx);
+
+      if (x)
+	{
+	  for (int i = 0; i < 2; i++)
+	    x[i] = xg(i);
+	}
+	  
+      if (dxdxi)
+	{
+	  for (int i=0; i<2; i++)
+	    {
+	      dxdxi[2*i] = dx(i,0);
+	      dxdxi[2*i+1] = dx(i,1);
+	    }
+	}
+    }
+  else
+    {
+      Point<3> xl(xi[0], xi[1], xi[2]);
+      Point<3> xg;
+      Mat<3,3> dx;
+
+      mesh->GetCurvedElements().CalcElementTransformation (xl, ei-1, xg, dx);
+
+      if (x)
+	{
+	  for (int i = 0; i < 3; i++)
+	    x[i] = xg(i);
+	}
+
+      if (dxdxi)
+	{
+	  for (int i=0; i<3; i++)
+	    {
+	      dxdxi[3*i] = dx(i,0);
+	      dxdxi[3*i+1] = dx(i,1);
+              dxdxi[3*i+2] = dx(i,2);
+	    }
+	}
+    }
+}
+
+
+#ifdef OLD
+void Ng_GetBufferedElementTransformation (int ei, const double * xi, 
+                                          double * x, double * dxdxi,
+                                          void * buffer, int buffervalid)
+{
+  // buffer = 0;
+  // buffervalid = 0;
+  if (mesh->GetDimension() == 2)
+    {
+      return Ng_GetElementTransformation (ei, xi, x, dxdxi);
+    }
+  else
+    {
+      mesh->GetCurvedElements().CalcElementTransformation (reinterpret_cast<const Point<3> &> (*xi), 
+                                                           ei-1, 
+                                                           reinterpret_cast<Point<3> &> (*x), 
+                                                           reinterpret_cast<Mat<3,3> &> (*dxdxi), 
+                                                           buffer, (buffervalid != 0));
+
+      /*
+	Point<3> xl(xi[0], xi[1], xi[2]);
+	Point<3> xg;
+	Mat<3,3> dx;
+	// buffervalid = 0;
+	mesh->GetCurvedElements().CalcElementTransformation (xl, ei-1, xg, dx, buffer, buffervalid);
+
+	// still 1-based arrays
+	if (x)
+	{
+	for (int i = 0; i < 3; i++)
+	x[i] = xg(i);
+	}
+
+	if (dxdxi)
+	{
+	for (int i=0; i<3; i++)
+	{
+	dxdxi[3*i] = dx(i,0);
+	dxdxi[3*i+1] = dx(i,1);
+	dxdxi[3*i+2] = dx(i,2);
+	}
+	}
+      */
+    }
+}
+#endif
+
+
+
+
+
+
+void Ng_GetMultiElementTransformation (int ei, int n,
+                                       const double * xi, size_t sxi,
+                                       double * x, size_t sx,
+                                       double * dxdxi, size_t sdxdxi)
+{
+  if (mesh->GetDimension() == 2)
+    mesh->GetCurvedElements().CalcMultiPointSurfaceTransformation<2> (ei-1, n, xi, sxi, x, sx, dxdxi, sdxdxi);
+  else
+    mesh->GetCurvedElements().CalcMultiPointElementTransformation (ei-1, n, xi, sxi, x, sx, dxdxi, sdxdxi);
+}
+
+
+
+void Ng_GetSurfaceElementTransformation (int sei, const double * xi, 
+					 double * x, double * dxdxi)
+{
+  if (mesh->GetDimension() == 2)
+    {
+      Point<3> xg;
+      Vec<3> dx;
+
+      mesh->GetCurvedElements().CalcSegmentTransformation (xi[0], sei-1, xg, dx);
+
+      if (x)
+        for (int i = 0; i < 2; i++)
+	  x[i] = xg(i);
+	  
+      if (dxdxi)
+        for (int i=0; i<2; i++)
+	  dxdxi[i] = dx(i);
+
+    }
+  else
+    {
+      Point<2> xl(xi[0], xi[1]);
+      Point<3> xg;
+      Mat<3,2> dx;
+      
+      mesh->GetCurvedElements().CalcSurfaceTransformation (xl, sei-1, xg, dx);
+      
+      for (int i=0; i<3; i++)
+	{
+	  if (x)
+	    x[i] = xg(i);
+	  if (dxdxi)
+	    {
+	      dxdxi[2*i] = dx(i,0);
+	      dxdxi[2*i+1] = dx(i,1);
+	    }
+	}
+    }
+}
+
+
+
+
+
+int Ng_GetSegmentIndex (int ei)
+{
+  const Segment & seg = mesh->LineSegment (ei);
+  return seg.edgenr;
+}
+
+
+NG_ELEMENT_TYPE Ng_GetSegment (int ei, int * epi, int * np)
+{
+  const Segment & seg = mesh->LineSegment (ei);
+  
+  epi[0] = seg[0];
+  epi[1] = seg[1];
+
+  if (seg[2] < 0)
+    {
+      if (np) *np = 2;
+      return NG_SEGM;
+    }
+  else
+    {
+      epi[2] = seg[2];
+      if (np) *np = 3;
+      return NG_SEGM3;
+    }
+}
+
+
+
+
+
+
+void Ng_GetSurfaceElementNeighbouringDomains(const int selnr, int & in, int & out)
+{
+  if ( mesh->GetDimension() == 3 )
+    {
+      in = mesh->GetFaceDescriptor(mesh->SurfaceElement(selnr).GetIndex()).DomainIn();
+      out = mesh->GetFaceDescriptor(mesh->SurfaceElement(selnr).GetIndex()).DomainOut();
+    }
+  else
+    {
+      in = mesh -> LineSegment(selnr) . domin;
+      out = mesh -> LineSegment(selnr) . domout;
+    }
+}
+
+
+#ifdef PARALLEL
+// Is Element ei an element of this processor ??
+bool Ng_IsGhostEl (int ei)
+{
+  return false;
+  /*
+  if ( mesh->GetDimension() == 3 )
+    return mesh->VolumeElement(ei).IsGhost();
+  else
+    return false;
+  */
+}
+
+void Ng_SetGhostEl(const int ei, const bool aisghost )
+{
+  ;
+  /*
+  if ( mesh -> GetDimension () == 3 )
+    mesh -> VolumeElement(ei).SetGhost (aisghost);
+  */
+}
+
+bool Ng_IsGhostSEl (int ei)
+{
+  return false;
+  /*
+  if ( mesh -> GetDimension () == 3 )
+    return mesh->SurfaceElement(ei).IsGhost();
+  else
+    return false;
+  */
+}
+
+void Ng_SetGhostSEl(const int ei, const bool aisghost )
+{
+  ;
+  /*
+  if ( mesh -> GetDimension () == 3 )
+    mesh -> SurfaceElement(ei).SetGhost (aisghost);
+  */
+}
+
+
+bool Ng_IsGhostVert ( int pnum )
+{
+  return false;
+  // return mesh -> Point ( pnum ).IsGhost() ;  
+}
+bool Ng_IsGhostEdge ( int ednum )
+{
+  return false;
+  // return mesh -> GetParallelTopology() . IsGhostEdge ( ednum ); 
+}
+
+bool Ng_IsGhostFace ( int fanum )
+{
+  return false;
+  // return mesh -> GetParallelTopology() . IsGhostFace ( fanum ); 
+}
+
+// void Ng_SetGhostVert ( const int pnum, const bool aisghost );
+// void Ng_SetGhostEdge ( const int ednum, const bool aisghost );
+// void Ng_SetGhostFace ( const int fanum, const bool aisghost );
+
+
+bool Ng_IsExchangeEl ( int elnum )
+{ return mesh -> GetParallelTopology() . IsExchangeElement ( elnum ); }
+
+bool Ng_IsExchangeSEl ( int selnum )
+{ return mesh -> GetParallelTopology() . IsExchangeSEl ( selnum ); }
+
+void Ng_UpdateOverlap()
+{ 
+  ; // mesh->UpdateOverlap(); 
+}
+
+int Ng_Overlap ()
+{ 
+  return 0;
+  // return mesh->GetParallelTopology() . Overlap(); 
+}
+
+
+
+ int NgPar_GetLoc2Glob_VolEl ( int locnum )
+  { 
+    return mesh -> GetParallelTopology().GetLoc2Glob_VolEl ( locnum+1) -1; 
+  }
+
+  // gibt anzahl an distant pnums zurueck
+  // * pnums entspricht ARRAY<int[2] >
+  int NgPar_GetDistantNodeNums ( int nodetype, int locnum, int * distnums )
+  {
+    int size;
+    switch ( nodetype )
+      {
+      case 0:
+	size = mesh->GetParallelTopology().GetDistantPNums( locnum+1, distnums ); 
+	break;
+      case 1:
+	size = mesh->GetParallelTopology().GetDistantEdgeNums( locnum+1, distnums ); 
+	break;
+      case 2:
+	size = mesh->GetParallelTopology().GetDistantFaceNums( locnum+1, distnums );
+	break;
+      case 3:
+	size = mesh->GetParallelTopology().GetDistantElNums( locnum+1, distnums );
+	break;
+      default:
+	cerr << "NgPar_GetDistantNodeNums() Unknown nodetype " << nodetype << endl;
+	size = -1;
+      }
+    // 0 - based 
+    for ( int i = 0; i < size; i++ )
+      distnums[2*i+1]--;
+
+    return size;
+  }
+
+  int NgPar_GetNDistantNodeNums ( int nodetype, int locnum )
+  {
+    switch ( nodetype )
+      {
+      case 0:
+	return mesh->GetParallelTopology().GetNDistantPNums( locnum+1 );
+      case 1:
+	return mesh->GetParallelTopology().GetNDistantEdgeNums( locnum+1 );
+      case 2:
+	return mesh->GetParallelTopology().GetNDistantFaceNums( locnum+1 );
+      case 3:
+	return mesh->GetParallelTopology().GetNDistantElNums( locnum+1 );
+      }
+    return -1;
+  }
+
+  int NgPar_GetDistantPNum ( int proc, int locpnum )  
+  { 
+    return mesh->GetParallelTopology().GetDistantPNum( proc, locpnum+1) - 1; 
+  }
+
+  int NgPar_GetDistantEdgeNum ( int proc, int locpnum )  
+  { 
+    return mesh->GetParallelTopology().GetDistantEdgeNum( proc, locpnum+1) - 1; 
+  }
+
+  int NgPar_GetDistantFaceNum ( int proc, int locpnum )  
+  { 
+    return mesh->GetParallelTopology().GetDistantFaceNum (proc, locpnum+1 ) - 1; 
+  }
+
+  int NgPar_GetDistantElNum ( int proc, int locelnum )
+  { 
+    return mesh->GetParallelTopology().GetDistantElNum (proc, locelnum+1 ) - 1; 
+  }
+
+  bool NgPar_IsExchangeFace ( int fnr ) 
+  { 
+    return (mesh->GetParallelTopology().GetNDistantFaceNums( fnr+1 ) > 0);
+    // return mesh->GetParallelTopology().IsExchangeFace ( fnr+1 ); 
+  }
+
+  bool NgPar_IsExchangeVert ( int vnum )
+  { 
+    return (mesh->GetParallelTopology().GetNDistantPNums( vnum+1 ) > 0);
+    // return mesh->GetParallelTopology().IsExchangeVert ( vnum+1 ); 
+  }
+
+  bool NgPar_IsExchangeEdge ( int ednum )  
+  { 
+    return (mesh->GetParallelTopology().GetNDistantEdgeNums( ednum+1 ) > 0);
+    // return mesh->GetParallelTopology().IsExchangeEdge ( ednum+1 ); 
+  }
+
+  bool NgPar_IsExchangeElement ( int elnum )  
+  { 
+    return (mesh->GetParallelTopology().GetNDistantElNums( elnum+1 ) > 0);
+    // return mesh->GetParallelTopology().IsExchangeElement ( elnum+1 ); 
+  }
+
+
+  void NgPar_PrintParallelMeshTopology ()
+  { 
+    mesh -> GetParallelTopology().Print (); 
+  }
+
+
+#endif
+
+void Ng_SetRefinementFlag (int ei, int flag)
+{
+  if (mesh->GetDimension() == 3)
+    {
+      mesh->VolumeElement(ei).SetRefinementFlag (flag != 0);
+      mesh->VolumeElement(ei).SetStrongRefinementFlag (flag >= 10);
+    }
+  else
+    {
+      mesh->SurfaceElement(ei).SetRefinementFlag (flag != 0);
+      mesh->SurfaceElement(ei).SetStrongRefinementFlag (flag >= 10);
+    }
+}
+
+void Ng_SetSurfaceRefinementFlag (int ei, int flag)
+{
+  if (mesh->GetDimension() == 3)
+    {
+      mesh->SurfaceElement(ei).SetRefinementFlag (flag != 0);
+      mesh->SurfaceElement(ei).SetStrongRefinementFlag (flag >= 10);
+    }
+}
+
+
+void Ng_Refine (NG_REFINEMENT_TYPE reftype)
+{
+  NgLock meshlock (mesh->MajorMutex(), 1);
+
+  BisectionOptions biopt;
+  biopt.usemarkedelements = 1;
+  biopt.refine_p = 0;
+  biopt.refine_hp = 0;
+  if (reftype == NG_REFINE_P)
+    biopt.refine_p = 1;
+  if (reftype == NG_REFINE_HP)
+    biopt.refine_hp = 1;
+
+  const Refinement & ref = ng_geometry->GetRefinement();
+
+  // Refinement * ref;
+  MeshOptimize2d * opt = NULL;
+
+  /*
+    if (geometry2d)
+    ref = new Refinement2d(*geometry2d);
+    else if (stlgeometry)
+    ref = new RefinementSTLGeometry(*stlgeometry);
+    #ifdef OCCGEOMETRY
+    else if (occgeometry)
+    ref = new OCCRefinementSurfaces (*occgeometry);
+    #endif
+    #ifdef ACIS
+    else if (acisgeometry)
+    {
+    ref = new ACISRefinementSurfaces (*acisgeometry);
+    opt = new ACISMeshOptimize2dSurfaces(*acisgeometry);
+    ref->Set2dOptimizer(opt);
+    }
+    #endif
+    else if (geometry && mesh->GetDimension() == 3)
+    {
+    ref = new RefinementSurfaces(*geometry);
+    opt = new MeshOptimize2dSurfaces(*geometry);
+    ref->Set2dOptimizer(opt);
+    }
+    else
+    {
+    ref = new Refinement();
+    }
+  */
+
+  ref.Bisect (*mesh, biopt);
+
+  mesh -> UpdateTopology();
+  mesh -> GetCurvedElements().SetIsHighOrder (false);
+
+  // mesh -> GetCurvedElements().BuildCurvedElements (ref, mparam.elementorder);
+  // delete ref;
+  delete opt;
+}
+
+void Ng_SecondOrder ()
+{
+  const_cast<Refinement&> (ng_geometry->GetRefinement()).MakeSecondOrder(*mesh);
+  /*
+    if (stlgeometry)
+    {
+    RefinementSTLGeometry ref (*stlgeometry);
+    ref.MakeSecondOrder (*mesh);
+    }
+
+    else if (geometry2d)
+    {
+    Refinement2d ref (*geometry2d);
+    ref.MakeSecondOrder (*mesh);
+    }
+
+    else if (geometry && mesh->GetDimension() == 3)
+
+    {
+    RefinementSurfaces ref (*geometry);
+    ref.MakeSecondOrder (*mesh);
+    }
+    else
+    {
+    if (printmessage_importance>0)
+    cout << "no geom" << endl;
+    Refinement ref;
+    ref.MakeSecondOrder (*mesh);
+    }
+  */
+  mesh -> UpdateTopology();
+}
+
+/*
+  void Ng_HPRefinement (int levels)
+  {
+  Refinement * ref;
+
+  if (stlgeometry)
+  ref = new RefinementSTLGeometry (*stlgeometry);
+  else if (geometry2d)
+  ref = new Refinement2d (*geometry2d);
+  else
+  ref = new RefinementSurfaces (*geometry);
+
+
+  HPRefinement (*mesh, ref, levels);
+  }
+
+  void Ng_HPRefinement (int levels, double parameter)
+  {
+  Refinement * ref;
+
+  if (stlgeometry)
+  ref = new RefinementSTLGeometry (*stlgeometry);
+  else if (geometry2d)
+  ref = new Refinement2d (*geometry2d);
+  else
+  ref = new RefinementSurfaces (*geometry);
+
+
+  HPRefinement (*mesh, ref, levels, parameter);
+  }
+*/
+
+void Ng_HPRefinement (int levels, double parameter, bool setorders,
+                      bool ref_level)
+{
+  NgLock meshlock (mesh->MajorMutex(), true);
+  Refinement & ref = const_cast<Refinement&> (ng_geometry -> GetRefinement());
+  HPRefinement (*mesh, &ref, levels);
+  /*
+    Refinement * ref;
+
+    if (stlgeometry)
+    ref = new RefinementSTLGeometry (*stlgeometry);
+    else if (geometry2d)
+    ref = new Refinement2d (*geometry2d);
+    else
+    ref = new RefinementSurfaces (*geometry);
+
+    HPRefinement (*mesh, ref, levels, parameter, setorders, ref_level);
+  */
+}
+
+
+void Ng_HighOrder (int order, bool rational)
+{
+  NgLock meshlock (mesh->MajorMutex(), true);
+  /*
+    Refinement * ref;
+
+    if (stlgeometry)
+    ref = new RefinementSTLGeometry (*stlgeometry);
+    #ifdef OCCGEOMETRY
+    else if (occgeometry)
+    ref = new OCCRefinementSurfaces (*occgeometry);
+    #endif
+    #ifdef ACIS
+    else if (acisgeometry)
+    {
+    ref = new ACISRefinementSurfaces (*acisgeometry);
+    }
+    #endif
+    else if (geometry2d)
+    ref = new Refinement2d (*geometry2d);
+    else
+    {
+    ref = new RefinementSurfaces (*geometry);
+    }
+  */
+  // cout << "parameter 1: " << argv[1] << " (conversion to int = " << atoi(argv[1]) << ")" << endl;
+ 
+
+  mesh -> GetCurvedElements().BuildCurvedElements (&const_cast<Refinement&> (ng_geometry -> GetRefinement()),
+						   order, rational);
+  mesh -> SetNextMajorTimeStamp();
+
+  /*
+    if(mesh)
+    mesh -> GetCurvedElements().BuildCurvedElements (ref, order, rational);
+  */
+
+  // delete ref;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+int Ng_ME_GetNVertices (NG_ELEMENT_TYPE et)
+{
+  switch (et)
+    {
+    case NG_SEGM:
+    case NG_SEGM3:
+      return 2;
+
+    case NG_TRIG:
+    case NG_TRIG6:
+      return 3;
+
+    case NG_QUAD:
+      return 4;
+
+    case NG_TET:
+    case NG_TET10:
+      return 4;
+
+    case NG_PYRAMID:
+      return 5;
+
+    case NG_PRISM:
+    case NG_PRISM12:
+      return 6;
+
+    case NG_HEX:
+      return 8;
+
+    default:
+      cerr << "Ng_ME_GetNVertices, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+int Ng_ME_GetNEdges (NG_ELEMENT_TYPE et)
+{
+  switch (et)
+    {
+    case NG_SEGM:
+    case NG_SEGM3:
+      return 1;
+
+    case NG_TRIG:
+    case NG_TRIG6:
+      return 3;
+
+    case NG_QUAD:
+      return 4;
+
+    case NG_TET:
+    case NG_TET10:
+      return 6;
+
+    case NG_PYRAMID:
+      return 8;
+
+    case NG_PRISM:
+    case NG_PRISM12:
+      return 9;
+
+    case NG_HEX:
+      return 12;
+
+    default:
+      cerr << "Ng_ME_GetNEdges, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+int Ng_ME_GetNFaces (NG_ELEMENT_TYPE et)
+{
+  switch (et)
+    {
+    case NG_SEGM:
+    case NG_SEGM3:
+      return 0;
+
+    case NG_TRIG:
+    case NG_TRIG6:
+      return 1;
+
+    case NG_QUAD:
+    case NG_QUAD6:
+      return 1;
+
+    case NG_TET:
+    case NG_TET10:
+      return 4;
+
+    case NG_PYRAMID:
+      return 5;
+
+    case NG_PRISM:
+    case NG_PRISM12:
+      return 5;
+
+    case NG_HEX:
+      return 6;
+
+    default:
+      cerr << "Ng_ME_GetNVertices, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+const NG_POINT * Ng_ME_GetVertices (NG_ELEMENT_TYPE et)
+{
+  static double segm_points [][3] = 
+    { { 1, 0, 0 },
+      { 0, 0, 0 } };
+
+  static double trig_points [][3] = 
+    { { 1, 0, 0 },
+      { 0, 1, 0 },
+      { 0, 0, 0 } };
+
+  static double quad_points [][3] = 
+    { { 0, 0, 0 },
+      { 1, 0, 0 },
+      { 1, 1, 0 },
+      { 0, 1, 0 } };
+
+  static double tet_points [][3] = 
+    { { 1, 0, 0 },
+      { 0, 1, 0 },
+      { 0, 0, 1 },
+      { 0, 0, 0 } };
+
+  static double pyramid_points [][3] =
+    {
+      { 0, 0, 0 },
+      { 1, 0, 0 },
+      { 1, 1, 0 },
+      { 0, 1, 0 },
+      { 0, 0, 1-1e-7 },
+    };    
+  
+  static double prism_points[][3] = 
+    {
+      { 1, 0, 0 },
+      { 0, 1, 0 },
+      { 0, 0, 0 },
+      { 1, 0, 1 },
+      { 0, 1, 1 },
+      { 0, 0, 1 }
+    };
+
+  switch (et)
+    {
+    case NG_SEGM:
+    case NG_SEGM3:
+      return segm_points;
+
+    case NG_TRIG:
+    case NG_TRIG6:
+      return trig_points;
+
+    case NG_QUAD:
+    case NG_QUAD6:
+      return quad_points;
+
+    case NG_TET:
+    case NG_TET10:
+      return tet_points;
+
+    case NG_PYRAMID:
+      return pyramid_points;
+
+    case NG_PRISM:
+    case NG_PRISM12:
+      return prism_points;
+
+    case NG_HEX:
+    default:
+      cerr << "Ng_ME_GetVertices, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+
+const NG_EDGE * Ng_ME_GetEdges (NG_ELEMENT_TYPE et)
+{
+  static int segm_edges[1][2] =
+    { { 1, 2 }};
+
+  static int trig_edges[3][2] =
+    { { 3, 1 },
+      { 3, 2 },
+      { 1, 2 }};
+
+  static int quad_edges[4][2] =
+    { { 1, 2 },
+      { 4, 3 },
+      { 1, 4 },
+      { 2, 3 }};
+
+
+  static int tet_edges[6][2] =
+    { { 4, 1 },
+      { 4, 2 },
+      { 4, 3 }, 
+      { 1, 2 },
+      { 1, 3 },
+      { 2, 3 }};
+
+  static int prism_edges[9][2] =
+    { { 3, 1 },
+      { 1, 2 },
+      { 3, 2 },
+      { 6, 4 },
+      { 4, 5 },
+      { 6, 5 },
+      { 3, 6 },
+      { 1, 4 },
+      { 2, 5 }};
+
+  static int pyramid_edges[8][2] =
+    { { 1, 2 },
+      { 2, 3 },
+      { 1, 4 },
+      { 4, 3 },
+      { 1, 5 },
+      { 2, 5 },
+      { 3, 5 },
+      { 4, 5 }};
+
+
+
+  switch (et)
+    {
+    case NG_SEGM:
+    case NG_SEGM3:
+      return segm_edges;
+
+    case NG_TRIG:
+    case NG_TRIG6:
+      return trig_edges;
+
+    case NG_QUAD:
+    case NG_QUAD6:
+      return quad_edges;
+
+    case NG_TET:
+    case NG_TET10:
+      return tet_edges;
+
+    case NG_PYRAMID:
+      return pyramid_edges;
+
+    case NG_PRISM:
+    case NG_PRISM12:
+      return prism_edges;
+
+    case NG_HEX:
+    default:
+      cerr << "Ng_ME_GetEdges, illegal element type " << et << endl;
+    }
+  return 0;  
+}
+
+
+const NG_FACE * Ng_ME_GetFaces (NG_ELEMENT_TYPE et)
+{
+  static int tet_faces[4][4] =
+    { { 4, 2, 3, 0 },
+      { 4, 1, 3, 0 },
+      { 4, 1, 2, 0 },
+      { 1, 2, 3, 0 } };
+  
+  static int prism_faces[5][4] =
+    {
+      { 1, 2, 3, 0 },
+      { 4, 5, 6, 0 },
+      { 3, 1, 4, 6 },
+      { 1, 2, 5, 4 },
+      { 2, 3, 6, 5 } 
+    };
+  
+  static int pyramid_faces[5][4] =
+    {
+      { 1, 2, 5, 0 },
+      { 2, 3, 5, 0 },
+      { 3, 4, 5, 0 },
+      { 4, 1, 5, 0 },
+      { 1, 2, 3, 4 } 
+    };
+  
+  static int trig_faces[1][4] = 
+    {
+      { 1, 2, 3, 0 },
+    };
+
+  switch (et)
+    {
+    case NG_TET:
+    case NG_TET10:
+      return tet_faces;
+
+    case NG_PRISM:
+    case NG_PRISM12:
+      return prism_faces;
+
+    case NG_PYRAMID:
+      return pyramid_faces;
+
+
+    case NG_SEGM:
+    case NG_SEGM3:
+
+    case NG_TRIG:
+    case NG_TRIG6:
+      return trig_faces;
+    case NG_QUAD:
+
+
+    case NG_HEX:
+
+    default:
+      cerr << "Ng_ME_GetFaces, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+
+void Ng_UpdateTopology()
+{
+  if (mesh)
+    mesh -> UpdateTopology();
+}
+
+Ng_Mesh Ng_SelectMesh (Ng_Mesh newmesh)
+{
+  Mesh * hmesh = mesh.Ptr();
+  mesh.Ptr() = (Mesh*)newmesh;
+  return hmesh;
+}
+
+
+
+int Ng_GetNEdges()
+{
+  return mesh->GetTopology().GetNEdges();
+}
+int Ng_GetNFaces()
+{
+  return mesh->GetTopology().GetNFaces();
+}
+
+
+
+int Ng_GetElement_Edges (int elnr, int * edges, int * orient)
+{
+  const MeshTopology & topology = mesh->GetTopology();
+  if (mesh->GetDimension() == 3)
+    return topology.GetElementEdges (elnr, edges, orient);
+  else
+    return topology.GetSurfaceElementEdges (elnr, edges, orient);
+}
+
+int Ng_GetElement_Faces (int elnr, int * faces, int * orient)
+{
+  const MeshTopology & topology = mesh->GetTopology();
+  if (mesh->GetDimension() == 3)
+    return topology.GetElementFaces (elnr, faces, orient);
+  else
+    {
+      faces[0] = elnr;
+      if (orient) orient[0] = 0;
+      return 1;
+    }
+}
+
+int Ng_GetSurfaceElement_Edges (int elnr, int * edges, int * orient)
+{
+  const MeshTopology & topology = mesh->GetTopology();
+  if (mesh->GetDimension() == 3)
+    return topology.GetSurfaceElementEdges (elnr, edges, orient);
+  else
+    {
+      if (orient)
+	topology.GetSegmentEdge(elnr, edges[0], orient[0]);
+      else
+	edges[0] = topology.GetSegmentEdge(elnr);
+    }
+  return 1;
+  /*
+    int i, ned;
+    const MeshTopology & topology = mesh->GetTopology();
+    Array<int> ia;
+    topology.GetSurfaceElementEdges (elnr, ia);
+    ned = ia.Size();
+    for (i = 1; i <= ned; i++)
+    edges[i-1] = ia.Get(i);
+
+    if (orient)
+    {
+    topology.GetSurfaceElementEdgeOrientations (elnr, ia);
+    for (i = 1; i <= ned; i++)
+    orient[i-1] = ia.Get(i);
+    }
+    return ned;
+  */
+}
+
+int Ng_GetSurfaceElement_Face (int selnr, int * orient)
+{
+  if (mesh->GetDimension() == 3)
+    {
+      const MeshTopology & topology = mesh->GetTopology();
+      if (orient)
+	*orient = topology.GetSurfaceElementFaceOrientation (selnr);
+      return topology.GetSurfaceElementFace (selnr);
+    }
+  return -1;
+}
+
+int Ng_GetFace_Vertices (int fnr, int * vert)
+{
+  const MeshTopology & topology = mesh->GetTopology();
+  ArrayMem<int,4> ia;
+  topology.GetFaceVertices (fnr, ia);
+  for (int i = 0; i < ia.Size(); i++)
+    vert[i] = ia[i];
+  //  cout << "face verts = " << ia << endl;
+  return ia.Size();
+}
+
+
+int Ng_GetFace_Edges (int fnr, int * edge)
+{
+  const MeshTopology & topology = mesh->GetTopology();
+  ArrayMem<int,4> ia;
+  topology.GetFaceEdges (fnr, ia);
+  for (int i = 0; i < ia.Size(); i++)
+    edge[i] = ia[i];
+  return ia.Size();
+}
+
+void Ng_GetEdge_Vertices (int ednr, int * vert)
+{
+  const MeshTopology & topology = mesh->GetTopology();
+  topology.GetEdgeVertices (ednr, vert[0], vert[1]);
+}
+
+
+int Ng_GetNVertexElements (int vnr)
+{
+  if (mesh->GetDimension() == 3)
+    return mesh->GetTopology().GetVertexElements(vnr).Size();
+  else
+    return mesh->GetTopology().GetVertexSurfaceElements(vnr).Size();
+}
+
+void Ng_GetVertexElements (int vnr, int * els)
+{
+  FlatArray<int> ia(0,0);
+  if (mesh->GetDimension() == 3)
+    ia = mesh->GetTopology().GetVertexElements(vnr);
+  else
+    ia = mesh->GetTopology().GetVertexSurfaceElements(vnr);
+  for (int i = 0; i < ia.Size(); i++)
+    els[i] = ia[i];
+}
+
+
+int Ng_GetElementOrder (int enr)
+{
+  if (mesh->GetDimension() == 3)
+    return mesh->VolumeElement(enr).GetOrder();
+  else
+    return mesh->SurfaceElement(enr).GetOrder();
+}
+
+void Ng_GetElementOrders (int enr, int * ox, int * oy, int * oz)
+{
+  if (mesh->GetDimension() == 3)
+    mesh->VolumeElement(enr).GetOrder(*ox, *oy, *oz);
+  else
+    mesh->SurfaceElement(enr).GetOrder(*ox, *oy, *oz);
+}
+
+void Ng_SetElementOrder (int enr, int order)
+{
+  if (mesh->GetDimension() == 3)
+    return mesh->VolumeElement(enr).SetOrder(order);
+  else
+    return mesh->SurfaceElement(enr).SetOrder(order);
+}
+
+void Ng_SetElementOrders (int enr, int ox, int oy, int oz)
+{
+  if (mesh->GetDimension() == 3)
+    mesh->VolumeElement(enr).SetOrder(ox, oy, oz);
+  else
+    mesh->SurfaceElement(enr).SetOrder(ox, oy);
+}
+
+
+int Ng_GetSurfaceElementOrder (int enr)
+{
+  return mesh->SurfaceElement(enr).GetOrder();
+}
+
+//HERBERT: falsche Anzahl von Argumenten
+//void Ng_GetSurfaceElementOrders (int enr, int * ox, int * oy, int * oz)
+void Ng_GetSurfaceElementOrders (int enr, int * ox, int * oy)
+{
+  int d; 
+  mesh->SurfaceElement(enr).GetOrder(*ox, *oy, d);
+}
+
+void Ng_SetSurfaceElementOrder (int enr, int order)
+{
+  return mesh->SurfaceElement(enr).SetOrder(order);
+}
+
+void Ng_SetSurfaceElementOrders (int enr, int ox, int oy)
+{
+  mesh->SurfaceElement(enr).SetOrder(ox, oy);
+}
+
+
+int Ng_GetNLevels ()
+{
+  return (mesh) ? mesh->mglevels : 0;
+}
+
+
+void Ng_GetParentNodes (int ni, int * parents)
+{
+  if (ni <= mesh->mlbetweennodes.Size())
+    {
+      parents[0] = mesh->mlbetweennodes.Get(ni).I1();
+      parents[1] = mesh->mlbetweennodes.Get(ni).I2();
+    }
+  else
+    parents[0] = parents[1] = 0;
+}
+
+
+int Ng_GetParentElement (int ei)
+{
+  if (mesh->GetDimension() == 3)
+    {
+      if (ei <= mesh->mlparentelement.Size())
+	return mesh->mlparentelement.Get(ei);
+    }
+  else
+    {
+      if (ei <= mesh->mlparentsurfaceelement.Size())
+	return mesh->mlparentsurfaceelement.Get(ei);
+    }
+  return 0;
+}
+
+
+int Ng_GetParentSElement (int ei)
+{
+  if (mesh->GetDimension() == 3)
+    {
+      if (ei <= mesh->mlparentsurfaceelement.Size())
+	return mesh->mlparentsurfaceelement.Get(ei);
+    }
+  else
+    {
+      return 0;
+    }
+  return 0;
+}
+
+
+
+
+
+int Ng_GetClusterRepVertex (int pi)
+{
+  return mesh->GetClusters().GetVertexRepresentant(pi);
+}
+
+int Ng_GetClusterRepEdge (int pi)
+{
+  return mesh->GetClusters().GetEdgeRepresentant(pi);
+}
+
+int Ng_GetClusterRepFace (int pi)
+{
+  return mesh->GetClusters().GetFaceRepresentant(pi);
+}
+
+int Ng_GetClusterRepElement (int pi)
+{
+  return mesh->GetClusters().GetElementRepresentant(pi);
+}
+
+
+
+
+		
+int Ng_GetNPeriodicVertices (int idnr)
+{
+  Array<INDEX_2> apairs;
+  mesh->GetIdentifications().GetPairs (idnr, apairs);
+  return apairs.Size();
+}
+
+
+// pairs should be an integer array of 2*npairs
+void Ng_GetPeriodicVertices (int idnr, int * pairs)
+{
+  Array<INDEX_2> apairs;
+  mesh->GetIdentifications().GetPairs (idnr, apairs);
+  for (int i = 0; i < apairs.Size(); i++)
+    {
+      pairs[2*i] = apairs[i].I1();
+      pairs[2*i+1] = apairs[i].I2();
+    }
+      
+}
+
+
+
+int Ng_GetNPeriodicEdges (int idnr)
+{
+  Array<INDEX,PointIndex::BASE> map;
+  //const MeshTopology & top = mesh->GetTopology();
+  int nse = mesh->GetNSeg();
+
+  int cnt = 0;
+  //  for (int id = 1; id <= mesh->GetIdentifications().GetMaxNr(); id++)
+  {
+    mesh->GetIdentifications().GetMap(idnr, map);
+    //(*testout) << "ident-map " << id << ":" << endl << map << endl;
+
+    for (SegmentIndex si = 0; si < nse; si++)
+      {
+	PointIndex other1 = map[(*mesh)[si][0]];
+	PointIndex other2 = map[(*mesh)[si][1]];
+	//  (*testout) << "seg = " << (*mesh)[si] << "; other = " 
+	//     << other1 << "-" << other2 << endl;
+	if (other1 && other2 && mesh->IsSegment (other1, other2))
+	  {
+	    cnt++;
+	  }
+      }
+  }
+  return cnt;
+}
+
+void Ng_GetPeriodicEdges (int idnr, int * pairs)
+{
+  Array<INDEX,PointIndex::BASE> map;
+  const MeshTopology & top = mesh->GetTopology();
+  int nse = mesh->GetNSeg();
+
+  int cnt = 0;
+  //  for (int id = 1; id <= mesh->GetIdentifications().GetMaxNr(); id++)
+  {
+    mesh->GetIdentifications().GetMap(idnr, map);
+      
+    //(*testout) << "map = " << map << endl;
+
+    for (SegmentIndex si = 0; si < nse; si++)
+      {
+	PointIndex other1 = map[(*mesh)[si][0]];
+	PointIndex other2 = map[(*mesh)[si][1]];
+	if (other1 && other2 && mesh->IsSegment (other1, other2))
+	  {
+	    SegmentIndex otherseg = mesh->SegmentNr (other1, other2);
+	    pairs[cnt++] = top.GetSegmentEdge (si+1);
+	    pairs[cnt++] = top.GetSegmentEdge (otherseg+1);
+	  }
+      }
+  }
+}
+
+
+
+void Ng_PushStatus (const char * str)
+{
+  PushStatus (MyStr (str));
+}
+
+void Ng_PopStatus ()
+{
+  PopStatus ();
+}
+
+void Ng_SetThreadPercentage (double percent)
+{
+  SetThreadPercent (percent);
+}
+
+void Ng_GetStatus (char ** str, double & percent)
+{
+  MyStr s;
+  GetStatus(s,percent);
+  *str = new char[s.Length()+1];
+  strcpy(*str,s.c_str());  
+}
+
+
+void Ng_SetTerminate(void)
+{
+  multithread.terminate = 1;
+}
+void Ng_UnSetTerminate(void)
+{
+  multithread.terminate = 0;
+}
+
+int Ng_ShouldTerminate(void)
+{
+  return multithread.terminate;
+}
+
+void Ng_SetRunning(int flag)
+{
+  multithread.running = flag;
+}
+int Ng_IsRunning()
+{
+  return multithread.running;
+}
+
+///// Added by Roman Stainko ....
+int Ng_GetVertex_Elements( int vnr, int* elems )
+{
+  const MeshTopology& topology = mesh->GetTopology();
+  ArrayMem<int,4> indexArray;
+  topology.GetVertexElements( vnr, indexArray );
+  
+  for( int i=0; i<indexArray.Size(); i++ )
+    elems[i] = indexArray[i];
+  
+  return indexArray.Size();
+}
+
+///// Added by Roman Stainko ....
+int Ng_GetVertex_SurfaceElements( int vnr, int* elems )
+{
+  const MeshTopology& topology = mesh->GetTopology();
+  ArrayMem<int,4> indexArray;
+  topology.GetVertexSurfaceElements( vnr, indexArray );
+  
+  for( int i=0; i<indexArray.Size(); i++ )
+    elems[i] = indexArray[i];
+  
+  return indexArray.Size();
+}
+
+///// Added by Roman Stainko ....
+int Ng_GetVertex_NElements( int vnr )
+{
+  const MeshTopology& topology = mesh->GetTopology();
+  ArrayMem<int,4> indexArray;
+  topology.GetVertexElements( vnr, indexArray );
+  
+  return indexArray.Size();
+}
+
+///// Added by Roman Stainko ....
+int Ng_GetVertex_NSurfaceElements( int vnr )
+{
+  const MeshTopology& topology = mesh->GetTopology();
+  ArrayMem<int,4> indexArray;
+  topology.GetVertexSurfaceElements( vnr, indexArray );
+
+  return indexArray.Size();
+}
+
+
+
+#ifdef SOCKETS
+int Ng_SocketClientOpen( const int port, const char * host )
+{
+  try
+    {
+      if(host)
+	clientsocket.Reset(new ClientSocket(port,host));
+      else
+	clientsocket.Reset(new ClientSocket(port));
+    }
+  catch( SocketException e)
+    {
+      cerr << e.Description() << endl;
+      return 0;
+    }
+  return 1;
+}
+ 
+void Ng_SocketClientWrite( const char * write, char** reply)
+{
+  string output = write;
+  (*clientsocket) << output;
+  string sreply;
+  (*clientsocket) >> sreply;
+  *reply = new char[sreply.size()+1];
+  strcpy(*reply,sreply.c_str());
+}
+
+
+void Ng_SocketClientClose ( void )
+{
+  clientsocket.Reset(NULL);
+}
+
+
+void Ng_SocketClientGetServerHost ( const int number, char ** host )
+{
+  *host = new char[servers[number]->host.size()+1];
+  strcpy(*host,servers[number]->host.c_str());
+}
+
+void Ng_SocketClientGetServerPort ( const int number, int * port )
+{
+  *port = servers[number]->port;
+}
+
+void Ng_SocketClientGetServerClientID ( const int number, int * id )
+{
+  *id = servers[number]->clientid;
+}
+
+#endif // SOCKETS
+
+
+
+
+#ifdef PARALLEL
+void Ng_SetElementPartition ( const int elnr, const int part )
+{
+  mesh->VolumeElement(elnr+1).SetPartition(part);
+
+}
+int Ng_GetElementPartition ( const int elnr )
+{
+  return mesh->VolumeElement(elnr+1).GetPartition();
+}
+#endif
+
+
+void Ng_InitPointCurve(double red, double green, double blue)
+{
+  mesh->InitPointCurve(red, green, blue);
+}
+
+void Ng_AddPointCurvePoint(const double * point)
+{
+  Point3d pt;
+  pt.X() = point[0];
+  pt.Y() = point[1];
+  pt.Z() = point[2];
+  mesh->AddPointCurvePoint(pt);
+}
+
+
+void Ng_SaveMesh ( const char * meshfile )
+{
+  mesh -> Save(string(meshfile));
+}
+
+
+int Ng_Bisect_WithInfo ( const char * refinementfile, double ** qualityloss, int * qualityloss_size )
+{
+  BisectionOptions biopt;
+  biopt.outfilename = NULL; // "ngfepp.vol";
+  biopt.femcode = "fepp";
+  biopt.refinementfilename = refinementfile;
+  
+  Refinement * ref = const_cast<Refinement*> (&ng_geometry -> GetRefinement());
+  MeshOptimize2d * opt = NULL;
+  /*
+    if (stlgeometry)
+    ref = new RefinementSTLGeometry(*stlgeometry);
+    #ifdef OCCGEOMETRY
+    else if (occgeometry)
+    ref = new OCCRefinementSurfaces (*occgeometry);
+    #endif
+    #ifdef ACIS
+    else if (acisgeometry)
+    {
+    ref = new ACISRefinementSurfaces(*acisgeometry);
+    opt = new ACISMeshOptimize2dSurfaces(*acisgeometry);
+    ref->Set2dOptimizer(opt);
+    }
+    #endif
+    else
+    {
+    ref = new RefinementSurfaces(*geometry);
+    opt = new MeshOptimize2dSurfaces(*geometry);
+    ref->Set2dOptimizer(opt);
+    }
+  */
+#ifdef ACIS
+  if (acisgeometry)
+    {
+      // ref = new ACISRefinementSurfaces(*acisgeometry);
+      opt = new ACISMeshOptimize2dSurfaces(*acisgeometry);
+      ref->Set2dOptimizer(opt);
+    }
+  else
+#endif
+    {
+      // ref = new RefinementSurfaces(*geometry);
+      CSGeometry * geometry = dynamic_cast<CSGeometry*> (ng_geometry);
+      if (geometry)
+	{
+	  opt = new MeshOptimize2dSurfaces(*geometry);
+	  ref->Set2dOptimizer(opt);
+	}
+    }
+
+  if(!mesh->LocalHFunctionGenerated())
+    mesh->CalcLocalH(mparam.grading);
+  
+  mesh->LocalHFunction().SetGrading (mparam.grading);
+
+  Array<double> * qualityloss_arr = NULL;
+  if(qualityloss != NULL)
+    qualityloss_arr = new Array<double>;
+
+  ref -> Bisect (*mesh, biopt, qualityloss_arr);
+
+  int retval = 0;
+
+  if(qualityloss != NULL)
+    {
+      *qualityloss = new double[qualityloss_arr->Size()+1];
+
+      for(int i = 0; i<qualityloss_arr->Size(); i++)
+	(*qualityloss)[i+1] = (*qualityloss_arr)[i];
+
+      retval = qualityloss_arr->Size();
+
+      delete qualityloss_arr;
+    }
+
+  mesh -> UpdateTopology();
+  mesh -> GetCurvedElements().BuildCurvedElements (ref, mparam.elementorder);
+  
+  multithread.running = 0;
+  delete ref;
+  delete opt;
+
+  return retval;
+}
+
+void Ng_Bisect ( const char * refinementfile )
+{
+  Ng_Bisect_WithInfo( refinementfile, NULL, NULL );
+}
+
+
+
+
+
+/*
+  number of nodes of type nt
+  nt = 0 is Vertex
+  nt = 1 is Edge
+  nt = 2 is Face
+  nt = 3 is Cell
+*/
+int Ng_GetNNodes (int nt)
+{
+  switch (nt)
+    {
+    case 0: return mesh -> GetNV();
+    case 1: return mesh->GetTopology().GetNEdges();
+    case 2: return mesh->GetTopology().GetNFaces();
+    case 3: return mesh -> GetNE();
+    }
+  return -1;
+}
+
+
+int Ng_GetClosureNodes (int nt, int nodenr, int nodeset, int * nodes)
+{
+  switch (nt)
+    {
+    case 3:  // The closure of a cell
+      {
+        int cnt = 0;
+        if (nodeset & 1)  // Vertices
+          {
+            const Element & el = (*mesh)[ElementIndex(nodenr)];
+            for (int i = 0; i < el.GetNP(); i++)
+              { 
+                nodes[cnt++] = 0;
+                nodes[cnt++] = el[i] - PointIndex::BASE;
+              }
+          }
+
+        if (nodeset & 2)  // Edges
+          {
+            int edges[12];
+            int ned;
+            ned = mesh->GetTopology().GetElementEdges (nodenr+1, edges, 0);
+            for (int i = 0; i < ned; i++)
+              {
+                nodes[cnt++] = 1;
+                nodes[cnt++] = edges[i]-1;
+              }
+          }
+
+        if (nodeset & 4)  // Faces
+          {
+            int faces[12];
+            int nfa;
+            nfa = mesh->GetTopology().GetElementFaces (nodenr+1, faces, 0);
+            for (int i = 0; i < nfa; i++)
+              {
+                nodes[cnt++] = 2;
+                nodes[cnt++] = faces[i]-1;
+              }
+          }
+
+        if (nodeset & 8)  // Cell
+          {
+            nodes[cnt++] = 3;
+            nodes[cnt++] = nodenr;
+          }
+
+        return cnt/2;
+      }
+    default:
+      {
+        cerr << "GetClosureNodes not implemented for Nodetype " << nt << endl;
+      }
+    }
+  return 0;
+}
+
+
+
+int Ng_GetNElements (int dim)
+{
+  switch (dim)
+    {
+    case 0: return mesh -> GetNV();
+    case 1: return mesh -> GetNSeg();
+    case 2: return mesh -> GetNSE();
+    case 3: return mesh -> GetNE();
+    }
+  return -1;
+}
+
+
+
+/*
+  closure nodes of element
+  nodeset is bit-coded, bit 0 includes Vertices, bit 1 edges, etc
+  E.g., nodeset = 6 includes edge and face nodes
+  nodes is pair of integers (nodetype, nodenr) 
+  return value is number of nodes
+*/
+int Ng_GetElementClosureNodes (int dim, int elementnr, int nodeset, int * nodes)
+{
+  switch (dim)
+    {
+    case 3:  // The closure of a volume element = CELL
+      {
+        return Ng_GetClosureNodes (3, elementnr, nodeset, nodes);
+      }
+    case 2:
+      {
+        int cnt = 0;
+        if (nodeset & 1)  // Vertices
+          {
+            const Element2d & el = (*mesh)[SurfaceElementIndex(elementnr)];
+            for (int i = 0; i < el.GetNP(); i++)
+              { 
+                nodes[cnt++] = 0;
+                nodes[cnt++] = el[i] - PointIndex::BASE;
+              }
+          }
+
+        if (nodeset & 2)  // Edges
+          {
+            int edges[12];
+            int ned;
+            ned = mesh->GetTopology().GetSurfaceElementEdges (elementnr+1, edges, 0);
+            for (int i = 0; i < ned; i++)
+              {
+                nodes[cnt++] = 1;
+                nodes[cnt++] = edges[i]-1;
+              }
+          }
+
+        if (nodeset & 4)  // Faces
+          {
+            int face = mesh->GetTopology().GetSurfaceElementFace (elementnr+1);
+            nodes[cnt++] = 2;
+            nodes[cnt++] = face-1;
+          }
+
+        return cnt/2;
+      }
+    default:
+      {
+        cerr << "GetClosureNodes not implemented for Element of dimension " << dim << endl;
+      }
+    }
+  return 0;
+}
diff --git a/contrib/Netgen/libsrc/interface/nginterface_v2.cpp b/contrib/Netgen/libsrc/interface/nginterface_v2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f59d61130ca901d1f8885405e9de8f7e6ef90d5e
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/nginterface_v2.cpp
@@ -0,0 +1,221 @@
+#include <mystdlib.h>
+#include <meshing.hpp>
+
+
+
+#ifdef SOCKETS
+#include "../sockets/sockets.hpp"
+#endif
+
+#ifndef NOTCL
+#include <visual.hpp>
+#endif
+
+
+#include "nginterface.h"
+#include "nginterface_v2.hpp"
+
+
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+  extern AutoPtr<Mesh> mesh;
+}
+
+
+
+namespace netgen
+{
+
+
+
+  template <> int DLL_HEADER Ng_GetNElements<1> ()
+  {
+    return mesh->GetNSeg();
+  }
+
+  template <> DLL_HEADER int Ng_GetNElements<2> ()
+  {
+    return mesh->GetNSE();
+  }
+
+  template <> DLL_HEADER int Ng_GetNElements<3> ()
+  {
+    return mesh->GetNE();
+  }
+
+
+
+
+  template <> DLL_HEADER Ng_Element Ng_GetElement<1> (int nr)
+  {
+    const Segment & el = mesh->LineSegment (SegmentIndex(nr));
+
+    Ng_Element ret;
+    ret.type = NG_ELEMENT_TYPE(el.GetType());
+
+    ret.points.num = el.GetNP();
+    ret.points.ptr = (int*)&(el[0]);
+
+    ret.vertices.num = 2;
+    ret.vertices.ptr = (int*)&(el[0]);
+
+    ret.edges.num = 1;
+    ret.edges.ptr = mesh->GetTopology().GetSegmentElementEdgesPtr (nr);
+
+    ret.faces.num = 0;
+    ret.faces.ptr = NULL;
+
+    return ret;
+  }
+
+  template <> DLL_HEADER Ng_Element Ng_GetElement<2> (int nr)
+  {
+    const Element2d & el = mesh->SurfaceElement (SurfaceElementIndex (nr));
+  
+    Ng_Element ret;
+    ret.type = NG_ELEMENT_TYPE(el.GetType());
+    ret.points.num = el.GetNP();
+    ret.points.ptr  = (int*)&el[0];
+
+    ret.vertices.num = el.GetNV();
+    ret.vertices.ptr = (int*)&(el[0]);
+
+    ret.edges.num = MeshTopology::GetNEdges (el.GetType());
+    ret.edges.ptr = mesh->GetTopology().GetSurfaceElementEdgesPtr (nr);
+
+    ret.faces.num = MeshTopology::GetNFaces (el.GetType());
+    ret.faces.ptr = mesh->GetTopology().GetSurfaceElementFacesPtr (nr);
+
+    return ret;
+  }
+
+  template <> DLL_HEADER Ng_Element Ng_GetElement<3> (int nr)
+  {
+    const Element & el = mesh->VolumeElement (ElementIndex (nr));
+  
+    Ng_Element ret;
+    ret.type = NG_ELEMENT_TYPE(el.GetType());
+    ret.points.num = el.GetNP();
+    ret.points.ptr = (int*)&el[0];
+
+    ret.vertices.num = el.GetNV();
+    ret.vertices.ptr = (int*)&(el[0]);
+
+    ret.edges.num = MeshTopology::GetNEdges (el.GetType());
+    ret.edges.ptr = mesh->GetTopology().GetElementEdgesPtr (nr);
+
+    ret.faces.num = MeshTopology::GetNFaces (el.GetType());
+    ret.faces.ptr = mesh->GetTopology().GetElementFacesPtr (nr);
+
+    return ret;
+  }
+
+
+  DLL_HEADER Ng_Point Ng_GetPoint (int nr)
+  {
+    Ng_Point ret;
+    ret.pt = &mesh->Point(nr + PointIndex::BASE)(0);
+    return ret;
+  }
+
+
+  template <>
+  DLL_HEADER int Ng_GetElementIndex<1> (int nr)
+  {
+    return (*mesh)[SegmentIndex(nr)].si;
+  }
+  
+  template <>
+  DLL_HEADER int Ng_GetElementIndex<2> (int nr)
+  {
+    int ind = (*mesh)[SurfaceElementIndex(nr)].GetIndex(); 
+    return mesh->GetFaceDescriptor(ind).BCProperty();
+  }
+  
+  template <>
+  DLL_HEADER int Ng_GetElementIndex<3> (int nr)
+  {
+    return (*mesh)[ElementIndex(nr)].GetIndex();
+  }
+  
+  
+  template <>
+  DLL_HEADER void Ng_MultiElementTransformation<3,3> (int elnr, int npts,
+                                                      const double * xi, size_t sxi,
+                                                      double * x, size_t sx,
+                                                      double * dxdxi, size_t sdxdxi)
+  {
+    mesh->GetCurvedElements().CalcMultiPointElementTransformation (elnr, npts, xi, sxi, x, sx, dxdxi, sdxdxi);
+  }
+  
+  template <>
+  DLL_HEADER void Ng_MultiElementTransformation<2,2> (int elnr, int npts,
+                                                      const double * xi, size_t sxi,
+                                                      double * x, size_t sx,
+                                                      double * dxdxi, size_t sdxdxi)
+  {
+    mesh->GetCurvedElements().CalcMultiPointSurfaceTransformation<2> (elnr, npts, xi, sxi, x, sx, dxdxi, sdxdxi);
+  }
+
+  template <>
+  DLL_HEADER void Ng_MultiElementTransformation<2,3> (int elnr, int npts,
+                                                      const double * xi, size_t sxi,
+                                                      double * x, size_t sx,
+                                                      double * dxdxi, size_t sdxdxi)
+  {
+    mesh->GetCurvedElements().CalcMultiPointSurfaceTransformation<3> (elnr, npts, xi, sxi, x, sx, dxdxi, sdxdxi);
+  }
+
+  template <>
+  DLL_HEADER void Ng_MultiElementTransformation<1,2> (int elnr, int npts,
+                                                      const double * xi, size_t sxi,
+                                                      double * x, size_t sx,
+                                                      double * dxdxi, size_t sdxdxi)
+  {
+    mesh->GetCurvedElements().CalcMultiPointSegmentTransformation<2> (elnr, npts, xi, sxi, x, sx, dxdxi, sdxdxi);
+  }
+
+  template <>
+  DLL_HEADER void Ng_MultiElementTransformation<1,1> (int elnr, int npts,
+                                                      const double * xi, size_t sxi,
+                                                      double * x, size_t sx,
+                                                      double * dxdxi, size_t sdxdxi)
+  {
+    cout << "1D not supported" << endl;
+  }
+
+
+
+  template <> DLL_HEADER int Ng_GetNNodes<1> ()
+  {
+    return mesh->GetTopology().GetNEdges();
+  }
+
+  template <> DLL_HEADER int Ng_GetNNodes<2> ()
+  {
+    return mesh->GetTopology().GetNFaces();
+  }
+
+  template <> DLL_HEADER Ng_Node<1> Ng_GetNode<1> (int nr)
+  {
+    Ng_Node<1> node;
+    node.vertices.ptr = mesh->GetTopology().GetEdgeVerticesPtr(nr);
+    return node;
+  }
+
+  template <> DLL_HEADER Ng_Node<2> Ng_GetNode<2> (int nr)
+  {
+    Ng_Node<2> node;
+    node.vertices.ptr = mesh->GetTopology().GetFaceVerticesPtr(nr);
+    node.vertices.nv = (node.vertices.ptr[3] == 0) ? 3 : 4;
+    return node;
+  }
+
+}
+
+
+int link_it_nginterface_v2;
+
diff --git a/contrib/Netgen/libsrc/interface/read_fnf_mesh.cpp b/contrib/Netgen/libsrc/interface/read_fnf_mesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d1cff5fc4f5616dd4087c58df20e25a71a66a360
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/read_fnf_mesh.cpp
@@ -0,0 +1,451 @@
+
+//
+//  Read Pro/ENGINEER neutral format
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+#include <sys/stat.h>
+
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+  bool ReadLine (istream & in, string & buf)
+  {
+    do
+      {
+        buf = "";
+        
+        while (in.good())
+          {
+            char ch = in.get();
+            if (ch == '\n') break;
+            if (ch == '\r') break;
+            if (ch == '\\')
+              {
+                // while (iswhite (ch = in.get() )
+                ch = in.get();   // '\n'   CR
+                ch = in.get();   // '\n'   LF
+              }
+            else
+              buf += ch;
+          }
+      }
+    while (in.good() && (buf == "" || buf[0] == '#'));
+    
+    return in.good();
+  }
+  
+
+
+
+  
+  class LoadType
+  {
+  public:
+    int id;
+    string name;
+    string placement;
+    string valuetype;
+    Array<double> places;
+  };
+
+
+
+  
+  void ReadFNFFormat (Mesh & mesh, 
+                      const string & filename)
+  {
+    ifstream fin (filename.c_str());
+
+    string buf;
+
+    mesh.SetDimension (3);
+
+    while (ReadLine (fin, buf))
+      {
+        stringstream sbuf(buf);
+        string start_sect, token; char ch;
+        
+        sbuf >> start_sect;
+
+        if (start_sect == "%START_SECT")
+          {
+            sbuf >> ch >> token;
+            
+            if (token == "HEADER")
+              {
+                while (1)
+                  {
+                    ReadLine (fin, buf);
+                    stringstream sbuf(buf);
+                    string token;
+                    
+                    sbuf >> token;
+                    
+                    if (token == "%TITLE")
+                      {
+                        char ch;
+                        string name;
+                        sbuf >> ch >> name;
+                        cout << "Title: " << name << endl;
+                      }
+                    else if (token == "%STATISTICS")
+                      {
+                        ;
+                      }
+                    else if (token == "%END_SECT")
+                      {
+                        break;
+                      }
+                    else
+                      {
+                        cout << "SECTION HEADER, unknown field: " << buf << endl;
+                      }
+                  }                
+              }
+ 
+
+            else if (token == "ELEM_TYPES")
+              {
+                while (1)
+                  {
+                    ReadLine (fin, buf);
+                    stringstream sbuf(buf);
+                    string token;
+                    
+                    sbuf >> token;
+                    
+                    if (token == "%ELEM_TYPE")
+                      {
+			int nr;
+			string def;
+                        char ch;
+                        sbuf >> nr >> def >> ch;
+			if (def == "DEF")
+			  {
+			    string classname, type;
+			    sbuf >> classname >> type;
+			    if (classname != "SOLID" || type != "TETRA")
+			      cerr << "Element not supported: " << buf << endl;
+			  }
+                      }
+                    else if (token == "%END_SECT")
+                      {
+                        break;
+                      }
+                    else
+                      {
+                        cout << "SECTION ELEM_TYPE, unknown field: " << buf << endl;
+                      }
+                  }                
+              }
+ 
+
+            else if (token == "COORD_SYSTEMS")
+              {
+                while (1)
+                  {
+                    ReadLine (fin, buf);
+                    stringstream sbuf(buf);
+                    string token;
+                    
+                    sbuf >> token;
+                    
+                    if (token == "%END_SECT")
+                      {
+                        break;
+                      }
+                    else
+                      {
+                        // cout << "COORD_SYSTEMS, unknown field: " << buf << endl;
+                      }
+                  }                
+              }
+
+
+ 
+            else if (token == "MATERIALS")
+              {
+		*testout << "parse materials" << endl;
+                Array<double> young_modulus, poisson_ratio, mass_density;
+
+                while (1)
+                  {
+                    ReadLine (fin, buf);
+                    stringstream sbuf(buf);
+                    string token;
+                    
+                    sbuf >> token;
+                    
+                    if (token == "%MATERIAL")
+                      {
+                        int nr;
+                        string prop;
+                        char ch;
+                        double val;
+
+                        sbuf >> nr >> prop >> ch;
+                        if (prop == "DEF")
+                          {
+                            ;
+                          }
+                        else
+                          {
+                            sbuf >> val;
+			    *testout << "prop = " << prop << ", val = " << val << endl;
+                            if (prop == "YOUNG_MODULUS")
+                              young_modulus.Append (val);
+                            else if  (prop == "POISSON_RATIO")
+                              poisson_ratio.Append (val);
+                            else if  (prop == "MASS_DENSITY")
+                              mass_density.Append (val);
+                          }
+                      }
+                    else if (token == "%END_SECT")
+                      {
+                        mesh.SetUserData ("YOUNG_MODULUS", young_modulus);
+                        mesh.SetUserData ("POISSON_RATIO", poisson_ratio);
+                        mesh.SetUserData ("MASS_DENSITY", mass_density);
+			*testout << "young = " << young_modulus << endl;
+			*testout << "poisson = " << poisson_ratio << endl;
+                        break;
+                      }
+                    else
+                      {
+                        cout << "SECTION MATERIALS, unknown field: " << buf << endl;
+                      }
+                  }
+              }
+
+            
+            else if (token == "MESH")
+              {
+                while (1)
+                  {
+                    ReadLine (fin, buf);
+                    stringstream sbuf(buf);
+                    string token;
+                    sbuf >> token;
+                    if (token == "%NODE")
+                      {
+                        string st;
+                        char ch;
+                        int nr, ks_id;
+                        double x,y,z;
+                        sbuf >> nr >> st >> ch >> x >> y >> z >> ks_id;
+                        mesh.AddPoint (Point3d (x,y,z) );
+                      }
+                    else if (token == "%ELEM")
+                      {
+                        string elemid, def;
+                        char ch;
+                        int elnr, typid, matid;
+                        string propid;
+                        sbuf >> elnr >> def >> ch;
+                        sbuf >> typid >> matid >> propid;
+                        Array<int> pnums;
+                        while (1)
+                          {
+                            int pn;
+                            sbuf >> pn;
+                            if (!sbuf.good()) break;
+                            pnums.Append (pn);
+                          }
+                        int pe2ng [] = { 0, 1, 2, 3, 4, 7, 5, 6,  8, 9 };
+                        Element el(pnums.Size());
+                        for (int j = 0; j < pnums.Size(); j++)
+                          el[pe2ng[j]] = pnums[j];
+                        el.SetIndex (matid);
+                        mesh.AddVolumeElement (el);
+                      }
+                    else if (token == "%END_SECT")
+                      {
+                        break;
+                      }
+                    else
+                      {
+                        cout << "SECTION MESH, unknown: " << buf << endl;
+                      }
+                  }
+              }
+            else if (token == "MESH_TOPOLOGY")
+              {
+                while (1)
+                  {
+                    ReadLine (fin, buf);
+                    stringstream sbuf(buf);
+                    string token, kw;
+                    int nr;
+                    char ch;
+
+                    sbuf >> token;
+                    if (token == "%EDGE")
+                      {
+                        sbuf >> nr >> kw >> ch;
+                        if (kw == "NODES")
+                          {
+                            Array<int> enums;
+                            while (1)
+                              {
+                                int en;
+                                sbuf >> en;
+                                if (!sbuf.good()) break;
+                                enums.Append (en);
+                              }
+                            for (int j = 0; j+2 < enums.Size(); j+=2)
+                              {
+                                Segment seg;
+                                seg[0] = enums[j];
+                                seg[1] = enums[j+2];
+                                seg[2] = enums[j+1];
+                                seg.edgenr = nr;
+                                mesh.AddSegment (seg);
+                              }
+                          }
+                      }
+                    else if (token == "%SURFACE")
+                      {
+                        sbuf >> nr >> kw >> ch;
+                        if (kw == "FACES")
+                          {
+                            Array<int> fnums;
+                            while (1)
+                              {
+                                int fn;
+                                sbuf >> fn;
+                                if (!sbuf.good()) break;
+                                fnums.Append (fn);
+                              }                            
+
+                            FaceDescriptor fd(-1, -1, -1, -1);
+                            fd.SetBCProperty (nr);
+			    *testout << "add fd " << mesh.GetNFD() << ", nr = " << nr << endl;
+                            mesh.AddFaceDescriptor (fd);
+                              
+                            for (int j = 0; j < fnums.Size(); j += 2)
+                              {
+                                int elnr = fnums[j];
+                                int fnr = fnums[j+1];
+                                
+                                const Element & el = mesh.VolumeElement (elnr);
+                                Element2d el2d;
+                                el.GetFace (fnr, el2d);
+                                el2d.SetIndex (nr);
+                                  
+                                mesh.AddSurfaceElement (el2d);
+                              }
+                          }
+                      }
+                    else if (token == "%END_SECT")
+                      {
+                        break;
+                      }
+                    else
+                      {
+                        cout << "SECTION MESH, unknown: " << buf << endl;
+                      }
+                  }
+              }
+
+
+
+ 
+            else if (token == "LOADS")
+              {
+                Array<LoadType*> loadtypes;
+
+                while (1)
+                  {
+                    ReadLine (fin, buf);
+                    stringstream sbuf(buf);
+                    string token;
+                    
+                    sbuf >> token;
+                    
+                    if (token == "%LOAD_TYPE")
+                      {
+                        string def;
+                        char ch;
+
+                        LoadType * lt = new LoadType;
+                        sbuf >> lt->id >> def >> ch >> lt->name >> lt->placement >> lt->valuetype;
+                        
+                        if (lt->name == "DISPLACEMENT")
+                          cout << "loadtype DISPLACEMENT found" << endl;
+
+                        if (lt->placement != "FACE" && lt->placement != "EDGE" && lt->placement != "NODE")
+                          cout << "unsupported placement " << lt->placement << endl;
+
+                        loadtypes.Append (lt);
+                      }
+
+                    else if (token == "%LOAD")
+                      {
+                        int id;
+                        string def;
+                        char ch;
+                        int placement;
+                        int load_type_id, con_case_id;
+                        sbuf >> id >> def >> ch;
+                        
+                        if (def == "DEF")
+                          {
+                            sbuf >> load_type_id >> con_case_id;
+                          }
+                        if (def == "VAL")
+                          {
+                            sbuf >> placement;
+                            for (int i = 0; i < loadtypes.Size(); i++)
+                              if (load_type_id == loadtypes[i]->id)
+                                loadtypes[i]->places.Append (placement);
+                          }
+                      }                    
+                    
+                    else if (token == "%END_SECT")
+                      {
+                        for (int i = 0; i < loadtypes.Size(); i++)
+                          {
+                            if (loadtypes[i]->placement == "FACE" && loadtypes[i]->name == "DISPLACEMENT")
+                              {
+                                mesh.SetUserData ("CONSTRAINT_DISP_FACE", loadtypes[i]->places);
+                                cout << "constrained faces: " << loadtypes[i]->places << endl;
+                              }
+                            if (loadtypes[i]->placement == "EDGE" && loadtypes[i]->name == "DISPLACEMENT")
+                              {
+                                mesh.SetUserData ("CONSTRAINT_DISP_EDGE", loadtypes[i]->places);
+                                cout << "constrained edges: " << loadtypes[i]->places << endl;
+                              }
+                            if (loadtypes[i]->placement == "NODE" && loadtypes[i]->name == "DISPLACEMENT")
+                              {
+                                mesh.SetUserData ("CONSTRAINT_DISP_NODE", loadtypes[i]->places);
+                                cout << "constrained nodes: " << loadtypes[i]->places << endl;
+                              }
+                          }
+                        break;
+                      }
+                    else
+                      {
+                        cout << "SECTION LOADS, unknown field: " << buf << endl;
+                      }
+                  }
+              }
+
+
+
+            else
+              {
+                cout << "unknown section " << token << endl;
+              }
+          }
+        else
+          cout << "parse line: (" << buf << ")" << endl;
+      }
+  }
+}
diff --git a/contrib/Netgen/libsrc/interface/readtetmesh.cpp b/contrib/Netgen/libsrc/interface/readtetmesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1258068b9d05462709f2ad5192a894dc7b279532
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/readtetmesh.cpp
@@ -0,0 +1,797 @@
+
+//
+//  Read CST file format
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+#include <sys/stat.h>
+
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+
+
+
+  void ReadTETFormat (Mesh & mesh, 
+                      const string & hfilename)
+  {
+    const char * filename = hfilename.c_str();
+
+    cout << "Reading .tet mesh" << endl;
+
+    ifstream in (filename);
+
+    int inputsection = 0;
+    bool done = false;
+
+    char ch;
+    string str;
+
+    string version;
+
+    int unitcode;
+    double tolerance;
+    double dS1, dS2, alphaDeg, x3D, y3D, z3D;
+    int nelts,nfaces,nedges,nnodes;
+    int nperiodicmasternodes,ncornerperiodicmasternodes,ncubicperiodicmasternodes;
+    int nperiodicmasteredges,ncornerperiodicmasteredges;
+    int nperiodicmasterfaces;
+    int nodeid,type,pid;
+    int dummyint;
+    int modelverts,modeledges,modelfaces,modelcells;
+    Point3d p;
+    int numObj3D,numObj2D,numObj1D,numObj0D;
+    bool nullstarted;
+    Array<int> eldom;
+    int minId3D = -1, minId2D = -1;
+    int maxId3D(-1), maxId2D(-1), maxId1D(-1), maxId0D(-1);
+    Array<Array<int> *> segmentdata;
+    Array<Element2d* > tris;
+
+    Array<int> userdata_int;  // just save data for 1:1 output
+    Array<double> userdata_double;
+    Array<int> point_pids;
+    Array<int> tetfacedata;
+    Array<int> uid_to_group_3D, uid_to_group_2D, uid_to_group_1D, uid_to_group_0D;
+
+    while(!done)
+      {
+        // skip "//" comment
+        bool comment = true;
+        while(comment)
+          {
+            ch = in.get();
+            while(ch == ' ' || ch == '\n' || ch == '\t' || ch =='\r')
+              ch = in.get();
+	      
+            if(ch != '/')
+              {
+                comment = false;
+                in.putback(ch);
+              }
+            else
+              {
+                ch = in.get();
+                if(ch != '/')
+                  {
+                    comment = false;
+                    in.putback(ch);
+                    in.putback('/');
+                  }
+                else
+                  {
+                    in.ignore(10000,'\n');
+                  }
+              }
+          }
+
+	  
+        switch(inputsection)
+          {
+          case 0:
+            // version number
+            in >> version;
+            cout << "Version number " << version << endl;
+            if(version != "1.1" && version != "2" && version != "2.0")
+              {
+                cerr << "WARNING: import only tested for versions 1.1 and 2" << endl;
+                //done = true;
+              }
+            userdata_double.Append(atof(version.c_str()));
+            break;
+
+          case 1:
+            // unit code (1=CM 2=MM 3=M 4=MIC 5=NM 6=FT 7=IN 8=MIL)
+            in >> unitcode;
+            cout << "unit code " << unitcode << endl;
+            userdata_int.Append(unitcode);
+            break;
+
+          case 2:
+            // Geometric coord "zero" tolerance threshold
+            in >> tolerance;
+            cout << "tolerance " << tolerance << endl;
+            userdata_double.Append(tolerance);
+            break;
+
+          case 3:
+            // Periodic UnitCell dS1 , dS2 , alphaDeg
+            in >> dS1 >> dS2 >> alphaDeg;
+            userdata_double.Append(dS1);
+            userdata_double.Append(dS2);
+            userdata_double.Append(alphaDeg);
+            break;
+
+          case 4:
+            // Periodic UnitCell origin in global coords (x3D,y3D,z3D)
+            in >> x3D >> y3D >> z3D;
+            userdata_double.Append(x3D);
+            userdata_double.Append(y3D);
+            userdata_double.Append(z3D);
+            break;
+
+          case 5:
+            // Model entity count: Vertices, Edges, Faces, Cells (Version 2)
+            in >> modelverts >> modeledges >> modelfaces >> modelcells;
+            userdata_int.Append(modelverts);
+            userdata_int.Append(modeledges);
+            userdata_int.Append(modelfaces);
+            userdata_int.Append(modelcells);
+            break;
+
+          case 6:
+            // Topological mesh-entity counts (#elements,#faces,#edges,#nodes)
+            in >> nelts >> nfaces >> nedges >> nnodes;
+            cout << nelts << " elements, " << nfaces << " faces, " << nedges << " edges, " << nnodes << " nodes" << endl;
+            mesh.SetAllocSize(nnodes,2*nedges,nfaces,nelts);
+            break;
+
+          case 7:
+            // NodeID, X, Y, Z, Type (0=Reg 1=PMaster 2=PSlave 3=CPMaster 4=CPSlave), PID:
+            {
+              cout << "read nodes" << endl;
+              for(int i=0; i<nnodes; i++)
+                {
+                  in >> nodeid >> p.X() >> p.Y() >> p.Z() >> type >> pid;
+                  mesh.AddPoint(p);		  
+                  point_pids.Append(pid);
+                  if(pid > maxId0D)
+                    maxId0D = pid;
+                  //(*testout) << "point " << p << " type " << type << " mastersexist " << mastersexist << endl;
+                }
+            }
+            break;
+
+          case 8:
+            // Number of Periodic Master Nodes
+            in >> nperiodicmasternodes;
+            break;
+
+          case 9:
+            // MasterNodeID, SlaveNodeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2)
+            for(int i=0; i<nperiodicmasternodes; i++)
+              {
+                for(int j=0; j<2; j++)
+                  in >> dummyint;
+
+                in >> dummyint;
+              }
+            break;
+
+          case 10:
+            // Number of Corner Periodic Master Nodes
+            in >> ncornerperiodicmasternodes;
+            break;
+
+          case 11:
+            // MasterNodeID, 3-SlaveNodeID's, 3-TranslCodes (1=dS1 2=dS2 3=dS1+dS2)
+            for(int i=0; i<ncornerperiodicmasternodes; i++)
+              {
+                for(int j=0; j<4; j++)
+                  in >> dummyint;
+
+                for(int j=0; j<3; j++)
+                  in >> dummyint;
+              }
+            break;
+
+          case 12:
+            // Number of Cubic Periodic Master Nodes
+            in >> ncubicperiodicmasternodes;
+            break;
+
+          case 13:
+            //MasterNodeID, 7-SlaveNodeID's, TranslCodes
+            for(int i=0; i<ncubicperiodicmasternodes; i++)
+              {
+                for(int j=0; j<8; j++)
+                  in >> dummyint;
+
+                for(int j=0; j<7; j++)
+                  in >> dummyint;
+              }
+            break;
+
+          case 14:
+            // EdgeID, NodeID0, NodeID1, Type (0=Reg 1=PMaster 2=PSlave 3=CPMaster 4=CPSlave), PID
+            cout << "read edges" << endl;
+            nullstarted = false;
+            segmentdata.SetSize(nedges);
+            for(int i=0; i<nedges; i++)
+              {
+                segmentdata[i] = new Array<int>(7);
+                *segmentdata[i] = -1;
+                in >> dummyint;
+                in >> (*segmentdata[i])[0] >> (*segmentdata[i])[1];
+                in >> type;
+                in >> (*segmentdata[i])[2];
+                if((*segmentdata[i])[2] > maxId1D)
+                  maxId1D = (*segmentdata[i])[2];
+              }
+            break;
+
+          case 15:
+            // Number of Periodic Master Edges
+            in >> nperiodicmasteredges;
+            break;
+
+          case 16:
+            // MasterEdgeID, SlaveEdgeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2)
+            for(int i=0; i<nperiodicmasteredges; i++)
+              in >> dummyint >> dummyint >> dummyint;
+            break;
+
+          case 17:
+            // Number of Corner Periodic Master Edges
+            in >> ncornerperiodicmasteredges;
+            break;
+
+          case 18:
+            // MasterEdgeID, 3 SlaveEdgeID's, 3 TranslCode (1=dS1 2=dS2 3=dS1+dS2)
+            for(int i=0; i<ncornerperiodicmasteredges; i++)
+              {
+                in >> dummyint;
+                for(int j=0; j<3; j++)
+                  in >> dummyint;
+                for(int j=0; j<3; j++)
+                  in >> dummyint;
+              }
+            break;
+
+          case 19:
+            // FaceID, EdgeID0, EdgeID1, EdgeID2, FaceType (0=Reg 1=PMaster 2=PSlave), PID
+            {
+              //Segment seg;
+              int segnum_ng[3];
+              bool neg[3];
+              cout << "read faces" << endl;
+              nullstarted = false;
+              for(int i=0; i<nfaces; i++)
+                {
+                  int trinum;
+                  int segnum;
+		    
+                  tris.Append(new Element2d(TRIG));
+
+                  in >> trinum;
+                  for(int j=0; j<3; j++)
+                    {
+                      in >> segnum;
+                      neg[j] = (segnum<0);
+                      if(!neg[j])
+                        segnum_ng[j] = segnum-1;
+                      else
+                        segnum_ng[j] = -segnum-1;
+			
+                      if(neg[j])
+                        tris.Last()->PNum(j+1) = (*segmentdata[segnum_ng[j]])[1];
+                      else
+                        tris.Last()->PNum(j+1) = (*segmentdata[segnum_ng[j]])[0];
+
+                      tris.Last()->GeomInfoPi(j+1).trignum = trinum;
+                    }
+                  in >> type;
+                  int faceid;
+                  in >> faceid;
+		    
+                  if(faceid > maxId2D)
+                    maxId2D = faceid;
+
+                  if(i==0 || faceid < minId2D)
+                    minId2D = faceid;
+		    
+                  tris.Last()->SetIndex(faceid);
+
+                  if(faceid > 0)
+                    {
+                      //if(nullstarted)
+                      //  {
+                      //    cout << "Faces: Assumption about index 0 wrong (face"<<trinum <<")" << endl;
+                      //  }
+                      //mesh.AddSurfaceElement(tri);
+			
+                      for(int j=0; j<3; j++)
+                        {
+                          if(neg[j])
+                            {
+                              (*segmentdata[segnum_ng[j]])[4] = faceid;
+                              (*segmentdata[segnum_ng[j]])[6] = trinum;
+                            }
+                          else
+                            {
+                              (*segmentdata[segnum_ng[j]])[3] = faceid;
+                              (*segmentdata[segnum_ng[j]])[5] = trinum;
+                            }
+                        }
+                    }
+                  else
+                    nullstarted = true;
+                }
+            }
+            break;
+
+          case 20:
+            // Number of Periodic Master Faces
+            in >> nperiodicmasterfaces;
+            break;
+
+          case 21:
+            // MasterFaceID, SlaveFaceID, TranslCode (1=dS1 2=dS2)
+            {
+              Vec<3> randomvec(-1.32834,3.82399,0.5429151);
+              int maxtransl = -1;
+              for(int i=0; i<nperiodicmasterfaces; i++)
+                {
+                  int tri1,tri2,transl;
+                  Array<PointIndex> nodes1(3),nodes2(3);
+                  Array<double> sortval1(3),sortval2(3);
+                  in >> tri1 >> tri2 >> transl;
+
+                  if(transl > maxtransl)
+                    maxtransl = transl;
+		    
+		    
+                  for(int j=0; j<3; j++)
+                    {
+                      nodes1[j] = tris[tri1-1]->PNum(j+1);
+                      sortval1[j] = Vec<3>(mesh[nodes1[j]])*randomvec;
+                      nodes2[j] = tris[tri2-1]->PNum(j+1);
+                      sortval2[j] = Vec<3>(mesh[nodes2[j]])*randomvec;
+                    }
+
+                  BubbleSort(sortval1,nodes1);
+                  BubbleSort(sortval2,nodes2);
+
+                  for(int j=0; j<3; j++)
+                    mesh.GetIdentifications().Add(nodes1[j],nodes2[j],transl);
+			
+                }
+              for(int i=1; i<= maxtransl; i++)
+                mesh.GetIdentifications().SetType(i,Identifications::PERIODIC);
+            }	      
+            break;
+
+          case 22:
+            // ElemID, FaceID0, FaceID1, FaceID2, FaceID3, PID
+            {
+              cout << "read elements (1)" << endl;
+
+              //SurfaceElementIndex surf[4];
+              bool neg[4];
+              int elemid;
+              int domain;
+		
+              eldom.SetSize(nelts);
+
+              for(int i=0; i<nelts; i++)
+                {
+                  if(int(100.*i/nelts) % 5 == 0)
+                    cout << int(100.*i/nelts)
+#ifdef WIN32
+                         << "%%\r"
+#else
+                         << "\%\r"
+#endif 
+                         << flush;
+                  in >> elemid;
+                  for(int j=0; j<4;j++)
+                    {
+                      in >> dummyint;
+                      neg[j] = (dummyint < 0);
+                      if(neg[j])
+                        tetfacedata.Append(-dummyint-1);
+                      //surf[j] = -dummyint-1;
+                      else
+                        tetfacedata.Append(dummyint-1);
+                      tetfacedata.Append(((neg[j]) ? 1 : 0));
+                      //surf[j] = dummyint-1;
+                    }
+		    
+                  in >> domain;
+                  eldom[i] = domain;
+                  tetfacedata.Append(domain);
+
+                  if(i==0 || domain < minId3D)
+                    minId3D = domain;
+
+                  if(domain > maxId3D)
+                    maxId3D = domain;
+		    
+                  // 		    for(int j=0; j<4; j++)
+                  // 		      {
+                  // 			if(mesh.GetNSE() <= surf[j])
+                  // 			  continue;
+
+                  // 			int faceind = 0;
+                  // 			for(int k=1; k<=mesh.GetNFD(); k++)
+                  // 			  {
+                  // 			    if(mesh.GetFaceDescriptor(k).SurfNr() == mesh[surf[j]].GetIndex())
+                  // 			      faceind = k;
+                  // 			  }
+                  // 			if(faceind)
+                  // 			  {
+                  // 			    if(neg[j])
+                  // 			      mesh.GetFaceDescriptor(faceind).SetDomainOut(domain);
+                  // 			    else
+                  // 			      mesh.GetFaceDescriptor(faceind).SetDomainIn(domain);
+                  // 			  }
+                  // 			else
+                  // 			  {
+                  // 			    if(neg[j])
+                  // 			      faceind = mesh.AddFaceDescriptor(FaceDescriptor(mesh[surf[j]].GetIndex(),0,domain,0));
+                  // 			    else
+                  // 			      faceind = mesh.AddFaceDescriptor(FaceDescriptor(mesh[surf[j]].GetIndex(),domain,0,0));
+                  // 			    mesh.GetFaceDescriptor(faceind).SetBCProperty(mesh[surf[j]].GetIndex());
+                  // 			  }
+                  // 		      }
+                }
+              cout << endl;
+		
+		
+              // 		Array<int> indextodescriptor(maxId2D+1);
+		
+              // 		for(int i=1; i<=mesh.GetNFD(); i++)
+              // 		  indextodescriptor[mesh.GetFaceDescriptor(i).SurfNr()] = i;
+		
+		
+              // 		for(SurfaceElementIndex i=0; i<mesh.GetNSE(); i++)
+              // 		  mesh[i].SetIndex(indextodescriptor[mesh[i].GetIndex()]);
+            }
+            break;
+
+          case 23:
+            // ElemID, NodeID0, NodeID1, NodeID2, NodeID3
+            { 
+              cout << "read elements (2)" << endl;
+              Element el(TET);
+              for(ElementIndex i=0; i<nelts; i++)
+                {
+                  in >> dummyint;
+                  for(int j=1; j<=4; j++)
+                    in >> el.PNum(j);
+                  swap(el.PNum(1),el.PNum(2));
+		    
+                  el.SetIndex(eldom[i]);
+                  mesh.AddVolumeElement(el);
+                }	
+            }	  
+            break;
+	      
+          case 24:
+            // Physical Object counts (#Obj3D,#Obj2D,#Obj1D,#Obj0D)
+            {
+              in >> numObj3D;
+              userdata_int.Append(numObj3D);
+              in >> numObj2D;
+              userdata_int.Append(numObj2D);
+              in >> numObj1D;
+              userdata_int.Append(numObj1D);
+              in >> numObj0D;
+              userdata_int.Append(numObj0D);
+            }
+            break;
+
+          case 25:
+            // Number of Ports (Ports are a subset of Object2D list)
+            {
+              in >> dummyint;
+              //userdata_int.Append(dummyint);
+            }
+            break;
+
+          case 26:
+            // Object3D GroupID, #Elems <immediately followed by> ElemID List
+            {
+              uid_to_group_3D.SetSize(maxId3D+1);
+              uid_to_group_3D = -1;
+              for(int i=0; i<numObj3D; i++)
+                {
+                  int groupid;
+                  in >> groupid;
+                  (*testout) << "3d groupid " << groupid << endl;
+                  //userdata_int.Append(groupid);
+                  int nelems;
+                  in >> nelems;
+                  //userdata_int.Append(nelems);
+                  for(int j=0; j<nelems; j++)
+                    {
+                      in >> dummyint;
+			
+                      (*testout) << "read " << dummyint << endl;
+                      //userdata_int.Append(dummyint);
+			
+                      if(dummyint < 0) 
+                        dummyint *= -1;
+                      uid_to_group_3D[eldom[dummyint-1]] = groupid;
+                    }
+                }
+            }
+            break;
+
+          case 27:
+            // Object2D GroupID, #Faces <immediately followed by> FaceID List
+            {
+              Array<int> ports;
+              //int totnum = 0;
+              uid_to_group_2D.SetSize(maxId2D+1);
+              uid_to_group_2D = -1;
+
+              for(int i=0; i<numObj2D; i++)
+                {
+                  int groupid;
+                  in >> groupid;
+                  (*testout) << "2d groupid " << groupid << endl;
+                  //userdata_int.Append(groupid);
+                  int nelems;
+                  in >> nelems;
+                  //userdata_int.Append(nelems);
+                  for(int j=0; j<nelems; j++)
+                    {
+                      in >> dummyint;
+                      char port;
+                      while((port = in.get()) == ' ')
+                        ;
+
+                      (*testout) << "read " << dummyint << endl;
+                      if(dummyint < 0) 
+                        dummyint *= -1;
+                      int uid = tris[dummyint-1]->GetIndex();
+
+                      if(port == 'P' || port == 'p')
+                        {
+                          if(!ports.Contains(uid))
+                            ports.Append(uid);
+                        }
+                      else
+                        in.putback(port);
+			
+                      //userdata_int.Append(dummyint);
+			
+                      uid_to_group_2D[uid] = groupid;
+                      (*testout) << "setting " << uid << endl;
+
+                      //totnum++;
+                    }
+                }
+              mesh.SetUserData("TETmesh:ports",ports);
+            }
+            break;
+
+          case 28:
+            // Object1D GroupID, #Edges <immediately followed by> EdgeID List
+            {
+              uid_to_group_1D.SetSize(maxId1D+1);
+              uid_to_group_1D = -1;
+
+              for(int i=0; i<numObj1D; i++)
+                {
+                  int groupid;
+                  in >> groupid;
+                  //userdata_int.Append(groupid);
+                  int nelems;
+                  in >> nelems;
+                  //userdata_int.Append(nelems);
+                  for(int j=0; j<nelems; j++)
+                    {
+                      in >> dummyint;
+                      //userdata_int.Append(dummyint);
+
+                      if(dummyint < 0) 
+                        dummyint *= -1;
+                      uid_to_group_1D[(*segmentdata[dummyint-1])[2]] = groupid;
+                    }
+                }
+            }
+            break;
+
+          case 29:
+            // Object0D GroupID, #Nodes <immediately followed by> NodeID List
+            {
+              uid_to_group_0D.SetSize(maxId0D+1);
+              uid_to_group_0D = -1;
+              for(int i=0; i<numObj0D; i++)
+                {
+                  int groupid;
+                  in >> groupid;
+                  //userdata_int.Append(groupid);
+                  int nelems;
+                  in >> nelems;
+                  //userdata_int.Append(nelems);
+                  for(int j=0; j<nelems; j++)
+                    {
+                      in >> dummyint;
+                      //userdata_int.Append(dummyint);
+
+                      if(dummyint < 0) 
+                        dummyint *= -1;
+                      uid_to_group_0D[point_pids[dummyint-1]] = groupid;
+                    }
+                }
+            }
+            break;
+
+
+
+          default:
+            done = true;
+	      
+          }
+	  
+        if(inputsection == 4 && version == "1.1")
+          inputsection++;
+
+        inputsection++;
+      }
+    in.close();
+
+
+    mesh.SetUserData("TETmesh:double",userdata_double);
+    userdata_int.Append(minId2D);
+    userdata_int.Append(minId3D);
+    mesh.SetUserData("TETmesh:int",userdata_int);   
+    //if(version == "1.1")
+    mesh.SetUserData("TETmesh:point_id",point_pids);
+
+    mesh.SetUserData("TETmesh:uid_to_group_3D",uid_to_group_3D);
+    mesh.SetUserData("TETmesh:uid_to_group_2D",uid_to_group_2D);
+    mesh.SetUserData("TETmesh:uid_to_group_1D",uid_to_group_1D);
+    mesh.SetUserData("TETmesh:uid_to_group_0D",uid_to_group_0D);
+
+
+    Array<SurfaceElementIndex> surfindices(tris.Size());
+    surfindices = -1;
+
+    for(int i=0; i<tris.Size(); i++)
+      {
+        if(atof(version.c_str()) <= 1.999999)
+          {
+            if(tris[i]->GetIndex() > 0)
+              surfindices[i] = mesh.AddSurfaceElement(*tris[i]);
+          }
+        else
+          {
+            if(tris[i]->GetIndex() > 0 &&
+               tris[i]->GetIndex() < minId3D)
+              {
+                tris[i]->SetIndex(tris[i]->GetIndex()-minId2D+1);
+                surfindices[i] = mesh.AddSurfaceElement(*tris[i]);
+              }
+          }
+        delete tris[i];
+      }
+
+      
+    mesh.ClearFaceDescriptors();
+    if(atof(version.c_str()) <= 1.999999)
+      for(int i = 1; i <= maxId2D; i++)
+        mesh.AddFaceDescriptor(FaceDescriptor(i,0,0,0));
+    else
+      for(int i=minId2D; i<minId3D; i++)
+        mesh.AddFaceDescriptor(FaceDescriptor(i,0,0,0));
+	
+
+    for(int i=0; i<tetfacedata.Size(); i+=9)
+      {
+        for(int j=0; j<4; j++)
+          {
+            SurfaceElementIndex surf = surfindices[tetfacedata[i+2*j]];
+	      
+            //if(mesh.GetNSE() <= surf)
+            if(surf == -1)
+              continue;
+
+            if(tetfacedata[i+2*j+1] == 1)
+              mesh.GetFaceDescriptor(mesh[surf].GetIndex()).SetDomainOut(tetfacedata[i+8]);
+            else
+              mesh.GetFaceDescriptor(mesh[surf].GetIndex()).SetDomainIn(tetfacedata[i+8]);
+			
+
+            /*
+	      int faceind = 0;
+	      for(int k=1; k<=mesh.GetNFD(); k++)
+              {
+              if(mesh.GetFaceDescriptor(k).SurfNr() == mesh[surf].GetIndex())
+              faceind = k;
+              }
+	      if(faceind)
+              {
+              if(tetfacedata[i+4+j] == 1)
+              mesh.GetFaceDescriptor(faceind).SetDomainOut(tetfacedata[i+8]);
+              else
+              mesh.GetFaceDescriptor(faceind).SetDomainIn(tetfacedata[i+8]);
+              }
+	      else
+              {
+              if(tetfacedata[i+4+j] == 1)
+              faceind = mesh.AddFaceDescriptor(FaceDescriptor(mesh[surf].GetIndex(),0,tetfacedata[i+8],0));
+              else
+              faceind = mesh.AddFaceDescriptor(FaceDescriptor(mesh[surf].GetIndex(),tetfacedata[i+8],0,0));
+              mesh.GetFaceDescriptor(faceind).SetBCProperty(mesh[surf].GetIndex());
+              }
+            */
+          }
+
+      }
+      
+    //       Array<int> indextodescriptor(maxId2D+1);
+		
+    //       for(int i=1; i<=mesh.GetNFD(); i++)
+    // 	indextodescriptor[mesh.GetFaceDescriptor(i).SurfNr()] = i;
+		
+		
+    //       for(SurfaceElementIndex i=0; i<mesh.GetNSE(); i++)
+    // 	mesh[i].SetIndex(indextodescriptor[mesh[i].GetIndex()]);
+
+
+    for(int i=0; i<segmentdata.Size(); i++)
+      {
+        Segment seg;
+
+	  
+        if((atof(version.c_str()) <= 1.999999 && (*segmentdata[i])[2] > 0) ||
+           (atof(version.c_str()) > 1.999999  && (*segmentdata[i])[2] > 0 && (*segmentdata[i])[2] < minId2D))
+          {
+            seg[0] = (*segmentdata[i])[0];
+            seg[1] = (*segmentdata[i])[1];
+            seg.edgenr = (*segmentdata[i])[2];
+            seg.epgeominfo[0].edgenr = (*segmentdata[i])[2];
+            seg.epgeominfo[1].edgenr = (*segmentdata[i])[2];
+            seg.si = (*segmentdata[i])[3]-minId2D+1;
+            seg.surfnr1 = -1;//(*segmentdata[i])[3];
+            seg.surfnr2 = -1;//(*segmentdata[i])[4];
+            seg.geominfo[0].trignum = (*segmentdata[i])[5];
+            seg.geominfo[1].trignum = (*segmentdata[i])[5];
+            mesh.AddSegment(seg);
+
+            seg[0] = (*segmentdata[i])[1];
+            seg[1] = (*segmentdata[i])[0];
+            seg.si = (*segmentdata[i])[4]-minId2D+1;
+            seg.surfnr1 = -1;//(*segmentdata[i])[3];
+            seg.surfnr2 = -1;//(*segmentdata[i])[4];
+            seg.geominfo[0].trignum = (*segmentdata[i])[6];
+            seg.geominfo[1].trignum = (*segmentdata[i])[6];
+            mesh.AddSegment(seg);
+          }
+        delete segmentdata[i];
+      }
+
+    /*
+      for(int i=mesh.GetNSeg(); i>=1; i--)
+      if(mesh.LineSegment(i).epgeominfo[0].edgenr == 0 ||
+      mesh.LineSegment(i).epgeominfo[1].edgenr == 0)
+      mesh.FullDeleteSegment(i);
+    */	
+  
+    mesh.CalcSurfacesOfNode();
+      
+  }
+}
+
+
diff --git a/contrib/Netgen/libsrc/interface/readuser.cpp b/contrib/Netgen/libsrc/interface/readuser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f21466af8df5d60200a62718038927777415b3fa
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/readuser.cpp
@@ -0,0 +1,422 @@
+//
+//  Read user dependent output file
+//
+
+
+#include <mystdlib.h>
+
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+  void ReadFile (Mesh & mesh,
+                 const string & hfilename)
+  {
+    cout << "Read User File" << endl;
+
+    const char * filename = hfilename.c_str();
+
+    char reco[100];
+    int np, nbe;
+
+
+
+    // ".surf" - mesh
+  
+    if ( (strlen (filename) > 5) &&
+         strcmp (&filename[strlen (filename)-5], ".surf") == 0 )
+    
+      {
+        cout << "Surface file" << endl;
+      
+        ifstream in (filename);
+      
+        in >> reco;
+        in >> np;
+        for (int i = 1; i <= np; i++)
+          {
+            Point3d p;
+            in >> p.X() >> p.Y() >> p.Z();
+	    p.Z() *= 10;
+            mesh.AddPoint (p);
+          }
+
+        mesh.ClearFaceDescriptors();
+        mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0));
+      
+        in >> nbe;
+        //      int invert = globflags.GetDefineFlag ("invertsurfacemesh");
+        for (int i = 1; i <= nbe; i++)
+          {
+            Element2d el;
+            el.SetIndex(1);
+
+            for (int j = 1; j <= 3; j++)
+              {
+                in >> el.PNum(j);
+                // el.PNum(j)++;
+                if (el.PNum(j) < PointIndex(1) || 
+                    el.PNum(j) > PointIndex(np))
+                  {
+                    cerr << "Point Number " << el.PNum(j) << " out of range 1..."
+                         << np << endl;
+                    return;
+                  }
+              }
+            /*
+              if (invert)
+              swap (el.PNum(2), el.PNum(3));
+            */
+	  
+            mesh.AddSurfaceElement (el);
+          }
+      
+      
+        cout << "points: " << np << " faces: " << nbe << endl;
+      }
+  
+  
+  
+
+  
+    if ( (strlen (filename) > 4) &&
+         strcmp (&filename[strlen (filename)-4], ".unv") == 0 )
+      {  
+        char reco[100];
+        int invert;
+      
+        ifstream in(filename);
+
+        mesh.ClearFaceDescriptors();
+        mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0));
+
+
+        while (in.good())
+          {
+            in >> reco;
+	    cout << "reco = " << reco << endl;
+
+            if (strcmp (reco, "2411") == 0)
+              {
+                cout << "nodes found" << endl;
+
+                while (1)
+                  {
+                    int pi, hi;
+                    Point<3> p;
+
+                    in >> pi;
+                    if (pi == -1)
+                      break;
+		    
+                    in >> hi >> hi >> hi;
+                    in >> p(0) >> p(1) >> p(2);
+
+		    cout << "p(" << pi << ") = "
+			 << p << endl;
+
+                    mesh.AddPoint (p);
+                  }
+		cout << "read " << mesh.GetNP() << " points" << endl;
+              }
+
+            if (strcmp (reco, "2412") == 0)
+              {
+                cout << "elements found" << endl;
+
+                while (1)
+                  {
+		    int label, fe_id, phys_prop, mat_prop, color, nnodes;
+		    int nodes[100];
+		    int hi;
+
+		    in >> label;
+		    if (label == -1) break;
+		    in >> fe_id >> phys_prop >> mat_prop >> color >> nnodes;
+		    
+		    cout << "fe_id = " << fe_id << " col = " << color << ", nnodes = " << nnodes << endl;
+
+		    if (fe_id >= 11 && fe_id <= 32)
+		      in >> hi >> hi >> hi;
+		      
+
+		    for (int j = 0; j < nnodes; j++)
+		      in >> nodes[j];
+		    
+		    switch (fe_id)
+		      {
+		      case 41:
+			{
+			  Element2d el (TRIG);
+			  el.SetIndex (1);
+			  for (int j = 0; j < nnodes; j++)
+			    el[j] = nodes[j];
+			  mesh.AddSurfaceElement (el);
+			  
+			  break;
+			}
+		      case 111:
+			{
+			  Element el (TET);
+			  el.SetIndex (1);
+			  for (int j = 0; j < nnodes; j++)
+			    el[j] = nodes[j];
+			  mesh.AddVolumeElement (el);
+			  
+			  break;
+			}
+		      }
+                  }
+              }
+          }
+      
+
+        Point3d pmin, pmax;
+        mesh.GetBox (pmin, pmax);
+        cout << "bounding-box = " << pmin << "-" << pmax << endl;
+      }
+
+
+
+    // fepp format2d:
+  
+    if ( (strlen (filename) > 7) &&
+         strcmp (&filename[strlen (filename)-7], ".mesh2d") == 0 )
+      {
+        cout << "Reading FEPP2D Mesh" << endl;
+      
+        char buf[100];
+        int np, ne, nseg, i, j;
+
+        ifstream in (filename);
+
+        in >> buf;
+
+        in >> nseg;
+        for (i = 1; i <= nseg; i++)
+          {
+            int bound, p1, p2;
+            in >> bound >> p1 >> p2;
+            // forget them
+          }
+
+        in >> ne;
+        for (i = 1; i <= ne; i++)
+          {
+            int mat, nelp;
+            in >> mat >> nelp;
+            Element2d el (nelp == 3 ? TRIG : QUAD);
+            el.SetIndex (mat);
+            for (j = 1; j <= nelp; j++)
+              in >> el.PNum(j);
+            mesh.AddSurfaceElement (el);
+          }
+
+        in >> np;
+        for (i = 1; i <= np; i++)
+          {
+            Point3d p(0,0,0);
+            in >> p.X() >> p.Y();
+            mesh.AddPoint (p);
+          }
+      }
+
+  
+    else if ( (strlen (filename) > 5) &&
+              strcmp (&filename[strlen (filename)-5], ".mesh") == 0 )
+      {
+        cout << "Reading Neutral Format" << endl;
+      
+        int np, ne, nse, i, j;
+
+        ifstream in (filename);
+
+        in >> np;
+
+        if (in.good())
+          {
+            // file starts with an integer
+
+            for (i = 1; i <= np; i++)
+              {
+                Point3d p(0,0,0);
+                in >> p.X() >> p.Y() >> p.Z();
+                mesh.AddPoint (p);
+              }
+	  
+            in >> ne;
+            for (i = 1; i <= ne; i++)
+              {
+                int mat;
+                in >> mat;
+                Element el (4);
+                el.SetIndex (mat);
+                for (j = 1; j <= 4; j++)
+                  in >> el.PNum(j);
+                mesh.AddVolumeElement (el);
+              }
+
+            mesh.AddFaceDescriptor (FaceDescriptor (1, 1, 0, 0));
+	  
+            in >> nse;
+            for (i = 1; i <= nse; i++)
+              {
+                int mat; // , nelp;
+                in >> mat;
+                Element2d el (TRIG);
+                el.SetIndex (mat);
+                for (j = 1; j <= 3; j++)
+                  in >> el.PNum(j);
+                mesh.AddSurfaceElement (el);
+              }
+          }
+        else
+          {
+            char buf[100];
+            in.clear();
+            do
+              {
+                in >> buf;
+                cout << "buf = " << buf << endl;
+                if (strcmp (buf, "points") == 0)
+                  {
+                    in >> np;
+                    cout << "np = " << np << endl;
+                  }
+              }
+            while (in.good());
+          }
+      }
+
+
+    if ( (strlen (filename) > 4) &&
+         strcmp (&filename[strlen (filename)-4], ".emt") == 0 )
+      {
+        ifstream inemt (filename);
+      
+        string pktfile = filename;
+        int len = strlen (filename);
+        pktfile[len-3] = 'p';
+        pktfile[len-2] = 'k';
+        pktfile[len-1] = 't';
+        cout << "pktfile = " << pktfile << endl;
+
+        int np, nse, i;
+        int bcprop;
+        ifstream inpkt (pktfile.c_str());
+        inpkt >> np;
+        Array<double> values(np);
+        for (i = 1; i <= np; i++)
+          {
+            Point3d p(0,0,0);
+            inpkt >> p.X() >> p.Y() >> p.Z()
+                  >> bcprop >> values.Elem(i);
+            mesh.AddPoint (p);
+          }      
+
+        mesh.ClearFaceDescriptors();
+        mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0));
+        mesh.GetFaceDescriptor(1).SetBCProperty (1);
+        mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0));
+        mesh.GetFaceDescriptor(2).SetBCProperty (2);
+        mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0));
+        mesh.GetFaceDescriptor(3).SetBCProperty (3);
+        mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0));
+        mesh.GetFaceDescriptor(4).SetBCProperty (4);
+        mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0));
+        mesh.GetFaceDescriptor(5).SetBCProperty (5);
+
+        int p1, p2, p3;
+        double value;
+        inemt >> nse;
+        for (i = 1; i <= nse; i++)
+          {
+            inemt >> p1 >> p2 >> p3 >> bcprop >> value;
+
+            if (bcprop < 1 || bcprop > 4)
+              cerr << "bcprop out of range, bcprop = " << bcprop << endl;
+            p1++;
+            p2++;
+            p3++;
+            if (p1 < 1 || p1 > np || p2 < 1 || p2 > np || p3 < 1 || p3 > np)
+              {
+                cout << "p1 = " << p1 << " p2 = " << p2 << " p3 = " << p3 << endl;
+              }
+
+            if (i > 110354) Swap (p2, p3);
+            if (mesh.Point(p1)(0) < 0.25)
+              Swap (p2,p3);
+
+            Element2d el(TRIG);
+
+            if (bcprop == 1)
+              {
+                if (values.Get(p1) < -69999)
+                  el.SetIndex(1);
+                else
+                  el.SetIndex(2);
+              }
+            else
+              el.SetIndex(3);
+
+
+            el.PNum(1) = p1;
+            el.PNum(2) = p2;
+            el.PNum(3) = p3;
+            mesh.AddSurfaceElement (el);
+          }
+
+
+        ifstream incyl ("ngusers/guenter/cylinder.surf");
+        int npcyl, nsecyl; 
+        incyl >> npcyl;
+        cout << "npcyl = " << npcyl << endl;
+        for (i = 1; i <= npcyl; i++)
+          {
+            Point3d p(0,0,0);
+            incyl >> p.X() >> p.Y() >> p.Z();
+            mesh.AddPoint (p);
+          }
+        incyl >> nsecyl;
+        cout << "nsecyl = " << nsecyl << endl;
+        for (i = 1; i <= nsecyl; i++)
+          {
+            incyl >> p1 >> p2 >> p3;
+            p1 += np;
+            p2 += np;
+            p3 += np;
+            Element2d el(TRIG);
+            el.SetIndex(5);
+            el.PNum(1) = p1;
+            el.PNum(2) = p2;
+            el.PNum(3) = p3;
+            mesh.AddSurfaceElement (el);
+          }
+      }
+
+
+    // .tet mesh
+    if ( (strlen (filename) > 4) &&
+         strcmp (&filename[strlen (filename)-4], ".tet") == 0 )
+      {
+        ReadTETFormat (mesh, filename);
+      }
+
+
+    // .fnf mesh (FNF - PE neutral format)
+    if ( (strlen (filename) > 4) &&
+         strcmp (&filename[strlen (filename)-4], ".fnf") == 0 )
+      {
+        ReadFNFFormat (mesh, filename);
+      }
+
+  }
+  
+}
+
diff --git a/contrib/Netgen/libsrc/interface/writeOpenFOAM15x.cpp b/contrib/Netgen/libsrc/interface/writeOpenFOAM15x.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..337e49eff11abcf45c5b1453011f6628a98d9b4c
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writeOpenFOAM15x.cpp
@@ -0,0 +1,768 @@
+/*! \file writeOpenFOAM15x.cpp
+*  \brief Export Netgen Mesh in the OpenFOAM 1.5+ File format
+*  \author Philippose Rajan
+*  \date 25 October 2009
+*
+*  This function extends the export capabilities of
+*  Netgen to include the OpenFOAM 1.5+ File Format.
+*
+*  The OpenFOAM 1.5+ mesh format consists of a set of 5 files 
+*  which together define the mesh points, faces, cells and 
+*  boundary conditions. 
+*
+*  The files are:
+*  1. points    -> A list of the point co-ordinates
+*  2. faces     -> A list of the faces with format <n>(pnt_ind1 pnt_ind2 .... pnt_ind<n>)
+*  3. owner     -> The owner cell of each face 
+*  4. neighbour -> The neighbour cell of each face
+*  5. boundary  -> The set of boundaries with name, start face, and num. of faces
+*
+*  For a detailed description of the format, refer to the following link:
+*  http://openfoamwiki.net/index.php/Write_OpenFOAM_meshes
+*
+*/
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+#include <sys/stat.h>
+
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+   // Global arrays used to maintain the owner, neighbour and face lists 
+   // so that they are accessible across functions
+   static Array<int> owner_facelist;
+   static Array<int> owner_celllist;
+   static Array<int> neighbour_celllist;
+   static Array<int> surfelem_bclist;
+   static Array<INDEX_2> surfelem_lists;
+
+
+
+   static void WriteOpenFOAM15xBanner(ofstream & outfile)
+   {
+      static char FOAMversion[4] = "1.5";
+      static char spaces[40];
+
+      memset(spaces, ' ', 40);
+      spaces[38 - strlen(FOAMversion)] = '\0';
+      
+      outfile << 
+              "/*--------------------------------*- C++ -*----------------------------------*\\\n";
+
+      outfile <<
+              "| =========                 |                                                 |\n"
+              "| \\\\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |\n"
+              "|  \\\\    /   O peration     | Version:  " << FOAMversion << spaces << "|\n"
+              "|   \\\\  /    A nd           | Web:      http://www.OpenFOAM.org               |\n"
+              "|    \\\\/     M anipulation  |                                                 |\n"
+              "\\*---------------------------------------------------------------------------*/\n";
+
+   }
+
+
+
+   static void WriteOpenFOAM15xDividerStart(ofstream & outfile)
+   {
+      outfile  <<
+               "// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n";
+   }
+
+
+
+   static void WriteOpenFOAM15xDividerEnd(ofstream & outfile)
+   {
+      outfile <<
+              "// ************************************************************************* //\n";
+   }
+
+
+
+   static bool BuildOwnerNeighbourLists (const Mesh & mesh)
+   {
+      // Clear all the arrays
+      owner_facelist.DeleteAll();
+      owner_celllist.DeleteAll();
+      neighbour_celllist.DeleteAll();
+      surfelem_bclist.DeleteAll();
+      surfelem_lists.DeleteAll();
+
+      const MeshTopology& meshtopo = mesh.GetTopology();
+      
+      // Update the mesh topology structures
+      const_cast<MeshTopology&> (meshtopo).SetBuildEdges(true);
+      const_cast<MeshTopology&> (meshtopo).SetBuildFaces(true);
+      const_cast<MeshTopology&> (meshtopo).Update();
+
+      // Extract important mesh metrics
+      int ne = mesh.GetNE();
+      int nse = mesh.GetNSE();
+      int totfaces = meshtopo.GetNFaces();
+
+      // Preset the size of the arrays to speed up future operations
+      // Number of internal faces = total faces - num. of surface faces
+      owner_facelist.SetSize(totfaces - nse);
+      owner_celllist.SetSize(totfaces - nse);
+      neighbour_celllist.SetSize(totfaces - nse);
+      surfelem_bclist.SetSize(nse);
+      surfelem_lists.SetSize(nse);
+
+      // Initialise arrays to zero if required
+      neighbour_celllist = 0;
+
+      // Array used to keep track of Faces which have already been 
+      // processed and added to the Owner list... In addition, also the 
+      // location where the face appears in the Owner list is also stored 
+      // to speed up creation of the Neighbour list
+      Array<int> ownerfaces(totfaces);
+      ownerfaces = 0;
+
+      // Array to hold the set of local faces of each volume element 
+      // while running through the set of volume elements
+      // NOTE: The size is set automatically by the Netgen topology function
+      Array<int> locfaces;
+
+      // Secondary indices used to independently advance the owner 
+      // and boundary condition arrays within the main loop
+      int owner_ind = 1;
+      int bc_ind = 1;
+
+      // Loop through all the volume elements
+      for(int elind = 1; elind <= ne; elind++)
+      {
+         // Extract the current volume element
+	// const Element & el = mesh.VolumeElement(elind);
+
+         // Get the face numbers of the faces of the current volume element
+         // The values returned are given a sign depending on the orientation 
+         // of the faces. This is used while writing the faces file, to 
+         // determine whether or not to invert the face triangle before writing 
+         // it to file
+         meshtopo.GetElementFaces(elind,locfaces,true);
+
+         // Loop through the faces
+         for(int i = 1; i <= locfaces.Size(); i++)
+         {
+            // The absolute value of a face number (because the faces 
+            // returned by the GetElementFaces function prepend it 
+            // with a sign depending on the face orientation)
+            int absfacenr = abs(locfaces.Elem(i));
+
+            // If the face already exists in the owner list, add 
+            // the current cell into the neighbour list, in the 
+            // same location where the face appears in the owner list
+            int owner_face = ownerfaces.Elem(absfacenr);
+            if(owner_face)
+            {
+               neighbour_celllist.Elem(owner_face) = elind;
+
+               // From this point on, the code within this "if" block 
+               // basically sorts the order of the the Neighbour cells (along 
+               // with the faces list) in ascending order.
+               // The approach used is..... to traverse the owner and neighbour cell lists
+               // up and down, and sort the neighbour cells of a given owner cell 
+               // as the list evolves.
+               // NOTE: A value of "zero" in the neighbour list implies that 
+               // the neighbour has not been found yet, so the "zero" locations need 
+               // to be skipped while sorting in ascending order
+               int curr_owner = owner_celllist.Elem(owner_face);
+
+               int peek_loc = owner_face - 1;
+               int new_loc = owner_face;
+
+               // Traversing upwards in the list
+               while((owner_celllist.Elem(peek_loc) == curr_owner) && (peek_loc >= 1))
+               {
+                  if((neighbour_celllist.Elem(peek_loc) != 0) 
+                     && (neighbour_celllist.Elem(new_loc) < neighbour_celllist.Elem(peek_loc)))
+                  {
+                     Swap(neighbour_celllist.Elem(new_loc),neighbour_celllist.Elem(peek_loc));
+                     Swap(owner_facelist.Elem(new_loc),owner_facelist.Elem(peek_loc));
+                     new_loc = peek_loc;
+                  }
+
+                  peek_loc--;
+               }
+
+               peek_loc = owner_face + 1;
+
+               // Traversing downwards in the list
+               while((owner_celllist.Elem(peek_loc) == curr_owner) && (peek_loc <= owner_ind))
+               {
+                  if((neighbour_celllist.Elem(peek_loc) != 0) 
+                     && (neighbour_celllist.Elem(new_loc) > neighbour_celllist.Elem(peek_loc)))
+                  {
+                     Swap(neighbour_celllist.Elem(new_loc),neighbour_celllist.Elem(peek_loc));
+                     Swap(owner_facelist.Elem(new_loc),owner_facelist.Elem(peek_loc));
+                     new_loc = peek_loc;
+                  }
+
+                  peek_loc++;
+               }
+
+               continue;
+            }
+
+            // Check if the face is a surface element (boundary face)
+            // if not, add the current volume element and the corresponding face into 
+            // the owner list
+            int surfelem = meshtopo.GetFace2SurfaceElement(absfacenr);
+            if(!surfelem)
+            {
+               // If it is a new face which has not been listed before, 
+               // add the current cell into the owner list, and save 
+               // the index location to be used later by the neighbour list
+               owner_celllist.Elem(owner_ind) = elind;
+               owner_facelist.Elem(owner_ind) = locfaces.Elem(i);
+               // Update the array to indicate that the face is already processed
+               ownerfaces.Elem(absfacenr) = owner_ind;
+
+               owner_ind++;
+            }
+            // If the face is a boundary face, extract the boundary condition number of the 
+            // face, and append that along with the face number and the current cell 
+            // into the various surface elements lists
+            else
+            {
+               Element2d sel = mesh.SurfaceElement(surfelem);
+               surfelem_bclist.Elem(bc_ind) = mesh.GetFaceDescriptor(sel.GetIndex()).BCProperty();
+               surfelem_lists.Elem(bc_ind) = INDEX_2(locfaces.Elem(i),elind);
+
+               bc_ind++;
+            }
+         }
+      }
+
+      // This correction is required in cases where the mesh has been "uniform refined".... for 
+      // some reason, the number of faces reported by Netgen is higher than the actual number 
+      // of faces in the mesh
+      owner_facelist.SetSize(owner_ind-1);
+      owner_celllist.SetSize(owner_ind-1);
+      neighbour_celllist.SetSize(owner_ind-1);
+
+
+      // Sort the list of surface elements in ascending order of boundary condition number
+      // also sort the cell list in the same manner
+      QuickSort(surfelem_bclist,surfelem_lists);
+
+/*    
+      // Debugging output to a file 
+      ofstream dbg("OpenFOAMDebug.log");
+
+      dbg << " ------- Boundary List -------- \n";
+
+      for(int i = 1; i <= surfelem_bclist.Size(); i++)
+      {
+         dbg << "bc = " << surfelem_bclist.Elem(i) 
+              << " : face = " << surfelem_lists.Elem(i).I1()
+              << " : cell = " << surfelem_lists.Elem(i).I2() << "\n";
+      }
+
+      dbg << "\n ------- Owner / Face / Neighbour List ------- \n";
+
+      for(int i = 1; i <= owner_celllist.Size(); i++)
+      {
+         dbg << "Ind:" << i << " :: (" 
+              << owner_celllist.Elem(i) << " "
+              << owner_facelist.Elem(i) << "  "
+              << neighbour_celllist.Elem(i) << ")\n";
+      }
+
+      dbg.close();
+*/
+      return(false);
+   }
+
+
+
+   static void WriteNeighbourFile (ofstream & outfile)
+   {
+      // Write the OpenFOAM standard banner and dividers, etc...
+      WriteOpenFOAM15xBanner(outfile);
+      outfile << "FoamFile \n"
+              << "{ \n"
+              << "    version     2.0; \n"
+              << "    format      ascii; \n"
+              << "    class       labelList; \n"
+              << "    note        \"Mesh generated and converted using NETGEN-" << PACKAGE_VERSION << "\"; \n"
+              << "    location    \"constant\\polyMesh\"; \n"
+              << "    object      neighbour; \n"
+              << "} \n";
+      WriteOpenFOAM15xDividerStart(outfile);
+
+      outfile << "\n\n";
+
+      int nneighbours = neighbour_celllist.Size();
+
+      outfile << nneighbours << "\n";
+
+      outfile << "(\n";
+
+      // Write the neighbour cells to file
+      for(int i = 1; i <= neighbour_celllist.Size(); i++)
+      {
+         outfile << neighbour_celllist.Elem(i) - 1 << "\n";
+      }
+      outfile << ")\n\n";
+      WriteOpenFOAM15xDividerEnd(outfile);
+   }
+
+
+
+   static void WriteOwnerFile (ofstream & outfile)
+   {
+      // Write the OpenFOAM standard banner and dividers, etc...
+      WriteOpenFOAM15xBanner(outfile);
+      outfile << "FoamFile \n"
+              << "{ \n"
+              << "    version     2.0; \n"
+              << "    format      ascii; \n"
+              << "    class       labelList; \n"
+              << "    note        \"Mesh generated and converted using NETGEN-" << PACKAGE_VERSION << "\"; \n"
+              << "    location    \"constant\\polyMesh\"; \n"
+              << "    object      owner; \n"
+              << "} \n";
+      WriteOpenFOAM15xDividerStart(outfile);
+
+      outfile << "\n\n";
+
+      int nowners = owner_celllist.Size() + surfelem_lists.Size();
+
+      outfile << nowners << "\n";
+
+      outfile << "(\n";
+
+      // Write the owners of the internal cells to file
+      for(int i = 1; i <= owner_celllist.Size(); i++)
+      {
+         outfile << owner_celllist.Elem(i) - 1 << "\n";
+      }
+
+      // Write the owners of the boundary cells to file
+      // (Written in order of ascending boundary condition numbers)
+      for(int i = 1; i <= surfelem_lists.Size(); i++)
+      {
+         outfile << surfelem_lists.Elem(i).I2() - 1 << "\n";
+      }
+      outfile << ")\n\n";
+      WriteOpenFOAM15xDividerEnd(outfile);
+   }
+
+
+
+   static void WriteFacesFile (ofstream & outfile, const Mesh & mesh)
+   {
+      const MeshTopology& meshtopo = mesh.GetTopology();
+
+      // Write the OpenFOAM standard banner and dividers, etc...
+      WriteOpenFOAM15xBanner(outfile);
+      outfile << "FoamFile \n"
+              << "{ \n"
+              << "    version     2.0; \n"
+              << "    format      ascii; \n"
+              << "    class       faceList; \n"
+              << "    note        \"Mesh generated and converted using NETGEN-" << PACKAGE_VERSION << "\"; \n"
+              << "    location    \"constant\\polyMesh\"; \n"
+              << "    object      faces; \n"
+              << "} \n";
+      WriteOpenFOAM15xDividerStart(outfile);
+
+      outfile << "\n\n";
+
+      int nfaces = owner_facelist.Size() + surfelem_lists.Size();
+
+      outfile << nfaces << "\n";
+
+      outfile << "(\n";
+
+      // Array to hold the indices of the points of each face to 
+      // flip if required 
+      Array<int> facepnts;
+
+      // Write the faces in the order specified in the owners lists of the 
+      // internal cells and the boundary cells
+      for(int i = 1; i <= owner_facelist.Size(); i++)
+      {
+         int face_w_orientation = owner_facelist.Elem(i);
+         int facenr = abs(face_w_orientation);
+
+         meshtopo.GetFaceVertices(facenr,facepnts);
+
+         // Get the orientation of the face, and invert it if required
+         // Since the faces already have the orientation "embedded" into 
+         // them by means of the prepended sign, only this needs to be 
+         // checked for...
+         if(face_w_orientation > 0)
+         {
+            int tmppnts = 0;
+
+            if(facepnts.Size() == 4)
+            {
+               tmppnts = facepnts.Elem(1);
+               facepnts.Elem(1) = facepnts.Elem(2);
+               facepnts.Elem(2) = tmppnts;
+               
+               tmppnts = facepnts.Elem(3);
+               facepnts.Elem(3) = facepnts.Elem(4);
+               facepnts.Elem(4) = tmppnts;
+            }
+            else if(facepnts.Size() == 3)
+            {
+               tmppnts = facepnts.Elem(1);
+               facepnts.Elem(1) = facepnts.Elem(3);
+               facepnts.Elem(3) = tmppnts;
+            }
+         }
+
+         outfile << facepnts.Size();
+         outfile << "(";
+         for(int j = 1; j <= facepnts.Size(); j++)
+         {
+            outfile << facepnts.Elem(j)-1;
+            if(j != facepnts.Size()) outfile << " ";
+         }
+         outfile << ")\n";
+      }
+
+      // Now append the faces of the surface elements (written in 
+      // ascending order of boundary condition number) also into 
+      // the faces file
+      for(int i = 1; i <= surfelem_lists.Size(); i++)
+      {
+         int face_w_orientation = surfelem_lists.Elem(i).I1();
+         int facenr = abs(face_w_orientation);
+
+         meshtopo.GetFaceVertices(facenr,facepnts);
+
+         // Get the orientation of the face, and invert it if required
+         if(face_w_orientation > 0)
+         {
+            int tmppnts = 0;
+
+            if(facepnts.Size() == 4)
+            {
+               tmppnts = facepnts.Elem(1);
+               facepnts.Elem(1) = facepnts.Elem(2);
+               facepnts.Elem(2) = tmppnts;
+               
+               tmppnts = facepnts.Elem(3);
+               facepnts.Elem(3) = facepnts.Elem(4);
+               facepnts.Elem(4) = tmppnts;
+            }
+            else if(facepnts.Size() == 3)
+            {
+               tmppnts = facepnts.Elem(1);
+               facepnts.Elem(1) = facepnts.Elem(3);
+               facepnts.Elem(3) = tmppnts;
+            }
+         }
+
+         outfile << facepnts.Size();
+         outfile << "(";
+         for(int j = 1; j <= facepnts.Size(); j++)
+         {
+            outfile << facepnts.Elem(j)-1;
+            if(j != facepnts.Size()) outfile << " ";
+         }
+         outfile << ")\n";
+      }
+
+      outfile << ")\n\n";
+      WriteOpenFOAM15xDividerEnd(outfile);
+   }
+
+
+ 
+   static void WritePointsFile (ofstream & outfile, const Mesh & mesh)
+   {
+      int np = mesh.GetNP();
+
+      // Write the OpenFOAM standard banner and dividers, etc...
+      WriteOpenFOAM15xBanner(outfile);
+      outfile << "FoamFile \n"
+              << "{ \n"
+              << "    version     2.0; \n"
+              << "    format      ascii; \n"
+              << "    class       vectorField; \n"
+              << "    note        \"Mesh generated and converted using NETGEN-" << PACKAGE_VERSION << "\"; \n"
+              << "    location    \"constant\\polyMesh\"; \n"
+              << "    object      points; \n"
+              << "} \n";
+      WriteOpenFOAM15xDividerStart(outfile);
+
+      outfile << "\n\n";
+
+      // Number of points in the following list
+      outfile << np << "\n";
+
+      outfile.precision(6);
+      outfile.setf (ios::fixed, ios::floatfield);
+      outfile.setf (ios::showpoint);
+
+      // Coordinate list starts here
+      outfile << "(\n";
+
+      for(int i = 1; i <= np; i++)
+      {
+         const Point3d & p = mesh.Point(i);
+
+         // Write coordinates to file
+         outfile << "(";
+         outfile << p.X() << " ";
+         outfile << p.Y() << " ";
+         outfile << p.Z();
+         outfile << ")\n";
+      }
+      outfile << ")\n\n";
+      WriteOpenFOAM15xDividerEnd(outfile);
+   }
+
+
+
+   static void WriteBoundaryFile (ofstream & outfile)
+   {
+      // Write the OpenFOAM standard banner and dividers, etc...
+      WriteOpenFOAM15xBanner(outfile);
+      outfile << "FoamFile \n"
+              << "{ \n"
+              << "    version     2.0; \n"
+              << "    format      ascii; \n"
+              << "    class       polyBoundaryMesh; \n"
+              << "    note        \"Mesh generated and converted using NETGEN-" << PACKAGE_VERSION << "\"; \n"
+              << "    location    \"constant\\polyMesh\"; \n"
+              << "    object      boundary; \n"
+              << "} \n";
+      WriteOpenFOAM15xDividerStart(outfile);
+
+      outfile << "\n";
+
+
+      Array<INDEX_3> bcarray;
+      int ind = 1;
+
+      // Since the boundary conditions are already sorted in ascending 
+      // order, the last element will give the maximum number of possible 
+      // boundary condition entries
+      int bcmax = surfelem_bclist.Elem(surfelem_bclist.Size());
+
+      bcarray.SetSize(bcmax+1);
+
+      bcarray.Elem(ind) = INDEX_3(surfelem_bclist.Elem(1),1,0);
+            
+      for(int i = 2; i <= surfelem_bclist.Size(); i++)
+      {
+         if(surfelem_bclist.Elem(i) == bcarray.Elem(ind).I1())
+         {
+            bcarray.Elem(ind).I2() = bcarray.Elem(ind).I2()+1;
+         }
+         else
+         {
+            ind++;
+            bcarray.Elem(ind) = INDEX_3(surfelem_bclist.Elem(i),1,i-1);
+         }
+      }
+
+      bcarray.SetSize(ind);
+
+      outfile << bcarray.Size() << "\n";
+      outfile << "(\n";
+
+      int startface = 0;
+
+      for(int i = 1; i <= bcarray.Size(); i++)
+      {
+         startface = owner_celllist.Size() + bcarray.Elem(i).I3();
+
+         outfile << "    patch" << bcarray.Elem(i).I1() << "\n"
+                 << "    {\n"
+                 << "        type            patch;\n"
+                 << "        physicalType    patch;\n"
+                 << "        nFaces          " << bcarray.Elem(i).I2() << ";\n"
+                 << "        startFace       " << startface << ";\n"
+                 << "    }\n";
+      }
+
+      outfile << ")\n\n";
+      WriteOpenFOAM15xDividerEnd(outfile);
+   }
+
+
+
+   void WriteOpenFOAM15xFormat (const Mesh & mesh, const string & casename)
+   {
+      bool error = false;
+      char casefiles[256];
+
+      // Make sure that the mesh data has been updated
+      const_cast<Mesh&> (mesh).Compress();
+      const_cast<Mesh&> (mesh).CalcSurfacesOfNode();
+      const_cast<Mesh&> (mesh).RebuildSurfaceElementLists();
+      const_cast<Mesh&> (mesh).BuildElementSearchTree();
+
+
+      int np = mesh.GetNP();
+      int nse = mesh.GetNSE();
+      int ne = mesh.GetNE();
+
+      cout << "Write OpenFOAM 1.5+ Mesh Files....\n";
+
+      // Abort if there are no points, surface elements or volume elements
+      if((np <= 0) || (ne <= 0) || (nse <= 0))
+      {
+         cout << "Export Error: Invalid mesh.... Aborting!\n";
+         return;
+      }
+
+      // OpenFOAM only supports linear meshes!
+      if(mparam.secondorder || mesh.GetCurvedElements().IsHighOrder())
+      {
+         cout << "Export Error: OpenFOAM 1.5+ does not support non-linear elements.... Aborting!\n";
+         return;
+      }
+
+      if(( (mesh.SurfaceElement(nse/2).GetType() != TRIG) 
+	   && (mesh.SurfaceElement(nse/2).GetType() != QUAD) )
+         || (mesh.VolumeElement(ne/2).GetType() == TET10)
+         || (mesh.VolumeElement(ne/2).GetType() == PRISM12))
+      {
+         cout << "Export Error: OpenFOAM 1.5+ does not support non-linear elements.... Aborting!\n";
+         return;
+      }
+
+
+      cout << "Writing OpenFOAM 1.5+ Mesh files to case: " << casename << "\n";
+
+      // Create the case directory if it does not already exist
+      // NOTE: This needs to be improved for the Linux variant....!!!
+   #ifdef WIN32
+      char casedir[256];
+      sprintf(casedir, "mkdir %s\\constant\\polyMesh", casename.c_str());
+      system(casedir);
+   #else
+      char casedir[256];
+      mkdir(casename.c_str(), S_IRWXU|S_IRWXG);
+      sprintf(casedir, "%s/constant", casename.c_str());
+      mkdir(casedir, S_IRWXU|S_IRWXG);
+      sprintf(casedir, "%s/constant/polyMesh", casename.c_str());
+      mkdir(casedir, S_IRWXU|S_IRWXG);
+   #endif
+
+      // Open handles to the five required mesh files
+      // points
+      // faces
+      // owner
+      // neighbour
+      // boundary
+      sprintf(casefiles, "%s/constant/polyMesh/points", casename.c_str());
+      ofstream outfile_pnts(casefiles);
+      sprintf(casefiles, "%s/constant/polyMesh/faces", casename.c_str()); 
+      ofstream outfile_faces(casefiles);
+      sprintf(casefiles, "%s/constant/polyMesh/owner", casename.c_str()); 
+      ofstream outfile_own(casefiles);
+      sprintf(casefiles, "%s/constant/polyMesh/neighbour", casename.c_str()); 
+      ofstream outfile_nei(casefiles);
+      sprintf(casefiles, "%s/constant/polyMesh/boundary", casename.c_str()); 
+      ofstream outfile_bnd(casefiles);
+
+      ResetTime();
+
+      // Build the owner, neighbour, faces and boundary lists 
+      // from the Netgen mesh
+      cout << "\nBuilding Owner, Neighbour and Face Lists: ";
+
+      error = BuildOwnerNeighbourLists(mesh);
+
+      cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n";
+
+
+      // Write the "owner" file
+      if(outfile_own.good() && !error)
+      {
+         cout << "Writing the owner file: ";
+         WriteOwnerFile(outfile_own);
+         outfile_own.close();
+         cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n";
+      }
+      else
+      {
+         cout << "Export Error: Error creating file: owner.... Aborting\n";
+         error = true;
+      }
+
+
+      // Write the "neighbour" file
+      if(outfile_nei.good() && !error)
+      {
+         cout << "Writing the neighbour file: ";
+         WriteNeighbourFile(outfile_nei);
+         outfile_nei.close();
+         cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n";
+      }
+      else
+      {
+         cout << "Export Error: Error creating file: neighbour.... Aborting\n";
+         error = true;
+      }
+
+
+      // Write the "faces" file
+      if(outfile_faces.good() && !error)
+      {
+         cout << "Writing the faces file: ";
+         WriteFacesFile(outfile_faces, mesh);
+         outfile_faces.close();
+         cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n";
+      }
+      else
+      {
+         cout << "Export Error: Error creating file: faces.... Aborting\n";
+         error = true;
+      }
+
+
+      // Write the "points" file
+      if(outfile_pnts.good() && !error)
+      {
+         cout << "Writing the points file: ";
+         WritePointsFile(outfile_pnts,mesh);
+         outfile_pnts.close();
+         cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n";
+      }
+      else
+      {
+         cout << "Export Error: Error creating file: points.... Aborting\n";
+         error = true;
+      }
+
+
+      // Write the "boundary" file
+      if(outfile_bnd.good() && !error)
+      {
+         cout << "Writing the boundary file: ";
+         WriteBoundaryFile(outfile_bnd);
+         outfile_bnd.close();
+         cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n";
+      }
+      else
+      {
+         cout << "Export Error: Error creating file: boundary.... Aborting\n";
+         error = true;
+      }
+
+      if(!error)
+      {
+         cout << "OpenFOAM 1.5+ Export successfully completed (Time elapsed = " << GetTime() << " sec) !\n";
+      }
+      else
+      {
+         cout << "Error in OpenFOAM 1.5+ Export.... Aborted!\n";
+      }
+   }
+}
+
diff --git a/contrib/Netgen/libsrc/interface/writeabaqus.cpp b/contrib/Netgen/libsrc/interface/writeabaqus.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6f2f165cb71fc77cd666cc4eebba804b45359d60
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writeabaqus.cpp
@@ -0,0 +1,237 @@
+//
+//  Write Abaqus file
+//
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+
+
+
+void WriteAbaqusFormat (const Mesh & mesh,
+			const string & filename)
+
+{
+      
+  cout << "\nWrite Abaqus Volume Mesh" << endl;
+
+  ofstream outfile (filename.c_str());
+
+  outfile << "*Heading" << endl;
+  outfile << " " << filename << endl;
+
+  outfile.precision(8);
+
+  outfile << "*Node" << endl;
+
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+  int i, j, k;
+
+  for (i = 1; i <= np; i++)
+    {
+      outfile << i << ", ";
+      outfile << mesh.Point(i)(0) << ", ";
+      outfile << mesh.Point(i)(1) << ", ";
+      outfile << mesh.Point(i)(2) << "\n";
+    }
+
+  int elemcnt = 0; //element counter
+  int finished = 0;
+  int indcnt = 1; //index counter
+
+  while (!finished)
+    {
+      int actcnt = 0;
+      const Element & el1 = mesh.VolumeElement(1);
+      int non = el1.GetNP();
+      if (non == 4)
+	{
+	  outfile << "*Element, type=C3D4, ELSET=PART" << indcnt << endl;
+	} 
+      else if (non == 10)
+	{
+	  outfile << "*Element, type=C3D10, ELSET=PART" << indcnt << endl;
+	} 
+      else
+	{
+	  cout << "unsupported Element type!!!" << endl;	  
+	}
+
+      for (i = 1; i <= ne; i++)
+	{
+	  const Element & el = mesh.VolumeElement(i);
+	      
+	  if (el.GetIndex() == indcnt)
+	    {
+	      actcnt++;
+	      if (el.GetNP() != non) 
+		{
+		  cout << "different element-types in a subdomain are not possible!!!" << endl;
+		  continue;
+		}
+		  
+	      elemcnt++;
+	      outfile << elemcnt << ", ";
+	      if (non == 4)
+		{
+		  outfile << el.PNum(1) << ", ";
+		  outfile << el.PNum(2) << ", ";
+		  outfile << el.PNum(4) << ", ";
+		  outfile << el.PNum(3) << "\n";
+		}
+	      else if (non == 10)
+		{
+		  outfile << el.PNum(1) << ", ";
+		  outfile << el.PNum(2) << ", ";
+		  outfile << el.PNum(4) << ", ";
+		  outfile << el.PNum(3) << ", ";
+		  outfile << el.PNum(5) << ", ";
+		  outfile << el.PNum(9) << ", ";
+		  outfile << el.PNum(7) << ", " << "\n";
+		  outfile << el.PNum(6) << ", ";
+		  outfile << el.PNum(8) << ", ";
+		  outfile << el.PNum(10) << "\n";
+		}
+	      else
+		{
+		  cout << "unsupported Element type!!!" << endl;
+		  for (j = 1; j <= el.GetNP(); j++)
+		    {
+		      outfile << el.PNum(j);
+		      if (j != el.GetNP()) outfile << ", ";
+		    }
+		  outfile << "\n";
+		}
+	    }
+	}	  
+      indcnt++;
+      if (elemcnt == ne) {finished = 1; cout << "all elements found by Index!" << endl;}
+      if (actcnt == 0) {finished = 1;}
+    }
+
+  if (mesh.GetIdentifications().GetMaxNr())
+    {
+      // periodic identification, implementation for
+      // Helmut J. Boehm, TU Vienna
+	  
+      char cfilename[255];
+      strcpy (cfilename, filename.c_str());
+
+      char mpcfilename[255];
+      strcpy (mpcfilename, cfilename);
+      size_t len = strlen (cfilename);
+      if (len >= 4 && (strcmp (mpcfilename+len-4, ".inp") == 0))
+	strcpy (mpcfilename+len-4, ".mpc");
+      else
+	strcat (mpcfilename, ".mpc");
+	  
+      ofstream mpc (mpcfilename);
+
+      int masternode(0);
+
+      Array<INDEX_2> pairs;
+      BitArray master(np), help(np);
+      master.Set();
+      for (i = 1; i <= 3; i++)
+	{
+	  mesh.GetIdentifications().GetPairs (i, pairs);
+	  help.Clear();
+	  for (j = 1; j <= pairs.Size(); j++)
+	    {
+	      help.Set (pairs.Get(j).I1());
+	    }
+	  master.And (help);
+	}
+      for (i = 1; i <= np; i++)
+	if (master.Test(i))
+	  masternode = i;
+
+      cout << "masternode = " << masternode << " = "
+	   << mesh.Point(masternode) << endl;
+      Array<int> slaves(3);
+      for (i = 1; i <= 3; i++)
+	{
+	  mesh.GetIdentifications().GetPairs (i, pairs);
+	  for (j = 1; j <= pairs.Size(); j++)
+	    {
+	      if (pairs.Get(j).I1() == masternode)
+		slaves.Elem(i) = pairs.Get(j).I2();
+	    }
+	  cout << "slave(" << i << ") = " << slaves.Get(i)
+	       << " = " << mesh.Point(slaves.Get(i)) << endl;
+	}
+	  
+	  
+      outfile << "**\n"
+	      << "*NSET,NSET=CTENODS\n"
+	      << slaves.Get(1) << ", " 
+	      << slaves.Get(2) << ", " 
+	      << slaves.Get(3) << endl;
+
+	  
+      outfile << "**\n"
+	      << "**POINT_fixed\n"
+	      << "**\n"
+	      << "*BOUNDARY, OP=NEW\n";
+      for (j = 1; j <= 3; j++)
+	outfile << masternode << ", " << j << ",,    0.\n";
+
+      outfile << "**\n"
+	      << "*BOUNDARY, OP=NEW\n";
+      for (j = 1; j <= 3; j++)
+	{
+	  Vec3d v(mesh.Point(masternode), mesh.Point(slaves.Get(j)));
+	  double vlen = v.Length();
+	  int dir = 0;
+	  if (fabs (v.X()) > 0.9 * vlen) dir = 2;
+	  if (fabs (v.Y()) > 0.9 * vlen) dir = 3;
+	  if (fabs (v.Z()) > 0.9 * vlen) dir = 1;
+	  if (!dir)
+	    cout << "ERROR: Problem with rigid body constraints" << endl;
+	  outfile << slaves.Get(j) << ", " << dir << ",,    0.\n";
+	}
+
+      outfile << "**\n"
+	      << "*EQUATION, INPUT=" << mpcfilename << endl;
+	  
+
+      BitArray eliminated(np);
+      eliminated.Clear();
+      for (i = 1; i <= mesh.GetIdentifications().GetMaxNr(); i++)
+	{
+	  mesh.GetIdentifications().GetPairs (i, pairs);
+	  if (!pairs.Size())
+	    continue;
+	      
+	  for (j = 1; j <= pairs.Size(); j++)
+	    if (pairs.Get(j).I1() != masternode && 
+		!eliminated.Test(pairs.Get(j).I2()))
+	      {
+		eliminated.Set (pairs.Get(j).I2());
+		for (k = 1; k <= 3; k++)
+		  {
+		    mpc << "4" << "\n";
+		    mpc << pairs.Get(j).I2() << "," << k << ", -1.0, ";
+		    mpc << pairs.Get(j).I1() << "," << k << ", 1.0, ";
+		    mpc << slaves.Get(i) << "," << k << ", 1.0, ";
+		    mpc << masternode << "," << k << ", -1.0 \n";
+		  }
+	      }
+	}
+    }
+
+
+  cout << "done" << endl;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/interface/writediffpack.cpp b/contrib/Netgen/libsrc/interface/writediffpack.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..82eb236dc0ea073acac9c2684c208f4d99726ecb
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writediffpack.cpp
@@ -0,0 +1,296 @@
+//
+//  Write diffpack file
+//
+//  by
+//  Bartosz Sawicki <sawickib@ee.pw.edu.pl>
+//  extended by
+//  Jacques Lechelle <jacques.lechelle@wanadoo.fr>
+//
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+
+void WriteDiffPackFormat (const Mesh & mesh,
+			  const CSGeometry & geom,
+			  const string & filename)
+{
+  //   double scale = globflags.GetNumFlag ("scale", 1);
+  double scale = 1;
+
+  ofstream outfile(filename.c_str());
+
+  if (mesh.GetDimension() == 3)
+
+    {
+      // Output compatible to Diffpack grid format
+      // Bartosz Sawicki <sawickib@ee.pw.edu.pl>
+
+      int np = mesh.GetNP();
+      int ne = mesh.GetNE();
+      int nse = mesh.GetNSE();
+      Array <int> BIname;
+      Array <int> BCsinpoint;
+      int i, j, k, l;
+
+
+      outfile.precision(6);
+      outfile.setf (ios::fixed, ios::floatfield);
+      outfile.setf (ios::showpoint);
+
+      const Element & eldummy = mesh.VolumeElement((int)1);
+      outfile << "\n\n"
+	"Finite element mesh (GridFE):\n\n"
+	"  Number of space dim. =   3\n"
+	"  Number of elements   =  " << ne << "\n"
+	"  Number of nodes      =  " << np << "\n\n"
+	"  All elements are of the same type : dpTRUE\n"
+	"  Max number of nodes in an element: "<< eldummy.GetNP() << "\n"
+	"  Only one subdomain               : dpFALSE\n"
+	"  Lattice data                     ? 0\n\n\n\n";
+      
+      for (i = 1; i <= nse; i++) 
+	{
+	  int BI=mesh.GetFaceDescriptor(mesh.SurfaceElement(i).GetIndex()).BCProperty();
+	  int nbi=BIname.Size();
+	  int found=0;
+	  for (j = 1; j <= nbi; j++)
+	    if(BI == BIname.Get(j)) found = 1;
+	  if( ! found ) BIname.Append(BI);	    	     
+	}
+      
+      outfile << "  " << BIname.Size() <<  " Boundary indicators:  ";
+      for (i =1 ; i <= BIname.Size(); i++)
+	outfile << BIname.Get(i) << " ";
+      outfile << "\n\n\n";
+      
+      outfile << "  Nodal coordinates and nodal boundary indicators,\n"
+	"  the columns contain:\n"
+	"   - node number\n"
+	"   - coordinates\n"
+	"   - no of boundary indicators that are set (ON)\n"
+	"   - the boundary indicators that are set (ON) if any.\n"
+	"#\n";
+
+      for (i = 1; i <= np; i++)
+        {
+          const Point3d & p = mesh.Point(i);
+
+          outfile.width(4);
+          outfile << i << "  (";
+          outfile.width(10);
+          outfile << p.X()/scale << ", ";
+          outfile.width(9);
+          outfile << p.Y()/scale << ", ";
+          outfile.width(9);
+          outfile << p.Z()/scale << ") ";
+	 
+	  if(mesh[PointIndex(i)].Type() != INNERPOINT) 
+	    {
+	      BCsinpoint.DeleteAll();
+	      for (j = 1; j <= nse; j++) 
+		{
+		  for (k = 1; k <= mesh.SurfaceElement(j).GetNP(); k++) 
+		    {
+		      if(mesh.SurfaceElement(j).PNum(k)==i) 
+			{
+			  int BC=mesh.GetFaceDescriptor(mesh.SurfaceElement(j).GetIndex()).BCProperty();
+			  int nbcsp=BCsinpoint.Size();
+			  int found = 0;
+			  for (l = 1; l <= nbcsp; l++)
+			    if(BC == BCsinpoint.Get(l)) found = 1;
+			  if( ! found ) BCsinpoint.Append(BC); 	    	     
+			}
+		    }
+		}
+	      int nbcsp = BCsinpoint.Size();
+	      outfile << "[" << nbcsp << "] ";
+	      for (j = 1; j <= nbcsp; j++)
+		outfile << BCsinpoint.Get(j) << " ";
+	      outfile << "\n";
+            }
+          else outfile << "[0]\n";
+
+        }
+
+      outfile << "\n"
+	"  Element types and connectivity\n"
+	"  the columns contain:\n"
+	"   - element number\n"
+	"   - element type\n"
+	"   - subdomain number\n"
+	"   - the global node numbers of the nodes in the element.\n"
+	"#\n";
+
+      for (i = 1; i <= ne; i++)
+        {
+          const Element & el = mesh.VolumeElement(i);
+          outfile.width(5);
+          if(el.GetNP()==4)
+            outfile << i << "  ElmT4n3D ";
+          else
+            outfile << i << "  ElmT10n3D ";
+          outfile.width(4);
+          outfile << el.GetIndex() << "    ";
+          if(el.GetNP()==10)
+            {
+	      outfile.width(8);
+	      outfile << el.PNum(1);
+	      outfile.width(8);
+	      outfile << el.PNum(3);
+	      outfile.width(8);
+	      outfile << el.PNum(2);
+	      outfile.width(8);
+	      outfile << el.PNum(4);
+	      outfile.width(8);
+	      outfile << el.PNum(6);
+	      outfile.width(8);
+	      outfile << el.PNum(8);
+	      outfile.width(8);
+	      outfile << el.PNum(5);
+	      outfile.width(8);
+	      outfile << el.PNum(7);
+	      outfile.width(8);
+	      outfile << el.PNum(10);
+	      outfile.width(8);
+	      outfile << el.PNum(9);
+            }
+          else
+            {
+	      outfile.width(8);
+	      outfile << el.PNum(1);
+	      outfile.width(8);
+	      outfile << el.PNum(3);
+	      outfile.width(8);
+	      outfile << el.PNum(2);
+	      outfile.width(8);
+	      outfile << el.PNum(4);
+            }
+          outfile << "\n";
+        }
+    } /* Diffpack */
+
+  else
+
+    {
+      // Output compatible to Diffpack grid format 2D
+
+      int np = mesh.GetNP();
+      //int ne = mesh.GetNE();
+      int nse = mesh.GetNSE();
+      Array <int> BIname;
+      Array <int> BCsinpoint;
+      int i, j, k, l;
+
+
+      outfile.precision(6);
+      outfile.setf (ios::fixed, ios::floatfield);
+      outfile.setf (ios::showpoint);
+
+      outfile << "\n\n"
+	"Finite element mesh (GridFE):\n\n"
+	"  Number of space dim. =  2\n"
+	"  Number of elements   =  " << nse << "\n"
+	"  Number of nodes      =  " << np << "\n\n"
+	"  All elements are of the same type : dpTRUE\n"
+	"  Max number of nodes in an element: 3\n"
+	"  Only one subdomain               : dpFALSE\n"
+	"  Lattice data                     ? 0\n\n\n\n";
+      
+      for (i = 1; i <= nse; i++) 
+	{
+	  int BI=mesh.GetFaceDescriptor(mesh.SurfaceElement(i).GetIndex()).BCProperty();
+	  int nbi=BIname.Size();
+	  int found=0;
+	  for (j = 1; j <= nbi; j++)
+	    if(BI == BIname.Get(j)) found = 1;
+	  if( ! found ) BIname.Append(BI);	    	     
+	}
+      
+      outfile << "  " << BIname.Size() <<  " Boundary indicators:  ";
+      for (i =1 ; i <= BIname.Size(); i++)
+	outfile << BIname.Get(i) << " ";
+      outfile << "\n\n\n";
+      
+      outfile << "  Nodal coordinates and nodal boundary indicators,\n"
+	"  the columns contain:\n"
+	"   - node number\n"
+	"   - coordinates\n"
+	"   - no of boundary indicators that are set (ON)\n"
+	"   - the boundary indicators that are set (ON) if any.\n"
+	"#\n";
+
+      for (i = 1; i <= np; i++)
+        {
+          const Point3d & p = mesh.Point(i);
+
+          outfile.width(4);
+          outfile << i << "  (";
+          outfile.width(10);
+          outfile << p.X()/scale << ", ";
+          outfile.width(9);
+          outfile << p.Y()/scale << ", ";
+	 
+	  if(mesh[PointIndex(i)].Type() != INNERPOINT) 
+	    {
+	      BCsinpoint.DeleteAll();
+	      for (j = 1; j <= nse; j++) 
+		{
+		  for (k = 1; k <= 2; k++) 
+		    {
+		      if(mesh.SurfaceElement(j).PNum(k)==i) 
+			{
+			  int BC=mesh.GetFaceDescriptor(mesh.SurfaceElement(j).GetIndex()).BCProperty();
+			  int nbcsp=BCsinpoint.Size();
+			  int found = 0;
+			  for (l = 1; l <= nbcsp; l++)
+			    if(BC == BCsinpoint.Get(l)) found = 1;
+			  if( ! found ) BCsinpoint.Append(BC); 	    	     
+			}
+		    }
+		}
+	      int nbcsp = BCsinpoint.Size();
+	      outfile << "[" << nbcsp << "] ";
+	      for (j = 1; j <= nbcsp; j++)
+		outfile << BCsinpoint.Get(j) << " ";
+	      outfile << "\n";
+            }
+          else outfile << "[0]\n";
+
+        }
+
+      outfile << "\n"
+	"  Element types and connectivity\n"
+	"  the columns contain:\n"
+	"   - element number\n"
+	"   - element type\n"
+	"   - subdomain number\n"
+	"   - the global node numbers of the nodes in the element.\n"
+	"#\n";
+
+      for (i = 1; i <= nse; i++)
+        {
+          const Element2d & el = mesh.SurfaceElement(i);
+          outfile.width(5);
+          outfile << i << "  ElmT3n2D ";
+          outfile.width(4);
+          outfile << el.GetIndex() << "    ";
+	  outfile.width(8);
+	  outfile << el.PNum(1);
+	  outfile.width(8);
+	  outfile << el.PNum(3);
+	  outfile.width(8);
+	  outfile << el.PNum(2);
+          outfile << "\n";
+        }
+    }
+}
+}
diff --git a/contrib/Netgen/libsrc/interface/writedolfin.cpp b/contrib/Netgen/libsrc/interface/writedolfin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8fd9e7497a3fd6c5951cffcfd62a0136634d4c87
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writedolfin.cpp
@@ -0,0 +1,69 @@
+//
+//  Write dolfin file
+//
+//  by
+//  Kent-Andre Mardal <kent-and@simula.no>
+
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+
+#include "writeuser.hpp"
+
+
+
+  void WriteDolfinFormat (const Mesh & mesh, const string & filename)
+  {
+    cout << "start writing dolfin export" << endl;
+
+    int np = mesh.GetNP();
+    int ne = mesh.GetNE();
+    // int nse = mesh.GetNSE();
+    int nsd = mesh.GetDimension(); 
+    // int invertsurf = mparam.inverttrigs;
+    // int i, j;
+
+    ofstream outfile (filename.c_str());
+
+    // char str[100];
+    outfile.precision(8);
+    outfile.setf (ios::fixed, ios::floatfield);
+    outfile.setf (ios::showpoint);
+
+    if ( nsd == 3) {
+
+      outfile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" <<endl; 
+      outfile << ""<<endl; 
+
+      outfile << "<dolfin xmlns:dolfin=\"http://www.phi.chalmers.se/dolfin/\">"<<endl;
+      outfile << "  <mesh celltype=\"tetrahedron\" dim=\"3\">" <<endl; 
+      outfile << "      <vertices size=\""<<np<<"\">"<<endl; 
+      for (int i = 1; i <= np; i++) { 
+        const Point3d & p = mesh.Point(i);
+        outfile << "      <vertex index=\""<<i-1<<"\" x=\""<<p.X()<<"\" y=\""<<p.Y()<<"\" z=\""<<p.Z()<<"\"/>"<<endl; 
+      }
+      outfile << "      </vertices>"<<endl; 
+
+
+
+      outfile << "      <cells size=\""<<ne<<"\">"<<endl; 
+      for (int i = 1; i <= ne; i++) {
+        const Element & el = mesh.VolumeElement(i);
+
+        outfile << "      <tetrahedron index=\""<<i-1<<"\" v0=\""<<el.PNum(1)-1<<"\" v1=\""<<el.PNum(2)-1<<"\" v2=\""<<el.PNum(3)-1<<"\" v3=\""<<el.PNum(4)-1<<"\"/>"<<endl; 
+      }
+      outfile << "      </cells>"<<endl; 
+    }
+    outfile << "   </mesh>"<<endl; 
+    outfile << "</dolfin>"<<endl; 
+
+    cout << "done writing dolfin export" << endl;
+  }
+}
diff --git a/contrib/Netgen/libsrc/interface/writeelmer.cpp b/contrib/Netgen/libsrc/interface/writeelmer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..664a6dadabe5d29043c2e258df8a157ed04bdc64
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writeelmer.cpp
@@ -0,0 +1,132 @@
+
+//
+//  Write Elmer file
+//
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+#include <sys/stat.h>
+
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+
+
+void WriteElmerFormat (const Mesh &mesh,
+			 const string &filename)
+{
+  cout << "write elmer mesh files" << endl;
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+  int nse = mesh.GetNSE();
+  int i, j;
+  char str[200];
+  
+  int inverttets = mparam.inverttets;
+  int invertsurf = mparam.inverttrigs;
+
+#ifdef WIN32
+  char a[256];
+  sprintf( a, "mkdir %s", filename.c_str() );
+  system( a );
+#else
+  // int rc = 
+  mkdir(filename.c_str(), S_IRWXU|S_IRWXG);
+#endif
+
+  sprintf( str, "%s/mesh.header", filename.c_str() );
+  ofstream outfile_h(str);
+  sprintf( str, "%s/mesh.nodes", filename.c_str() );
+  ofstream outfile_n(str);
+  sprintf( str, "%s/mesh.elements", filename.c_str() );
+  ofstream outfile_e(str);
+  sprintf( str, "%s/mesh.boundary", filename.c_str() );
+  ofstream outfile_b(str);
+
+  // fill hashtable
+
+  INDEX_3_HASHTABLE<int> face2volelement(ne);
+
+  for (i = 1; i <= ne; i++)
+    {
+      const Element & el = mesh.VolumeElement(i);
+      INDEX_3 i3;
+      int k, l;
+      for (j = 1; j <= 4; j++)   // loop over faces of tet
+	{
+	  l = 0;
+	  for (k = 1; k <= 4; k++)
+	    if (k != j)
+	      {
+		l++;
+		i3.I(l) = el.PNum(k);
+	      }
+	  i3.Sort();
+	  face2volelement.Set (i3, i);
+	}
+    }
+
+//  outfile.precision(6);
+//  outfile.setf (ios::fixed, ios::floatfield);
+//  outfile.setf (ios::showpoint);
+  
+  outfile_h << np << " " << ne << " " << nse << "\n";
+  outfile_h << "2"     << "\n";
+  outfile_h << "303 " << nse << "\n";
+  outfile_h << "504 " << ne << "\n";
+  
+  for (i = 1; i <= np; i++)
+    {
+      const Point3d & p = mesh.Point(i);
+      
+      outfile_n << i << " -1 ";
+      outfile_n << p.X() << " ";
+      outfile_n << p.Y() << " ";
+      outfile_n << p.Z() << "\n";
+    }
+
+  for (i = 1; i <= ne; i++)
+    {
+      Element el = mesh.VolumeElement(i);
+      if (inverttets) el.Invert();
+      sprintf( str, "5%02d", (int)el.GetNP() );
+      outfile_e << i << " " << el.GetIndex() << " " << str <<  "  ";
+      for (j = 1; j <= el.GetNP(); j++)
+	{
+	  outfile_e << " ";
+	  outfile_e << el.PNum(j);
+	}
+      outfile_e << "\n";
+    }
+
+  for (i = 1; i <= nse; i++)
+    {
+      Element2d el = mesh.SurfaceElement(i);
+      if (invertsurf) el.Invert();
+      sprintf( str, "3%02d", (int)el.GetNP() );
+      {
+	  INDEX_3 i3;
+	  for (j = 1; j <= 3; j++) i3.I(j) = el.PNum(j);
+	  i3.Sort();
+	  
+	  int elind = face2volelement.Get(i3);
+          outfile_b << i << " " << mesh.GetFaceDescriptor(el.GetIndex()).BCProperty() << 
+         " " << elind << " 0 "  << str << "    ";
+      }
+      for (j = 1; j <= el.GetNP(); j++)
+	{
+	  outfile_b << " ";
+	  outfile_b << el.PNum(j);
+	}
+      outfile_b << "\n";
+    }
+}
+
+}
diff --git a/contrib/Netgen/libsrc/interface/writefeap.cpp b/contrib/Netgen/libsrc/interface/writefeap.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..85681aa0cf63d1ad1186864a8aefaf4f01aa5bb8
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writefeap.cpp
@@ -0,0 +1,220 @@
+//
+// Write FEAP file
+// FEAP by Bob Taylor, Berkely
+//
+// contact Peter Wriggers or Albrecht Rieger, Hannover
+// rieger@ibnm.uni-hannover.de
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+
+#include "writeuser.hpp"
+
+
+void WriteFEAPFormat (const Mesh & mesh,
+		      const string & filename)
+  
+{
+  // Feap format by A. Rieger 
+  // rieger@ibnm.uni-hannover.de
+
+  int inverttets = mparam.inverttets;
+  //int invertsurf = mparam.inverttrigs;
+
+  int i, j;
+
+  double scale = 1;   // globflags.GetNumFlag ("scale", 1);
+  
+  ofstream outfile(filename.c_str());
+ 
+  outfile << "feap" << "\n";
+  outfile << mesh.GetNP();
+  outfile << ",";
+  outfile << mesh.GetNE();
+  outfile << ",";
+  outfile << "1,3,3,4" << "\n" << "\n"; 
+  outfile << "!numnp,numel,nummat,ndm,ndf,nen";
+  outfile << "\n";
+      
+  outfile << "\n" << "\n";
+  outfile << "!node,,         X           Y           Z" << "\n";
+  outfile << "COOR" << "\n";
+  outfile.precision(4);
+  outfile.setf (ios::fixed, ios::floatfield);
+  outfile.setf (ios::showpoint);
+
+  for (i = 1; i <= mesh.GetNP(); i++)
+    {
+      outfile.width(5);
+      outfile << i;
+      outfile << ",,";
+      outfile.width(10);
+      outfile << mesh.Point(i)(0)/scale << "  ";
+      outfile.width(10);
+      outfile << mesh.Point(i)(1)/scale << "  ";
+      outfile.width(10);
+      outfile << mesh.Point(i)(2)/scale << "\n";
+    }   
+      
+  outfile << "\n" << "\n";
+  outfile << "!elm,,mat,     n1      n2      n3      n4" << "\n";
+  outfile << "ELEM" << "\n";
+
+  for (i = 1; i <= mesh.GetNE(); i++)
+    {
+      Element el = mesh.VolumeElement(i);
+      if (inverttets)
+	el.Invert();
+
+
+      outfile.width(5);
+      outfile << i;
+      outfile << ",,";
+      outfile << el.GetIndex();
+      outfile << ",";
+
+
+      for (j = 1; j <= el.NP(); j++)
+	{
+	  outfile.width(8);
+	  outfile << el.PNum(j);
+	}
+      outfile << "\n";
+    }
+      
+  outfile << "\n" << "\n";
+      
+      
+  /*
+
+  //outfile << "SLOA" << "\n";
+  //outfile << "2,3,3" << "\n";
+  //outfile << GetNSE() << "\n";
+  outfile << "selm" << "\n" << GetNSE() << "\n";
+  for (i = 1; i <= GetNSE(); i++)
+  {
+  if (SurfaceElement(i).GetIndex())
+  {
+  outfile.width(8);
+  outfile << facedecoding.Get(SurfaceElement(i).GetIndex ()).surfnr;
+  //outfile.width(8);	  
+  //outfile << facedecoding.Get(SurfaceElement(i).GetIndex ()).domin;
+  //outfile.width(8);	  
+  //outfile << facedecoding.Get(SurfaceElement(i).GetIndex ()).domout;
+  }
+  else
+  outfile << "       0       0       0";
+  
+  
+  Element2d sel = SurfaceElement(i);
+  if (invertsurf)
+  sel.Invert();
+  //outfile.width(8);
+  //outfile << sel.GetNP();
+  //if (facedecoding.Get(SurfaceElement(i).GetIndex ()).surfnr == 4)
+  //{
+  for (j = 1; j <= sel.GetNP(); j++)
+  {
+  outfile.width(8);	  
+  outfile << sel.PNum(j);
+  }
+  //outfile.width(8);	
+  //outfile << "0.0";
+  //outfile.width(8);	
+  //outfile << "0.0";
+  //outfile.width(8);	
+  //outfile << "1.0" << "\n";
+  //}
+  outfile << "\n";
+  //outfile << endl;
+  }
+  */
+
+
+
+  // BEGIN CONTACT OUTPUT
+  /*      
+	  int masterindex, slaveindex;
+	  cout << "Master Surface index = ";
+	  cin >> masterindex;
+	  cout << "Slave Surface index  = ";
+	  cin >> slaveindex;
+
+
+	  // CONTACT SURFACE 1
+	  outfile << "\n";
+	  outfile << "\n";
+	  outfile << "surface,1" << "\n";;
+	  outfile.width(6);
+	  outfile << "tria" << "\n";;
+	  outfile.width(13);
+	  outfile << "facet" << "\n";;
+	  zz = 0;
+	  for (i = 1; i <= mesh.GetNSE(); i++)
+	  {
+	  Element2d sel = mesh.SurfaceElement(i);
+	  if (invertsurf)
+	  sel.Invert();
+	  if (mesh.GetFaceDescriptor(sel.GetIndex ()).BCProperty() == masterindex)
+	  {
+	  zz++;
+	  outfile.width(14);
+	  outfile << zz;
+	  outfile << ",,";
+	  for (j = 1; j <= sel.GetNP(); j++)
+	  {
+	  outfile << sel.PNum(j);
+	  outfile << ",";
+	  }
+	  outfile << "\n";
+	  }
+	  }
+
+
+	  // CONTACT SURFACE 2
+	  outfile << "\n";
+	  outfile << "\n";
+	  outfile << "surface,2" << "\n";;
+	  outfile.width(6);
+	  outfile << "tria" << "\n";;
+	  outfile.width(13);
+	  outfile << "facet" << "\n";;
+	  zz = 0;
+	  for (i = 1; i <= mesh.GetNSE(); i++)
+	  {
+	  
+	  Element2d sel = mesh.SurfaceElement(i);
+	  if (invertsurf)
+	  sel.Invert();
+	  if (mesh.GetFaceDescriptor(sel.GetIndex ()).BCProperty() == slaveindex)
+	  {
+	  zz++;
+	  outfile.width(14);
+	  outfile << zz;
+	  outfile << ",,";
+	  for (j = 1; j <= sel.GetNP(); j++)
+	  {
+	  outfile << sel.PNum(j);
+	  outfile << ",";
+	  }
+	  outfile << "\n";
+	  }
+	  }
+      
+	  outfile << "\n";
+	  outfile << "\n";
+  */      
+      
+  // END CONTACT OUTPUT
+
+  cout << "done" << endl;
+}
+}
diff --git a/contrib/Netgen/libsrc/interface/writefluent.cpp b/contrib/Netgen/libsrc/interface/writefluent.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c5dac392f67ddfe7673491beb1e37bbc12468b8e
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writefluent.cpp
@@ -0,0 +1,193 @@
+//
+//  Write Fluent file
+//  Johannes Gerstmayr, University Linz
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+
+#include "writeuser.hpp"
+
+
+
+void WriteFluentFormat (const Mesh & mesh,
+			const string & filename)
+
+{
+  cout << "start writing fluent export" << endl;
+      
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+  int nse = mesh.GetNSE();
+  int i, j;
+
+  ofstream outfile (filename.c_str());
+  char str[100];
+
+  outfile.precision(6);
+  //outfile.setf (ios::fixed, ios::floatfield);
+  //outfile.setf (ios::showpoint);
+      
+  outfile << "(0 \"Exported file from NETGEN \")" << endl;
+  outfile << "(0 \"Dimension:\")" << endl;
+  outfile << "(2 3)" << endl << endl;
+
+  outfile << "(0 \"Nodes:\")" << endl;
+
+  //number of nodes:
+  sprintf(str,"(10 (0 1 %x 1))",np); //hexadecimal!!!
+  outfile << str << endl;
+
+  //nodes of zone 1:
+  sprintf(str,"(10 (7 1 %x 1)(",np); //hexadecimal!!!
+  outfile << str << endl;
+  for (i = 1; i <= np; i++)
+    {
+      const Point3d & p = mesh.Point(i);
+
+      //outfile.width(10);
+      outfile << p.X() << " ";
+      outfile << p.Y() << " ";
+      outfile << p.Z() << "\n";
+    }
+  outfile << "))" << endl << endl;
+
+  //write faces with elements
+
+  outfile << "(0 \"Faces:\")" << endl;
+
+  Element2d face, face2;
+  int i2, j2;
+  Array<INDEX_3> surfaceelp;
+  Array<int> surfaceeli;
+  Array<int> locels;
+
+  //no cells=no tets
+  //no faces=2*tets
+
+  int noverbface = 2*ne-nse/2;
+      
+  sprintf(str,"(13 (0 1 %x 0))",(noverbface+nse)); //hexadecimal!!!
+  outfile << str << endl;
+      
+  sprintf(str,"(13 (4 1 %x 2 3)(",noverbface); //hexadecimal!!!
+  outfile << str << endl;
+
+  const_cast<Mesh&> (mesh).BuildElementSearchTree();
+
+  for (i = 1; i <= ne; i++)
+    {
+      if (ne > 2000)
+	{
+	  if (i%2000 == 0)
+	    {
+	      cout << (double)i/(double)ne*100. << "%" << endl;
+	    }
+	}
+
+      Element el = mesh.VolumeElement(i);
+      //if (inverttets)
+      //  el.Invert();
+	  
+      //outfile << el.GetIndex() << "    ";
+      if (el.GetNP() != 4) {cout << "only tet-meshes supported in write fluent!" << endl;}
+	  
+      //faces:
+	  
+      Box3d box;
+      el.GetBox(mesh.Points(), box);
+      box.IncreaseRel(1e-6);
+
+      mesh.GetIntersectingVolEls(box.PMin(),box.PMax(),locels);
+      int nel = locels.Size();
+      int locind;
+
+      //cout << "nel=" << nel << endl;
+
+      for (j = 1; j <= el.GetNFaces(); j++)
+	{
+	  el.GetFace(j, face);
+	  face.Invert();
+	  int eli2 = 0;
+	  int stopsig = 0;
+	      
+	  for (i2 = 1; i2 <= nel; i2++)
+	    {
+	      locind = locels.Get(i2);
+	      //cout << "  locind=" << locind << endl;
+
+	      Element el2 = mesh.VolumeElement(locind);
+	      //if (inverttets)
+	      //  el2.Invert();
+
+	      for (j2 = 1; j2 <= el2.GetNFaces(); j2++)
+		{
+		  el2.GetFace(j2, face2);
+
+		  if (face2.HasFace(face)) {eli2 = locind; stopsig = 1; break;}
+		}
+	      if (stopsig) break;
+	    }
+	      
+	  if (eli2==i) cout << "error in WRITE_FLUENT!!!" << endl;
+	      
+	  if (eli2 > i) //dont write faces two times!
+	    {
+	      //i: left cell, eli: right cell
+	      outfile << hex << face.PNum(2) << " "
+		<< hex << face.PNum(1) << " "
+		<< hex << face.PNum(3) << " "
+		<< hex << i  << " "
+		<< hex << eli2 << "\n";
+	    }
+	  if (eli2 == 0) 
+	    {
+	      surfaceelp.Append(INDEX_3(face.PNum(2),face.PNum(1),face.PNum(3)));
+	      surfaceeli.Append(i);
+	    }
+	}
+    }
+  outfile << "))" << endl;
+      
+  sprintf(str,"(13 (2 %x %x 3 3)(",(noverbface+1),noverbface+nse); //hexadecimal!!!
+  outfile << str << endl;
+
+  for (i = 1; i <= surfaceelp.Size(); i++)
+    {
+      outfile << hex << surfaceelp.Get(i).I1() << " "
+	      << hex << surfaceelp.Get(i).I2() << " "
+	      << hex << surfaceelp.Get(i).I3() << " "
+	      << hex << surfaceeli.Get(i) << " " << 0 << "\n";
+    }
+
+  outfile << "))" << endl << endl;
+
+  outfile << "(0 \"Cells:\")" << endl;
+      
+  sprintf(str,"(12 (0 1 %x 0))",ne); //hexadecimal!!!
+  outfile << str << endl;
+
+  sprintf(str,"(12 (1 1 %x 1 2))",ne); //hexadecimal!!!
+  outfile << str << endl << endl;
+
+
+
+
+  outfile << "(0 \"Zones:\")\n"
+	  << "(45 (1 fluid fluid)())\n"
+    //      << "(45 (2 velocity-inlet velocity_inlet.1)())\n"
+    //      << "(45 (3 pressure-outlet pressure_outlet.2)())\n"
+	  << "(45 (2 wall wall)())\n"
+	  << "(45 (4 interior default-interior)())\n" << endl;
+
+  cout << "done" << endl;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/interface/writegmsh.cpp b/contrib/Netgen/libsrc/interface/writegmsh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..93def677835846f85917ccf3243f841e8d9480e6
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writegmsh.cpp
@@ -0,0 +1,200 @@
+/*************************************
+ * Write Gmsh file
+ * First issue the 04/26/2004 by Paul CARRICO (paul.carrico@free.fr)
+ * At the moment, the GMSH format is available for
+ * linear tetrahedron elements i.e. in 3D
+ * (based on Neutral Format)
+ *
+ * Second issue the 05/05/2004 by Paul CARRICO
+ * Thanks to Joachim Schoeberl for the correction of a minor bug
+ * the 2 initial Gmsh Format (i.e. volume format and surface format) are group together)
+ * in only one file
+ **************************************/
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+
+
+/*
+ *  GMSH mesh format
+ *  points, elements, surface elements and physical entities
+ */
+
+void WriteGmshFormat (const Mesh & mesh,
+			 const CSGeometry & geom,
+			 const string & filename)
+{
+  ofstream outfile (filename.c_str());
+  outfile.precision(6);
+  outfile.setf (ios::fixed, ios::floatfield);
+  outfile.setf (ios::showpoint);
+
+  int np = mesh.GetNP();  /// number of point
+  int ne = mesh.GetNE();  /// number of element
+  int nse = mesh.GetNSE();  /// number of surface element (BC)
+  int i, j, k, l;
+
+
+  /*
+   * 3D section : Linear volume elements (only tetrahedra)
+   */
+
+   if (ne > 0 && mesh.VolumeElement(1).GetNP() == 4)
+      {
+      cout << "Write GMSH Format \n";
+      cout << "The GMSH format is available for linear tetrahedron elements only in 3D\n" << endl;
+
+      int inverttets = mparam.inverttets;
+      int invertsurf = mparam.inverttrigs;
+
+
+      /// Write nodes
+      outfile << "$NOD\n";
+      outfile << np << "\n";
+  
+      for (i = 1; i <= np; i++)
+          {
+          const Point3d & p = mesh.Point(i);
+          outfile << i << " "; /// node number
+          outfile << p.X() << " ";
+          outfile << p.Y() << " ";
+          outfile << p.Z() << "\n";
+          }
+      outfile << "$ENDNOD\n";
+
+      /// write elements
+      outfile << "$ELM\n";
+      outfile << ne + nse << "\n";  ////  number of elements + number of surfaces BC
+
+     for (i = 1; i <= nse; i++)
+         {
+         Element2d el = mesh.SurfaceElement(i);
+         if (invertsurf) el.Invert();
+         outfile << i;
+         outfile << " ";
+         outfile << "2";
+         outfile << " ";
+         outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << " ";
+         /// that means that physical entity = elementary entity (arbitrary approach)
+         outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << " ";
+         outfile << "3";
+         outfile << " ";
+                 for (j = 1; j <= el.GetNP(); j++)
+                     {
+                     outfile << " ";
+                     outfile << el.PNum(j);
+                     }
+                     outfile << "\n";
+         }
+
+
+         for (i = 1; i <= ne; i++)
+             {
+             Element el = mesh.VolumeElement(i);
+             if (inverttets) el.Invert();
+             outfile << nse + i; /// element number
+             outfile << " ";
+             outfile << "4"; /// element type i.e. Tetraedron == 4
+             outfile << " ";
+             outfile << 100000 + el.GetIndex();
+             /// that means that physical entity = elementary entity (arbitrary approach)
+             outfile << " ";
+             outfile << 100000 + el.GetIndex();   /// volume number
+             outfile << " ";
+             outfile << "4";  /// number of nodes i.e. 4 for a tetrahedron
+                                                                                                        
+             for (j = 1; j <= el.GetNP(); j++)
+                 {
+                 outfile << " ";
+                 outfile << el.PNum(j);
+                 }
+             outfile << "\n";
+             }
+
+
+             outfile << "$ENDELM\n";
+   }
+
+   /*
+    * End of 3D section
+    */
+
+
+
+
+
+  /*
+   * 2D section : available for triangles and quadrangles
+   */
+      else if (ne == 0)   /// means that there's no 3D element
+              {
+              cout << "\n Write Gmsh Surface Mesh (triangle and/or quadrangles)" << endl;
+
+              /// Write nodes
+              outfile << "$NOD\n";
+              outfile << np << "\n";
+
+              for (i = 1; i <= np; i++)
+              {
+              const Point3d & p = mesh.Point(i);
+              outfile << i << " "; /// node number
+              outfile << p.X() << " ";
+              outfile << p.Y() << " ";
+              outfile << p.Z() << "\n";
+              }
+              outfile << "$ENDNOD\n";
+
+
+              /// write triangles & quadrangles
+              outfile << "$ELM\n";
+              outfile << nse << "\n";
+
+              for (k = 1; k <= nse; k++)
+              {
+              const Element2d & el = mesh.SurfaceElement(k);
+
+
+              outfile << k;
+              outfile << " ";
+              outfile << (el.GetNP()-1);   // 2 for a triangle and 3 for a quadrangle
+              outfile << " ";
+              outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << " ";
+              /// that means that physical entity = elementary entity (arbitrary approach)
+              outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << " ";
+              outfile << (el.GetNP());    // number of node per surfacic element
+              outfile << " ";
+
+              for (l = 1; l <= el.GetNP(); l++)
+                  {
+                  outfile << " ";
+                  outfile << el.PNum(l);
+                  }
+	              outfile << "\n";
+		  
+               }
+               outfile << "$ENDELM$ \n";
+    }
+
+   /*
+    * End of 2D section
+    */
+
+     else
+    {
+    cout << " Invalide element type for Gmsh volume Format !\n";
+    }
+
+
+}
+}
+
+
diff --git a/contrib/Netgen/libsrc/interface/writegmsh2.cpp b/contrib/Netgen/libsrc/interface/writegmsh2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5ec3dc8f0c4c12b0d27843152efdb3899c015ef1
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writegmsh2.cpp
@@ -0,0 +1,261 @@
+/*! \file writegmsh2.cpp
+*  \brief Export Netgen Mesh in the GMSH v2.xx File format
+*  \author Philippose Rajan
+*  \date 02 November 2008
+*
+*  This function extends the export capabilities of
+*  Netgen to include the GMSH v2.xx File Format.
+*
+*  Current features of this function include:
+*
+*  1. Exports Triangles, Quadrangles and Tetrahedra \n
+*  2. Supports upto second order elements of each type
+*
+*/
+
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+   // Mapping of entities from Netgen definitions to GMSH definitions
+   enum GMSH_ELEMENTS {GMSH_TRIG = 2, GMSH_TRIG6 = 9,
+      GMSH_QUAD = 3, GMSH_QUAD8 = 16,
+      GMSH_TET = 4, GMSH_TET10 = 11};
+   const int triGmsh[7] = {0,1,2,3,6,4,5};
+   const int quadGmsh[9] = {0,1,2,3,4,5,8,6,7};
+   const int tetGmsh[11] = {0,1,2,3,4,5,8,6,7,10,9};
+
+
+   /*! GMSH v2.xx mesh format export function
+   *
+   *  This function extends the export capabilities of
+   *  Netgen to include the GMSH v2.xx File Format.
+   *
+   *  Current features of this function include:
+   *
+   *  1. Exports Triangles, Quadrangles and Tetrahedra \n
+   *  2. Supports upto second order elements of each type
+   *
+   */
+   void WriteGmsh2Format (const Mesh & mesh,
+      const CSGeometry & geom,
+      const string & filename)
+   {
+      ofstream outfile (filename.c_str());
+      outfile.precision(6);
+      outfile.setf (ios::fixed, ios::floatfield);
+      outfile.setf (ios::showpoint);
+
+      int np = mesh.GetNP();  /// number of points in mesh
+      int ne = mesh.GetNE();  /// number of 3D elements in mesh
+      int nse = mesh.GetNSE();  /// number of surface elements (BC)
+      int i, j, k, l;
+
+
+      /*
+      * 3D section : Volume elements (currently only tetrahedra)
+      */
+
+      if ((ne > 0)
+         && (mesh.VolumeElement(1).GetNP() <= 10)
+         && (mesh.SurfaceElement(1).GetNP() <= 6))
+      {
+         cout << "Write GMSH v2.xx Format \n";
+         cout << "The GMSH v2.xx export is currently available for elements upto 2nd Order\n" << endl;
+
+         int inverttets = mparam.inverttets;
+         int invertsurf = mparam.inverttrigs;
+
+         /// Prepare GMSH 2.0 file (See GMSH 2.0 Documentation)
+         outfile << "$MeshFormat\n";
+         outfile << (float)2.0 << " "
+            << (int)0 << " "
+            << (int)sizeof(double) << "\n";
+         outfile << "$EndMeshFormat\n";
+
+         /// Write nodes
+         outfile << "$Nodes\n";
+         outfile << np << "\n";
+
+         for (i = 1; i <= np; i++)
+         {
+            const Point3d & p = mesh.Point(i);
+            outfile << i << " "; /// node number
+            outfile << p.X() << " ";
+            outfile << p.Y() << " ";
+            outfile << p.Z() << "\n";
+         }
+
+         outfile << "$EndNodes\n";
+
+         /// write elements (both, surface elements and volume elements)
+         outfile << "$Elements\n";
+         outfile << ne + nse << "\n";  ////  number of elements + number of surfaces BC
+
+         for (i = 1; i <= nse; i++)
+         {
+            int elType = 0;
+
+            Element2d el = mesh.SurfaceElement(i);
+            if(invertsurf) el.Invert();
+
+            if(el.GetNP() == 3) elType = GMSH_TRIG;	//// GMSH Type for a 3 node triangle
+            if(el.GetNP() == 6) elType = GMSH_TRIG6;  //// GMSH Type for a 6 node triangle
+            if(elType == 0)
+            {
+               cout << " Invalid surface element type for Gmsh 2.0 3D-Mesh Export Format !\n";
+               return;
+            }
+
+            outfile << i;
+            outfile << " ";
+            outfile << elType;
+            outfile << " ";
+            outfile << "2";                  //// Number of tags (2 => Physical and elementary entities)
+            outfile << " ";
+            outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << " ";
+            /// that means that physical entity = elementary entity (arbitrary approach)
+            outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << " ";
+            for (j = 1; j <= el.GetNP(); j++)
+            {
+               outfile << " ";
+               outfile << el.PNum(triGmsh[j]);
+            }
+            outfile << "\n";
+         }
+
+
+         for (i = 1; i <= ne; i++)
+         {
+            int elType = 0;
+
+            Element el = mesh.VolumeElement(i);
+            if (inverttets) el.Invert();
+
+            if(el.GetNP() == 4) elType = GMSH_TET;    //// GMSH Element type for 4 node tetrahedron
+            if(el.GetNP() == 10) elType = GMSH_TET10; //// GMSH Element type for 10 node tetrahedron
+            if(elType == 0)
+            {
+               cout << " Invalid volume element type for Gmsh 2.0 3D-Mesh Export Format !\n";
+               return;
+            }
+
+            outfile << nse + i;                       //// element number (Remember to add on surface elements)
+            outfile << " ";
+            outfile << elType;
+            outfile << " ";
+            outfile << "2";                   //// Number of tags (2 => Physical and elementary entities)
+            outfile << " ";
+            outfile << 100000 + el.GetIndex();
+            /// that means that physical entity = elementary entity (arbitrary approach)
+            outfile << " ";
+            outfile << 100000 + el.GetIndex();   /// volume number
+            outfile << " ";
+            for (j = 1; j <= el.GetNP(); j++)
+            {
+               outfile << " ";
+               outfile << el.PNum(tetGmsh[j]);
+            }
+            outfile << "\n";
+         }
+         outfile << "$EndElements\n";
+      }
+      /*
+      * End of 3D section
+      */
+
+
+      /*
+      * 2D section : available for triangles and quadrangles
+      *              upto 2nd Order
+      */
+      else if(ne == 0)   /// means that there's no 3D element
+      {
+         cout << "\n Write Gmsh v2.xx Surface Mesh (triangle and/or quadrangles upto 2nd Order)" << endl;
+
+         /// Prepare GMSH 2.0 file (See GMSH 2.0 Documentation)
+         outfile << "$MeshFormat\n";
+         outfile << (float)2.0 << " "
+            << (int)0 << " "
+            << (int)sizeof(double) << "\n";
+         outfile << "$EndMeshFormat\n";
+
+         /// Write nodes
+         outfile << "$Nodes\n";
+         outfile << np << "\n";
+
+         for (i = 1; i <= np; i++)
+         {
+            const Point3d & p = mesh.Point(i);
+            outfile << i << " "; /// node number
+            outfile << p.X() << " ";
+            outfile << p.Y() << " ";
+            outfile << p.Z() << "\n";
+         }
+         outfile << "$EndNodes\n";
+
+         /// write triangles & quadrangles
+         outfile << "$Elements\n";
+         outfile << nse << "\n";
+
+         for (k = 1; k <= nse; k++)
+         {
+            int elType = 0;
+
+            const Element2d & el = mesh.SurfaceElement(k);
+
+            if(el.GetNP() == 3) elType = GMSH_TRIG;   //// GMSH Type for a 3 node triangle
+            if(el.GetNP() == 6) elType = GMSH_TRIG6;  //// GMSH Type for a 6 node triangle
+            if(el.GetNP() == 4) elType = GMSH_QUAD;   //// GMSH Type for a 4 node quadrangle
+            if(el.GetNP() == 8) elType = GMSH_QUAD8;  //// GMSH Type for an 8 node quadrangle
+            if(elType == 0)
+            {
+               cout << " Invalid surface element type for Gmsh 2.0 2D-Mesh Export Format !\n";
+               return;
+            }
+
+            outfile << k;
+            outfile << " ";
+            outfile << elType;
+            outfile << " ";
+            outfile << "2";
+            outfile << " ";
+            outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << " ";
+            /// that means that physical entity = elementary entity (arbitrary approach)
+            outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << " ";
+            for (l = 1; l <= el.GetNP(); l++)
+            {
+               outfile << " ";
+               if((elType == GMSH_TRIG) || (elType == GMSH_TRIG6))
+               {
+                  outfile << el.PNum(triGmsh[l]);
+               }
+               else if((elType == GMSH_QUAD) || (elType == GMSH_QUAD8))
+               {
+                  outfile << el.PNum(quadGmsh[l]);
+               }
+            }
+            outfile << "\n";
+         }
+         outfile << "$EndElements\n";
+      }
+      /*
+      * End of 2D section
+      */
+
+      else
+      {
+         cout << " Invalid element type for Gmsh v2.xx Export Format !\n";
+      }
+   } // End: WriteGmsh2Format
+} // End: namespace netgen
+
+
diff --git a/contrib/Netgen/libsrc/interface/writejcm.cpp b/contrib/Netgen/libsrc/interface/writejcm.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc00955d8f4ed9691c7a6dd71fc69b71721a8da0
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writejcm.cpp
@@ -0,0 +1,430 @@
+//
+//  Write JCMwave file
+//  07.07.2005, Sven Burger, ZIB Berlin
+//
+
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+#include <sys/stat.h>
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+void WriteJCMFormat (const Mesh & mesh,
+                     const CSGeometry & geom,
+                     const string & filename)
+{
+  if (mesh.GetDimension() != 3)
+  {
+    cout <<"\n Error: Dimension 3 only supported by this output format!"<<endl;
+    return;
+  }
+
+  int bc_at_infinity = 0;
+  int i, j, jj, ct(0), counter;
+  double dx1, dx2, dx3, dy1, dy2, dy3, dz1, dz2, dz3, vol;
+
+  // number of points
+  int np = mesh.GetNP();
+
+  // Identic points
+  Array<int,1> identmap1, identmap2, identmap3;
+  mesh.GetIdentifications().GetMap(1, identmap1);
+  mesh.GetIdentifications().GetMap(2, identmap2);
+  mesh.GetIdentifications().GetMap(3, identmap3);
+
+  // number of volume elements
+  int ne = mesh.GetNE();
+  int ntets = 0;
+  int nprisms = 0;
+  for (i = 1; i <= ne; i++)
+  {
+    Element el = mesh.VolumeElement(i);
+    if (el.GetNP() == 4)
+    {
+      ntets++;
+      // Check that no two points on a tetrahedron are identified with each other
+      for (j = 1; j <= 4; j++)
+        for (jj = 1; jj <=4; jj++)
+        {
+          if (identmap1.Elem(el.PNum(j)) == el.PNum(jj))
+          {
+            cout << "\n Error: two points on a tetrahedron identified (1) with each other"
+                 << "\n REFINE MESH !" << endl;
+            return;
+          }
+          if (identmap2.Elem(el.PNum(j)) == el.PNum(jj))
+          {
+            cout << "\n Error: two points on a tetrahedron identified (2) with each other"
+                 << "\n REFINE MESH !" << endl;
+            return;
+          }
+          if (identmap3.Elem(el.PNum(j)) == el.PNum(jj))
+          {
+            cout << "\n Error: two points on a tetrahedron identified (3) with each other"
+                 << "\n REFINE MESH !" << endl;
+            return;
+          }
+        }      
+      
+    }
+    else if (el.GetNP() == 6)
+      nprisms++;
+  }
+  if ( ne != (ntets+nprisms))
+  {
+    cout<< "\n Error in determining number of volume elements!\n"
+        << "\n Prisms and tetrahedra only implemented in the JCMwave format!\n"<<endl;
+    return;
+  }
+
+  if (nprisms > 0)
+    cout << " Please note: Boundaries at infinity have to carry the bc-attribute '-bc="
+         << bc_at_infinity <<"'."<<endl; 
+
+  // number of surface elements
+  int nse = mesh.GetNSE();
+  // number of boundary triangles
+  int nbtri = 0;
+  // number of boundary quadrilaterals
+  int nbquad = 0;
+  // array with 1 if point on any tetra, 0 else 
+  // this is needed in order to arrange the prism points in the right order
+  Array<int,1> pointsOnTetras;
+  pointsOnTetras.SetSize (mesh.GetNP());
+  pointsOnTetras = 0;
+  for (i = 1; i <= ne; i++)
+  {
+    Element el = mesh.VolumeElement(i);
+    if (el.GetNP() == 4)
+    {
+      for (j = 1; j <= 4; j++)
+        pointsOnTetras.Set(el.PNum(j).GetInt(),1);     
+    }
+  }
+
+  // number of boundary triangles and boundary quadrilaterals
+  for (i = 1; i <= nse; i++)
+  {
+    Element2d el = mesh.SurfaceElement(i);
+    if (el.GetNP() == 3 &&
+        ( mesh.GetFaceDescriptor (el.GetIndex()).DomainIn()==0  ||
+          mesh.GetFaceDescriptor (el.GetIndex()).DomainOut()==0 ) )
+      nbtri++;
+    else if (el.GetNP() == 4 &&
+             ( mesh.GetFaceDescriptor (el.GetIndex()).DomainIn()==0 ||
+               mesh.GetFaceDescriptor (el.GetIndex()).DomainOut()==0 ) )
+      nbquad++;
+  }
+  
+  ofstream outfile (filename.c_str());
+  outfile.precision(6);
+  outfile.setf (ios::fixed, ios::floatfield);
+  outfile.setf (ios::showpoint);
+  
+  outfile << "/* <BLOBHead>\n";
+  outfile << "__BLOBTYPE__=Grid\n";
+  outfile << "__OWNER__=JCMwave\n";
+  outfile << "<I>SpaceDim=3\n";
+  outfile << "<I>ManifoldDim=3\n";
+  outfile << "<I>NRefinementSteps=0\n";
+  outfile << "<I>NPoints="<<np<<"\n";
+  outfile << "<I>NTetrahedra="<<ntets<<"\n";
+  outfile << "<I>NPrisms="<<nprisms<<"\n";
+  outfile << "<I>NBoundaryTriangles="<<nbtri<<"\n";
+  outfile << "<I>NBoundaryQuadrilaterals="<<nbquad<<"\n";
+  outfile << "*/\n";
+  outfile << "\n";
+  outfile << "# output from Netgen\n\n";
+  int nDomains=mesh.GetNDomains();
+  for (i=1; i<=nDomains; i++)
+  {
+    if (mesh.GetMaterial(i))
+      outfile << "#" << mesh.GetMaterial(i) 
+              << ": Material ID = " 
+              << i << "\n";
+  }
+
+  outfile << "# Points\n";
+  cout << " Please note: The unit of length in the .geo file is assumed to be 'microns'."<<endl; 
+  for (i = 1; i <= np; i++)
+  {
+    const Point<3> & p = mesh.Point(i);
+    outfile << i << "\n";
+    outfile << p(0) << "e-6\n";
+    outfile << p(1) << "e-6\n";
+    outfile << p(2) << "e-6\n\n";
+  }
+
+  outfile << "\n";
+  outfile << "# Tetrahedra\n";
+  counter = 0;
+  for (i = 1; i <= ne; i++)
+  {
+    Element el = mesh.VolumeElement(i);
+    if (el.GetNP() == 4)
+    {
+      counter++;
+      dx1 = mesh.Point(el.PNum(2))(0) - mesh.Point(el.PNum(1))(0);
+      dx2 = mesh.Point(el.PNum(3))(0) - mesh.Point(el.PNum(1))(0);
+      dx3 = mesh.Point(el.PNum(4))(0) - mesh.Point(el.PNum(1))(0);
+      dy1 = mesh.Point(el.PNum(2))(1) - mesh.Point(el.PNum(1))(1);
+      dy2 = mesh.Point(el.PNum(3))(1) - mesh.Point(el.PNum(1))(1);
+      dy3 = mesh.Point(el.PNum(4))(1) - mesh.Point(el.PNum(1))(1);
+      dz1 = mesh.Point(el.PNum(2))(2) - mesh.Point(el.PNum(1))(2);
+      dz2 = mesh.Point(el.PNum(3))(2) - mesh.Point(el.PNum(1))(2);
+      dz3 = mesh.Point(el.PNum(4))(2) - mesh.Point(el.PNum(1))(2);
+      vol = (dy1*dz2-dz1*dy2)*dx3 + (dz1*dx2-dx1*dz2)*dy3 + (dx1*dy2-dy1*dx2)*dz3;
+
+      if ( vol > 0 )
+        for (j = 1; j <= 4; j++)
+          outfile << el.PNum(j)<<"\n";
+      else
+      {
+        for (j = 2; j >= 1; j--)
+          outfile << el.PNum(j)<<"\n";
+        for (j = 3; j <= 4; j++)
+          outfile << el.PNum(j)<<"\n";
+      }  
+      outfile << el.GetIndex() << "\n\n";
+    }
+  }
+  if ( counter != ntets)
+  {
+    cout<< "\n Error in determining number of tetras!\n"<<endl;
+    return;
+  }
+
+  outfile << "\n";
+  outfile << "# Prisms\n";
+  counter = 0;
+  for (i = 1; i <= ne; i++)
+  {
+    Element el = mesh.VolumeElement(i);
+    if (el.GetNP() == 6)
+    {
+      counter++;
+      dx1 = mesh.Point(el.PNum(2))(0) - mesh.Point(el.PNum(1))(0);
+      dx2 = mesh.Point(el.PNum(3))(0) - mesh.Point(el.PNum(1))(0);
+      dx3 = mesh.Point(el.PNum(4))(0) - mesh.Point(el.PNum(1))(0);
+      dy1 = mesh.Point(el.PNum(2))(1) - mesh.Point(el.PNum(1))(1);
+      dy2 = mesh.Point(el.PNum(3))(1) - mesh.Point(el.PNum(1))(1);
+      dy3 = mesh.Point(el.PNum(4))(1) - mesh.Point(el.PNum(1))(1);
+      dz1 = mesh.Point(el.PNum(2))(2) - mesh.Point(el.PNum(1))(2);
+      dz2 = mesh.Point(el.PNum(3))(2) - mesh.Point(el.PNum(1))(2);
+      dz3 = mesh.Point(el.PNum(4))(2) - mesh.Point(el.PNum(1))(2);
+      vol = (dy1*dz2-dz1*dy2)*dx3 + (dz1*dx2-dx1*dz2)*dy3 + (dx1*dy2-dy1*dx2)*dz3;
+
+      if (pointsOnTetras.Get(el.PNum(1)) &&
+          pointsOnTetras.Get(el.PNum(2)) &&
+          pointsOnTetras.Get(el.PNum(3)))
+      {
+        if (vol > 0)
+          for (j = 1; j <= 6; j++)
+            outfile << el.PNum(j)<<"\n";
+        else
+        {
+          for (j = 3; j >= 1; j--)
+            outfile << el.PNum(j)<<"\n";
+          for (j = 6; j >= 4; j--)
+            outfile << el.PNum(j)<<"\n";
+        }
+      }
+      else if ( pointsOnTetras.Get(el.PNum(4)) &&
+                pointsOnTetras.Get(el.PNum(5)) &&
+                pointsOnTetras.Get(el.PNum(6))    )
+      {
+        if ( vol < 0 )
+        {
+          for (j = 4; j <= 6; j++)
+            outfile << el.PNum(j)<<"\n";
+          for (j = 1; j <= 3; j++)
+            outfile << el.PNum(j)<<"\n";
+        }
+        else
+        {
+          for (j = 6; j >= 4; j--)
+            outfile << el.PNum(j)<<"\n";
+          for (j = 3; j >= 1; j--)
+            outfile << el.PNum(j)<<"\n";
+        }
+      }
+      else 
+      {
+        cout << "\n Error in determining prism point numbering!\n"<<endl;
+        return;
+      }
+      outfile << el.GetIndex() << "\n\n";
+    }
+  }
+  if ( counter != nprisms)
+  {
+    cout<< "\n Error in determining number of prisms!\n"<<endl;
+    return;
+  }
+
+  int npid1 = 0;
+  int npid2 = 0;
+  int npid3 = 0;
+  for (i=1; i<=np; i++)
+  {
+    if (identmap1.Elem(i))
+      npid1++;
+    if (identmap2.Elem(i))
+      npid2++;
+    if (identmap3.Elem(i))
+      npid3++;
+  }
+
+  outfile << "\n";
+  outfile << "# Boundary triangles\n";  
+  outfile << "# Number of identified points in 1-direction: " << npid1 << "\n";
+  outfile << "# Number of identified points in 2-direction: " << npid2 << "\n";
+  outfile << "# Number of identified points in 3-direction: " << npid3 << "\n";
+  for (i = 1; i <= nse; i++)
+  {
+    Element2d el = mesh.SurfaceElement(i);
+    if (el.GetNP() == 3
+        && (mesh.GetFaceDescriptor (el.GetIndex()).DomainIn()==0
+            || mesh.GetFaceDescriptor (el.GetIndex()).DomainOut()==0))
+    {
+      outfile <<"# T\n";
+      for (j = 1; j <= 3; j++)
+        outfile << el.PNum(j)<<"\n";
+      if (mesh.GetFaceDescriptor (el.GetIndex()).BCProperty()==bc_at_infinity)
+        outfile << 1000 << "\n";      
+      else
+        outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << "\n";      
+      if (mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() == bc_at_infinity)
+        outfile << "-2\n\n";
+      else if (identmap1.Elem(el.PNum(1))
+               &&identmap1.Elem(el.PNum(2))
+               &&identmap1.Elem(el.PNum(3)))
+      {
+        outfile << "-1\n";
+        for (j = 1; j <= 3; j++)
+          outfile << identmap1.Elem(el.PNum(j))<<"\n";
+        outfile << "\n";
+      }
+      else if (identmap2.Elem(el.PNum(1))
+               &&identmap2.Elem(el.PNum(2))
+               &&identmap2.Elem(el.PNum(3)))
+      {
+        outfile << "-1\n";
+        for (j = 1; j <= 3; j++)
+          outfile << identmap2.Elem(el.PNum(j))<<"\n";
+        outfile << "\n";
+      }
+      else if (identmap3.Elem(el.PNum(1))
+               &&identmap3.Elem(el.PNum(2))
+               &&identmap3.Elem(el.PNum(3)))
+      {
+        outfile << "-1\n";
+        for (j = 1; j <= 3; j++)
+          outfile << identmap3.Elem(el.PNum(j))<<"\n";
+        outfile << "\n";
+      }
+      else
+        outfile << "1\n\n";
+        
+    }
+  }
+
+  outfile << "\n";
+  outfile << "# Boundary quadrilaterals\n";
+  for (i = 1; i <= nse; i++)
+  {
+    Element2d el = mesh.SurfaceElement(i);
+
+    if (el.GetNP() == 4
+        && (mesh.GetFaceDescriptor (el.GetIndex()).DomainIn()==0
+            || mesh.GetFaceDescriptor (el.GetIndex()).DomainOut()==0))
+    {
+      if      (pointsOnTetras.Get(el.PNum(1)) &&
+               pointsOnTetras.Get(el.PNum(2)))
+        ct = 0;
+      else if (pointsOnTetras.Get(el.PNum(2)) &&
+               pointsOnTetras.Get(el.PNum(3)))
+        ct = 1;
+      else if (pointsOnTetras.Get(el.PNum(3)) &&
+               pointsOnTetras.Get(el.PNum(4)))
+        ct = 2;
+      else if (pointsOnTetras.Get(el.PNum(4)) &&
+               pointsOnTetras.Get(el.PNum(1)))
+        ct = 3;
+      else
+        cout << "\nWarning: Quadrilateral with inconsistent points found!"<<endl;
+      
+      for (j = 1; j <= 4; j++)
+      {
+        jj = j + ct;
+        if ( jj >= 5 )
+          jj = jj - 4;
+        outfile << el.PNum(jj)<<"\n";
+      }
+      outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << "\n";      
+      if (mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() == bc_at_infinity)
+      {
+        outfile << "-2\n\n";
+        cout << "\nWarning: Quadrilateral at infinity found (this should not occur)!"<<endl;
+      }
+      else if ( identmap1.Elem(el.PNum(1)) &&
+                identmap1.Elem(el.PNum(2)) &&
+                identmap1.Elem(el.PNum(3)) &&
+                identmap1.Elem(el.PNum(4))    )
+      {
+        outfile << "-1\n";
+        for (j = 1; j <= 4; j++)
+        {
+          jj = j + ct;
+          if ( jj >= 5 )
+            jj = jj - 4;
+          outfile << identmap1.Elem(el.PNum(jj))<<"\n";
+        }
+        outfile << "\n";
+      }
+      else if ( identmap2.Elem(el.PNum(1)) &&
+                identmap2.Elem(el.PNum(2)) &&
+                identmap2.Elem(el.PNum(3)) &&
+                identmap2.Elem(el.PNum(4))    )
+      {
+        outfile << "-1\n";
+        for (j = 1; j <= 4; j++)
+        {
+          jj = j + ct;
+          if ( jj >= 5 )
+            jj = jj - 4;
+          outfile << identmap2.Elem(el.PNum(jj))<<"\n";
+        }
+        outfile << "\n";
+      }
+      else if ( identmap3.Elem(el.PNum(1)) &&
+                identmap3.Elem(el.PNum(2)) &&
+                identmap3.Elem(el.PNum(3)) &&
+                identmap3.Elem(el.PNum(4))    )
+      {
+        outfile << "-1\n";
+        for (j = 1; j <= 4; j++)
+        {
+          jj = j + ct;
+          if ( jj >= 5 )
+            jj = jj - 4;
+          outfile << identmap3.Elem(el.PNum(jj))<<"\n";
+        }
+        outfile << "\n";
+      }
+      else
+        outfile << "1\n\n";
+    }
+  }
+
+  cout << " JCMwave grid file written." << endl;
+}
+
+}
+
diff --git a/contrib/Netgen/libsrc/interface/writepermas.cpp b/contrib/Netgen/libsrc/interface/writepermas.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc46e87cd9c9e225d4cb2a961d4e27e570ac9ccb
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writepermas.cpp
@@ -0,0 +1,208 @@
+//
+// Write Permas file
+// for Intes GmbH, Stuttgart
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+#include <string>
+
+using namespace std;
+
+namespace netgen
+{
+#include "writeuser.hpp"
+    // Forward declarations (don't know, where to define them, sorry)
+    int addComponent(string &strComp, string &strSitu, ofstream &out);
+
+
+    // This should be the new function to export a PERMAS file
+    void WritePermasFormat (const Mesh &mesh, const string &filename, 
+                            string &strComp, string &strSitu) 
+    {
+        ofstream outfile (filename.c_str());
+        addComponent(strComp, strSitu, outfile);
+        WritePermasFormat ( mesh, filename);
+    }
+
+    void WritePermasFormat (const Mesh &mesh, const string &filename)
+    {
+        string strComp, strSitu;
+        ofstream outfile (filename.c_str());
+        
+        outfile.precision(8);
+
+        strSitu = strComp = "";
+        if (addComponent(strComp, strSitu, outfile) == 1) {
+            printf("Error while exporting PERMAS dat!\n");
+            return;
+        }
+    
+        int np = mesh.GetNP();
+        int ne = mesh.GetNE();
+        int nse = mesh.GetNSE();
+        int i, j, k;
+    
+        if (ne == 0)
+        {
+            // pure surface mesh
+            cout << "\nWrite Permas Surface Mesh" << endl;
+            
+            int elnr = 0;
+            for (j = 1; j <= 2; j++)
+            {
+                int nelp(0);
+                switch (j)
+                {
+                case 1:
+                    nelp = 3;
+                    outfile << "$ELEMENT TYPE = TRIA3  ESET = ALLQUAD" << endl;		  
+                    break;
+                case 2:
+                    nelp = 4;
+                    outfile << "$ELEMENT TYPE = QUAD4  ESET = ALLQUAD" << endl;		  
+                    break;
+                }
+                
+                for (i = 1; i <= nse; i++)
+                {
+                    const Element2d & el = mesh.SurfaceElement(i);
+                    if (el.GetNP() != nelp)
+                        continue;
+                    
+                    elnr++;
+                    outfile << elnr << "  ";
+                    for (k = 1; k <= nelp; k++)
+                        outfile << " " << el.PNum(k);
+                    outfile << endl;
+                    
+                }
+            }
+        }
+        else
+        {
+            cout << "\nWrite Permas Volume Mesh" << endl;
+            
+            int secondorder = (mesh.VolumeElement(1).GetNP() == 10);
+            
+            if (!secondorder)
+            {
+                outfile << "$ELEMENT TYPE = TET4  ESET = ALLTET" << endl;
+                for (i = 1; i <= ne; i++)
+                {
+                    const Element & el = mesh.VolumeElement(i);
+                    outfile << i 
+                            << " " << el.PNum(1) 
+                            << " " << el.PNum(2) 
+                            << " " << el.PNum(3) 
+                            << " " << el.PNum(4) << endl;
+                }
+            }
+            else
+            {
+                outfile << "$ELEMENT TYPE = TET10  ESET = ALLTET" << endl;
+                for (i = 1; i <= ne; i++)
+                {
+                    const Element & el = mesh.VolumeElement(i);
+                    outfile << i 
+                            << " " << el.PNum(1) 
+                            << " " << el.PNum(5) 
+                            << " " << el.PNum(2) 
+                            << " " << el.PNum(8) 
+                            << " " << el.PNum(3) 
+                            << " " << el.PNum(6) << endl << "& "
+                            << " " << el.PNum(7) 
+                            << " " << el.PNum(9) 
+                            << " " << el.PNum(10) 
+                            << " " << el.PNum(4) << endl;
+                }
+            }
+            
+            outfile << endl << endl;
+            
+            
+            outfile << "$SURFACE GEO  SURFID = 1  SFSET = ALLSUR" << endl;
+            for (i = 1; i <= nse; i++)
+            {
+                const Element2d & el = mesh.SurfaceElement(i);
+                if (el.GetNP() == 3)
+                    outfile << "STRIA3"
+                            << " " << el.PNum(1) 
+                            << " " << el.PNum(2) 
+                            << " " << el.PNum(3) << endl;
+            }    
+            
+            for (i = 1; i <= nse; i++)
+            {
+                const Element2d & el = mesh.SurfaceElement(i);
+                if (el.GetNP() == 4)
+                    outfile << "SQUAD4"
+                            << " " << el.PNum(1) 
+                            << " " << el.PNum(2) 
+                            << " " << el.PNum(3) 
+                            << " " << el.PNum(4) << endl;
+            }      
+            
+            for (i = 1; i <= nse; i++)
+            {
+                const Element2d & el = mesh.SurfaceElement(i);
+                if (el.GetNP() == 6)
+                    outfile << "STRIA6"
+                            << " " << el.PNum(1) 
+                            << " " << el.PNum(4) 
+                            << " " << el.PNum(2) 
+                            << " " << el.PNum(5) 
+                            << " " << el.PNum(3) 
+                            << " " << el.PNum(6) << endl;
+            }      
+        }
+        
+        
+        outfile << endl << endl;
+        
+        outfile << "$COOR  NSET = ALLNODES" << endl;
+        
+        outfile.precision(6);
+        outfile.setf (ios::fixed, ios::floatfield);
+        outfile.setf (ios::showpoint);
+        
+        for (i = 1; i <= np; i++)
+        {
+            outfile << i << " ";
+            outfile << mesh.Point(i)(0) << " ";
+            outfile << mesh.Point(i)(1) << " ";
+            outfile << mesh.Point(i)(2) << "\n";
+        }
+    }
+    ////////////////////////////////////////////////////////////////////////////////// 
+    // \brief Writes PERMAS configuration header into export file
+    //        Returns >0 in case of errors
+    // \par string &strComp  : Reference to component description
+    // \par string &strComp  : Reference to situation description
+    ////////////////////////////////////////////////////////////////////////////////// 
+    int addComponent(string &strComp, string &strSitu, ofstream &out)
+    {
+        if (strComp.size() > 12 || strSitu > 12) 
+            return 1;
+
+        if (0 == strComp.size()) 
+            strComp = "KOMPO1";
+        
+        if (0 == strSitu.size())
+            strSitu = "SIT1";
+
+        // Writing description header of configuration
+        out << "$ENTER COMPONENT  NAME = " << strComp << "  DOFTYPE = DISP MATH" << endl << endl;
+        out << "   $SITUATION  NAME = " << strSitu << endl;
+        out << "   $END SITUATION" << endl << endl;
+        out << "   $STRUCTURE" << endl;
+        
+        return 0;
+    }
+    
+}
diff --git a/contrib/Netgen/libsrc/interface/writetecplot.cpp b/contrib/Netgen/libsrc/interface/writetecplot.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4730b983a5f5d47646e06904a30d8042639acc80
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writetecplot.cpp
@@ -0,0 +1,127 @@
+//
+//
+// TECPLOT file by Jawor Georgiew
+//
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+void WriteTecPlotFormat (const Mesh & mesh,
+			 const CSGeometry & geom,
+			 const string & filename)
+{
+  INDEX i;
+  int j, k, e, z;
+  Vec<3> n;
+  
+  INDEX np = mesh.GetNP();
+  INDEX ne = mesh.GetNE();
+  INDEX nse = mesh.GetNSE();
+  
+  Array<int> sn(np);
+  ofstream outfile(filename.c_str());
+  
+  outfile << "TITLE=\" " << filename << "\"" << endl;
+
+  // fill hashtable
+
+  INDEX_3_HASHTABLE<int> face2volelement(ne);
+
+  for (i = 1; i <= ne; i++)
+    {
+      const Element & el = mesh.VolumeElement(i);
+      INDEX_3 i3;
+      int l;
+      for (j = 1; j <= 4; j++)   // loop over faces of tet
+	{
+	  l = 0;
+	  for (k = 1; k <= 4; k++)
+	    if (k != j)
+	      {
+		l++;
+		i3.I(l) = el.PNum(k);
+	      }
+	  i3.Sort();
+	  face2volelement.Set (i3, i);
+	}
+    }
+      
+      
+  for (j = 1; j <= geom.GetNSurf(); j++)       /* Flaeche Nummer j */
+    {
+      for (i = 1; i <= np; i++)
+	sn.Elem(i) = 0;
+
+      e = 0;
+       
+      for (i = 1; i <= nse; i++)
+	{
+	  const Element2d & el = mesh.SurfaceElement(i);
+	  if (j ==  mesh.GetFaceDescriptor (el.GetIndex ()).SurfNr())
+	    {
+	      for (k = 1; k <= 3; k++)
+		sn.Elem(el.PNum(k)) = 1;
+	      e++;                     /* e= Anzahl der neuen Elemente */
+	    }
+	}
+
+      z = 0;
+      for (i = 1; i <= np; i++)
+	if (sn.Elem(i) == 1)
+	  sn.Elem(i) = ++z;
+
+      outfile << "ZONE T=\" Surface " << j << " \", N=" << z
+	      << ", E=" << e << ", ET=TRIANGLE, F=FEPOINT" << endl;
+
+      for (i = 1; i <= np; i++)
+	if (sn.Elem(i) != 0)
+	  {
+	    n = geom.GetSurface(j) -> GetNormalVector ( mesh.Point(i) );
+		
+	    outfile << mesh.Point(i)(0) << " " /* Knoten Koordinaten */
+		    << mesh.Point(i)(1) << " "
+		    << mesh.Point(i)(2) << " "
+		    << n(0) << " "
+		    << n(1) << " "
+		    << n(2) << " "
+		    << i     << endl;
+	  }
+	  
+
+      for (i = 1; i <= nse; i++)
+	{
+	  const Element2d & el = mesh.SurfaceElement(i);
+	  if (j ==  mesh.GetFaceDescriptor(el.GetIndex ()).SurfNr())
+	    /* FlaechenKnoten (3) */
+	    outfile << sn.Get(el.PNum(1)) << " " 
+		    << sn.Get(el.PNum(2)) << " "
+		    << sn.Get(el.PNum(3)) << endl;
+	      
+	  /// Hier soll noch die Ausgabe der Nummer des angrenzenden
+	      /// Vol.elements erfolgen !
+
+	      for (k = 1; k <= nse; k++)
+		{
+		  const Element2d & sel = mesh.SurfaceElement(k);
+		  INDEX_3 i3;
+		  for (j = 1; j <= 3; j++)
+		    i3.I(j) = sel.PNum(j);
+		  i3.Sort();
+		  
+		  //int elind = face2volelement.Get(i3);
+		}
+	}
+    }
+}
+
+
+}
diff --git a/contrib/Netgen/libsrc/interface/writetet.cpp b/contrib/Netgen/libsrc/interface/writetet.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..221f03013e2e3365b52352010500b24165afb8af
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writetet.cpp
@@ -0,0 +1,1096 @@
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <acisgeom.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+
+#include "writeuser.hpp"
+  
+  
+  void WriteTETFormat (const Mesh & mesh,
+		       const string & filename)//, const string& problemType )
+  {
+    string problemType = "";
+    if(!mesh.PureTetMesh())
+      throw NgException("Can only export pure tet mesh in this format");
+
+    cout << "starting .tet export to file " << filename << endl;
+
+
+    Array<int> point_ids,edge_ids,face_ids;
+    Array<int> elnum(mesh.GetNE());
+    elnum = -1;
+
+    
+    Array<int> userdata_int;
+    Array<double> userdata_double;
+    Array<int> ports;
+
+    Array<int> uid_to_group_3D, uid_to_group_2D, uid_to_group_1D, uid_to_group_0D;
+
+    int pos_int = 0;
+    int pos_double = 0;
+    
+    bool haveuserdata = 
+      (mesh.GetUserData("TETmesh:double",userdata_double) &&
+       mesh.GetUserData("TETmesh:int",userdata_int) && 
+       mesh.GetUserData("TETmesh:ports",ports) &&
+       mesh.GetUserData("TETmesh:point_id",point_ids,PointIndex::BASE) &&
+       mesh.GetUserData("TETmesh:uid_to_group_3D",uid_to_group_3D) &&
+       mesh.GetUserData("TETmesh:uid_to_group_2D",uid_to_group_2D) &&
+       mesh.GetUserData("TETmesh:uid_to_group_1D",uid_to_group_1D) &&
+       mesh.GetUserData("TETmesh:uid_to_group_0D",uid_to_group_0D));
+
+
+    int version,subversion;
+
+    if(haveuserdata)
+      {
+	version = int(userdata_double[0]);
+	subversion = int(10*(userdata_double[0] - version));
+	pos_double++;
+      }
+    else
+      {
+	version = 2;
+	subversion = 0;
+      }
+
+    
+    if(version >= 2)
+      {
+	// test if ids are disjunct, if not version 2.0 not possible
+	int maxbc(-1),mindomain(-1);
+	
+	for(ElementIndex i=0; i<mesh.GetNE(); i++)
+	  if(i==0 || mesh[i].GetIndex() < mindomain)
+	    mindomain = mesh[i].GetIndex();
+	for(int i=1; i<=mesh.GetNFD(); i++)
+	  if(i==1 || mesh.GetFaceDescriptor(i).BCProperty() > maxbc)
+	    maxbc = mesh.GetFaceDescriptor(i).BCProperty();
+	
+	if(maxbc >= mindomain)
+	  {
+	    cout << "WARNING: writing version " << version << "." << subversion << " tetfile not possible, ";
+	    version = 1; subversion = 1;
+	    cout << "using version " << version << "." << subversion << endl;
+	  }
+      }
+
+
+
+    int startsize = point_ids.Size();
+    point_ids.SetSize(mesh.GetNP()+1);
+    for(int i=startsize; i<point_ids.Size(); i++)
+      point_ids[i] = -1;
+
+
+    for(int i=0; i<PointIndex::BASE; i++)
+      point_ids[i] = -1;
+
+
+    INDEX_2_CLOSED_HASHTABLE<int> edgenumbers(6*mesh.GetNE()+3*mesh.GetNSE());;
+    INDEX_3_CLOSED_HASHTABLE<int> facenumbers(4*mesh.GetNE()+mesh.GetNSE());
+
+    Array<INDEX_2> edge2node;
+    Array<INDEX_3> face2edge;
+    Array<INDEX_4> element2face;
+
+    int numelems(0),numfaces(0),numedges(0),numnodes(0);
+
+    for(SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+      {
+	const Segment & seg = mesh[si];
+	INDEX_2 i2(seg[0],seg[1]);
+	i2.Sort();
+	if(edgenumbers.Used(i2))
+	  continue;
+
+	numedges++;
+	edgenumbers.Set(i2,numedges);
+	edge2node.Append(i2);
+
+	edge_ids.Append(seg.edgenr);
+
+	if(point_ids[seg[0]] == -1)
+	  point_ids[seg[0]] = (version >= 2) ? seg.edgenr : 0;
+	if(point_ids[seg[1]] == -1)
+	  point_ids[seg[1]] = (version >= 2) ? seg.edgenr : 0;
+      }
+
+    for(SurfaceElementIndex si = 0; si < mesh.GetNSE(); si++)
+      {
+	if(mesh[si].IsDeleted())
+	  continue;
+
+	const Element2d & elem = mesh[si];
+
+	numfaces++;
+	INDEX_3 i3(elem[0], elem[1], elem[2]);
+
+	int min = i3[0];
+	int minpos = 0;
+	for(int j=1; j<3; j++)
+	  if(i3[j] < min)
+	    {
+	      min = i3[j]; minpos = j;
+	    }
+	if(minpos == 1)
+	  {
+	    int aux = i3[0]; i3[0] = i3[1]; i3[1] = i3[2]; i3[2] = aux;
+	  }
+	else if(minpos == 2)
+	  {
+	    int aux = i3[0]; i3[0] = i3[2]; i3[2] = i3[1]; i3[1] = aux;
+	  }
+	facenumbers.Set(i3,numfaces);
+
+	int bc = mesh.GetFaceDescriptor(elem.GetIndex()).BCProperty();
+	face_ids.Append(bc);
+
+	for(int j=0; j<3; j++)
+	  if(point_ids[elem[j]] == -1)
+	    point_ids[elem[j]] = (version >= 2) ? bc : 0;
+
+	INDEX_2 i2a,i2b;
+	INDEX_3 f_to_n;
+	for(int j=0; j<3; j++)
+	  {
+	    i2a = INDEX_2(i3[j],i3[(j+1)%3]);
+	    i2b[0] = i2a[1]; i2b[1] = i2a[0];
+	    if(edgenumbers.Used(i2a))
+	      f_to_n[j] = edgenumbers.Get(i2a);
+	    else if(edgenumbers.Used(i2b))
+	      f_to_n[j] = -edgenumbers.Get(i2b);
+	    else
+	      {
+		numedges++;
+		edgenumbers.Set(i2a,numedges);
+		edge2node.Append(i2a);
+		f_to_n[j] = numedges;
+		if(version >= 2)
+		  edge_ids.Append(bc);
+		else
+		  edge_ids.Append(0);
+	      }
+	  }
+	face2edge.Append(f_to_n);
+      }
+    
+    for(ElementIndex ei = 0; ei < mesh.GetNE(); ei++)
+      {
+	const Element & el = mesh[ei];
+
+	if(el.IsDeleted())
+	  continue;
+
+	numelems++;
+	elnum[ei] = numelems;
+
+	static int tetfaces[4][3] =
+	  { { 0, 2, 1 },
+	    { 0, 1, 3 },
+	    { 1, 2, 3 },
+	    { 2, 0, 3 } };
+	
+	for(int j=0; j<4; j++)
+	  if(point_ids[el[j]] == -1)
+	    point_ids[el[j]] = (version >= 2) ? el.GetIndex() : 0;
+
+	INDEX_4 e_to_f;
+
+	for(int i = 0; i < 4; i++)
+	  {
+	    INDEX_3 i3a(el[tetfaces[i][0]],el[tetfaces[i][1]],el[tetfaces[i][2]]);
+	    
+	    int min = i3a[0];
+	    int minpos = 0;
+	    for(int j=1; j<3; j++)
+	      if(i3a[j] < min)
+		{
+		  min = i3a[j]; minpos = j;
+		}
+	    if(minpos == 1)
+	      {
+		int aux = i3a[0]; i3a[0] = i3a[1]; i3a[1] = i3a[2]; i3a[2] = aux;
+	      }
+	    else if(minpos == 2)
+	      {
+		int aux = i3a[0]; i3a[0] = i3a[2]; i3a[2] = i3a[1]; i3a[1] = aux;
+	      }
+	    INDEX_3 i3b(i3a[0],i3a[2],i3a[1]);
+	    
+
+	    if(facenumbers.Used(i3a))
+	      e_to_f[i] = facenumbers.Get(i3a);
+	    else if(facenumbers.Used(i3b))
+	      e_to_f[i] = -facenumbers.Get(i3b);
+	    else
+	      {
+		numfaces++;
+		facenumbers.Set(i3a,numfaces);
+		e_to_f[i] = numfaces;
+		if(version >= 2)
+		  face_ids.Append(el.GetIndex());
+		else
+		  face_ids.Append(0);
+
+		INDEX_2 i2a,i2b;
+		INDEX_3 f_to_n;
+		for(int j=0; j<3; j++)
+		  {
+		    i2a = INDEX_2(i3a[j],i3a[(j+1)%3]);
+		    i2b[0] = i2a[1]; i2b[1] = i2a[0];
+		    if(edgenumbers.Used(i2a))
+		      f_to_n[j] = edgenumbers.Get(i2a);
+		    else if(edgenumbers.Used(i2b))
+		      f_to_n[j] = -edgenumbers.Get(i2b);
+		    else
+		      {
+			numedges++;
+			edgenumbers.Set(i2a,numedges);
+			edge2node.Append(i2a);
+			f_to_n[j] = numedges;
+			if(version >= 2)
+			  edge_ids.Append(el.GetIndex());
+			else
+			  edge_ids.Append(0);
+		      }
+		  }
+		face2edge.Append(f_to_n);	  
+	      }
+	  }
+	element2face.Append(e_to_f);
+      }
+
+
+
+
+    ofstream outfile(filename.c_str());
+
+    outfile.precision(16);
+
+    int unitcode;
+    double tolerance;
+    double dS1,dS2, alphaDeg;
+    double x3D,y3D,z3D;
+    int modelverts(0), modeledges(0), modelfaces(0), modelcells(0);
+
+    int numObj0D,numObj1D,numObj2D,numObj3D;
+    int numports = ports.Size();
+
+    Array<int> nodenum(point_ids.Size()+1);
+
+    nodenum = -1;
+	    
+
+
+    numnodes = 0;
+    for(int i=0; i<point_ids.Size(); i++)
+      {
+	if(point_ids[i] != -1)
+	  {
+	    numnodes++;
+	    nodenum[i] = numnodes;
+	  }
+      }
+
+
+    if(haveuserdata)
+      {
+	unitcode = userdata_int[pos_int];
+	pos_int++;
+
+	tolerance = userdata_double[pos_double];
+	pos_double++;
+
+	dS1 = userdata_double[pos_double];
+	pos_double++;
+	dS2 = userdata_double[pos_double];
+	pos_double++;
+	alphaDeg = userdata_double[pos_double];
+	pos_double++;
+
+	x3D = userdata_double[pos_double];
+	pos_double++;
+	y3D = userdata_double[pos_double];
+	pos_double++;
+	z3D = userdata_double[pos_double];
+	pos_double++;
+
+	if(version == 2)
+	  {
+	    modelverts = userdata_int[pos_int];
+	    pos_int++;
+	    modeledges = userdata_int[pos_int];
+	    pos_int++;
+	    modelfaces = userdata_int[pos_int];
+	    pos_int++;
+	    modelcells = userdata_int[pos_int];
+	    pos_int++;
+	  }
+
+	numObj3D = userdata_int[pos_int];
+	pos_int++;
+	numObj2D = userdata_int[pos_int];
+	pos_int++;
+	numObj1D = userdata_int[pos_int];
+	pos_int++;
+	numObj0D = userdata_int[pos_int];
+	pos_int++;
+      }
+    else
+      {
+	unitcode = 3;
+
+	tolerance = 1e-5;
+
+	dS1 = dS2 = alphaDeg = 0;
+
+	x3D = y3D = z3D = 0;
+
+	modelverts = modeledges = modelfaces = modelcells = 0;
+	
+	numObj3D = numObj2D = numObj1D = numObj0D = 0;
+      }
+
+    string uidpid;
+    if(version == 1)
+      uidpid = "PID";
+    else if (version == 2)
+      uidpid = "UID";
+    
+
+    Array< Array<int,PointIndex::BASE>* > idmaps;
+    for(int i=1; i<=mesh.GetIdentifications().GetMaxNr(); i++)
+      {
+	if(mesh.GetIdentifications().GetType(i) == Identifications::PERIODIC)
+	  {
+	    idmaps.Append(new Array<int,PointIndex::BASE>);
+	    mesh.GetIdentifications().GetMap(i,*idmaps.Last(),true);
+	  }
+      }
+
+    Array<int> id_num,id_type;
+    Array< Array<int> *> id_groups;
+
+
+	// sst 2008-03-12: Write problem class...
+	{
+		std::string block;
+		block  = "// CST Tetrahedral ";
+		block += !problemType.empty() ? problemType : "High Frequency";
+		block += " Mesh, Version no.:\n";
+		
+		size_t size = block.size()-3;
+		block += "// ";
+		block.append( size, '^' );
+		block += "\n";
+
+		outfile
+			<< block
+			<< version << "." << subversion << "\n\n";
+	}
+
+	outfile 
+	    << "// User Units Code (1=CM 2=MM 3=M 4=MIC 5=NM 6=FT 7=IN 8=MIL):\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \
+	    << unitcode << "\n\n"					\
+	    << "// Geometric coord \"zero\" tolerance threshold:\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \
+	    << tolerance << "\n\n"				  \
+	    << "// Periodic UnitCell dS1 , dS2 , alphaDeg:\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \
+	    << dS1 << " " << dS2 << " " << alphaDeg <<"\n\n"	\
+	    << "// Periodic UnitCell origin in global coords (x3D,y3D,z3D):\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \
+	    << x3D << " " << y3D << " " << z3D << "\n" << endl;
+
+    if(version == 2)
+      {
+	outfile << "// Model entity count: Vertices, Edges, Faces, Cells:\n" \
+		<< "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \
+		<< modelverts << " " << modeledges << " " << modelfaces << " " << modelcells << endl << endl;
+      }
+
+
+    outfile << "// Topological mesh-entity counts (#elements,#faces,#edges,#nodes):\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    outfile << numelems << " " 
+	    << numfaces << " "
+	    << numedges << " " 
+	    << numnodes << endl << endl;
+
+    outfile << "// NodeID, X, Y, Z, Type (0=Reg 1=PMaster 2=PSlave 3=CPMaster 4=CPSlave), "<< uidpid <<":\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+       
+
+
+    id_num.SetSize(mesh.GetNP()+1);
+    id_type.SetSize(mesh.GetNP()+1);
+    id_num = 0;
+    id_type = 0;
+
+    int n2,n4,n8;
+    n2 = n4 = n8 = 0;
+
+ 
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      {
+	if(id_num[i] != 0)
+	  continue;
+
+	if(nodenum[i] == -1)
+	  continue;
+
+	Array<int> group;
+	group.Append(i);
+	for(int j=0; j<idmaps.Size(); j++)
+	  {
+	    startsize = group.Size();
+	    for(int k=0; k<startsize; k++)
+	      {
+		int id = (*idmaps[j])[group[k]];
+		if(id != 0 && !group.Contains(id) && nodenum[id] != -1)
+		  {
+		    group.Append(id);
+		    id_num[id] = j+1+id_num[group[k]];
+		  }
+	      }
+	  }
+	if(group.Size() > 1)
+	  {
+	    id_groups.Append(new Array<int>(group));
+	    if(group.Size() == 2)
+	      {
+		id_type[i] = 1;
+		id_type[group[1]] = 2;
+		n2++;
+	      }
+	    else if(group.Size() == 4)
+	      {
+		id_type[i] = 3;
+		for(int j=1; j<group.Size(); j++)
+		  id_type[group[j]] = 4;
+		n4++;
+	      }
+	    else if(group.Size() == 8)
+	      {
+		id_type[i] = 5;
+		for(int j=1; j<group.Size(); j++)
+		  id_type[group[j]] = 6;
+		n8++;
+	      }
+	    else
+	      cerr << "ERROR: Identification group size = " << group.Size() << endl;
+	  }
+	
+      }
+
+
+    for(PointIndex i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      {
+	if(nodenum[i] == -1)
+	  continue;
+	outfile << nodenum[i] << " "
+		<< mesh[i](0) << " "
+		<< mesh[i](1) << " "
+		<< mesh[i](2) << " " << id_type[i] << " ";
+	if(i-PointIndex::BASE < point_ids.Size())
+	  outfile << point_ids[i];
+	else
+	  outfile << "0";
+	outfile << "\n";
+      }
+    outfile << endl;
+
+    outfile << "\n// Number of Periodic Master Nodes:\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \
+	    << n2 << "\n"			       \
+	    << "\n" \
+	    << "// MasterNodeID, SlaveNodeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2):\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    for(int i=0; i<id_groups.Size(); i++)
+      {
+	if(id_groups[i]->Size() != 2)
+	  continue;
+
+	for(int j=0; j<id_groups[i]->Size(); j++)
+	  outfile << nodenum[(*id_groups[i])[j]] << " ";
+	for(int j=1; j<id_groups[i]->Size(); j++)
+	  outfile << id_num[(*id_groups[i])[j]] << " ";
+	outfile << "\n";
+
+	delete id_groups[i];
+	id_groups[i] = NULL;
+      }
+    outfile << endl;
+	
+	
+    outfile << "// Number of Corner Periodic Master Nodes:\n"	      \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \
+	    << n4 << "\n"				      \
+	    << "\n" \
+	    << "// MasterNodeID, 3-SlaveNodeID's, 3-TranslCodes (1=dS1 2=dS2 3=dS1+dS2):\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+
+
+    for(int i=0; i<id_groups.Size(); i++)
+      {
+	if(!id_groups[i] || id_groups[i]->Size() != 4)
+	  continue;
+
+	for(int j=0; j<id_groups[i]->Size(); j++)
+	  outfile << nodenum[(*id_groups[i])[j]] << " ";
+	for(int j=1; j<id_groups[i]->Size(); j++)
+	  {
+	    outfile << id_num[(*id_groups[i])[j]] << " ";
+	  }
+	outfile << "\n";
+
+	delete id_groups[i];
+	id_groups[i] = NULL;
+      }
+    outfile << endl;
+
+
+    outfile << "// Number of Cubic Periodic Master Nodes:\n"	     \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \
+	    << n8 << "\n"				     \
+	    << "\n" \
+	    << "// MasterNodeID, 7-SlaveNodeID's, TranslCodes:\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    for(int i=0; i<id_groups.Size(); i++)
+      {
+	if(!id_groups[i] || id_groups[i]->Size() != 8)
+	  continue;
+
+	for(int j=0; j<id_groups[i]->Size(); j++)
+	  outfile << nodenum[(*id_groups[i])[j]] << " ";
+	for(int j=1; j<id_groups[i]->Size(); j++)
+	  outfile << id_num[(*id_groups[i])[j]] << " ";
+	outfile << "\n";
+
+	delete id_groups[i];
+	id_groups[i] = NULL;
+      }
+    outfile << endl;
+
+    
+
+
+    outfile << "// EdgeID, NodeID0, NodeID1, Type (0=Reg 1=PMaster 2=PSlave 3=CPMaster 4=CPSlave), "<<uidpid<<":\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+
+    
+      
+    Array< Array<int>* > vertex_to_edge(mesh.GetNP()+1);
+    for(int i=0; i<=mesh.GetNP(); i++)
+      vertex_to_edge[i] = new Array<int>;
+
+    Array< Array<int,PointIndex::BASE>* > idmaps_edge(idmaps.Size());
+    for(int i=0; i<idmaps_edge.Size(); i++)
+      {
+	idmaps_edge[i] = new Array<int,PointIndex::BASE>(numedges);
+	(*idmaps_edge[i]) = 0;
+      }
+
+    Array<int> possible;
+    for(int i=0; i<edge2node.Size(); i++)
+      {
+	const INDEX_2 & v = edge2node[i];
+	for(int j=0; j<idmaps.Size(); j++)
+	  {
+	    INDEX_2 vid((*idmaps[j])[v[0]], (*idmaps[j])[v[1]]);
+	    if(vid[0] != 0 && vid[0] != v[0] && vid[1] != 0 && vid[1] != v[1])
+	      {
+		Intersection(*vertex_to_edge[vid[0]],*vertex_to_edge[vid[1]],possible);
+		if(possible.Size() == 1)
+		  {
+		    (*idmaps_edge[j])[possible[0]] = i+1;
+		    (*idmaps_edge[j])[i+1] = possible[0];
+		  }
+		else if(possible.Size() > 0)
+		  {
+		    cerr << "ERROR: too many possible edge identifications" << endl;
+		    (*testout) << "ERROR: too many possible edge identifications" << endl
+			       << "*vertex_to_edge["<<vid[0]<<"] " << *vertex_to_edge[vid[0]] << endl
+			       << "*vertex_to_edge["<<vid[1]<<"] " << *vertex_to_edge[vid[1]] << endl
+			       << "possible " << possible << endl;
+		  }
+	      }
+	  }
+	vertex_to_edge[v[0]]->Append(i+1);
+	vertex_to_edge[v[1]]->Append(i+1);
+      }
+
+
+    for(int i=0; i<vertex_to_edge.Size(); i++)
+      delete vertex_to_edge[i];
+
+
+    id_groups.SetSize(0);
+    id_num.SetSize(numedges+1);
+    id_num = 0;
+    id_type.SetSize(numedges+1);
+    id_type = 0;
+
+    n2 = n4 = n8 = 0;
+
+    for(int i=1; i<=edge2node.Size(); i++)
+      {
+	if(id_num[i] != 0)
+	  continue;
+
+
+	Array<int> group;
+	group.Append(i);
+	for(int j=0; j<idmaps_edge.Size(); j++)
+	  {
+	    startsize = group.Size();
+	    for(int k=0; k<startsize; k++)
+	      {
+		int id = (*idmaps_edge[j])[group[k]];
+		if(id != 0 && !group.Contains(id))
+		  {
+		    group.Append(id);
+		    id_num[id] = j+1+id_num[group[k]];
+		  }
+	      }
+	  }
+	if(group.Size() > 1)
+	  {
+	    id_num[i] = 1;
+	    id_groups.Append(new Array<int>(group));
+	    if(group.Size() == 2)
+	      {
+		id_type[i] = 1;
+		id_type[group[1]] = 2;
+		n2++;
+	      }
+	    else if(group.Size() == 4)
+	      {
+		id_type[i] = 3;
+		for(int j=1; j<group.Size(); j++)
+		  id_type[group[j]] = 4;
+		n4++;
+	      }
+	    else
+	      {
+		cerr << "ERROR: edge identification group size = " << group.Size() << endl;
+		(*testout) << "edge group " << group << endl;
+		for(int j=0; j<idmaps_edge.Size(); j++)
+		  {
+		    (*testout) << "edge id map " << j << endl << *idmaps_edge[j] << endl;
+		  }
+	      }
+	  }
+      }
+
+
+
+    for(int i=1; i<=edge2node.Size(); i++)
+      {
+	if(id_num[i] != 0)
+	  continue;
+
+
+	Array<int> group;
+	group.Append(i);
+	for(int j=0; j<idmaps_edge.Size(); j++)
+	  {
+	    startsize = group.Size();
+	    for(int k=0; k<startsize; k++)
+	      {
+		int id = (*idmaps_edge[j])[group[k]];
+		if(id != 0 && !group.Contains(id))
+		  {
+		    group.Append(id);
+		    id_num[id] = j+1+id_num[group[k]];
+		  }
+	      }
+	  }
+	if(group.Size() > 1)
+	  {
+	    id_num[i] = 1;
+	    id_groups.Append(new Array<int>(group));
+	    if(group.Size() == 2)
+	      {
+		id_type[i] = 1;
+		id_type[group[1]] = 2;
+		n2++;
+	      }
+	    else if(group.Size() == 4)
+	      {
+		id_type[i] = 3;
+		for(int j=1; j<group.Size(); j++)
+		  id_type[group[j]] = 4;
+		n4++;
+	      }
+	    else
+	      {
+		cerr << "ERROR: edge identification group size = " << group.Size() << endl;
+		(*testout) << "edge group " << group << endl;
+		for(int j=0; j<idmaps_edge.Size(); j++)
+		  {
+		    (*testout) << "edge id map " << j << endl << *idmaps_edge[j] << endl;
+		  }
+	      }
+	  }
+	
+      }
+
+    
+    for(int i=0; i<edge2node.Size(); i++)
+      outfile << i+1 << " " << nodenum[edge2node[i][0]] << " " << nodenum[edge2node[i][1]] 
+	      << " " << id_type[i+1] << " " << edge_ids[i] << "\n";
+
+    outfile << endl;
+
+    
+
+    outfile << "// Number of Periodic Master Edges:\n"\
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"\
+	    << n2 << "\n"			      \
+	    << "\n"\
+	    << "// MasterEdgeID, SlaveEdgeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2):\n"\
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    for(int i=0; i<id_groups.Size(); i++)
+      {
+	if(id_groups[i]->Size() != 2)
+	  continue;
+
+	for(int j=0; j<id_groups[i]->Size(); j++)
+	  outfile << (*id_groups[i])[j] << " ";
+	for(int j=1; j<id_groups[i]->Size(); j++)
+	  outfile << id_num[(*id_groups[i])[j]] << " ";
+	outfile << "\n";
+
+	delete id_groups[i];
+	id_groups[i] = NULL;
+      }
+    outfile << endl;
+
+    outfile << "// Number of Corner Periodic Master Edges:\n"		\
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"\
+	    << n4 << "\n"				     \
+	    << "\n"\
+	    << "// MasterEdgeID, 3 SlaveEdgeID's, 3 TranslCode (1=dS1 2=dS2 3=dS1+dS2):\n"\
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    for(int i=0; i<id_groups.Size(); i++)
+      {
+	if(!id_groups[i] || id_groups[i]->Size() != 4)
+	  continue;
+
+	for(int j=0; j<id_groups[i]->Size(); j++)
+	  outfile << (*id_groups[i])[j] << " ";
+	for(int j=1; j<id_groups[i]->Size(); j++)
+	  outfile << id_num[(*id_groups[i])[j]] << " ";
+	outfile << "\n";
+
+	delete id_groups[i];
+	id_groups[i] = NULL;
+      }
+    outfile << endl;
+
+
+    outfile << "// FaceID, EdgeID0, EdgeID1, EdgeID2, FaceType (0=Reg 1=PMaster 2=PSlave), "<<uidpid<<":\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+
+    
+    
+    Array< Array<int>* > edge_to_face(numedges+1);
+    for(int i=0; i<edge_to_face.Size(); i++)
+      edge_to_face[i] = new Array<int>;
+
+    
+    for(int i=0; i<idmaps.Size(); i++)
+      {
+	idmaps[i]->SetSize(numfaces);
+	(*idmaps[i]) = 0;
+      }
+
+    
+    for(int i=0; i<face2edge.Size(); i++)
+      {
+	for(int j=0; j<idmaps_edge.Size(); j++)
+	  {
+	    int e1id,e2id,e3id;
+	    e1id = (*idmaps_edge[j])[abs(face2edge[i][0])];
+	    e2id = (*idmaps_edge[j])[abs(face2edge[i][1])];
+	    e3id = (*idmaps_edge[j])[abs(face2edge[i][2])];
+	    if(e1id != 0 && e1id != abs(face2edge[i][0]) &&
+	       e2id != 0 && e2id != abs(face2edge[i][1]) &&
+	       e3id != 0 && e3id != abs(face2edge[i][2]))
+	      {
+		Intersection(*edge_to_face[e1id],*edge_to_face[e2id],*edge_to_face[e3id],possible);
+		if(possible.Size() == 1)
+		  {
+		    (*idmaps[j])[possible[0]] = i+1;
+		    (*idmaps[j])[i+1] = possible[0];
+		  }
+		else if(possible.Size() > 0)
+		  cerr << "ERROR: too many possible face identifications" << endl;
+	      }
+	  }
+
+	edge_to_face[abs(face2edge[i][0])]->Append(i+1);
+	edge_to_face[abs(face2edge[i][1])]->Append(i+1);
+	edge_to_face[abs(face2edge[i][2])]->Append(i+1);
+      }
+
+    for(int i=0; i<edge_to_face.Size(); i++)
+      delete edge_to_face[i];
+
+
+    for(int i=0; i<idmaps_edge.Size(); i++)
+      delete idmaps_edge[i];
+
+    
+    id_groups.SetSize(0);
+    id_num.SetSize(numfaces+1);
+    id_num = 0;
+
+    n2 = n4 = n8 = 0;
+
+    for(int i=1; i<=numfaces; i++)
+      {
+	if(id_num[i] != 0)
+	  continue;
+
+	Array<int> group;
+	group.Append(i);
+	for(int j=0; j<idmaps.Size(); j++)
+	  {
+	    startsize = group.Size();
+	    for(int k=0; k<startsize; k++)
+	      {
+		int id = (*idmaps[j])[group[k]];
+		if(id != 0 && !group.Contains(id))
+		  {
+		    group.Append(id);
+		    id_num[id] = j+1+id_num[group[k]];
+		  }
+	      }
+	  }
+	if(group.Size() > 1)
+	  {
+	    id_num[i] = -1;
+	    id_groups.Append(new Array<int>(group));
+	    if(group.Size() == 2)
+	      n2++;
+	    else
+	      cerr << "ERROR: face identification group size = " << group.Size() << endl;
+	  }
+	
+      }
+
+
+    for(int i=0; i<idmaps.Size(); i++)
+      delete idmaps[i];
+
+
+
+
+    for(int i=0; i<face2edge.Size(); i++)
+      {	
+	outfile << i+1 << " ";
+	for(int j=0; j<3; j++)
+	  outfile << face2edge[i][j] << " ";
+
+	if(id_num[i+1] == 0)
+	  outfile << 0;
+	else if(id_num[i+1] == -1)
+	  outfile << 1;
+	else
+	  outfile << 2;
+
+	outfile << " " << face_ids[i] <<"\n";
+      }
+    outfile << endl;
+
+
+    outfile << "// Number of Periodic Master Faces:\n"\
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"\
+	    << n2 << "\n"			      \
+	    << "\n"\
+	    << "// MasterFaceID, SlaveFaceID, TranslCode (1=dS1 2=dS2):\n"\
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    for(int i=0; i<id_groups.Size(); i++)
+      {
+	if(id_groups[i]->Size() != 2)
+	  continue;
+
+	for(int j=0; j<id_groups[i]->Size(); j++)
+	  outfile << (*id_groups[i])[j] << " ";
+	for(int j=1; j<id_groups[i]->Size(); j++)
+	  outfile << id_num[(*id_groups[i])[j]] << " ";
+	outfile << "\n";
+
+	delete id_groups[i];
+      }
+    outfile << endl;
+
+    
+
+
+    outfile << "// ElemID, FaceID0, FaceID1, FaceID2, FaceID3, "<<uidpid<<":\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+
+    for(ElementIndex i=0; i<mesh.GetNE(); i++)
+      {
+	if(elnum[i] >= 0)
+	  {
+	    outfile << elnum[i] << " ";
+	    for(int j=0; j<4; j++)
+	      outfile << element2face[elnum[i]-1][j] << " ";
+
+	    outfile << mesh[i].GetIndex() << "\n";
+	  }
+      }
+    outfile << endl;
+
+    outfile << "// ElemID, NodeID0, NodeID1, NodeID2, NodeID3:\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+
+    
+    for(ElementIndex i=0; i<mesh.GetNE(); i++)
+      {
+	if(elnum[i] >= 0)
+	  outfile << elnum[i] << " "
+		  << nodenum[mesh[i][1]] << " " << nodenum[mesh[i][0]] << " " << nodenum[mesh[i][2]] << " " << nodenum[mesh[i][3]] << "\n";
+      }
+    outfile << endl;
+    
+
+    
+
+    outfile << "// Physical Object counts (#Obj3D,#Obj2D,#Obj1D,#Obj0D):\n"
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"
+	    << " "<< numObj3D << " " << numObj2D << " " << numObj1D << " " << numObj0D << "\n" \
+	    << "\n" \
+	    << "// Number of Ports (Ports are a subset of Object2D list):\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \
+	    << numports << "\n"						\
+	    << endl;
+
+
+    Array< Array<int> * > groups;
+
+    int maxg = -1;
+    for(int i = 0; i<uid_to_group_3D.Size(); i++)
+      if(uid_to_group_3D[i] > maxg)
+	maxg = uid_to_group_3D[i];
+    for(int i = 0; i<uid_to_group_2D.Size(); i++)
+      if(uid_to_group_2D[i] > maxg)
+	maxg = uid_to_group_2D[i];
+    for(int i = 0; i<uid_to_group_1D.Size(); i++)
+      if(uid_to_group_1D[i] > maxg)
+	maxg = uid_to_group_1D[i];
+    for(int i = 0; i<uid_to_group_0D.Size(); i++)
+      if(uid_to_group_0D[i] > maxg)
+	maxg = uid_to_group_0D[i];
+
+    groups.SetSize(maxg+1);
+    for(int i=0; i<groups.Size(); i++)
+      groups[i] = new Array<int>;
+
+    for(ElementIndex i=0; i<mesh.GetNE(); i++)
+      if(uid_to_group_3D[mesh[i].GetIndex()] >= 0)
+	groups[uid_to_group_3D[mesh[i].GetIndex()]]->Append(i+1);
+      
+    
+
+
+    outfile << "// Object3D GroupID, #Elems <immediately followed by> ElemID List:\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    for(int i=0; i<numObj3D; i++)
+      {
+	outfile << i << " " << groups[i]->Size() << "\n";
+	for(int j=0; j<groups[i]->Size(); j++)
+	  outfile << (*groups[i])[j] << "\n";
+      }
+
+    for(int i=0; i<groups.Size(); i++)
+      groups[i]->SetSize(0);
+
+    for(int i=0; i<face_ids.Size(); i++)
+      if(uid_to_group_2D[face_ids[i]] >= 0)
+	groups[uid_to_group_2D[face_ids[i]]]->Append(i+1);
+      
+
+    outfile << "// Object2D GroupID, #Faces <immediately followed by> FaceID List:\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    for(int i=0; i<numObj2D; i++)
+      {
+	outfile << i << " " << groups[i]->Size() << "\n";
+	for(int j=0; j<groups[i]->Size(); j++)
+	  {
+	    outfile << (*groups[i])[j];
+	    if(ports.Contains(face_ids[(*groups[i])[j]-1]))
+	      outfile << " P";
+	    outfile << "\n";
+	  }
+      }
+    outfile << endl;
+
+    
+    for(int i=0; i<groups.Size(); i++)
+      groups[i]->SetSize(0);
+
+    for(int i=0; i<edge_ids.Size(); i++)
+      if(uid_to_group_1D[edge_ids[i]] >= 0)
+	groups[uid_to_group_1D[edge_ids[i]]]->Append(i+1);
+
+
+
+    outfile << "// Object1D GroupID, #Edges <immediately followed by> EdgeID List:\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    for(int i=0; i<numObj1D; i++)
+      {
+	outfile << i << " " << groups[i]->Size() << "\n";
+	for(int j=0; j<groups[i]->Size(); j++)
+	  outfile << (*groups[i])[j] << "\n";
+      }
+    outfile << endl;
+
+    
+    for(int i=0; i<groups.Size(); i++)
+      groups[i]->SetSize(0);
+    for(PointIndex i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      {
+	if(i-PointIndex::BASE < point_ids.Size())
+	  {
+	    if(uid_to_group_0D[point_ids[i]] >= 0)
+	      groups[uid_to_group_0D[point_ids[i]]]->Append(i+1-PointIndex::BASE);
+	  }
+	else
+	  groups[uid_to_group_0D[0]]->Append(i+1-PointIndex::BASE);
+      }
+
+
+    outfile << "// Object0D GroupID, #Nodes <immediately followed by> NodeID List:\n" \
+	    << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
+    for(int i=0; i<numObj0D; i++)
+      {
+	outfile << i << " " << groups[i]->Size() << "\n";
+	for(int j=0; j<groups[i]->Size(); j++)
+	  outfile << (*groups[i])[j] << "\n";
+      }
+    outfile << endl;
+
+    for(int i=0; i<groups.Size(); i++)
+      delete groups[i];
+
+
+    outfile.close();
+
+    cout << ".tet export done" << endl;
+  }
+}
diff --git a/contrib/Netgen/libsrc/interface/writetochnog.cpp b/contrib/Netgen/libsrc/interface/writetochnog.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c9ec6e3ce9c6b2c6faae0b771f484a928eb0ce17
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writetochnog.cpp
@@ -0,0 +1,108 @@
+//
+//  Write Tochnog file
+//
+//  by
+//
+//  Andreas Seltmann
+//  email:  A.Seltmann@lsw.uni-heidelberg.de
+//
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+
+void WriteTochnogFormat (const Mesh & mesh,
+			 const string & filename)
+{
+  cout << "\nWrite Tochnog Volume Mesh" << endl;
+
+  ofstream outfile (filename.c_str());
+
+  outfile << "(Nodes and Elements generated with NETGEN" << endl;
+  outfile << " " << filename << ")" << endl;
+
+  outfile.precision(8);
+
+  outfile << "(Nodes)" << endl;
+
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+  int i, j;
+
+  for (i = 1; i <= np; i++)
+    {
+      outfile << "node " << " " << i << " ";
+      outfile << mesh.Point(i)(0) << " ";
+      outfile << mesh.Point(i)(1) << " ";
+      outfile << mesh.Point(i)(2) << "\n";
+    }
+
+  int elemcnt = 0; //element counter
+  int finished = 0;
+  int indcnt = 1; //index counter
+
+  while (!finished)
+    {
+      int actcnt = 0;
+      const Element & el1 = mesh.VolumeElement(1);
+      int non = el1.GetNP();
+      if (non == 4)
+	{
+	  outfile << "(Elements, type=-tet4)" << endl;
+	} 
+      else
+	{
+	  cout << "unsupported Element type!!!" << endl;	  
+	}
+
+      for (i = 1; i <= ne; i++)
+	{
+	  const Element & el = mesh.VolumeElement(i);
+	      
+	  if (el.GetIndex() == indcnt)
+	    {
+	      actcnt++;
+	      if (el.GetNP() != non) 
+		{
+		  cout << "different element-types in a subdomain are not possible!!!" << endl;
+		  continue;
+		}
+		  
+	      elemcnt++;
+	      outfile << "element " << elemcnt << " -tet4 ";
+	      if (non == 4)
+		{
+		  outfile << el.PNum(1) << " ";
+		  outfile << el.PNum(2) << " ";
+		  outfile << el.PNum(4) << " ";
+		  outfile << el.PNum(3) << "\n";
+		}
+	      else
+		{
+		  cout << "unsupported Element type!!!" << endl;
+		  for (j = 1; j <= el.GetNP(); j++)
+		    {
+		      outfile << el.PNum(j);
+		      if (j != el.GetNP()) outfile << ", ";
+		    }
+		  outfile << "\n";
+		}
+	    }
+	}	  
+      indcnt++;
+      if (elemcnt == ne) {finished = 1; cout << "all elements found by Index!" << endl;}
+      if (actcnt == 0) {finished = 1;}
+    }
+
+  cout << "done" << endl;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/interface/writeuser.cpp b/contrib/Netgen/libsrc/interface/writeuser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6e3f918566c938183c3d4449c11a4f71831691d9
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writeuser.cpp
@@ -0,0 +1,1026 @@
+//
+//  Write user dependent output file
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <geometry2d.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+
+  void RegisterUserFormats (Array<const char*> & names,
+			    Array<const char*> & extensions)
+			    
+{
+  const char *types[] =
+    {
+      "Neutral Format",  ".mesh",
+      "Surface Mesh Format", ".mesh" ,
+      "DIFFPACK Format", ".mesh",
+      "TecPlot Format", ".mesh",
+      "Tochnog Format", ".mesh",
+      "Abaqus Format", ".mesh",
+      "Fluent Format", ".mesh",
+      "Permas Format", ".mesh",
+      "FEAP Format", ".mesh",
+      "Elmer Format", "*",
+      "STL Format", ".stl",
+      "STL Extended Format", ".stl",
+      "VRML Format", ".*",
+      "Gmsh Format", ".gmsh",
+      "Gmsh2 Format", ".gmsh2",
+      "OpenFOAM 1.5+ Format", "*",
+      "JCMwave Format", ".jcm",
+      "TET Format", ".tet",
+      //      { "Chemnitz Format" },
+      0
+    };
+  
+  for (int i = 0; types[2*i]; i++)
+    {
+      names.Append (types[2*i]);
+      extensions.Append (types[2*i+1]);
+    }
+}
+  
+
+
+bool WriteUserFormat (const string & format,
+		      const Mesh & mesh,
+		      const NetgenGeometry & hgeom,
+		      const string & filename)
+{
+  const CSGeometry & geom = *dynamic_cast<const CSGeometry*> (&hgeom);
+
+  PrintMessage (1, "Export mesh to file ", filename,
+		", format is ", format);
+
+  if (format == "Neutral Format")
+    WriteNeutralFormat (mesh, geom, filename);
+
+  else if (format == "Surface Mesh Format")
+    WriteSurfaceFormat (mesh, filename);
+
+  else if (format == "DIFFPACK Format")
+    WriteDiffPackFormat (mesh, geom, filename);
+
+  else if (format == "Tochnog Format")
+    WriteTochnogFormat (mesh, filename);
+
+  else if (format == "TecPlot Format")
+    cerr << "ERROR: TecPlot format currently out of order" << endl;
+      // WriteTecPlotFormat (mesh, geom, filename);
+
+  else if (format == "Abaqus Format")
+    WriteAbaqusFormat (mesh, filename);
+
+  else if (format == "Fluent Format")
+    WriteFluentFormat (mesh, filename);
+
+  else if (format == "Permas Format")
+    WritePermasFormat (mesh, filename);
+
+  else if (format == "FEAP Format")
+    WriteFEAPFormat (mesh, filename);
+
+  else if (format == "Elmer Format")
+    WriteElmerFormat (mesh, filename);
+
+  else if (format == "STL Format")
+    WriteSTLFormat (mesh, filename);
+
+  // Philippose - 16 August 2010
+  // Added additional STL Export in which
+  // each face of the geometry is treated
+  // as a separate "solid" entity
+  else if (format == "STL Extended Format")
+	WriteSTLExtFormat (mesh, filename);
+
+  else if (format == "VRML Format")
+    WriteVRMLFormat (mesh, 1, filename);
+
+  else if (format == "Fepp Format")
+    WriteFEPPFormat (mesh, geom, filename);
+
+  else if (format ==  "EdgeElement Format")
+    WriteEdgeElementFormat (mesh, geom, filename);
+
+  else if (format == "Chemnitz Format")
+    WriteUserChemnitz (mesh, filename);
+
+  else if (format == "Gmsh Format")
+    WriteGmshFormat (mesh, geom, filename);
+
+  // Philippose - 29/01/2009
+  // Added Gmsh v2.xx Mesh export capability
+  else if (format == "Gmsh2 Format")
+    WriteGmsh2Format (mesh, geom, filename);
+
+  // Philippose - 25/10/2009
+  // Added OpenFOAM 1.5+ Mesh export capability
+  else if (format == "OpenFOAM 1.5+ Format")
+    WriteOpenFOAM15xFormat (mesh, filename);
+
+  else if (format == "JCMwave Format")
+    WriteJCMFormat (mesh, geom, filename);
+
+#ifdef OLIVER
+  else if (format == "TET Format")
+    WriteTETFormat( mesh, filename);//, "High Frequency" );
+#endif
+
+  else
+    {
+      return 1;
+    }
+
+  return 0;
+}
+
+
+
+
+/*
+ *  Neutral mesh format
+ *  points, elements, surface elements
+ */
+
+void WriteNeutralFormat (const Mesh & mesh,
+			 const CSGeometry & geom,
+			 const string & filename)
+{
+  cout << "write neutral, new" << endl;
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+  int nse = mesh.GetNSE();
+  int nseg = mesh.GetNSeg();
+  int i, j;
+
+  int inverttets = mparam.inverttets;
+  int invertsurf = mparam.inverttrigs;
+
+  ofstream outfile (filename.c_str());
+
+  outfile.precision(6);
+  outfile.setf (ios::fixed, ios::floatfield);
+  outfile.setf (ios::showpoint);
+
+  outfile << np << "\n";
+
+  for (i = 1; i <= np; i++)
+    {
+      const Point3d & p = mesh.Point(i);
+
+      outfile.width(10);
+      outfile << p.X() << " ";
+      outfile.width(9);
+      outfile << p.Y() << " ";
+      if (mesh.GetDimension() == 3)
+	{
+	  outfile.width(9);
+	  outfile << p.Z();
+	  }
+      outfile << "\n";
+    }
+
+  if (mesh.GetDimension() == 3)
+    {
+      outfile << ne << "\n";
+      for (i = 1; i <= ne; i++)
+	{
+	  Element el = mesh.VolumeElement(i);
+	  if (inverttets)
+	    el.Invert();
+	  outfile.width(4);
+	  outfile << el.GetIndex() << "  ";
+	  for (j = 1; j <= el.GetNP(); j++)
+	    {
+	      outfile << " ";
+	      outfile.width(8);
+	      outfile << el.PNum(j);
+	    }
+	  outfile << "\n";
+	}
+    }
+
+  outfile << nse << "\n";
+  for (i = 1; i <= nse; i++)
+    {
+      Element2d el = mesh.SurfaceElement(i);
+      if (invertsurf)
+	el.Invert();
+      outfile.width(4);
+      outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << "    ";
+      for (j = 1; j <= el.GetNP(); j++)
+	{
+	  outfile << " ";
+	  outfile.width(8);
+	  outfile << el.PNum(j);
+	}
+      outfile << "\n";
+    }
+
+
+  if (mesh.GetDimension() == 2)
+    {
+      outfile << nseg << "\n";
+      for (i = 1; i <= nseg; i++)
+	{
+	  const Segment & seg = mesh.LineSegment(i);
+	  outfile.width(4);
+	  outfile << seg.si << "    ";
+
+	  outfile << " ";
+	  outfile.width(8);
+	  outfile << seg[0];
+	  outfile << " ";
+	  outfile.width(8);
+	  outfile << seg[1];
+
+	  outfile << "\n";
+	}
+    }
+}
+
+
+
+
+
+
+
+
+
+void WriteSurfaceFormat (const Mesh & mesh,
+			 const string & filename)
+{
+  // surface mesh
+  int i, j;
+
+  cout << "Write Surface Mesh" << endl;
+
+  ofstream outfile (filename.c_str());
+
+  outfile << "surfacemesh" << endl;
+
+  outfile << mesh.GetNP() << endl;
+  for (i = 1; i <= mesh.GetNP(); i++)
+    {
+      for (j = 0; j < 3; j++)
+	{
+	  outfile.width(10);
+	  outfile << mesh.Point(i)(j) << " ";
+	}
+      outfile << endl;
+    }
+  outfile << mesh.GetNSE() << endl;
+  for (i = 1; i <= mesh.GetNSE(); i++)
+    {
+      for (j = 1; j <= 3; j++)
+	{
+	  outfile.width(8);
+	  outfile << mesh.SurfaceElement(i).PNum(j);
+	}
+      outfile << endl;
+    }
+}
+
+
+
+
+
+/*
+ *  save surface mesh as STL file
+ */
+
+void WriteSTLFormat (const Mesh & mesh,
+		     const string & filename)
+{
+  cout << "\nWrite STL Surface Mesh" << endl;
+
+  ofstream outfile (filename.c_str());
+
+  int i;
+
+  outfile.precision(10);
+
+  outfile << "solid" << endl;
+
+  for (i = 1; i <= mesh.GetNSE(); i++)
+    {
+      outfile << "facet normal ";
+      const Point3d& p1 = mesh.Point(mesh.SurfaceElement(i).PNum(1));
+      const Point3d& p2 = mesh.Point(mesh.SurfaceElement(i).PNum(2));
+      const Point3d& p3 = mesh.Point(mesh.SurfaceElement(i).PNum(3));
+
+      Vec3d normal = Cross(p2-p1,p3-p1);
+      if (normal.Length() != 0)
+	{
+	  normal /= (normal.Length());
+	}
+
+      outfile << normal.X() << " " << normal.Y() << " " << normal.Z() << "\n";
+      outfile << "outer loop\n";
+
+      outfile << "vertex " << p1.X() << " " << p1.Y() << " " << p1.Z() << "\n";
+      outfile << "vertex " << p2.X() << " " << p2.Y() << " " << p2.Z() << "\n";
+      outfile << "vertex " << p3.X() << " " << p3.Y() << " " << p3.Z() << "\n";
+
+      outfile << "endloop\n";
+      outfile << "endfacet\n";
+    }
+  outfile << "endsolid" << endl;
+}
+
+
+
+
+
+/*
+ *  Philippose - 16 August 2010
+ *  Save surface mesh as STL file
+ *  with a separate solid definition
+ *  for each face
+ *  - This helps in splitting up the
+ *    STL into named boundary faces
+ *    when using a third-party mesher
+ */
+void WriteSTLExtFormat (const Mesh & mesh,
+		     const string & filename)
+{
+  cout << "\nWrite STL Surface Mesh (with separated boundary faces)" << endl;
+
+  ofstream outfile (filename.c_str());
+
+  outfile.precision(10);
+
+  int numBCs = 0;
+
+  Array<int> faceBCs;
+  TABLE<int> faceBCMapping;
+
+  faceBCs.SetSize(mesh.GetNFD());
+  faceBCMapping.SetSize(mesh.GetNFD());
+
+  faceBCs = -1;
+
+  // Collect the BC numbers used in the mesh
+  for(int faceNr = 1; faceNr <= mesh.GetNFD(); faceNr++)
+  {
+	  int bcNum = mesh.GetFaceDescriptor(faceNr).BCProperty();
+
+	  if(faceBCs.Pos(bcNum) < 0)
+	  {
+        numBCs++;
+		  faceBCs.Set(numBCs,bcNum);
+        faceBCMapping.Add1(numBCs,faceNr);        
+	  }
+     else
+     {
+        faceBCMapping.Add1(faceBCs.Pos(bcNum)+1,faceNr);
+     }
+  }
+
+  faceBCs.SetSize(numBCs);
+  faceBCMapping.ChangeSize(numBCs);
+
+  // Now actually write the data to file
+  for(int bcInd = 1; bcInd <= faceBCs.Size(); bcInd++)
+  {
+      outfile << "solid Boundary_" << faceBCs.Elem(bcInd) << "\n";
+
+      for(int faceNr = 1;faceNr <= faceBCMapping.EntrySize(bcInd); faceNr++)
+      {
+          Array<SurfaceElementIndex> faceSei;
+          mesh.GetSurfaceElementsOfFace(faceBCMapping.Get(bcInd,faceNr),faceSei);
+
+          for (int i = 0; i < faceSei.Size(); i++)
+          {
+        	  outfile << "facet normal ";
+        	  const Point3d& p1 = mesh.Point(mesh.SurfaceElement(faceSei[i]).PNum(1));
+        	  const Point3d& p2 = mesh.Point(mesh.SurfaceElement(faceSei[i]).PNum(2));
+        	  const Point3d& p3 = mesh.Point(mesh.SurfaceElement(faceSei[i]).PNum(3));
+
+        	  Vec3d normal = Cross(p2-p1,p3-p1);
+        	  if (normal.Length() != 0)
+        	  {
+        		  normal /= (normal.Length());
+        	  }
+
+        	  outfile << normal.X() << " " << normal.Y() << " " << normal.Z() << "\n";
+        	  outfile << "outer loop\n";
+
+        	  outfile << "vertex " << p1.X() << " " << p1.Y() << " " << p1.Z() << "\n";
+        	  outfile << "vertex " << p2.X() << " " << p2.Y() << " " << p2.Z() << "\n";
+        	  outfile << "vertex " << p3.X() << " " << p3.Y() << " " << p3.Z() << "\n";
+
+        	  outfile << "endloop\n";
+        	  outfile << "endfacet\n";
+          }
+      }
+      outfile << "endsolid Boundary_" << faceBCs.Elem(bcInd) << "\n";
+  }
+}
+
+
+
+
+/*
+ *
+ *  write surface mesh as VRML file
+ *
+ */
+
+void WriteVRMLFormat (const Mesh & mesh,
+		      bool faces,
+		      const string & filename)
+{
+
+  if (faces)
+
+    {
+      // Output in VRML, IndexedFaceSet is used
+      // Bartosz Sawicki <sawickib@ee.pw.edu.pl>
+
+      int np = mesh.GetNP();
+      int nse = mesh.GetNSE();
+      int i, j;
+
+      ofstream outfile (filename.c_str());
+
+      outfile.precision(6);
+      outfile.setf (ios::fixed, ios::floatfield);
+      outfile.setf (ios::showpoint);
+
+      outfile << "#VRML V2.0 utf8 \n"
+	         "Background {\n"
+		 "    skyColor [1 1 1]\n"
+     		 "    groundColor [1 1 1]\n"
+		 "}\n"
+		 "Group{ children [\n"
+		 "Shape{ \n"
+		 "appearance Appearance { material Material { }} \n"
+                 "geometry IndexedFaceSet { \n"
+                 "coord Coordinate { point [ \n";
+
+
+      for (i = 1; i <= np; i++)
+        {
+          const Point3d & p = mesh.Point(i);
+          outfile.width(10);
+          outfile << p.X() << " ";
+          outfile << p.Y() << " ";
+          outfile << p.Z() << " \n";
+	}
+
+      outfile << "  ] } \n"
+                 "coordIndex [ \n";
+
+      for (i = 1; i <= nse; i++)
+	{
+	  const Element2d & el = mesh.SurfaceElement(i);
+
+	  for (j = 1; j <= 3; j++)
+	    {
+	      outfile.width(8);
+	      outfile << el.PNum(j)-1;
+	    }
+	  outfile << " -1 \n";
+	}
+
+      outfile << "  ] \n";
+
+      //define number and RGB definitions of colors
+      outfile << "color Color { color [1 0 0, 0 1 0, 0 0 1, 1 1 0]} \n"
+                 "colorIndex [\n";
+
+      for (i = 1; i <= nse; i++)
+	{
+	  outfile << mesh.GetFaceDescriptor(mesh.SurfaceElement(i).GetIndex ()).BCProperty();
+          outfile << endl;
+	}
+
+      outfile << " ] \n"
+                 "colorPerVertex FALSE \n"
+                 "creaseAngle 0 \n"
+		 "solid FALSE \n"
+                 "ccw FALSE \n"
+		 "convex TRUE \n"
+                 "} } # end of Shape\n"
+		 "] }\n";
+
+    } /* end of VRMLFACES */
+
+
+  else
+
+    {
+        // Output in VRML, IndexedLineSet is used
+	// Bartosz Sawicki <sawickib@ee.pw.edu.pl>
+
+      int np = mesh.GetNP();
+      int nse = mesh.GetNSE();
+      int i, j;
+
+      ofstream outfile (filename.c_str());
+
+      outfile.precision(6);
+      outfile.setf (ios::fixed, ios::floatfield);
+      outfile.setf (ios::showpoint);
+
+      outfile << "#VRML V2.0 utf8 \n"
+	         "Background {\n"
+		 "    skyColor [1 1 1]\n"
+     		 "    groundColor [1 1 1]\n"
+		 "}\n"
+		 "Group{ children [\n"
+	         "Shape{ \n"
+		 "appearance Appearance { material Material { }} \n"
+                 "geometry IndexedLineSet { \n"
+                 "coord Coordinate { point [ \n";
+
+
+      for (i = 1; i <= np; i++)
+        {
+          const Point3d & p = mesh.Point(i);
+          outfile.width(10);
+          outfile << p.X() << " ";
+          outfile << p.Y() << " ";
+          outfile << p.Z() << " \n";
+	}
+
+      outfile << "  ] } \n"
+                 "coordIndex [ \n";
+
+      for (i = 1; i <= nse; i++)
+	{
+	  const Element2d & el = mesh.SurfaceElement(i);
+
+	  for (j = 1; j <= 3; j++)
+	    {
+	      outfile.width(8);
+	      outfile << el.PNum(j)-1;
+	    }
+	  outfile.width(8);
+	  outfile << el.PNum(1)-1;
+	  outfile << " -1 \n";
+	}
+
+      outfile << "  ] \n";
+
+/* Uncomment if you want color mesh
+      outfile << "color Color { color [1 1 1, 0 1 0, 0 0 1, 1 1 0]} \n"
+                 "colorIndex [\n";
+
+      for (i = 1; i <= nse; i++)
+	{
+	  outfile << mesh.GetFaceDescriptor(mesh.SurfaceElement(i).GetIndex ()).BCProperty();
+          outfile << endl;
+	}
+
+      outfile << " ] \n"
+*/
+      outfile << "colorPerVertex FALSE \n"
+                 "} } #end of Shape\n"
+		 "] } \n";
+
+    }
+
+}
+
+
+
+
+
+
+/*
+ * FEPP .. a finite element package developed at University Linz, Austria
+ */
+void WriteFEPPFormat (const Mesh & mesh,
+		      const CSGeometry & geom,
+		      const string & filename)
+{
+
+  ofstream outfile (filename.c_str());
+
+  if (mesh.GetDimension() == 3)
+
+    {
+
+      // output for FEPP
+
+      int np = mesh.GetNP();
+      int ne = mesh.GetNE();
+      int nse = mesh.GetNSE();
+      int ns = mesh.GetNFD();
+      int i, j;
+
+      outfile.precision(5);
+      outfile.setf (ios::fixed, ios::floatfield);
+      outfile.setf (ios::showpoint);
+
+      outfile << "volumemesh4" << endl;
+      outfile << nse << endl;
+      for (i = 1; i <= nse; i++)
+	{
+	  const Element2d & el = mesh.SurfaceElement(i);
+
+	  //	  int facenr = mesh.facedecoding.Get(el.GetIndex()).surfnr;
+	  outfile.width(4);
+	  outfile << el.GetIndex() << " ";
+	  outfile.width(4);
+	  //	  outfile << mesh.GetFaceDescriptor(el.GetIndex()).BCProperty() << " ";
+	  outfile << mesh.GetFaceDescriptor(el.GetIndex()).BCProperty() << " ";
+	  outfile.width(4);
+	  outfile << el.GetNP() << "    ";
+	  for (j = 1; j <= el.GetNP(); j++)
+	    {
+	      outfile.width(8);
+	      outfile << el.PNum(j);
+	    }
+	  outfile << "\n";
+	}
+
+
+      outfile << ne << "\n";
+      for (i = 1; i <= ne; i++)
+	{
+	  const Element & el = mesh.VolumeElement(i);
+	  outfile.width(4);
+	  outfile << el.GetIndex() << " ";
+	  outfile.width(4);
+	  outfile << el.GetNP() << " ";
+	  for (j = 1; j <= el.GetNP(); j++)
+	    {
+	      outfile.width(8);
+	      outfile << el.PNum(j);
+	    }
+	  outfile << "\n";
+	}
+
+      outfile << np << "\n";
+      for (i = 1; i <= np; i++)
+	{
+	  const Point3d & p = mesh.Point(i);
+
+	  outfile.width(10);
+	  outfile << p.X() << " ";
+	  outfile.width(9);
+	  outfile << p.Y() << " ";
+	  outfile.width(9);
+	  outfile << p.Z() << "\n";
+	}
+
+      /*
+      if (typ == WRITE_FEPPML)
+	{
+	  int nbn =  mesh.mlbetweennodes.Size();
+	  outfile << nbn << "\n";
+	  for (i = 1; i <= nbn; i++)
+	    outfile << mesh.mlbetweennodes.Get(i).I1() << " "
+		    << mesh.mlbetweennodes.Get(i).I2() << "\n";
+
+
+	  //	  int ncon = mesh.connectedtonode.Size();
+	  //	  outfile << ncon << "\n";
+	  //	  for (i = 1; i <= ncon; i++)
+	  //	    outfile << i << " " << mesh.connectedtonode.Get(i) << endl;
+	}
+      */
+
+
+      // write CSG surfaces
+      if (&geom && geom.GetNSurf() >= ns)
+	{
+	  outfile << ns << endl;
+	  for (i = 1; i <= ns; i++)
+	    geom.GetSurface(mesh.GetFaceDescriptor(i).SurfNr())->Print(outfile);
+	}
+      else
+	outfile << "0" << endl;
+    }
+
+
+  else
+
+    { // 2D fepp format
+
+      ;
+      /*
+      extern SplineGeometry2d * geometry2d;
+      if (geometry2d)
+	Save2DMesh (mesh, &geometry2d->GetSplines(), outfile);
+      else
+	Save2DMesh (mesh, 0, outfile);
+      */
+    }
+}
+
+
+
+
+
+
+/*
+ *  Edge element mesh format
+ *  points, elements, edges
+ */
+
+void WriteEdgeElementFormat (const Mesh & mesh,
+			     const CSGeometry & geom,
+			     const string & filename)
+{
+  cout << "write edge element format" << endl;
+
+  const MeshTopology * top = &mesh.GetTopology();
+  int npoints = mesh.GetNP();
+  int nelements = mesh.GetNE();
+  int nsurfelem = mesh.GetNSE();
+  int nedges = top->GetNEdges();
+  int i, j;
+
+  int inverttets = mparam.inverttets;
+  int invertsurf = mparam.inverttrigs;
+  Array<int> edges;
+
+  ofstream outfile (filename.c_str());
+
+  outfile.precision(6);
+  outfile.setf (ios::fixed, ios::floatfield);
+  outfile.setf (ios::showpoint);
+
+
+  // vertices with coordinates
+  outfile << npoints << "\n";
+  for (i = 1; i <= npoints; i++)
+    {
+      const Point3d & p = mesh.Point(i);
+
+      outfile.width(10);
+      outfile << p.X() << " ";
+      outfile.width(9);
+      outfile << p.Y() << " ";
+      outfile.width(9);
+      outfile << p.Z() << "\n";
+    }
+
+  // element - edge - list
+  outfile << nelements << " " << nedges << "\n";
+  for (i = 1; i <= nelements; i++)
+    {
+      Element el = mesh.VolumeElement(i);
+      if (inverttets)
+      	el.Invert();
+      outfile.width(4);
+      outfile << el.GetIndex() << "  ";
+      outfile.width(8);
+      outfile << el.GetNP();
+      for (j = 1; j <= el.GetNP(); j++)
+	{
+	  outfile << " ";
+	  outfile.width(8);
+	  outfile << el.PNum(j);
+	}
+
+      top->GetElementEdges(i,edges);
+      outfile << endl << "      ";
+      outfile.width(8);
+      outfile << edges.Size();
+      for (j=1; j <= edges.Size(); j++)
+	{
+	  outfile << " ";
+	  outfile.width(8);
+	  outfile << edges[j-1];
+	}
+      outfile << "\n";
+
+      // orientation:
+      top->GetElementEdgeOrientations(i,edges);
+      outfile << "              ";
+      for (j=1; j <= edges.Size(); j++)
+	{
+	  outfile << " ";
+	  outfile.width(8);
+	  outfile << edges[j-1];
+	}
+      outfile << "\n";
+    }
+
+  // surface element - edge - list (with boundary conditions)
+  outfile << nsurfelem << "\n";
+  for (i = 1; i <= nsurfelem; i++)
+    {
+      Element2d el = mesh.SurfaceElement(i);
+      if (invertsurf)
+	el.Invert();
+      outfile.width(4);
+      outfile << mesh.GetFaceDescriptor (el.GetIndex()).BCProperty() << "  ";
+      outfile.width(8);
+      outfile << el.GetNP();
+      for (j = 1; j <= el.GetNP(); j++)
+	{
+	  outfile << " ";
+	  outfile.width(8);
+	  outfile << el.PNum(j);
+	}
+
+      top->GetSurfaceElementEdges(i,edges);
+      outfile << endl << "      ";
+      outfile.width(8);
+      outfile << edges.Size();
+      for (j=1; j <= edges.Size(); j++)
+	{
+	  outfile << " ";
+	  outfile.width(8);
+	  outfile << edges[j-1];
+	}
+      outfile << "\n";
+    }
+
+
+  int v1, v2;
+  // edge - vertex - list
+  outfile << nedges << "\n";
+  for (i=1; i <= nedges; i++)
+    {
+      top->GetEdgeVertices(i,v1,v2);
+      outfile.width(4);
+      outfile << v1;
+      outfile << " ";
+      outfile.width(8);
+      outfile << v2 << endl;
+    }
+}
+
+
+
+
+
+
+
+
+
+#ifdef OLDSTYLE_WRITE
+
+
+void WriteFile (int typ,
+		const Mesh & mesh,
+		const CSGeometry & geom,
+		const char * filename,
+		const char * geomfile,
+		double h)
+{
+
+
+  int inverttets = mparam.inverttets;
+  int invertsurf = mparam.inverttrigs;
+
+
+
+
+
+
+
+
+  if (typ == WRITE_EDGEELEMENT)
+    {
+      // write edge element file
+      // Peter Harscher, ETHZ
+
+      cout << "Write Edge-Element Format" << endl;
+
+      ofstream outfile (filename);
+
+      int i, j;
+      int ned;
+
+      // hash table representing edges;
+      INDEX_2_HASHTABLE<int> edgeht(mesh.GetNP());
+
+      // list of edges
+      Array<INDEX_2> edgelist;
+
+      // edge (point) on boundary ?
+      BitArray bedge, bpoint(mesh.GetNP());
+
+      static int eledges[6][2] = { { 1, 2 } , { 1, 3 } , { 1, 4 },
+				   { 2, 3 } , { 2, 4 } , { 3, 4 } };
+
+      // fill hashtable   (point1, point2)  ---->  edgenr
+      for (i = 1; i <= mesh.GetNE(); i++)
+	{
+	  const Element & el = mesh.VolumeElement (i);
+	  INDEX_2 edge;
+	  for (j = 1; j <= 6; j++)
+	    {
+	      edge.I1() = el.PNum (eledges[j-1][0]);
+	      edge.I2() = el.PNum (eledges[j-1][1]);
+	      edge.Sort();
+
+	      if (!edgeht.Used (edge))
+		{
+		  edgelist.Append (edge);
+		  edgeht.Set (edge, edgelist.Size());
+		}
+	    }
+	}
+
+
+      // set bedges, bpoints
+      bedge.SetSize (edgelist.Size());
+      bedge.Clear();
+      bpoint.Clear();
+
+      for (i = 1; i <= mesh.GetNSE(); i++)
+	{
+	  const Element2d & sel = mesh.SurfaceElement(i);
+	  for (j = 1; j <= 3; j++)
+	    {
+	      bpoint.Set (sel.PNum(j));
+
+	      INDEX_2 edge;
+	      edge.I1() = sel.PNum(j);
+	      edge.I2() = sel.PNum(j%3+1);
+	      edge.Sort();
+
+	      bedge.Set (edgeht.Get (edge));
+	    }
+	}
+
+
+
+      outfile << mesh.GetNE() << endl;
+      // write element ---> point
+      for (i = 1; i <= mesh.GetNE(); i++)
+	{
+	  const Element & el = mesh.VolumeElement(i);
+
+	  outfile.width(8);
+	  outfile << i;
+	  for (j = 1; j <= 4; j++)
+	    {
+	      outfile.width(8);
+	      outfile << el.PNum(j);
+	    }
+	  outfile << endl;
+	}
+
+      // write element ---> edge
+      for (i = 1; i <= mesh.GetNE(); i++)
+	{
+	  const Element & el = mesh.VolumeElement (i);
+	  INDEX_2 edge;
+	  for (j = 1; j <= 6; j++)
+	    {
+	      edge.I1() = el.PNum (eledges[j-1][0]);
+	      edge.I2() = el.PNum (eledges[j-1][1]);
+	      edge.Sort();
+
+	      outfile.width(8);
+	      outfile << edgeht.Get (edge);
+	    }
+	  outfile << endl;
+	}
+
+      // write points
+      outfile << mesh.GetNP() << endl;
+      outfile.precision (6);
+      for (i = 1; i <= mesh.GetNP(); i++)
+	{
+	  const Point3d & p = mesh.Point(i);
+
+	  for (j = 1; j <= 3; j++)
+	    {
+	      outfile.width(8);
+	      outfile << p.X(j);
+	    }
+	  outfile << "       "
+		  << (bpoint.Test(i) ? "1" : 0) << endl;
+	}
+
+      // write edges
+      outfile << edgelist.Size() << endl;
+      for (i = 1; i <= edgelist.Size(); i++)
+	{
+	  outfile.width(8);
+	  outfile << edgelist.Get(i).I1();
+	  outfile.width(8);
+	  outfile << edgelist.Get(i).I2();
+	  outfile << "       "
+		  << (bedge.Test(i) ? "1" : "0") << endl;
+	}
+    }
+
+
+
+
+}
+#endif
+}
+
diff --git a/contrib/Netgen/libsrc/interface/writeuser.hpp b/contrib/Netgen/libsrc/interface/writeuser.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e5745713cb8b1f96ce6c08a9bff6822175ae00de
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writeuser.hpp
@@ -0,0 +1,165 @@
+#ifndef WRITEUSER
+#define WRITEUSER
+
+/**************************************************************************/
+/* File:    writeuser.hh                                                  */
+/* Authors: many                                                          */
+/* Date:    10. Dec. 97                                                   */
+/**************************************************************************/
+
+
+extern
+void WriteFile (int typ,
+                const Mesh & mesh,
+                const CSGeometry & geom,
+                const char * filename,
+                const char * geomfile = NULL,
+                double h = 0);
+
+
+
+extern
+void ReadFile (Mesh & mesh,
+               const string & filename);
+
+
+
+
+
+
+extern
+void WriteNeutralFormat (const Mesh & mesh,
+                         const CSGeometry & geom,
+                         const string & filename);
+
+extern
+void WriteSurfaceFormat (const Mesh & mesh,
+                         const string & filename);
+
+extern
+void WriteSTLFormat (const Mesh & mesh,
+                     const string & filename);
+
+
+// Philippose - 16 August 2010
+// Added the STL Extended format in which
+// each face of the geometry is treated as
+// a separate "solid" entity in the STL file
+extern
+void WriteSTLExtFormat (const Mesh & mesh,
+                        const string & filename);
+
+
+extern
+void WriteVRMLFormat (const Mesh & mesh,
+                      bool faces,
+                      const string & filename);
+
+extern
+void WriteFEPPFormat (const Mesh & mesh,
+                      const CSGeometry & geom,
+                      const string & filename);
+
+extern
+void WriteGmshFormat (const Mesh & mesh,
+                      const CSGeometry & geom,
+                      const string & filename);
+
+
+// Philippose - 29/01/2009
+// Added GMSH v2.xx Mesh Export support
+void WriteGmsh2Format (const Mesh & mesh,
+                       const CSGeometry & geom,
+                       const string & filename);
+
+
+// Philippose - 25/10/2009
+// Added OpenFOAM 1.5+ Mesh Export support
+extern 
+void WriteOpenFOAM15xFormat (const Mesh & mesh, 
+                             const string & casename);
+
+
+extern
+void WriteUserChemnitz (const Mesh & mesh,
+                        const string & filename);
+
+extern
+void WriteJCMFormat (const Mesh & mesh,
+                     const CSGeometry & geom,
+                     const string & filename);
+
+
+extern
+void WriteDiffPackFormat (const Mesh & mesh,
+                          const CSGeometry & geom,
+                          const string & filename);
+
+extern
+void WriteTochnogFormat (const Mesh & mesh,
+                         const string & filename);
+
+extern
+void WriteTecPlotFormat (const Mesh & mesh,
+                         const CSGeometry & geom,
+                         const string & filename);
+
+extern
+void WriteAbaqusFormat (const Mesh & mesh,
+                        const string & filename);
+
+extern
+void WriteFluentFormat (const Mesh & mesh,
+                        const string & filename);
+
+extern
+void WritePermasFormat (const Mesh & mesh,
+                        const string & filename);
+
+extern
+void WriteFEAPFormat (const Mesh & mesh,
+                      const string & filename);
+
+extern
+void WriteElmerFormat (const Mesh & mesh,
+                       const string & filename);
+
+
+extern
+void WriteEdgeElementFormat (const Mesh & mesh,
+                             const CSGeometry & geom,
+                             const string & filename);
+
+
+
+#ifdef OLIVER
+extern
+void WriteTETFormat (const Mesh & mesh,
+                     const string & filename);
+
+#endif
+
+extern void ReadTETFormat (Mesh & mesh,
+                           const string & filename);
+
+
+extern void ReadFNFFormat (Mesh & mesh,
+                           const string & filename);
+
+
+
+void WriteDolfinFormat (const Mesh & mesh,
+                        const string & filename);
+
+
+extern void RegisterUserFormats (Array<const char*> & names,
+                                 Array<const char*> & extensions);
+
+
+extern bool WriteUserFormat (const string & format,
+                             const Mesh & mesh,
+                             const NetgenGeometry & geom,
+                             const string & filename);
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/interface/wuchemnitz.cpp b/contrib/Netgen/libsrc/interface/wuchemnitz.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8641a8cde9b0dbfb298e222fa9cc4ce07f0087fa
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/wuchemnitz.cpp
@@ -0,0 +1,317 @@
+// Write Chemnitz file format
+
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+
+  class POINT3D
+  {
+  public:
+    POINT3D () { };
+    double x, y, z;
+  };
+
+  class VOLELEMENT
+  {
+  public:
+    int domnr, p1, p2, p3, p4;
+    int faces[4];
+
+    VOLELEMENT () 
+    { for (int i = 0; i < 4; i++) faces[i] = 0; }
+  };
+  
+  class SURFELEMENT
+  {
+  public:
+    SURFELEMENT () { };
+    int snr, p1, p2, p3;
+  };
+  
+
+  class FACE
+  {
+  public:
+    int p1, p2, p3;
+    int edges[3];
+
+    FACE () 
+    { for (int i = 0; i < 3; i++) edges[i] = 0; }
+  };
+
+  class EDGE
+  {
+  public:
+    EDGE () { };
+    int p1, p2;
+  };
+
+  static Array<POINT3D> points;
+  static Array<VOLELEMENT> volelements;
+  static Array<SURFELEMENT> surfelements;
+
+  static Array<FACE> faces;
+  static Array<EDGE> edges;
+
+
+  void ReadFile (char * filename)
+  {
+    int i, n;
+    ifstream infile(filename);
+    char reco[100];
+  
+  
+    infile >> reco;  // file format recognition
+  
+    infile >> n;   // number of surface elements
+    cout << n << " Surface elements" << endl;
+  
+    for (i = 1; i <= n; i++)
+      {
+        SURFELEMENT sel;
+        infile >> sel.snr >> sel.p1 >> sel.p2 >> sel.p3;
+        surfelements.Append (sel);
+      }
+    
+    infile >> n;   // number of volume elements
+    cout << n << " Volume elements" << endl;
+  
+    for (i = 1; i <= n; i++)
+      {
+        VOLELEMENT el;
+        infile >> el.p1 >> el.p2 >> el.p3 >> el.p4;
+        volelements.Append (el);
+      }
+    
+    infile >> n;   // number of points 
+    cout << n << " Points" << endl;
+  
+    for (i = 1; i <= n; i++)
+      {
+        POINT3D p;
+        infile >> p.x >> p.y >> p.z;
+        points.Append (p);
+      }
+  }
+  
+  
+
+  void ReadFileMesh (const Mesh & mesh)
+  {
+    int i, n;
+  
+    n = mesh.GetNSE();   // number of surface elements
+    cout << n << " Surface elements" << endl;
+  
+    for (i = 1; i <= n; i++)
+      {
+        SURFELEMENT sel;
+        const Element2d & el = mesh.SurfaceElement(i);
+        sel.snr = el.GetIndex();
+        sel.p1 = el.PNum(1);
+        sel.p2 = el.PNum(2);
+        sel.p3 = el.PNum(3);
+        surfelements.Append (sel);
+      }
+    
+    n = mesh.GetNE();   // number of volume elements
+    cout << n << " Volume elements" << endl;
+  
+    for (i = 1; i <= n; i++)
+      {
+        VOLELEMENT el;
+        const Element & nel = mesh.VolumeElement(i);
+        el.p1 = nel.PNum(1);
+        el.p2 = nel.PNum(2);
+        el.p3 = nel.PNum(3);
+        el.p4 = nel.PNum(4);
+        //      infile >> el.p1 >> el.p2 >> el.p3 >> el.p4;
+        volelements.Append (el);
+      }
+    
+    n = mesh.GetNP();   // number of points 
+    cout << n << " Points" << endl;
+  
+    for (i = 1; i <= n; i++)
+      {
+        POINT3D p;
+        Point3d mp = mesh.Point(i);
+        p.x = mp.X();
+        p.y = mp.Y();
+        p.z = mp.Z();
+        //      infile >> p.x >> p.y >> p.z;
+        points.Append (p);
+      }
+  }
+  
+
+
+
+  void Convert ()
+  {
+    int i, j, facei, edgei;
+    INDEX_3 i3;
+    INDEX_2 i2;
+
+    INDEX_3_HASHTABLE<int> faceindex(volelements.Size()/5 + 1);
+    INDEX_2_HASHTABLE<int> edgeindex(volelements.Size()/5 + 1);
+  
+    for (i = 1; i <= volelements.Size(); i++)
+      {
+        for (j = 1; j <= 4; j++)
+          {
+            switch (j)
+              {
+              case 1:
+                i3.I1() = volelements.Get(i).p2;
+                i3.I2() = volelements.Get(i).p3;
+                i3.I3() = volelements.Get(i).p4;
+                break;
+              case 2:
+                i3.I1() = volelements.Get(i).p1;
+                i3.I2() = volelements.Get(i).p3;
+                i3.I3() = volelements.Get(i).p4;
+                break;
+              case 3:
+                i3.I1() = volelements.Get(i).p1;
+                i3.I2() = volelements.Get(i).p2;
+                i3.I3() = volelements.Get(i).p4;
+                break;
+              case 4:
+                i3.I1() = volelements.Get(i).p1;
+                i3.I2() = volelements.Get(i).p2;
+                i3.I3() = volelements.Get(i).p3;
+                break;
+              default:
+                i3.I1()=i3.I2()=i3.I3()=0;
+              }
+            i3.Sort();
+            if (faceindex.Used (i3)) 
+              facei = faceindex.Get(i3);
+            else
+              {
+                FACE fa;
+                fa.p1 = i3.I1();
+                fa.p2 = i3.I2();
+                fa.p3 = i3.I3();
+                facei = faces.Append (fa);
+                faceindex.Set (i3, facei);
+              } 
+        
+            volelements.Elem(i).faces[j-1] = facei;  
+          }    
+    
+      } 
+ 
+
+    for (i = 1; i <= faces.Size(); i++)
+      {
+        for (j = 1; j <= 3; j++)
+          {
+            switch (j)
+              {
+              case 1:
+                i2.I1() = faces.Get(i).p2;
+                i2.I2() = faces.Get(i).p3;
+                break;
+              case 2:
+                i2.I1() = faces.Get(i).p1;
+                i2.I2() = faces.Get(i).p3;
+                break;
+              case 3:
+                i2.I1() = faces.Get(i).p1;
+                i2.I2() = faces.Get(i).p2;
+                break;
+              default:
+                i2.I1()=i2.I2()=0;
+              }
+            if (i2.I1() > i2.I2()) swap (i2.I1(), i2.I2());
+            if (edgeindex.Used (i2)) 
+              edgei = edgeindex.Get(i2);
+            else
+              {
+                EDGE ed;
+                ed.p1 = i2.I1();
+                ed.p2 = i2.I2();
+                edgei = edges.Append (ed);
+                edgeindex.Set (i2, edgei);
+              } 
+        
+            faces.Elem(i).edges[j-1] = edgei;  
+          }    
+    
+      }  
+ 
+  }  
+  
+  
+  void WriteFile (ostream & outfile)
+  {
+    int i;
+  
+    outfile 
+      << "#VERSION: 1.0" << endl
+      << "#PROGRAM: NETGEN" << endl
+      << "#EQN_TYPE: POISSON" << endl
+      << "#DIMENSION: 3D" << endl
+      << "#DEG_OF_FREE: 1" << endl
+      << "#DESCRIPTION: I don't know" << endl
+      << "##RENUM: not done" << endl
+      << "#USER: Kleinzen" << endl
+      << "DATE: 10.06.1996" << endl;
+  
+    outfile << "#HEADER:   8" << endl
+            << points.Size() << "  " << edges.Size() << "  " 
+            << faces.Size() << "  " << volelements.Size() << "  0  0  0  0" << endl;
+  
+    outfile << "#VERTEX:   " << points.Size() << endl;
+    for (i = 1; i <= points.Size(); i++)
+      outfile << "  " << i << "  " << points.Get(i).x << "  " << points.Get(i).y 
+              << "  " << points.Get(i).z << endl;
+    	
+    outfile << "#EDGE:  " << edges.Size() << endl;
+    for (i = 1; i <= edges.Size(); i++)
+      outfile << "  " << i << "  1  " 
+              << edges.Get(i).p1 << "  " 
+              << edges.Get(i).p2 
+              << "  0" << endl;
+    
+    outfile << "#FACE:  " << faces.Size() << endl;  
+    for (i = 1; i <= faces.Size(); i++)
+      outfile << "  " << i << "  1  3  " 
+              << faces.Get(i).edges[0] << "  " 
+              << faces.Get(i).edges[1] << "  " 
+              << faces.Get(i).edges[2] << endl;
+    	
+    outfile << "#SOLID:  " << volelements.Size() << endl;
+    for (i = 1; i <= volelements.Size(); i++)
+      outfile << "  " << i << "  1  4  " 
+              << volelements.Get(i).faces[0] << "  "
+              << volelements.Get(i).faces[1] << "  "
+              << volelements.Get(i).faces[2] << "  "
+              << volelements.Get(i).faces[3] << endl;
+    	
+    outfile << "#END_OF_DATA" << endl;
+  }
+    
+
+  void WriteUserChemnitz (const Mesh & mesh,
+                          const string & filename)
+  {
+    ofstream outfile (filename.c_str());
+
+    ReadFileMesh (mesh);
+    Convert ();
+  
+    WriteFile (outfile);
+    cout << "Wrote Chemnitz standard file" << endl;
+  }
+}
diff --git a/contrib/Netgen/libsrc/linalg/Makefile.am b/contrib/Netgen/libsrc/linalg/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..9503b31ed7052e2ebe7da0722cb2a094e384c1ab
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/Makefile.am
@@ -0,0 +1,9 @@
+noinst_HEADERS = densemat.hpp linalg.hpp polynomial.hpp vector.hpp opti.hpp
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc/include
+METASOURCES = AUTO
+noinst_LTLIBRARIES = libla.la
+libla_la_SOURCES = densemat.cpp polynomial.cpp bfgs.cpp linopt.cpp linsearch.cpp
+
+#  vector.cpp
+
+libla_la_LDFLAGS = -rdynamic
diff --git a/contrib/Netgen/libsrc/linalg/bfgs.cpp b/contrib/Netgen/libsrc/linalg/bfgs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e0f40d687e6420f4d6aa010810c0c845ebdda332
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/bfgs.cpp
@@ -0,0 +1,407 @@
+/***************************************************************************/
+/*                                                                         */
+/* Vorlesung Optimierung I, Gfrerer, WS94/95                               */
+/* BFGS-Verfahren zur Lösung freier nichtlinearer Optimierungsprobleme     */
+/*                                                                         */
+/* Programmautor:  Joachim Schöberl                                        */
+/* Matrikelnummer: 9155284                                                 */
+/*                                                                         */
+/***************************************************************************/
+
+#include <mystdlib.h>
+#include <myadt.hpp> 
+
+#include <linalg.hpp>
+#include "opti.hpp"
+
+
+namespace netgen
+{
+
+void Cholesky (const DenseMatrix & a,
+	       DenseMatrix & l, Vector & d)
+{
+  // Factors   A = L D L^T
+
+  double x;
+
+  int n = a.Height();
+  
+  //  (*testout) << "a = " << a << endl;
+
+  l = a;
+
+  for (int i = 1; i <= n; i++)
+    {
+      for (int j = i; j <= n; j++)
+	{
+	  x = l.Get(i, j);
+
+	  for (int k = 1; k < i; k++)
+	    x -= l.Get(i, k) * l.Get(j, k) * d(k-1); 
+          
+	  if (i == j)
+	    {
+	      d(i-1) = x;
+	    }
+	  else
+	    {
+	      l.Elem(j, i) = x / d(i-1);
+	    }
+	}
+    }
+
+  for (int i = 1; i <= n; i++)
+    {
+      l.Elem(i, i) = 1;
+      for (int j = i+1; j <= n; j++)
+	l.Elem(i, j) = 0;
+    }
+
+  /*
+  // Multiply:
+  (*testout) << "multiplied factors: " << endl;
+  for (i = 1; i <= n; i++)
+    for (j = 1; j <= n; j++)
+      {
+	x = 0;
+	for (k = 1; k <= n; k++)
+	  x += l.Get(i, k) * l.Get(j, k) * d.Get(k);
+	(*testout) << x << " ";
+      }
+  (*testout) << endl;
+  */
+}
+
+
+void MultLDLt (const DenseMatrix & l, const Vector & d, const Vector & g, Vector & p)
+{
+  /*
+  int i, j, n;
+  double val;
+
+  n = l.Height();
+  p = g;
+  for (i = 1; i <= n; i++)
+    {
+      val = 0;
+      for (j = i; j <= n; j++)
+	val += p.Get(j) * l.Get(j, i);
+      p.Set(i, val);
+    }
+  for (i = 1; i <= n; i++)
+    p.Elem(i) *= d.Get(i);
+
+  for (i = n; i >= 1; i--)
+    {
+      val = 0;
+      for (j = 1; j <= i; j++)
+	val += p.Get(j) * l.Get(i, j);
+      p.Set(i, val);
+    }
+  */
+
+
+
+  double val;
+
+  int n = l.Height();
+  p = g;
+  
+  for (int i = 0; i < n; i++)
+    {
+      val = 0;
+      for (int j = i; j < n; j++)
+	val += p(j) * l(j, i);
+      p(i) = val;
+    }
+
+  for (int i = 0; i < n; i++)
+    p(i) *= d(i);
+
+  for (int i = n-1; i >= 0; i--)
+    {
+      val = 0;
+      for (int j = 0; j <= i; j++)
+	val += p(j) * l(i, j);
+      p(i) = val;
+    }
+}
+
+void SolveLDLt (const DenseMatrix & l, const Vector & d, const Vector & g, Vector & p)
+{
+  double val;
+
+  int n = l.Height();
+  p = g;
+
+  for (int i = 0; i < n; i++)
+    {
+      val = 0;
+      for (int j = 0; j < i; j++)
+	val += p(j) * l(i,j);
+      p(i) -= val;
+    }
+
+  for (int i = 0; i < n; i++)
+    p(i) /= d(i);
+  
+  for (int i = n-1; i >= 0; i--)
+    {
+      val = 0;
+      for (int j = i+1; j < n; j++)
+	val += p(j) * l(j, i);
+      p(i) -= val;
+    }
+}
+
+int LDLtUpdate (DenseMatrix & l, Vector & d, double a, const Vector & u)
+{
+  // Bemerkung: Es wird a aus R erlaubt
+  // Rueckgabewert: 0 .. D bleibt positiv definit
+  //                1 .. sonst
+
+  int n = l.Height();
+
+  Vector v(n);
+  double t, told, xi;
+
+  told = 1;
+  v = u;
+
+  for (int j = 1; j <= n; j++)
+    {
+      t = told + a * sqr (v(j-1)) / d(j-1);
+
+      if (t <= 0) 
+	{
+	  (*testout) << "update err, t = " << t << endl;
+	  return 1;
+	}
+
+      xi = a * v(j-1) / (d(j-1) * t);
+
+      d(j-1) *= t / told;
+
+      for (int i = j + 1; i <= n; i++)
+	{
+	  v(i-1) -= v(j-1) * l.Elem(i, j);
+	  l.Elem(i, j) += xi * v(i-1);
+	}
+
+      told = t;
+    }
+
+  return 0;
+}
+
+
+double BFGS (
+	     Vector & x,         // i: Startwert
+	     // o: Loesung, falls IFAIL = 0
+	     const MinFunction & fun,
+	     const OptiParameters & par,
+	     double eps
+	     )
+
+
+{
+  int n = x.Size();
+  long it;
+  char a1crit, a3acrit;
+
+
+  Vector d(n), g(n), p(n), temp(n), bs(n), xneu(n), y(n), s(n), x0(n);
+  DenseMatrix l(n);
+  DenseMatrix hesse(n);
+
+  double /* normg, */ alphahat, hd, fold;
+  double a1, a2;
+  const double mu1 = 0.1, sigma = 0.1, xi1 = 1, xi2 = 10;
+  const double tau = 0.1, tau1 = 0.1, tau2 = 0.6;
+
+  Vector typx(x.Size());      // i: typische Groessenordnung der Komponenten
+  double f, f0;
+  double typf;               // i: typische Groessenordnung der Loesung
+  double fmin = -1e5;           // i: untere Schranke fuer Funktionswert
+  //  double eps = 1e-8;            // i: Abbruchschranke fuer relativen Gradienten
+  double tauf = 0.1;            // i: Abbruchschranke fuer die relative Aenderung der
+                                //    Funktionswerte
+  int ifail;                    // o:  0 .. Erfolg
+                                //    -1 .. Unterschreitung von fmin
+                                //     1 .. kein Erfolg bei Liniensuche
+                                //     2 .. Überschreitung von itmax
+
+  typx = par.typx;
+  typf = par.typf;
+
+
+  l = 0;
+  for (int i = 1; i <= n; i++)
+    l.Elem(i, i) = 1;
+
+  f = fun.FuncGrad (x, g);
+  f0 = f;
+  x0 = x;
+
+  it = 0;
+  do
+    {
+      // Restart
+
+      if (it % (5 * n) == 0)
+	{
+
+	  for (int i = 1; i <= n; i++)
+	    d(i-1) = typf/ sqr (typx(i-1));   // 1;
+	  for (int i = 2; i <= n; i++)
+	    for (int j = 1; j < i; j++)
+	      l.Elem(i, j) = 0;
+
+	  /*
+	  hesse = 0;
+	  for (i = 1; i <= n; i++)
+	    hesse.Elem(i, i) = typf / sqr (typx.Get(i));  
+
+	  fun.ApproximateHesse (x, hesse);
+
+	  Cholesky (hesse, l, d);
+	  */
+	}
+
+      it++;
+      if (it > par.maxit_bfgs)
+	{
+	  ifail = 2;
+	  break;
+	}
+
+
+      // Solve with factorized B
+
+      SolveLDLt (l, d, g, p);
+
+ //      (*testout) << "l " << l << endl
+// 		 << "d " << d << endl
+// 		 << "g " << g << endl
+// 		 << "p " << p << endl;
+
+
+      p *= -1;
+      y = g;
+
+      fold = f;
+
+      // line search
+
+      alphahat = 1;
+      lines (x, xneu, p, f, g, fun, par, alphahat, fmin,
+	     mu1, sigma, xi1, xi2, tau, tau1, tau2, ifail);
+
+      if(ifail == 1)
+	(*testout) << "no success with linesearch" << endl;
+
+       /*
+      // if (it > par.maxit_bfgs/2)
+	{
+	  (*testout) << "x = " << x << endl;
+	  (*testout) << "xneu = " << xneu << endl;
+	  (*testout) << "f = " << f << endl;
+	  (*testout) << "g = " << g << endl;
+	}
+      */
+
+      //      (*testout) << "it = " << it << " f = " << f << endl;
+      //      if (ifail != 0) break;
+
+      s.Set2 (1, xneu, -1, x);
+      y *= -1;
+      y.Add (1,g); // y += g;
+
+      x = xneu;
+
+      // BFGS Update
+
+      MultLDLt (l, d, s, bs);
+
+      a1 = y * s;
+      a2 = s * bs;
+
+      if (a1 > 0 && a2 > 0)
+	{
+	  if (LDLtUpdate (l, d, 1 / a1, y) != 0)
+	    {
+              cerr << "BFGS update error1" << endl;
+	      (*testout) << "BFGS update error1" << endl;
+	      (*testout) << "l " << endl << l << endl
+			 << "d " << d << endl;
+	      ifail = 1;
+	      break;
+	    }
+
+	  if (LDLtUpdate (l, d, -1 / a2, bs) != 0)
+	    {
+              cerr << "BFGS update error2" << endl;
+	      (*testout) << "BFGS update error2" << endl;
+	      (*testout) << "l " << endl << l << endl
+			 << "d " << d << endl;
+	      ifail = 1;
+	      break;
+	    }
+	}
+
+      // Calculate stop conditions
+
+      hd = eps * max2 (typf, fabs (f));
+      a1crit = 1;
+      for (int i = 1; i <= n; i++)
+	if ( fabs (g(i-1)) * max2 (typx(i-1), fabs (x(i-1))) > hd)
+	  a1crit = 0;
+
+
+      a3acrit = (fold - f <= tauf * max2 (typf, fabs (f)));
+
+      //    testout << "g = " << g << endl;
+      //    testout << "a1crit, a3crit = " << int(a1crit) << ", " << int(a3acrit) << endl;
+
+      /*
+	// Output for tests
+
+	normg = sqrt (g * g);
+
+	testout << "it =" << setw (5) << it
+	<< " f =" << setw (12) << setprecision (5) << f
+	<< " |g| =" << setw (12) << setprecision (5) << normg;
+
+	testout << " x = (" << setw (12) << setprecision (5) << x.Elem(1);
+	for (i = 2; i <= n; i++)
+	testout << "," << setw (12) << setprecision (5) << x.Elem(i);
+	testout << ")" << endl;
+	*/
+
+      //(*testout) << "it = " << it << " f = " << f << " x = " << x << endl
+      //	 << " g = " << g << " p = " << p << endl << endl;
+
+      //      (*testout) << "|g| = " << g.L2Norm() << endl;
+
+      if (g.L2Norm() < fun.GradStopping (x)) break;
+
+    }
+  while (!a1crit || !a3acrit);
+
+  /*
+  (*testout) << "it = " << it << " g = " << g << " f = " << f 
+	     << " fail = " << ifail << endl;
+  */
+  if (f0 < f || (ifail == 1))
+    {
+      (*testout) << "fail, f = " << f << " f0 = " << f0 << endl;
+      f = f0;
+      x = x0;
+    }
+
+  //  (*testout) << "x = " << x << ", x0 = " << x0 << endl;
+  return f;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/linalg/densemat.cpp b/contrib/Netgen/libsrc/linalg/densemat.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a0066e8ffd9e370fee54b3cfec61a1fcdb83d233
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/densemat.cpp
@@ -0,0 +1,1384 @@
+#include <mystdlib.h>
+
+#include <linalg.hpp>
+
+
+namespace netgen
+{
+  DenseMatrix :: DenseMatrix () 
+  {
+    data = NULL;
+    height = 0;
+    width = 0;
+  }
+
+  DenseMatrix :: DenseMatrix (int h, int w)
+  {
+    if (!w) w = h;
+    width = w;
+    height = h;
+    if (h*w)
+      data = new double[h*w];
+    else 
+      data = 0;
+
+    for (int i = 0 ; i < (h * w); i++)
+      data[i] = 0;
+  }
+
+  /*
+    DenseMatrix :: DenseMatrix (int h, int w, const double * d) 
+    : BaseMatrix (h, w)
+    {
+    int size = h * w;  
+    int i;
+  
+    if (size)
+    {
+    data = new double[size]; 
+    for (i = 0; i < size; i++)
+    data[i] = d[i];
+    }
+    else
+    data = NULL;
+    }    
+  */
+
+  DenseMatrix :: DenseMatrix (const DenseMatrix & m2)
+  {
+    data = NULL; height = width = 0;
+    SetSize (m2.Height(), m2.Width());
+    memcpy (data, m2.data, sizeof(double) * Height() * Width());
+  }
+
+  DenseMatrix :: ~DenseMatrix ()
+  {
+    delete [] data;
+  }
+  
+  
+  void DenseMatrix :: SetSize (int h, int w)
+  {
+    if (!w) w = h;
+    if (height == h && width == w)
+      return;
+          
+    height = h;
+    width = w;
+    
+    delete[] data;
+    
+    if (h*w)  
+      data = new double[h*w];
+    else
+      data = NULL;
+  }
+
+
+  /*
+    DenseMatrix & DenseMatrix :: operator= (const BaseMatrix & m2)
+    {
+    int i, j;
+
+    SetSize (m2.Height(), m2.Width());
+
+    if (data)
+    for (i = 1; i <= Height(); i++)
+    for (j = 1; j <= Width(); j++)
+    Set (i, j, m2(i, j));
+    else
+    (*myerr) << "DenseMatrix::Operator=: Matrix not allocated" << endl;
+
+    return *this;
+    }
+  */
+
+
+  DenseMatrix & DenseMatrix :: operator= (const DenseMatrix & m2)
+  {
+    SetSize (m2.Height(), m2.Width());
+    
+    if (data) memcpy (data, m2.data, sizeof(double) * m2.Height() * m2.Width());
+    return *this;
+  }
+
+
+  DenseMatrix & DenseMatrix :: operator+= (const DenseMatrix & m2)
+  {
+    int i;
+    double * p, * q;
+    
+    if (Height() != m2.Height() || Width() != m2.Width())
+      {
+        (*myerr) << "DenseMatrix::Operator+=: Sizes don't fit" << endl;
+        return *this;
+      }
+    
+    if (data)
+      {
+	p = data;
+	q = m2.data;
+	for (i = Width() * Height(); i > 0; i--)
+          {
+            *p += *q;
+            p++;
+            q++;
+          }
+      }
+    else
+      (*myerr) << "DenseMatrix::Operator+=: Matrix not allocated" << endl;
+
+    return *this;
+  }
+
+
+  DenseMatrix & DenseMatrix :: operator-= (const DenseMatrix & m2)
+  {
+    int i;
+    double * p, * q;
+
+    if (Height() != m2.Height() || Width() != m2.Width())
+      {
+        (*myerr) << "DenseMatrix::Operator-=: Sizes don't fit" << endl;
+        return *this;
+      }
+
+    if (data)
+      {
+        p = data;
+        q = m2.data;
+        for (i = Width() * Height(); i > 0; i--)
+          {
+            *p -= *q;
+            p++;
+            q++;
+          }
+      }
+    else
+      (*myerr) << "DenseMatrix::Operator-=: Matrix not allocated" << endl;
+
+    return *this;
+  }
+
+  DenseMatrix & DenseMatrix :: operator= (double v)
+  {
+    double * p = data;
+
+    if (data)
+      for (int i = width*height; i > 0; i--, p++)
+        *p = v;
+
+    return *this;
+  }
+
+
+
+  DenseMatrix & DenseMatrix :: operator*= (double v)
+  {
+    double * p = data;
+
+    if (data)
+      for (int i = width*height; i > 0; i--, p++)
+        *p *= v;
+
+    return *this;
+  }
+
+
+  double DenseMatrix :: Det () const
+  {
+    if (width != height)
+      {
+        (*myerr) << "DenseMatrix :: Det: width != height" << endl;
+        return 0;
+      }
+
+    switch (width)
+      {
+      case 1: return data[0];
+      case 2: return data[0] * data[3] - data[1] * data[2];
+      case 3: return data[0] * data[4] * data[8]
+          + data[1] * data[5] * data[6]
+          + data[2] * data[3] * data[7] 
+          - data[0] * data[5] * data[7]
+          - data[1] * data[3] * data[8]
+          - data[2] * data[4] * data[6];
+      default:
+        {
+          (*myerr) << "Matrix :: Det:  general size not implemented (size=" << width << ")" << endl;
+          return 0;
+        }
+      }
+  }
+
+
+  void CalcInverse (const DenseMatrix & m1, DenseMatrix & m2)
+  {
+    double det;
+
+    if (m1.Width() != m1.Height())
+      {
+        (*myerr) << "CalcInverse: matrix not symmetric" << endl;
+        return;
+      }
+    if (m1.Width() != m2.Width() || m1.Height() != m2.Height())
+      {
+        (*myerr) << "CalcInverse: dim(m2) != dim(m1)" << endl;
+        return;
+      }
+
+
+    if (m1.Width() <= 3)
+      {
+        det = m1.Det();
+        if (det == 0)
+          {
+            (*myerr) << "CalcInverse: Matrix singular" << endl;
+            (*testout) << "CalcInverse: Matrix singular" << endl;
+            return;
+          }
+
+        det = 1.0 / det;
+        switch (m1.Width())
+          {
+          case 1:
+            {
+              m2(0,0) = det;
+              return;
+            }
+          case 2:
+            {
+              m2(0,0) = det * m1(3);
+              m2(1,1) = det * m1(0);  
+              m2(0,1) = -det * m1(1);
+              m2(1,0) = - det * m1(2);
+              return;
+            }
+          case 3:
+            {
+              m2(0, 0) =  det * (m1(4) * m1(8) - m1(5) * m1(7));
+              m2(1, 0) = -det * (m1(3) * m1(8) - m1(5) * m1(6));
+              m2(2, 0) =  det * (m1(3) * m1(7) - m1(4) * m1(6));
+
+              m2(0, 1) = -det * (m1(1) * m1(8) - m1(2) * m1(7));
+              m2(1, 1) =  det * (m1(0) * m1(8) - m1(2) * m1(6));
+              m2(2, 1) = -det * (m1(0) * m1(7) - m1(1) * m1(6));
+
+              m2(0, 2) =  det * (m1(1) * m1(5) - m1(2) * m1(4));
+              m2(1, 2) = -det * (m1(0) * m1(5) - m1(2) * m1(3));
+              m2(2, 2) =  det * (m1(0) * m1(4) - m1(1) * m1(3));
+              return;
+            }
+          }
+      }
+    
+    else
+      {
+        int i, j, k, n;
+        n = m1.Height();
+      
+
+#ifdef CHOL
+        int dots = (n > 200);
+
+        // Cholesky
+      
+        double x;
+        Vector p(n);
+
+        m2 = m1;
+        /*
+          m2.SetSymmetric();
+          if (!m2.Symmetric())
+          cerr << "m should be symmetric for Cholesky" << endl;
+        */
+
+        for (i = 1; i <= n; i++)
+          for (j = 1; j < i; j++)
+            m2.Elem(j, i) = m2.Get(i, j);
+      
+        for (i = 1; i <= n; i++)
+          {
+            if (dots && i % 10 == 0)
+              (*mycout) << "." << flush;
+
+            for (j = i; j <= n; j++)
+              {
+                x = m2.Get(i, j);
+
+                const double * pik = &m2.Get(i, 1);
+                const double * pjk = &m2.Get(j, 1);
+
+                for (k = i-2; k >= 0; --k, ++pik, ++pjk)
+                  x -= (*pik) * (*pjk);
+		  
+                // for (k = i-1; k >= 1; --k)
+                //   x -= m2.Get(j, k) * m2.Get(i, k);
+
+                if (i == j)
+                  {
+                    if (x <= 0)
+                      {
+                        cerr << "Matrix indefinite 1" << endl;
+                        return;
+                      }
+		  
+                    p.Elem(i) = 1 / sqrt(x);
+                  }
+                else
+                  {
+                    m2.Elem(j, i) = x * p.Get(i);
+                  }
+              }
+          }
+
+        for (i = 1; i <= n; i++)
+          m2.Elem(i, i) = 1 / p.Get(i);
+
+        // check: A = L L^t
+
+        //       for (i = 1; i <= n; i++)
+        // 	for (j = 1; j <= n; j++)
+        // 	  {
+        // 	    x = 0;
+        // 	    for (k = 1; k <= i && k <= j; k++)
+        // 	      x += m2.Get(i, k) * m2.Get(j, k);
+        // 	    (*testout) << "err " << i << "," << j << " = " << (m1.Get(i, j) - x) << endl;
+        // 	  }
+
+
+      
+        // calc L^{-1}, store upper triangle
+      
+        //      DenseMatrix hm(n);
+        //      hm = m2;
+
+        for (i = 1; i <= n; i++)
+          {
+            if (dots && i % 10 == 0)
+              (*mycout) << "+" << flush;
+
+            for (j = i; j <= n; j++)
+              {
+                x = 0;
+                if (j == i) x = 1;
+
+                const double * pjk = &m2.Get(j, i);
+                const double * pik = &m2.Get(i, i);
+                for (k = i; k < j; k++, ++pjk, ++pik)
+                  x -= *pik * *pjk;
+
+                //  for (k = i; k < j; k++)
+                //  x -= m2.Get(j, k) * m2.Get(i, k);
+
+                m2.Elem(i, j) = x / m2.Get(j, j);
+              }
+          }
+      
+        //      (*testout) << "check L^-1" << endl;
+        //      for (i = 1; i <= n; i++)
+        // 	for (j = 1; j <= n; j++)
+        // 	  {
+        // 	    x = 0;
+        // 	    for (k = j; k <= i; k++)
+        // 	      x += hm.Get(i, k) * m2.Get(j, k);
+        // 	    (*testout) << "i, j = " << i << "," << j << " x = " << x << endl;
+        // 	  }
+
+
+        // calc A^-1 = L^-T * L^-1
+
+        for (i = 1; i <= n; i++)
+          {
+            if (dots && i % 10 == 0)
+              (*mycout) << "-" << flush;
+
+            for (j = 1; j <= i; j++)
+              {
+                x = 0;
+                k = i;
+                if (j > i) k = j;
+
+                const double * pik = &m2.Get(i, k);
+                const double * pjk = &m2.Get(j, k);
+
+                for ( ; k <= n; ++k, ++pik, ++pjk)
+                  x += *pik * *pjk;
+                // for (  ; k <= n; k++)
+                //   x += m2.Get(i, k) * m2.Get(j, k);
+	      
+                m2.Elem(i, j) = x;
+              }
+          }
+	  
+        for (i = 1; i <= n; i++)
+          for (j = 1; j < i; j++)
+            m2.Elem(j, i) = m2.Get(i, j);
+      
+        if (dots) (*mycout) << endl;
+#endif
+
+
+
+        // Gauss - Jordan - algorithm
+      
+        int r, hi;
+        double max, hr;
+      
+
+        Array<int> p(n);   // pivot-permutation
+        Vector hv(n);
+    
+      
+        m2 = m1;
+
+        /*      
+                if (m2.Symmetric())
+                for (i = 1; i <= n; i++)
+                for (j = 1; j < i; j++)
+                m2.Elem(j, i) = m2.Get(i, j);
+        */
+      
+        // Algorithm of Stoer, Einf. i. d. Num. Math, S 145
+      
+        for (j = 1; j <= n; j++)
+          p.Set(j, j);
+      
+        for (j = 1; j <= n; j++)
+          {
+            // pivot search
+	  
+            max = fabs(m2.Get(j, j));
+            r = j;
+	  
+            for (i = j+1; i <= n ;i++)
+              if (fabs (m2.Get(i, j)) > max)
+                {
+                  r = i;
+                  max = fabs (m2.Get(i, j));
+                }
+	  
+            if (max < 1e-20)
+              {
+                cerr << "Inverse matrix: matrix singular" << endl;
+                *testout << "Inverse matrix: matrix singular" << endl;
+                return;
+              }
+	  
+            r = j;
+	  
+            // exchange rows
+            if (r > j)
+              {
+                for (k = 1; k <= n; k++)
+                  {
+                    hr = m2.Get(j, k);
+                    m2.Elem(j, k) = m2.Get(r, k);
+                    m2.Elem(r, k) = hr;
+                  }
+                hi = p.Get(j);
+                p.Elem(j) = p.Get(r);
+                p.Elem(r) = hi;
+              }
+	  
+	  
+            // transformation
+	  
+            hr = 1 / m2.Get(j, j);
+            for (i = 1; i <= n; i++)
+              m2.Elem(i, j) *= hr;
+            m2.Elem(j, j) = hr;
+	  
+            for (k = 1; k <= n; k++)
+              if (k != j)
+                {
+                  for (i = 1; i <= n; i++)
+                    if (i != j)
+                      m2.Elem(i, k) -= m2.Elem(i, j) * m2.Elem(j, k);
+                  m2.Elem(j, k) *= -hr;
+                }
+          }
+      
+        // col exchange
+      
+        for (i = 1; i <= n; i++)
+          {
+            for (k = 1; k <= n; k++)
+              hv(p.Get(k)-1) = m2.Get(i, k);
+            for (k = 1; k <= n; k++)
+              m2.Elem(i, k) = hv(k-1);
+          }
+
+
+
+        /*
+          if (m1.Symmetric())
+          for (i = 1; i <= n; i++)
+          for (j = 1; j < i; j++)
+	  m1.Elem(j, i) = m1.Get(i, j);
+
+          m2 = 0;
+    
+          for (i = 1; i <= n; i++)
+          m2.Elem(i, i) = 1;
+      
+          for (i = 1; i <= n; i++)
+          {
+          //	(*mycout) << '.' << flush;
+          q = m1.Get(i, i);
+          for (k = 1; k <= n; k++)
+          {
+          m1.Elem(i, k) /= q;
+          m2.Elem(i, k) /= q;
+          }
+        
+          for (j = i+1; j <= n; j++)
+          {
+          q = m1.Elem(j, i);
+
+          double * m1pi = &m1.Elem(i, i);
+          double * m1pj = &m1.Elem(j, i);
+
+          for (k = n; k >= i; --k, ++m1pi, ++m1pj)
+          *m1pj -= q * (*m1pi);
+
+          double * m2pi = &m2.Elem(i, 1);
+          double * m2pj = &m2.Elem(j, 1);
+
+          for (k = i; k > 0; --k, ++m2pi, ++m2pj)
+          *m2pj -= q * (*m2pi);
+
+          //        for (k = 1; k <= n; k++)  
+          //          {
+          //          m1.Elem(j, k) -= q * m1.Elem(i, k);
+          //          m2.Elem(j, k) -= q * m2.Elem(i, k);
+          //          }
+	  
+          }
+          }  
+            
+          for (i = n; i >= 1; i--)
+          {
+          //	(*mycout) << "+" << flush;
+          for (j = 1; j < i; j++)
+	  {
+          q = m1.Elem(j, i);
+
+          double * m2pi = &m2.Elem(i, 1);
+          double * m2pj = &m2.Elem(j, 1);
+
+          for (k = n; k > 0; --k, ++m2pi, ++m2pj)
+          *m2pj -= q * (*m2pi);	    
+
+	    
+          //	    for (k = 1; k <= n; k++)
+          //	      {
+          //		m1.Elem(j, k) -= q * m1.Elem(i, k);
+          //		m2.Elem(j, k) -= q * m2.Elem(i, k);
+          //	      }    
+	  }         
+          }
+
+          if (m2.Symmetric())
+          {
+          for (i = 1; i <= n; i++)
+	  for (j = 1; j < i; j++)
+          m2.Elem(i, j) = m2.Elem(j, i);
+          }
+        */
+      }
+  }
+
+
+  void CalcAAt (const DenseMatrix & a, DenseMatrix & m2)
+  {
+    int n1 = a.Height();
+    int n2 = a.Width();
+    int i, j, k;
+    double sum;
+    const double *p, *q, *p0;
+
+    if (m2.Height() != n1 || m2.Width() != n1)
+      {
+        (*myerr) << "CalcAAt: sizes don't fit" << endl;
+        return;
+      }
+
+    for (i = 1; i <= n1; i++)
+      {
+        sum = 0;
+        p = &a.ConstElem(i, 1);
+        for (k = 1; k <= n2; k++)
+          {
+            sum += *p * *p;
+            p++;
+          }
+        m2.Set(i, i, sum);
+
+        p0 = &a.ConstElem(i, 1);
+        q = a.data;
+        for (j = 1; j < i; j++)
+          {
+            sum = 0;
+            p = p0;
+
+            for (k = 1; k <= n2; k++)
+              {
+                sum += *p * *q;
+                p++;
+                q++;
+              }
+            m2.Set(i, j, sum);
+            m2.Set(j, i, sum);
+          }
+      }
+  }
+
+
+
+  void CalcAtA (const DenseMatrix & a, DenseMatrix & m2)
+  {
+    int n1 = a.Height();
+    int n2 = a.Width();
+    int i, j, k;
+    double sum;
+
+    if (m2.Height() != n2 || m2.Width() != n2)
+      {
+        (*myerr) << "CalcAtA: sizes don't fit" << endl;
+        return;
+      }
+
+    for (i = 1; i <= n2; i++)
+      for (j = 1; j <= n2; j++)
+        {
+          sum = 0;
+          for (k = 1; k <= n1; k++)
+            sum += a.Get(k, i) * a.Get(k, j);
+          m2.Elem(i, j) = sum;
+        }
+  }
+
+
+
+  void CalcABt (const DenseMatrix & a, const DenseMatrix & b, DenseMatrix & m2)
+  {
+    int n1 = a.Height();
+    int n2 = a.Width();
+    int n3 = b.Height();
+    int i, j, k;
+    double sum;
+
+    if (m2.Height() != n1 || m2.Width() != n3 || b.Width() != n2)
+      {
+        (*myerr) << "CalcABt: sizes don't fit" << endl;
+        return;
+      }
+
+    double * pm2 = &m2.Elem(1, 1);
+    const double * pa1 = &a.Get(1, 1);
+
+    for (i = 1; i <= n1; i++)
+      {
+        const double * pb = &b.Get(1, 1);
+        for (j = 1; j <= n3; j++)
+          {
+            sum = 0;
+            const double * pa = pa1;
+	  
+            for (k = 1; k <= n2; k++)
+              {
+                sum += *pa * *pb;
+                pa++; pb++;
+              }
+	  
+            *pm2 = sum;
+            pm2++;
+          }
+        pa1 += n2;
+      }
+  }
+
+
+  void CalcAtB (const DenseMatrix & a, const DenseMatrix & b, DenseMatrix & m2)
+  {
+    int n1 = a.Height();
+    int n2 = a.Width();
+    int n3 = b.Width();
+    int i, j, k;
+
+    if (m2.Height() != n2 || m2.Width() != n3 || b.Height() != n1)
+      {
+        (*myerr) << "CalcAtB: sizes don't fit" << endl;
+        return;
+      }
+
+    for (i = 1; i <= n2 * n3; i++)
+      m2.data[i-1] = 0;
+
+    for (i = 1; i <= n1; i++)
+      for (j = 1; j <= n2; j++)
+        {
+          const double va = a.Get(i, j);
+          double * pm2 = &m2.Elem(j, 1);
+          const double * pb = &b.Get(i, 1);
+
+          for (k = 1; k <= n3; ++k, ++pm2, ++pb)
+            *pm2 += va * *pb;
+          //	for (k = 1; k <= n3; k++)
+          //	  m2.Elem(j, k) += va * b.Get(i, k);
+        }
+    /*
+      for (i = 1; i <= n2; i++)
+      for (j = 1; j <= n3; j++)
+      {
+      sum = 0;
+      for (k = 1; k <= n1; k++)
+      sum += a.Get(k, i) * b.Get(k, j);
+      m2.Elem(i, j) = sum;
+      }
+    */
+  }
+
+
+
+
+
+
+
+  DenseMatrix operator* (const DenseMatrix & m1, const DenseMatrix & m2)
+  {
+    DenseMatrix temp (m1.Height(), m2.Width());
+
+    if (m1.Width() != m2.Height())
+      {
+        (*myerr) << "DenseMatrix :: operator*: Matrix Size does not fit" << endl;
+      }
+    else if (temp.Height() != m1.Height())
+      {
+        (*myerr) << "DenseMatrix :: operator*: temp not allocated" << endl;
+      }
+    else
+      {
+        Mult (m1, m2, temp);
+      }
+    return temp;
+  }
+
+
+  void Mult (const DenseMatrix & m1, const DenseMatrix & m2, DenseMatrix & m3)
+  {
+    double sum;
+    double *p1, *p1s, *p1sn, *p1snn, *p2, *p2s, *p2sn, *p3;
+
+    if (m1.Width() != m2.Height() || m1.Height() != m3.Height() ||
+        m2.Width() != m3.Width() )
+      {
+        (*myerr) << "DenseMatrix :: Mult: Matrix Size does not fit" << endl;
+        (*myerr) << "m1: " << m1.Height() << " x " << m1.Width() << endl;
+        (*myerr) << "m2: " << m2.Height() << " x " << m2.Width() << endl;
+        (*myerr) << "m3: " << m3.Height() << " x " << m3.Width() << endl;
+        return;
+      }
+    /*
+      else if (m1.Symmetric() || m2.Symmetric() || m3.Symmetric())
+      {
+      (*myerr) << "DenseMatrix :: Mult: not implemented for symmetric matrices" << endl;
+      return;
+      }
+    */
+    else
+      {
+        //      int i, j, k;
+        int n1 = m1.Height();
+        int n2 = m2.Width();
+        int n3 = m1.Width();
+
+        /*
+          for (i = n1 * n2-1; i >= 0; --i)
+          m3.data[i] = 0;
+
+          const double * pm1 = &m1.Get(1, 1);
+          for (i = 1; i <= n1; i++)
+          {
+	  const double * pm2 = &m2.Get(1, 1);
+	  double * pm3i = &m3.Elem(i, 1);
+
+	  for (j = 1; j <= n3; j++)
+          {
+          const double vm1 = *pm1;
+          ++pm1;
+          //	      const double vm1 = m1.Get(i, j);
+          double * pm3 = pm3i;
+          //	      const double * pm2 = &m2.Get(j, 1);
+
+          for (k = 0; k < n2; k++)
+          {
+          *pm3 += vm1 * *pm2;
+          ++pm2;
+          ++pm3;
+          }
+
+          //	    for (k = 1; k <= n2; k++)
+          //	      m3.Elem(i, k) += m1.Get(i, j) * m2.Get(j, k);
+          }
+          }
+	*/
+
+        /*
+          for (i = 1; i <= n1; i++)
+          for (j = 1; j <= n2; j++)
+	  {
+          sum = 0;
+          for (k = 1; k <= n3; k++)
+          sum += m1.Get(i, k) * m2.Get(k, j);
+          m3.Set(i, j, sum);
+	  }
+        */
+
+
+        /*
+          for (i = 1; i <= n1; i++)
+          {
+	  const double pm1i = &m1.Get(i, 1);
+	  const double pm2j = &m2.Get(1, 1);
+
+	  for (j = 1; j <= n2; j++)
+          {
+          double sum = 0;
+          const double * pm1 = pm1i;
+          const double * pm2 = pm2j;
+          pm2j++;
+
+          for (k = 1; k <= n3; k++)
+          {
+          sum += *pm1 * *pm2;
+          ++pm1;
+          pm2 += n2;
+          }
+	      
+          m3.Set (i, j, sum);
+          }
+          }
+	*/
+
+
+        p3 = m3.data;
+        p1s = m1.data;
+        p2sn = m2.data + n2;
+        p1snn = p1s + n1 * n3;
+
+        while (p1s != p1snn)
+          {
+            p1sn = p1s + n3;
+            p2s = m2.data;
+	  
+            while (p2s != p2sn)
+              {
+                sum = 0;
+                p1 = p1s;
+                p2 = p2s;
+                p2s++;
+
+                while (p1 != p1sn)
+                  {
+                    sum += *p1 * *p2;
+                    p1++;
+                    p2 += n2;
+                  }
+                *p3++ = sum;
+              }
+            p1s = p1sn;
+          }
+      }
+  }  
+
+
+
+  DenseMatrix operator+ (const DenseMatrix & m1, const DenseMatrix & m2)
+  {
+    DenseMatrix temp (m1.Height(), m1.Width());
+    int i, j;
+
+    if (m1.Width() != m2.Width() || m1.Height() != m2.Height())
+      {
+        (*myerr) << "BaseMatrix :: operator+: Matrix Size does not fit" << endl;
+      }
+    else if (temp.Height() != m1.Height())
+      {
+        (*myerr) << "BaseMatrix :: operator+: temp not allocated" << endl;
+      }
+    else
+      {
+        for (i = 1; i <= m1.Height(); i++)
+          for (j = 1; j <= m1.Width(); j++)
+            {
+              temp.Set(i, j, m1.Get(i, j) + m2.Get(i, j));
+            }
+      }
+    return temp;
+  }
+
+
+
+
+  void Transpose (const DenseMatrix & m1, DenseMatrix & m2)
+  {
+    int w = m1.Width();
+    int h = m1.Height();
+    int i, j;
+
+    m2.SetSize (w, h);
+
+    double * pm2 = &m2.Elem(1, 1);
+    for (j = 1; j <= w; j++)
+      {
+        const double * pm1 = &m1.Get(1, j);
+        for (i = 1; i <= h; i++)
+          {
+            *pm2 = *pm1;
+            pm2 ++;
+            pm1 += w;
+          }
+      }
+  }
+
+
+  /*
+    void DenseMatrix :: Mult (const Vector & v, Vector & prod) const
+    {
+    double sum, val;
+    const double * mp, * sp;
+    double * dp;
+    // const Vector & v = bv.CastToVector();
+    // Vector & prod = bprod.CastToVector();
+  
+
+    int n = Height();
+    int m = Width();
+
+    if (prod.Size() != n)
+    prod.SetSize (n);
+
+    #ifdef DEVELOP
+    if (!n) 
+    {
+    cout << "DenseMatrix::Mult  mheight = 0" << endl;
+    }
+    if (!m) 
+    {
+    cout << "DenseMatrix::Mult mwidth = 0" << endl;
+    }
+
+    if (m != v.Size())
+    {
+    (*myerr) << "\nMatrix and Vector don't fit" << endl;
+    }
+    else if (Height() != prod.Size())
+    {
+    (*myerr) << "Base_Matrix::operator*(Vector): prod vector not ok" << endl;
+    }
+    else
+    #endif
+    {
+    if (Symmetric())
+    {
+    int i, j;
+
+
+    for (i = 1; i <= n; i++)
+    {
+    sp = &v.Get(1);
+    dp = &prod.Elem(1);
+    mp = &Get(i, 1);
+
+    val = v.Get(i);
+    sum = Get(i, i) * val;
+
+    for (j = 1; j < i; ++j, ++mp, ++sp, ++dp)
+    {
+    sum += *mp * *sp;
+    *dp += val * *mp;
+    }
+
+    prod.Elem(i) = sum;
+    }
+    }
+    else
+    {
+    mp = data;
+    dp = &prod.Elem(1);
+    for (int i = 1; i <= n; i++)
+    {
+    sum = 0;
+    sp = &v.Get(1);
+	      
+    for (int j = 1; j <= m; j++)
+    {
+    //        sum += Get(i,j) * v.Get(j);
+    sum += *mp * *sp;
+    mp++;
+    sp++;
+    }
+	      
+    //      prod.Set (i, sum);
+    *dp = sum;
+    dp++;
+    }
+    }
+    }
+    }
+  */
+
+  void DenseMatrix :: MultTrans (const Vector & v, Vector & prod) const
+  {
+    // const Vector & v = (const Vector&)bv; // .CastToVector();
+    // Vector & prod = (Vector & )bprod;     // .CastToVector();
+
+    /*
+      if (Height() != v.Size())
+      {
+      (*myerr) << "\nMatrix and Vector don't fit" << endl;
+      }
+      else if (Width() != prod.Size())
+      {
+      (*myerr) << "Base_Matrix::operator*(Vector): prod vector not ok" << endl;
+      }
+      else
+    */
+    {
+      int i, j;
+      int w = Width(), h = Height();
+      if (prod.Size() != w)
+	prod.SetSize (w);
+
+      const double * pmat = &Get(1, 1);
+      const double * pv = &v(0);
+
+      prod = 0;
+
+      for (i = 1; i <= h; i++)
+	{
+	  double val = *pv;
+	  ++pv;
+
+	  double * pprod = &prod(0);
+
+	  for (j = w-1; j >= 0; --j, ++pmat, ++pprod)
+	    {
+	      *pprod += val * *pmat;
+	    }
+	}
+	
+      /*
+        double sum;
+
+        for (i = 1; i <= Width(); i++)
+	{
+        sum = 0;
+	  
+        for (int j = 1; j <= Height(); j++)
+        sum += Get(j, i) * v.Get(j);
+	  
+        prod.Set (i, sum);
+	}
+      */
+    }
+  }
+
+
+  void DenseMatrix :: Residuum (const Vector & x, const Vector & b,
+                                Vector & res) const
+  {
+    double sum;
+    //   const Vector & x = bx.CastToVector();
+    //  const Vector & b = bb.CastToVector();
+    //  Vector & res = bres.CastToVector();
+
+    res.SetSize (Height());
+
+    if (Width() != x.Size() || Height() != b.Size())
+      {
+        (*myerr) << "\nMatrix and Vector don't fit" << endl;
+      }
+    else if (Height() != res.Size())
+      {
+        (*myerr) << "Base_Matrix::operator*(Vector): prod vector not ok" << endl;
+      }
+    else
+      {
+        int h = Height(); 
+        int w = Width();
+        const double * mp = &Get(1, 1);
+
+        for (int i = 1; i <= h; i++)
+          {
+            sum = b(i-1);
+            const double * xp = &x(0);
+
+            for (int j = 1; j <= w; ++j, ++mp, ++xp)
+              sum -= *mp * *xp;
+	  
+            res(i-1) = sum;
+          }
+      }
+  }
+
+#ifdef ABC
+  double DenseMatrix :: EvaluateBilinearform (const Vector & hx) const
+  {
+    double sum = 0, hsum;
+    // const Vector & hx = x.CastToVector();
+    int i, j;
+
+    if (Width() != hx.Size() || Height() != hx.Size())
+      {
+        (*myerr) << "Matrix::EvaluateBilinearForm: sizes don't fit" << endl;
+      }
+    else
+      {
+        for (i = 1; i <= Height(); i++)
+          {
+            hsum = 0;
+            for (j = 1; j <= Height(); j++)
+              {
+                hsum += Get(i, j) * hx.Get(j);
+              }
+            sum += hsum * hx.Get(i);
+          }
+      }
+
+    //  testout << "sum = " << sum << endl;
+    return sum;
+  }
+
+
+  void DenseMatrix :: MultElementMatrix (const Array<int> & pnum, 
+                                         const Vector & hx, Vector & hy)
+  {
+    int i, j;
+    //  const Vector & hx = x.CastToVector();
+    //  Vector & hy = y.CastToVector();
+
+    if (Symmetric())
+      {
+        for (i = 1; i <= Height(); i++)
+          {
+            for (j = 1; j < i; j++)
+              {
+                hy.Elem(pnum.Get(i)) += Get(i, j) * hx.Get(pnum.Get(j));
+                hy.Elem(pnum.Get(j)) += Get(i, j) * hx.Get(pnum.Get(i));
+              }
+            hy.Elem(pnum.Get(j)) += Get(i, i) * hx.Get(pnum.Get(i));	
+          }
+      }
+    else
+      for (i = 1; i <= Height(); i++)
+        for (j = 1; j <= Width(); j++)
+          hy.Elem(pnum.Get(i)) += Get(i, j) * hx.Get(pnum.Get(j));
+    
+  }
+  
+  void DenseMatrix :: MultTransElementMatrix (const Array<int> & pnum, 
+                                              const Vector & hx, Vector & hy)
+  {
+    int i, j;
+    //  const Vector & hx = x.CastToVector();
+    //  Vector & hy = y.CastToVector();
+
+    if (Symmetric())
+      MultElementMatrix (pnum, hx, hy);
+    else
+      for (i = 1; i <= Height(); i++)
+        for (j = 1; j <= Width(); j++)
+          hy.Elem(pnum.Get(i)) += Get(j, i) * hx.Get(pnum.Get(j));
+  }
+#endif
+
+
+  void DenseMatrix :: Solve (const Vector & v, Vector & sol) const
+  {
+    DenseMatrix temp (*this);
+    temp.SolveDestroy (v, sol);
+  }
+
+
+  void DenseMatrix :: SolveDestroy (const Vector & v, Vector & sol)
+  {
+    double q;
+
+    if (Width() != Height())
+      {
+        (*myerr) << "SolveDestroy: Matrix not square";
+        return;
+      }
+    if (Width() != v.Size())
+      {
+        (*myerr) << "SolveDestroy: Matrix and Vector don't fit";
+        return;
+      }
+
+    sol = v;
+    if (Height() != sol.Size())
+      {
+        (*myerr) << "SolveDestroy: Solution Vector not ok";
+        return;
+      }
+
+
+    if (0 /* Symmetric() */)
+      {
+      
+        // Cholesky factorization
+
+        int i, j, k, n;
+        n = Height();
+      
+        // Cholesky
+      
+        double x;
+        Vector p(n);
+
+        for (i = 1; i <= n; i++)
+          for (j = 1; j < i; j++)
+            Elem(j, i) = Get(i, j);
+      
+        for (i = 1; i <= n; i++)
+          {
+            // (*mycout) << "." << flush;
+            for (j = i; j <= n; j++)
+              {
+                x = Get(i, j);
+
+                const double * pik = &Get(i, 1);
+                const double * pjk = &Get(j, 1);
+
+                for (k = i-2; k >= 0; --k, ++pik, ++pjk)
+                  x -= (*pik) * (*pjk);
+		  
+                // for (k = i-1; k >= 1; --k)
+                //   x -= Get(j, k) * Get(i, k);
+
+                if (i == j)
+                  {
+                    if (x <= 0)
+                      {
+                        cerr << "Matrix indefinite" << endl;
+                        return;
+                      }
+		  
+                    p(i-1) = 1 / sqrt(x);
+                  }
+                else
+                  {
+                    Elem(j, i) = x * p(i-1);
+                  }
+              }
+          }
+
+        for (int i = 1; i <= n; i++)
+          Elem(i, i) = 1 / p(i-1);
+
+        // A = L L^t 
+        // L stored in left-lower triangle
+
+
+        sol = v;
+
+        // Solve L sol = sol
+
+        for (int i = 1; i <= n; i++)
+          {
+            double val = sol(i-1);
+
+            const double * pij = &Get(i, 1);
+            const double * solj = &sol(0);
+
+            for (int j = 1; j < i; j++, ++pij, ++solj)
+              val -= *pij * *solj;
+            //	  for (j = 1; j < i; j++)
+            //	    val -= Get(i, j) * sol.Get(j);
+
+            sol(i-1) = val / Get(i, i);
+          }
+
+        // Solve L^t sol = sol
+
+        for (int i = n; i >= 1; i--)
+          {
+            double val = sol(i-1) / Get(i, i);
+            sol(i-1) = val;
+
+            double * solj = &sol(0);
+            const double * pij = &Get(i, 1);
+
+            for (j = 1; j < i; ++j, ++pij, ++solj)
+              *solj -= val * *pij;
+            //	  for (j = 1; j < i; j++)
+            //	    sol.Elem(j) -= Get(i, j) * val;
+          }
+
+
+      }
+    else
+      {
+        //      (*mycout) << "gauss" << endl;
+        int n = Height();
+        for (int i = 1; i <= n; i++)
+          {
+            for (int j = i+1; j <= n; j++)
+              {
+                q = Get(j,i) / Get(i,i);
+                if (q)
+                  {
+                    const double * pik = &Get(i, i+1);
+                    double * pjk = &Elem(j, i+1);
+
+                    for (int k = i+1; k <= n; ++k, ++pik, ++pjk)
+                      *pjk -= q * *pik;
+		  
+                    //  for (k = i+1; k <= Height(); k++)
+                    //	Elem(j, k) -= q * Get(i,k);
+
+
+                    sol(j-1) -= q * sol(i-1);
+                  }
+              }
+          }
+      
+        for (int i = n; i >= 1; i--)
+          {
+            q = sol(i-1);
+            for (int j = i+1; j <= n; j++)
+	      q -= Get(i,j) * sol(j-1);
+
+            sol(i-1) = q / Get(i,i);
+          }
+      }
+  }
+
+
+  /*
+    BaseMatrix * DenseMatrix :: Copy () const
+    {
+    return new DenseMatrix (*this);
+    }
+  */
+
+
+
+
+  ostream & operator<< (ostream & ost, const DenseMatrix & m)
+  {
+    for (int i = 0; i < m.Height(); i++)
+      {
+        for (int j = 0; j < m.Width(); j++)
+          ost << m.Get(i+1,j+1) << " ";
+        ost << endl;
+      }
+    return ost;
+  }
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/linalg/densemat.hpp b/contrib/Netgen/libsrc/linalg/densemat.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d721b5a98fcc41c4482c589d000d3a2d2d96835
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/densemat.hpp
@@ -0,0 +1,277 @@
+#ifndef FILE_DENSEMAT
+#define FILE_DENSEMAT
+
+/**************************************************************************/
+/* File:   densemat.hpp                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Oct. 94                                                    */
+/**************************************************************************/
+
+/** 
+    Data type dense matrix
+*/
+
+
+class DenseMatrix
+{
+protected:
+  int height;
+  int width;
+  double * data;
+
+public:
+  ///
+  DLL_HEADER DenseMatrix ();
+  ///
+  DLL_HEADER DenseMatrix (int h, int w = 0);
+  ///
+  DLL_HEADER DenseMatrix (const DenseMatrix & m2);
+  ///
+  DLL_HEADER ~DenseMatrix ();
+
+  ///
+  DLL_HEADER void SetSize (int h, int w = 0);
+
+  int Height() const { return height; }
+  int Width() const {return width; }
+
+  double & operator() (int i, int j) { return data[i*width+j]; }
+  double operator() (int i, int j) const { return data[i*width+j]; }
+  double & operator() (int i) { return data[i]; }
+  double operator() (int i) const { return data[i]; }
+
+  ///
+  DLL_HEADER DenseMatrix & operator= (const DenseMatrix & m2);
+  ///
+  DLL_HEADER DenseMatrix & operator+= (const DenseMatrix & m2);
+  ///
+  DLL_HEADER DenseMatrix & operator-= (const DenseMatrix & m2);
+
+  ///
+  DLL_HEADER DenseMatrix & operator= (double v);
+  ///
+  DLL_HEADER DenseMatrix & operator*= (double v);
+
+  ///
+  DLL_HEADER void Mult (const FlatVector & v, FlatVector & prod) const
+  {
+    double sum;
+    const double * mp, * sp;
+    double * dp;
+    
+#ifdef DEBUG
+    if (prod.Size() != height)
+      {
+	(*myerr) << "Mult: wrong vector size " << endl;
+      }
+    if (!height) 
+      {
+	cout << "DenseMatrix::Mult height = 0" << endl;
+      }
+    if (!width) 
+      {
+	cout << "DenseMatrix::Mult width = 0" << endl;
+      }
+    
+    if (width != v.Size())
+      {
+	(*myerr) << "\nMatrix and Vector don't fit" << endl;
+      }
+    else if (Height() != prod.Size())
+      {
+	(*myerr) << "Base_Matrix::operator*(Vector): prod vector not ok" << endl;
+      }
+    else
+#endif
+      {      
+	mp = data;
+	dp = &prod(0);
+        for (int i = 0; i < height; i++)
+	  {
+	    sum = 0;
+	    sp = &v(0);
+	    
+	    for (int j = 0; j < width; j++)
+	      {
+		//        sum += Get(i,j) * v.Get(j);
+		sum += *mp * *sp;
+		mp++;
+		sp++;
+	      }
+	    
+	    *dp = sum;
+	    dp++;
+	  }
+      }
+  }
+
+  ///
+  DLL_HEADER void MultTrans (const Vector & v, Vector & prod) const;
+  ///
+  DLL_HEADER void Residuum (const Vector & x, const Vector & b, Vector & res) const;
+  ///
+  DLL_HEADER double Det () const;
+
+  ///
+  friend DenseMatrix operator* (const DenseMatrix & m1, const DenseMatrix & m2);
+  ///
+  friend DenseMatrix operator+ (const DenseMatrix & m1, const DenseMatrix & m2);
+
+  /// 
+  friend void Transpose (const DenseMatrix & m1, DenseMatrix & m2);
+  ///
+  friend void Mult (const DenseMatrix & m1, const DenseMatrix & m2, DenseMatrix & m3);
+  ///
+//  friend void CalcInverse (const DenseMatrix & m1, DenseMatrix & m2);
+  ///
+  friend void CalcAAt (const DenseMatrix & a, DenseMatrix & m2);
+  ///
+//  friend void CalcAtA (const DenseMatrix & a, DenseMatrix & m2);
+  ///
+  friend void CalcABt (const DenseMatrix & a, const DenseMatrix & b, DenseMatrix & m2);
+  ///
+  friend void CalcAtB (const DenseMatrix & a, const DenseMatrix & b, DenseMatrix & m2);
+  ///
+  DLL_HEADER void Solve (const Vector & b, Vector & x) const;
+  ///
+  void SolveDestroy (const Vector & b, Vector & x);
+  ///
+  const double & Get(int i, int j) const { return data[(i-1)*width+j-1]; }
+  ///
+  const double & Get(int i) const { return data[i-1]; }
+  ///
+  void Set(int i, int j, double v) { data[(i-1)*width+j-1] = v; }
+  ///
+  double & Elem(int i, int j) { return data[(i-1)*width+j-1]; }
+  ///
+  const double & ConstElem(int i, int j) const { return data[(i-1)*width+j-1]; }
+};
+
+
+extern ostream & operator<< (ostream & ost, const DenseMatrix & m);
+
+
+
+template <int WIDTH>
+class MatrixFixWidth
+{
+protected:
+  int height;
+  double * data;
+
+public:
+  ///
+  MatrixFixWidth () 
+  { height = 0; data = 0; }
+  ///
+  MatrixFixWidth (int h)
+  { height = h; data = new double[WIDTH*height]; }
+  ///
+  ~MatrixFixWidth ()
+  { delete [] data; }
+
+  void SetSize (int h)
+  {
+    if (h != height)
+      {
+	delete data;
+	height = h;
+	data = new double[WIDTH*height]; 
+      }
+  }
+
+  ///
+  int Height() const { return height; }
+
+  ///
+  int Width() const { return WIDTH; }
+
+  ///
+  MatrixFixWidth & operator= (double v)
+  {
+    for (int i = 0; i < height*WIDTH; i++)
+      data[i] = v; 
+    return *this;
+  }
+
+  ///
+  void Mult (const FlatVector & v, FlatVector & prod) const
+  {
+    double sum;
+    const double * mp, * sp;
+    double * dp;
+
+    /*    
+    if (prod.Size() != height)
+      {
+	cerr << "MatrixFixWidth::Mult: wrong vector size " << endl;
+	assert (1);
+      }
+    */    
+
+    mp = data;
+    dp = &prod[0];
+    for (int i = 0; i < height; i++)
+      {
+	sum = 0;
+	sp = &v[0];
+	
+	for (int j = 0; j < WIDTH; j++)
+	  {
+	    sum += *mp * *sp;
+	    mp++;
+	    sp++;
+	  }
+	    
+	*dp = sum;
+	dp++;
+      }
+  }
+
+  double & operator() (int i, int j)
+  { return data[i*WIDTH+j]; }
+
+  const double & operator() (int i, int j) const
+  { return data[i*WIDTH+j]; }
+
+
+  MatrixFixWidth & operator*= (double v)
+  {
+    if (data)
+      for (int i = 0; i < height*WIDTH; i++)
+        data[i] *= v;
+    return *this;
+  }
+
+
+
+  const double & Get(int i, int j) const { return data[(i-1)*WIDTH+j-1]; }
+  ///
+  const double & Get(int i) const { return data[i-1]; }
+  ///
+  void Set(int i, int j, double v) { data[(i-1)*WIDTH+j-1] = v; }
+  ///
+  double & Elem(int i, int j) { return data[(i-1)*WIDTH+j-1]; }
+  ///
+  const double & ConstElem(int i, int j) const { return data[(i-1)*WIDTH+j-1]; }
+};
+
+
+template <int WIDTH>
+extern ostream & operator<< (ostream & ost, const MatrixFixWidth<WIDTH> & m)
+{
+  for (int i = 0; i < m.Height(); i++)
+    {
+      for (int j = 0; j < m.Width(); j++)
+	ost << m.Get(i+1,j+1) << " ";
+      ost << endl;
+    }
+  return ost;
+};
+
+
+extern DLL_HEADER void CalcAtA (const DenseMatrix & a, DenseMatrix & m2);
+extern DLL_HEADER void CalcInverse (const DenseMatrix & m1, DenseMatrix & m2);
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/linalg/linalg.hpp b/contrib/Netgen/libsrc/linalg/linalg.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..95d0c823c1878b33232503a49e3434db75642996
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/linalg.hpp
@@ -0,0 +1,32 @@
+#ifndef FILE_LINALG
+#define FILE_LINALG
+
+/* *************************************************************************/
+/* File:   linalg.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Oct. 94                                                    */
+/* *************************************************************************/
+
+/* 
+
+   Data types for basic linear algebra
+   
+   The basic concepts include the data types 
+   
+    Vector
+    SparseMatrix
+    DenseMatrix
+
+*/
+
+
+#include "../include/myadt.hpp"
+namespace netgen
+{
+#include "vector.hpp"
+#include "densemat.hpp"
+#include "polynomial.hpp"
+}
+#endif
+
+
diff --git a/contrib/Netgen/libsrc/linalg/linopt.cpp b/contrib/Netgen/libsrc/linalg/linopt.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc5b53fa479fe4cdc28ab056e92ee251c44cdfea
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/linopt.cpp
@@ -0,0 +1,73 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include "opti.hpp"
+
+namespace netgen
+{
+
+void LinearOptimize (const DenseMatrix & a, const Vector & b, 
+    const Vector & c, Vector & x)
+    
+  {
+  int i1, i2, i3, j;
+  DenseMatrix m(3), inv(3);
+  Vector rs(3), hx(3), res(a.Height()), res2(3);
+  double f, fmin;
+  int nrest;
+    
+  if (a.Width() != 3)
+    {
+    cerr << "LinearOptimize only implemented for 3 unknowns" << endl;
+    return;
+    }
+    
+  fmin = 1e10;
+  x = 0;
+  nrest = a.Height();
+  for (i1 = 1; i1 <= nrest; i1++)
+    for (i2 = i1 + 1; i2 <= nrest; i2++)
+      for (i3 = i2 + 1; i3 <= nrest; i3++)
+        {
+        for (j = 1; j <= 3; j++)
+          {
+          m.Elem(1, j) = a.Get(i1, j);
+          m.Elem(2, j) = a.Get(i2, j);
+          m.Elem(3, j) = a.Get(i3, j);
+          }
+          
+        rs(0) = b(i1-1);
+        rs(1) = b(i2-1);
+        rs(2) = b(i3-1);
+        
+        if (fabs (m.Det()) < 1e-12) continue;
+        
+        CalcInverse (m, inv);
+        inv.Mult (rs, hx);
+        
+        a.Residuum (hx, b, res);
+//        m.Residuum (hx, rs, res2);
+        f = c * hx;
+
+/*        
+        testout -> precision(12);
+        (*testout) << "i = (" << i1 << "," << i2 << "," << i3 
+           << "), f = " << f << " x = " << x << " res = " << res 
+           <<  " resmin = " << res.Min() 
+           << " res2 = " << res2 << " prod = " << prod << endl;
+*/
+
+	
+	double rmin = res(0);
+	for (int hi = 1; hi < res.Size(); hi++)
+	  if (res(hi) < rmin) rmin = res(hi);
+        
+        if ( (f < fmin) && rmin >= -1e-8)
+          {
+          fmin = f;
+          x = hx;
+          }
+        }
+  }
+}
diff --git a/contrib/Netgen/libsrc/linalg/linsearch.cpp b/contrib/Netgen/libsrc/linalg/linsearch.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a2dd38aabf2b762c1c9db514f7720149e452a3ca
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/linsearch.cpp
@@ -0,0 +1,349 @@
+/***************************************************************************/
+/*                                                                         */
+/* Problem:        Liniensuche                                             */
+/*                                                                         */
+/* Programmautor:  Joachim Schöberl                                        */
+/* Matrikelnummer: 9155284                                                 */
+/*                                                                         */
+/* Algorithmus nach:                                                       */
+/*                                                                         */
+/*   Optimierung I, Gfrerer, WS94/95                                       */
+/*   Algorithmus 2.1: Liniensuche Problem (ii)                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>  // min, max, sqr
+
+#include <linalg.hpp>
+#include "opti.hpp"
+
+
+namespace netgen
+{
+const double eps0 = 1E-15;
+
+// Liniensuche
+
+
+double MinFunction :: Func (const Vector & /* x */) const
+{
+  cerr << "Func of MinFunction called" << endl;
+  return 0;
+}
+
+void MinFunction :: Grad (const Vector & /* x */, Vector & /* g */) const
+{
+  cerr << "Grad of MinFunction called" << endl;
+}
+  
+double MinFunction :: FuncGrad (const Vector & x, Vector & g) const
+{
+  cerr << "Grad of MinFunction called" << endl;
+  return 0;
+  /*
+  int n = x.Size();
+
+  Vector xr(n);
+  Vector xl(n);
+
+  double eps = 1e-6;
+  double fl, fr;
+  
+  for (int i = 1; i <= n; i++)
+    {
+      xr.Set (1, x);
+      xl.Set (1, x);
+
+      xr.Elem(i) += eps;
+      fr = Func (xr);
+
+      xl.Elem(i) -= eps;
+      fl = Func (xl);
+
+      g.Elem(i) = (fr - fl) / (2 * eps);
+    }
+
+  double f = Func(x);
+  //  (*testout) << "f = " << f << " grad = " << g << endl;
+  return f;
+  */
+}
+
+
+double MinFunction :: FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const
+{
+  Vector g(x.Size());
+  double f = FuncGrad (x, g);
+  deriv = (g * dir);
+
+  //  (*testout) << "g = " << g << ", dir = " << dir << ", deriv = " << deriv << endl;
+  return f;
+}
+
+void MinFunction :: ApproximateHesse (const Vector & x,
+				      DenseMatrix & hesse) const
+{
+  int n = x.Size();
+  int i, j;
+
+  static Vector hx;
+  hx.SetSize(n);
+
+  double eps = 1e-6;
+  double f, f11, f12, f21, f22;
+  
+  for (i = 0; i < n; i++)
+    {
+      for (j = 0; j < i; j++)
+	{
+	  hx = x;
+	  hx(i) = x(i) + eps;
+	  hx(j) = x(j) + eps;
+	  f11 = Func(hx);
+	  hx(i) = x(i) + eps;
+	  hx(j) = x(j) - eps;
+	  f12 = Func(hx);
+	  hx(i) = x(i) - eps;
+	  hx(j) = x(j) + eps;
+	  f21 = Func(hx);
+	  hx(i) = x(i) - eps;
+	  hx(j) = x(j) - eps;
+	  f22 = Func(hx);
+
+	  hesse(i, j) = hesse(j, i) =
+	    (f11 + f22 - f12 - f21) / (2 * eps * eps);
+	}
+
+      hx = x;
+      f = Func(x);
+      hx(i) = x(i) + eps;
+      f11 = Func(hx);
+      hx(i) = x(i) - eps;
+      f22 = Func(hx);
+
+      hesse(i, i) = (f11 + f22 - 2 * f) / (eps * eps);
+    }
+  //  (*testout) << "hesse = " << hesse << endl;
+}
+
+
+
+
+
+
+
+/// Line search, modified Mangasarien conditions
+void lines (Vector & x,         // i: initial point of line-search
+	    Vector & xneu,      // o: solution, if successful
+	    Vector & p,         // i: search direction
+	    double & f,         // i: function-value at x
+	    // o: function-value at xneu, iff ifail = 0
+	    Vector & g,         // i: gradient at x
+	    // o: gradient at xneu, iff ifail = 0
+	    const MinFunction & fun,  // function to minimize
+	    const OptiParameters & par,
+	    double & alphahat,  // i: initial value for alpha_hat
+	    // o: solution alpha iff ifail = 0
+	    double fmin,        // i: lower bound for f
+	    double mu1,         // i: Parameter mu_1 of Alg.2.1
+	    double sigma,       // i: Parameter sigma of Alg.2.1
+	    double xi1,         // i: Parameter xi_1 of Alg.2.1
+	    double xi2,         // i: Parameter xi_1 of Alg.2.1
+	    double tau,         // i: Parameter tau of Alg.2.1
+	    double tau1,        // i: Parameter tau_1 of Alg.2.1
+	    double tau2,        // i: Parameter tau_2 of Alg.2.1
+	    int & ifail)        // o: 0 on success
+  //    -1 bei termination because lower limit fmin
+  //     1 bei illegal termination due to different reasons
+
+{
+  double phi0, phi0prime, phi1, phi1prime, phihatprime;
+  double alpha1, alpha2, alphaincr, c;
+  char flag = 1;
+  long it;
+
+  alpha1 = 0;
+  alpha2 = 1e50;
+  phi0 = phi1 = f;
+
+  phi0prime = g * p;
+
+  if (phi0prime > 0)
+    {
+      ifail = 1;
+      return;
+    }
+
+  ifail = 1;  // Markus
+
+  phi1prime = phi0prime;
+
+  //  (*testout) << "phi0prime = " << phi0prime << endl;
+
+  //  it = 100000l;
+  it = 0;
+
+  while (it++ <= par.maxit_linsearch)
+    {
+
+      xneu.Set2 (1, x, alphahat, p);
+
+
+      //    f = fun.FuncGrad (xneu, g);
+      //      f = fun.Func (xneu);
+      f = fun.FuncDeriv (xneu, p, phihatprime);
+
+      // (*testout) << "lines, f = " << f << " phip = " << phihatprime << endl;
+
+      if (f < fmin)
+	{
+	  ifail = -1;
+	  break;
+	}
+
+
+      if (alpha2 - alpha1 < eps0 * alpha2)
+	{
+	  ifail = 0;
+	  break;
+	}
+
+      // (*testout) << "i = " << it << " al = " << alphahat << " f = " << f << " fprime " << phihatprime << endl;;
+
+      if (f - phi0 > mu1 * alphahat * phi1prime + eps0 * fabs (phi0))
+
+	{
+
+	  flag = 0;
+	  alpha2 = alphahat;
+
+	  c = 
+	    (f - phi1 - phi1prime * (alphahat-alpha1)) / 
+	    sqr (alphahat-alpha1);
+
+	  alphahat = alpha1 - 0.5 * phi1prime / c;
+
+	  if (alphahat > alpha2)
+	    alphahat = alpha1 + 1/(4*c) *
+	      ( (sigma+mu1) * phi0prime - 2*phi1prime
+		+ sqrt (sqr(phi1prime - mu1 * phi0prime) -
+			4 * (phi1 - phi0 - mu1 * alpha1 * phi0prime) * c));
+
+	  alphahat = max2 (alphahat, alpha1 + tau * (alpha2 - alpha1));
+	  alphahat = min2 (alphahat, alpha2 - tau * (alpha2 - alpha1));
+	  
+	  //	  (*testout) << " if-branch" << endl;
+
+	}
+
+      else
+
+	{
+	  /*
+	  f = fun.FuncGrad (xneu, g);
+	  phihatprime = g * p;
+	  */
+	  f = fun.FuncDeriv (xneu, p, phihatprime);
+
+	  if (phihatprime < sigma * phi0prime * (1 + eps0))
+
+	    {
+	      if (phi1prime < phihatprime)   
+		// Approximationsfunktion ist konvex
+
+		alphaincr = (alphahat - alpha1) * phihatprime /
+		  (phi1prime - phihatprime);
+
+	      else
+		alphaincr = 1e99; // MAXDOUBLE;
+
+	      if (flag)
+		{
+		  alphaincr = max2 (alphaincr, xi1 * (alphahat-alpha1));
+		  alphaincr = min2 (alphaincr, xi2 * (alphahat-alpha1));
+		}
+	      else
+		{
+		  alphaincr = max2 (alphaincr, tau1 * (alpha2 - alphahat));
+		  alphaincr = min2 (alphaincr, tau2 * (alpha2 - alphahat));
+		}
+
+	      alpha1 = alphahat;
+	      alphahat += alphaincr;
+	      phi1 = f;
+	      phi1prime = phihatprime;
+	    }
+
+	  else
+
+	    {
+	      ifail = 0;     // Erfolg !!
+	      break;
+	    }
+	  
+	  //	  (*testout) << " else, " << endl;
+
+	}
+
+    }
+
+  //  (*testout) << "linsearch: it = " << it << " ifail = " << ifail << endl;
+
+  fun.FuncGrad (xneu, g);
+
+
+  if (it < 0)
+    ifail = 1;
+
+  //  (*testout) << "fail = " << ifail << endl;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void SteepestDescent (Vector & x, const MinFunction & fun,
+		      const OptiParameters & par)
+{
+  int it, n = x.Size();
+  Vector xnew(n), p(n), g(n), g2(n);
+  double val, alphahat;
+  int fail;
+
+  val = fun.FuncGrad(x, g);
+
+  alphahat = 1;
+  //  testout << "f = ";
+  for (it = 0; it < 10; it++)
+    {
+      //    testout << val << " ";
+
+      // p = -g;
+      p.Set (-1, g);
+
+      lines (x, xnew, p, val, g, fun, par, alphahat, -1e5,
+	     0.1, 0.1, 1, 10, 0.1, 0.1, 0.6, fail);
+
+      x = xnew;
+    }
+  //  testout << endl;
+}
+}
diff --git a/contrib/Netgen/libsrc/linalg/opti.hpp b/contrib/Netgen/libsrc/linalg/opti.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9875786900ea8cba360c04149ff4c57c5e0aa66e
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/opti.hpp
@@ -0,0 +1,142 @@
+#ifndef FILE_OPTI
+#define FILE_OPTI
+
+/**************************************************************************/
+/* File:   opti.hpp                                                       */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+
+
+namespace netgen
+{
+
+  /** 
+      Function to be minimized.
+  */
+  class MinFunction 
+  {
+  public:
+    ///
+    virtual double Func (const Vector & x) const;
+    ///
+    virtual void Grad (const Vector & x, Vector & g) const;
+    /// function and gradient
+    virtual double FuncGrad (const Vector & x, Vector & g) const;
+    /// directional derivative
+    virtual double FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const;
+    /// if |g| < gradaccuray, then stop bfgs
+    virtual double GradStopping (const Vector & /* x */) const { return 0; } 
+
+    ///
+    virtual void ApproximateHesse (const Vector & /* x */,
+				   DenseMatrix & /* hesse */) const;
+  };
+
+
+  class OptiParameters
+  {
+  public:
+    int maxit_linsearch;
+    int maxit_bfgs;
+    double typf;
+    double typx;
+
+    OptiParameters ()
+    {
+      maxit_linsearch = 100;
+      maxit_bfgs = 100;
+      typf = 1;
+      typx = 1;
+    }
+  };
+  
+  
+  /** Implementation of BFGS method.
+      Efficient method for non-linear minimiztion problems.
+      @param x initial value and solution 
+      @param fun function to be minimized
+  */
+  extern double BFGS (Vector & x, const MinFunction & fun, 
+		      const OptiParameters & par,
+		      double eps = 1e-8);
+
+  /** Steepest descent method.
+      Simple method for non-linear minimization problems.
+      @param x initial value and solution 
+      @param fun function to be minimized
+  */
+  void SteepestDescent (Vector & x, const MinFunction & fun,
+			const OptiParameters &  par);
+
+
+  extern void lines (
+		     Vector & x,         // i: Ausgangspunkt der Liniensuche
+		     Vector & xneu,      // o: Loesung der Liniensuche bei Erfolg
+		     Vector & p,         // i: Suchrichtung
+		     double & f,         // i: Funktionswert an der Stelle x
+		     // o: Funktionswert an der Stelle xneu, falls ifail = 0
+		     Vector & g,         // i: Gradient an der Stelle x
+		     // o: Gradient an der Stelle xneu, falls ifail = 0
+
+		     const MinFunction & fun,  // function to minmize
+		     const OptiParameters & par, // parameters
+		     double & alphahat,  // i: Startwert f�r alpha_hat
+		     // o: Loesung falls ifail = 0
+		     double fmin,        // i: untere Schranke f�r f
+		     double mu1,         // i: Parameter mu_1 aus Alg.2.1
+		     double sigma,       // i: Parameter sigma aus Alg.2.1
+		     double xi1,         // i: Parameter xi_1 aus Alg.2.1
+		     double xi2,         // i: Parameter xi_1 aus Alg.2.1
+		     double tau,         // i: Parameter tau aus Alg.2.1
+		     double tau1,        // i: Parameter tau_1 aus Alg.2.1
+		     double tau2,        // i: Parameter tau_2 aus Alg.2.1
+		     int & ifail);        // o:  0 bei erfolgreicher Liniensuche
+  //    -1 bei Abbruch wegen Unterschreiten von fmin
+  //    1 bei Abbruch, aus sonstigen Gr�nden
+
+
+
+
+  /**  
+       Solver for linear programming problem.
+
+       \begin{verbatim}
+       min      c^t x
+       A x <= b    
+       \end{verbatim}
+  */
+  extern void LinearOptimize (const DenseMatrix & a, const Vector & b, 
+			      const Vector & c, Vector & x);
+
+
+#ifdef NONE
+
+  /**
+     Simple projection iteration.
+  
+     find $u = argmin_{v >= 0}  0.5 u A u - f u$
+  */
+  extern void ApproxProject (const BaseMatrix & a, Vector & u, 
+			     const Vector & f,
+			     double tau, int its);
+ 
+
+  /**
+     CG Algorithm for quadratic programming problem.
+     See: Dostal ...
+
+     d ... diag(A) ^{-1}
+  */
+  extern void ApproxProjectCG (const BaseMatrix & a, Vector & x, 
+			       const Vector & b, const class DiagMatrix & d,
+			       double gamma, int & steps, int & changes);
+
+#endif
+
+
+}
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/linalg/polynomial.cpp b/contrib/Netgen/libsrc/linalg/polynomial.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cc515aac0a496152ca80ece3ab9ff8e0f182e6e8
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/polynomial.cpp
@@ -0,0 +1,198 @@
+#include <mystdlib.h>
+#include <linalg.hpp>
+
+namespace netgen
+{
+
+QuadraticPolynomial1V :: 
+QuadraticPolynomial1V (double ac, double acx, double acxx)
+{
+  c = ac;
+  cx = acx;
+  cxx = acxx;
+}
+
+double QuadraticPolynomial1V :: 
+Value (double x)
+{
+  return c + cx * x + cxx * x * x;
+}
+
+double QuadraticPolynomial1V ::  MaxUnitInterval ()
+{
+  // inner max
+  if (cxx < 0 && cx > 0 && cx < -2 * cxx)
+    {
+      return c - 0.25 * cx * cx / cxx;
+    }
+
+  
+  if (cx + cxx > 0)   // right edge
+    return c + cx + cxx;
+
+  // left end
+  return c;
+}
+
+
+
+
+LinearPolynomial2V :: 
+LinearPolynomial2V (double ac, double acx, double acy)
+{
+  c = ac;
+  cx = acx;
+  cy = acy;
+};
+
+
+QuadraticPolynomial2V ::   
+QuadraticPolynomial2V ()
+{
+  ;
+}
+
+
+QuadraticPolynomial2V :: 
+QuadraticPolynomial2V (double ac, double acx, double acy,
+		       double acxx, double acxy, double acyy)
+{
+  c = ac;
+  cx = acx;
+  cy = acy;
+  cxx = acxx;
+  cxy = acxy;
+  cyy = acyy;
+}
+
+void QuadraticPolynomial2V :: 
+Square (const LinearPolynomial2V & lp)
+{
+  c = lp.c * lp.c;
+  cx = 2 * lp.c * lp.cx;
+  cy = 2 * lp.c * lp.cy;
+
+  cxx = lp.cx * lp.cx;
+  cxy = 2 * lp.cx * lp.cy;
+  cyy = lp.cy * lp.cy;
+}
+
+void QuadraticPolynomial2V :: 
+Add (double lam, const QuadraticPolynomial2V & qp2)
+{
+  c += lam * qp2.c;
+  cx += lam * qp2.cx;
+  cy += lam * qp2.cy;
+  cxx += lam * qp2.cxx;
+  cxy += lam * qp2.cxy;
+  cyy += lam * qp2.cyy;
+}
+
+double QuadraticPolynomial2V :: 
+Value (double x, double y)
+{
+  return c + cx * x + cy * y + cxx * x * x + cxy * x * y + cyy * y * y;
+}
+
+/*
+double QuadraticPolynomial2V :: 
+MinUnitSquare ()
+{
+  double x, y;
+  double minv = 1e8;
+  double val;
+  for (x = 0; x <= 1; x += 0.1)
+    for (y = 0; y <= 1; y += 0.1)
+      {
+	val = Value (x, y);
+	if (val < minv)
+	  minv = val;
+      }
+  return minv;
+};
+*/
+
+double QuadraticPolynomial2V :: 
+MaxUnitSquare ()
+{
+  // find critical point
+
+  double maxv = c;
+  double hv;
+
+  double det, x0, y0;
+  det = 4 * cxx * cyy - cxy * cxy;
+
+  if (det > 0)
+    {
+      // definite surface
+      
+      x0 = (-2 * cyy * cx + cxy * cy) / det;
+      y0 = (cxy * cx -2 * cxx * cy) / det;
+
+      if (x0 >= 0 && x0 <= 1 && y0 >= 0 && y0 <= 1)
+	{
+	  hv = Value (x0, y0);
+	  if (hv > maxv) maxv = hv;
+	}
+    }
+  
+  QuadraticPolynomial1V e1(c, cx, cxx);
+  QuadraticPolynomial1V e2(c, cy, cyy);
+  QuadraticPolynomial1V e3(c+cy+cyy, cx+cxy, cxx);
+  QuadraticPolynomial1V e4(c+cx+cxx, cy+cxy, cyy);
+  
+  hv = e1.MaxUnitInterval();
+  if (hv > maxv) maxv = hv;
+  hv = e2.MaxUnitInterval();
+  if (hv > maxv) maxv = hv;
+  hv = e3.MaxUnitInterval();
+  if (hv > maxv) maxv = hv;
+  hv = e4.MaxUnitInterval();
+  if (hv > maxv) maxv = hv;
+
+  return maxv;
+};
+
+
+
+
+double QuadraticPolynomial2V :: 
+MaxUnitTriangle ()
+{
+  // find critical point
+  
+  double maxv = c;
+  double hv;
+
+  double det, x0, y0;
+  det = 4 * cxx * cyy - cxy * cxy;
+
+  if (cxx < 0 && det > 0)
+    { 
+      // definite surface
+      
+      x0 = (-2 * cyy * cx + cxy * cy) / det;
+      y0 = (cxy * cx -2 * cxx * cy) / det;
+
+      if (x0 >= 0 && y0 >= 0 && x0+y0 <= 1)
+	{
+	  return Value (x0, y0);
+	}
+    }
+  
+  
+  QuadraticPolynomial1V e1(c, cx, cxx);
+  QuadraticPolynomial1V e2(c, cy, cyy);
+  QuadraticPolynomial1V e3(c+cy+cyy, cx-cy+cxy-2*cyy, cxx-cxy+cyy);
+  
+  hv = e1.MaxUnitInterval();
+  if (hv > maxv) maxv = hv;
+  hv = e2.MaxUnitInterval();
+  if (hv > maxv) maxv = hv;
+  hv = e3.MaxUnitInterval();
+  if (hv > maxv) maxv = hv;
+
+  return maxv;
+}
+}
diff --git a/contrib/Netgen/libsrc/linalg/polynomial.hpp b/contrib/Netgen/libsrc/linalg/polynomial.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3108d4dd729172bd644052524aab62f3d150576f
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/polynomial.hpp
@@ -0,0 +1,45 @@
+#ifndef FILE_POLYNOMIAL
+#define FILE_POLYNOMIAL
+
+/* *************************************************************************/
+/* File:   polynomial.hh                                                   */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   25. Nov. 99                                                     */
+/* *************************************************************************/
+
+
+class QuadraticPolynomial1V 
+{
+  double c, cx, cxx;
+public:
+  QuadraticPolynomial1V (double ac, double acx, double acxx);
+  double Value (double x);
+  double MaxUnitInterval ();
+};
+
+class LinearPolynomial2V
+{
+  double c, cx, cy;
+public:
+  LinearPolynomial2V (double ac, double acx, double acy);
+  friend class QuadraticPolynomial2V;
+};
+
+
+class QuadraticPolynomial2V
+{
+  double c, cx, cy, cxx, cxy, cyy;
+public:
+  QuadraticPolynomial2V ();
+  QuadraticPolynomial2V (double ac, double acx, double acy,
+			 double acxx, double acxy, double acyy);
+  void Square (const LinearPolynomial2V & lp);
+  void Add (double lam, const QuadraticPolynomial2V & qp);
+
+  double Value (double x, double y);
+  //  double MinUnitSquare ();
+  double MaxUnitSquare ();
+  double MaxUnitTriangle ();
+};
+
+#endif
diff --git a/contrib/Netgen/libsrc/linalg/vector.hpp b/contrib/Netgen/libsrc/linalg/vector.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e7c52e812b2206af9ce2e9c6070ed3d21dcb800f
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/vector.hpp
@@ -0,0 +1,161 @@
+#ifndef FILE_VECTOR
+#define FILE_VECTOR
+
+/* *************************************************************************/
+/* File:   vector.hpp                                                      */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   01. Oct. 94                                                     */
+/* *************************************************************************/
+
+
+class FlatVector
+{
+protected:
+  int s;
+  double *data;
+public:
+  FlatVector () { ; }
+  FlatVector (int as, double * adata)
+  { s = as; data = adata; }
+
+  int Size () const
+  { return s; }
+
+  FlatVector & operator= (const FlatVector & v) 
+  { memcpy (data, v.data, s*sizeof(double)); return *this; }
+
+  FlatVector & operator= (double scal) 
+  {
+    for (int i = 0; i < s; i++) data[i] = scal; 
+    return *this;
+  }
+
+  double & operator[] (int i) { return data[i]; }
+  const double & operator[] (int i) const { return data[i]; }
+  double & operator() (int i) { return data[i]; }
+  const double & operator() (int i) const { return data[i]; }
+
+  // double & Elem (int i) { return data[i-1]; }
+  // const double & Get (int i) const { return data[i-1]; }
+  // void Set (int i, double val) { data[i-1] = val; }
+
+  FlatVector & operator*= (double scal)
+  {
+    for (int i = 0; i < s; i++) data[i] *= scal;
+    return *this;
+  }
+
+  FlatVector & Add (double scal, const FlatVector & v2)
+  {
+    for (int i = 0; i < s; i++) 
+      data[i] += scal * v2[i];
+    return *this;
+  }
+
+  FlatVector & Set (double scal, const FlatVector & v2)
+  {
+    for (int i = 0; i < s; i++) 
+      data[i] = scal * v2[i];
+    return *this;
+  }
+
+  FlatVector & Set2 (double scal1, const FlatVector & v1,
+		 double scal2, const FlatVector & v2)
+  {
+    for (int i = 0; i < s; i++) 
+      data[i] = scal1 * v1[i] + scal2 * v2[i];
+    return *this;
+  }
+  
+  double L2Norm() const
+  {
+    double sum = 0;
+    for (int i = 0; i < s; i++)
+      sum += data[i] * data[i];
+    return sqrt (sum);
+  }
+
+  friend double operator* (const FlatVector & v1, const FlatVector & v2);
+};
+
+
+
+class Vector : public FlatVector
+{
+  bool ownmem;
+public:
+  Vector () 
+  { s = 0; data = 0; ownmem = false; }
+  Vector (int as)
+  { s = as; data = new double[s]; ownmem = true; }
+  Vector (int as, double * mem)
+  { s = as; data = mem; ownmem = false; }
+  ~Vector ()
+  { if (ownmem) delete [] data; }
+
+  Vector & operator= (const FlatVector & v) 
+  { memcpy (data, &v(0), s*sizeof(double)); return *this; }
+
+  Vector & operator= (double scal) 
+  {
+    for (int i = 0; i < s; i++) data[i] = scal; 
+    return *this;
+  }
+
+  void SetSize (int as)
+  {
+    if (s != as)
+      {
+	s = as;
+	if (ownmem) delete [] data;
+	data = new double [s];
+        ownmem = true;
+      }
+  }
+
+};
+
+template <int S>
+class VectorMem : public Vector
+{
+  double mem[S];
+public:
+  VectorMem () : Vector(S, &mem[0]) { ; }
+
+  VectorMem & operator= (const FlatVector & v) 
+  { memcpy (data, &v(0), S*sizeof(double)); return *this; }
+
+  VectorMem & operator= (double scal) 
+  {
+    for (int i = 0; i < S; i++) data[i] = scal; 
+    return *this;
+  }
+};
+
+
+
+
+
+inline double operator* (const FlatVector & v1, const FlatVector & v2)
+{
+  double sum = 0;
+  for (int i = 0; i < v1.s; i++)
+    sum += v1.data[i] * v2.data[i];
+  return sum;
+}
+
+
+
+
+inline ostream & operator<< (ostream & ost, const FlatVector & v)
+{
+  for (int i = 0; i < v.Size(); i++)
+    ost << " " << setw(7) << v[i];
+  return ost;
+}
+
+
+
+#endif
+
+
diff --git a/contrib/Netgen/libsrc/meshing/Makefile.am b/contrib/Netgen/libsrc/meshing/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..2ff94a95250738d8fc24951d1360f297f1c1e153
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/Makefile.am
@@ -0,0 +1,31 @@
+AM_CPPFLAGS = $(MPI_INCLUDES) -I$(top_srcdir)/libsrc/include 
+
+
+noinst_HEADERS = adfront2.hpp hpref_quad.hpp meshfunc.hpp ruler3.hpp  \
+adfront3.hpp findip.hpp findip2.hpp hpref_segm.hpp meshing2.hpp	      \
+specials.hpp bisect.hpp geomsearch.hpp hpref_tet.hpp meshing3.hpp     \
+topology.hpp boundarylayer.hpp global.hpp hpref_trig.hpp meshing.hpp  \
+validate.hpp classifyhpel.hpp hpref_hex.hpp improve2.hpp meshtool.hpp \
+clusters.hpp hprefinement.hpp improve3.hpp meshtype.hpp		      \
+ hpref_prism.hpp localh.hpp msghandler.hpp curvedelems.hpp	      \
+ hpref_pyramid.hpp meshclass.hpp ruler2.hpp bcfunctions.hpp	      \
+ basegeom.hpp 
+
+
+
+METASOURCES = AUTO
+
+lib_LTLIBRARIES = libmesh.la
+
+libmesh_la_SOURCES = adfront2.cpp adfront3.cpp bisect.cpp boundarylayer.cpp \
+	clusters.cpp curvedelems.cpp delaunay.cpp delaunay2d.cpp	    \
+	geomsearch.cpp global.cpp hprefinement.cpp improve2.cpp		    \
+	improve2gen.cpp improve3.cpp localh.cpp meshclass.cpp		    \
+	meshfunc.cpp meshfunc2d.cpp meshing2.cpp meshing3.cpp		    \
+	meshtool.cpp meshtype.cpp msghandler.cpp netrule2.cpp		    \
+	netrule3.cpp parser2.cpp parser3.cpp prism2rls.cpp		    \
+	pyramid2rls.cpp pyramidrls.cpp quadrls.cpp refine.cpp		    \
+	ruler2.cpp ruler3.cpp secondorder.cpp smoothing2.5.cpp		    \
+	smoothing2.cpp smoothing3.cpp specials.cpp tetrarls.cpp		    \
+	topology.cpp triarls.cpp validate.cpp zrefine.cpp bcfunctions.cpp   \
+	parallelmesh.cpp  paralleltop.cpp  paralleltop.hpp basegeom.cpp 
diff --git a/contrib/Netgen/libsrc/meshing/adfront2.cpp b/contrib/Netgen/libsrc/meshing/adfront2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81bef640bed777637aea1c5aeb0ab40a523c2c3b
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/adfront2.cpp
@@ -0,0 +1,508 @@
+/*
+  Advancing front class for surfaces
+*/
+
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+  FrontPoint2 :: FrontPoint2 (const Point<3> & ap, PointIndex agi,
+			      MultiPointGeomInfo * amgi, bool aonsurface)
+  {
+    p = ap;
+    globalindex = agi;
+    nlinetopoint = 0;
+    frontnr = INT_MAX-10;
+    onsurface = aonsurface;
+
+    if (amgi)
+      {
+	mgi = new MultiPointGeomInfo (*amgi);
+	for (int i = 1; i <= mgi->GetNPGI(); i++)
+	  if (mgi->GetPGI(i).trignum <= 0)
+	    cout << "Add FrontPoint2, illegal geominfo = " << mgi->GetPGI(i).trignum << endl;
+      }
+    else
+      mgi = NULL;
+  }
+
+
+  AdFront2 :: AdFront2 (const Box3d & aboundingbox)
+    : boundingbox(aboundingbox), 
+      linesearchtree(boundingbox.PMin(), boundingbox.PMax()),
+      pointsearchtree(boundingbox.PMin(), boundingbox.PMax()),
+      cpointsearchtree(boundingbox.PMin(), boundingbox.PMax())
+  {
+    nfl = 0;
+    allflines = 0;
+
+    minval = 0;
+    starti = lines.Begin();
+  }
+
+  AdFront2 :: ~AdFront2 ()
+  {
+    delete allflines;
+  }
+
+
+  void AdFront2 :: PrintOpenSegments (ostream & ost) const
+  {
+    if (nfl > 0)
+      {
+	ost << nfl << " open front segments left:" << endl;
+	for (int i = lines.Begin(); i < lines.End(); i++)
+	  if (lines[i].Valid())
+	    ost << i << ": " 
+                << GetGlobalIndex (lines[i].L().I1()) << "-"
+		<< GetGlobalIndex (lines[i].L().I2()) << endl;
+      }
+  }
+
+  /*
+  void AdFront2 :: GetPoints (Array<Point<3> > & apoints) const
+  {
+    apoints.Append (points);
+    // for (int i = 0; i < points.Size(); i++)
+    // apoints.Append (points[i].P());
+  }
+  */
+
+
+
+  int AdFront2 :: AddPoint (const Point<3> & p, PointIndex globind, 
+                            MultiPointGeomInfo * mgi,
+                            bool pointonsurface)
+  {
+    // inserts at empty position or resizes array
+    int pi;
+
+    if (delpointl.Size() != 0)
+      {
+	pi = delpointl.Last();
+	delpointl.DeleteLast ();
+
+	points[pi] = FrontPoint2 (p, globind, mgi, pointonsurface);
+      }
+    else
+      {
+	pi = points.Append (FrontPoint2 (p, globind, mgi, pointonsurface)) - 1;
+      }
+
+    if (mgi)
+      cpointsearchtree.Insert (p, pi);
+
+    if (pointonsurface)
+      pointsearchtree.Insert (p, pi);
+    
+    return pi;
+  }
+
+
+  int AdFront2 :: AddLine (int pi1, int pi2,
+                           const PointGeomInfo & gi1, const PointGeomInfo & gi2)
+  {
+    int minfn;
+    int li;
+
+    FrontPoint2 & p1 = points[pi1];
+    FrontPoint2 & p2 = points[pi2];
+
+
+    nfl++;
+
+    p1.AddLine();
+    p2.AddLine();
+
+    minfn = min2 (p1.FrontNr(), p2.FrontNr());
+    p1.DecFrontNr (minfn+1);
+    p2.DecFrontNr (minfn+1);
+
+    if (dellinel.Size() != 0)
+      {
+	li = dellinel.Last();
+	dellinel.DeleteLast ();
+	lines[li] = FrontLine (INDEX_2(pi1, pi2));
+      }
+    else
+      {
+	li = lines.Append(FrontLine (INDEX_2(pi1, pi2))) - 1;
+      }
+
+  
+    if (!gi1.trignum || !gi2.trignum)
+      {
+	cout << "ERROR: in AdFront::AddLine, illegal geominfo" << endl;
+      }
+  
+    lines[li].SetGeomInfo (gi1, gi2);
+
+    Box3d lbox;
+    lbox.SetPoint(p1.P());
+    lbox.AddPoint(p2.P());
+
+    linesearchtree.Insert (lbox.PMin(), lbox.PMax(), li);
+
+    if (allflines)
+      {
+	if (allflines->Used (INDEX_2 (GetGlobalIndex (pi1), 
+				      GetGlobalIndex (pi2))))
+	  {
+	    cerr << "ERROR Adfront2::AddLine: line exists" << endl;
+	    (*testout) << "ERROR Adfront2::AddLine: line exists" << endl;
+	  }
+
+	allflines->Set (INDEX_2 (GetGlobalIndex (pi1), 
+				 GetGlobalIndex (pi2)), 1);
+      }
+
+    return li;
+  }
+
+
+  void AdFront2 :: DeleteLine (int li)
+  {
+    int pi;
+
+    nfl--;
+
+    for (int i = 1; i <= 2; i++)
+      {
+	pi = lines[li].L().I(i);
+	points[pi].RemoveLine();
+
+	if (!points[pi].Valid())
+	  {
+	    delpointl.Append (pi);
+	    if (points[pi].mgi)
+	      {
+		cpointsearchtree.DeleteElement (pi);
+		delete points[pi].mgi;
+		points[pi].mgi = NULL;
+	      }
+
+            pointsearchtree.DeleteElement (pi);
+	  }
+      }
+
+    if (allflines)
+      {
+	allflines->Set (INDEX_2 (GetGlobalIndex (lines[li].L().I1()),
+				 GetGlobalIndex (lines[li].L().I2())), 2);
+      }
+
+    lines[li].Invalidate();
+    linesearchtree.DeleteElement (li);
+
+    dellinel.Append (li);
+  }
+
+
+  int AdFront2 :: ExistsLine (int pi1, int pi2)
+  {
+    if (!allflines)
+      return 0;
+    if (allflines->Used (INDEX_2(pi1, pi2)))
+      return allflines->Get (INDEX_2 (pi1, pi2));
+    else
+      return 0;
+  }
+
+
+  /*
+  void AdFront2 :: IncrementClass (int li)
+  {
+    lines[li].IncrementClass();
+  }
+
+
+  void AdFront2 :: ResetClass (int li)
+  {
+    lines[li].ResetClass();
+  }
+  */
+
+  int AdFront2 :: SelectBaseLine (Point<3>  & p1, Point<3>  & p2, 
+				  const PointGeomInfo *& geominfo1,
+				  const PointGeomInfo *& geominfo2,
+				  int & qualclass)
+  {
+    int baselineindex = -1; 
+
+    for (int i = starti; i < lines.End(); i++)
+      {
+	if (lines[i].Valid())
+	  {
+	    int hi = lines[i].LineClass() +
+	      points[lines[i].L().I1()].FrontNr() +
+	      points[lines[i].L().I2()].FrontNr();
+	  
+	    if (hi <= minval)
+	      {
+		minval = hi;
+		baselineindex = i;
+		break;
+	      }
+	  }
+      }
+  
+    if (baselineindex == -1)
+      {
+	minval = INT_MAX;
+	for (int i = lines.Begin(); i < lines.End(); i++)
+	  if (lines[i].Valid())
+	    {
+	      int hi = lines[i].LineClass() +
+		points[lines[i].L().I1()].FrontNr() +
+		points[lines[i].L().I2()].FrontNr();
+	    
+	      if (hi < minval)
+		{
+		  minval = hi;
+		  baselineindex = i;
+		}
+	    }
+      }
+    starti = baselineindex+1;
+
+    p1 = points[lines[baselineindex].L().I1()].P();
+    p2 = points[lines[baselineindex].L().I2()].P();
+    geominfo1 = &lines[baselineindex].GetGeomInfo(1);
+    geominfo2 = &lines[baselineindex].GetGeomInfo(2);
+
+    qualclass = lines[baselineindex].LineClass();
+
+    return baselineindex;
+  }
+
+
+
+
+  int AdFront2 :: GetLocals (int baselineindex,
+			     Array<Point3d> & locpoints,
+			     Array<MultiPointGeomInfo> & pgeominfo,
+			     Array<INDEX_2> & loclines,   // local index
+			     Array<INDEX> & pindex,
+			     Array<INDEX> & lindex,
+			     double xh)
+  {
+    static int timer = NgProfiler::CreateTimer ("adfront2::GetLocals");
+    NgProfiler::RegionTimer reg (timer);
+
+
+    int pstind;
+    Point<3>  midp, p0;
+
+    pstind = lines[baselineindex].L().I1();
+    p0 = points[pstind].P();
+
+    loclines.Append(lines[baselineindex].L());
+    lindex.Append(baselineindex);  
+
+    ArrayMem<int, 1000> nearlines(0);
+    ArrayMem<int, 1000> nearpoints(0);
+
+    // dominating costs !!
+    linesearchtree.GetIntersecting (p0 - Vec3d(xh, xh, xh),
+				    p0 + Vec3d(xh, xh, xh),
+				    nearlines);
+
+    pointsearchtree.GetIntersecting (p0 - Vec3d(xh, xh, xh),
+                                     p0 + Vec3d(xh, xh, xh),
+                                     nearpoints);
+    
+    for (int ii = 0; ii < nearlines.Size(); ii++)
+      {
+	int i = nearlines[ii];
+	if (lines[i].Valid() && i != baselineindex) 
+	  {
+            loclines.Append(lines[i].L());
+            lindex.Append(i);
+	  }
+      }
+
+    // static Array<int> invpindex;
+    invpindex.SetSize (points.Size()); 
+    // invpindex = -1;
+    for (int i = 0; i < nearpoints.Size(); i++)
+      invpindex[nearpoints[i]] = -1;
+
+    for (int i = 0; i < loclines.Size(); i++)
+      {
+	invpindex[loclines[i].I1()] = 0;
+	invpindex[loclines[i].I2()] = 0;
+      }
+
+
+    for (int i = 0; i < loclines.Size(); i++)
+      {
+	for (int j = 0; j < 2; j++)
+	  {
+	    int pi = loclines[i][j];
+	    if (invpindex[pi] == 0)
+	      {
+		pindex.Append (pi);
+		invpindex[pi] = pindex.Size();
+		loclines[i][j] = locpoints.Append (points[pi].P());
+	      }
+	    else
+	      loclines[i][j] = invpindex[pi];
+	  }
+      }
+
+
+    // double xh2 = xh*xh;
+    for (int ii = 0; ii < nearpoints.Size(); ii++)
+      {
+        int i = nearpoints[ii];
+	if (points[i].Valid() && 
+	    points[i].OnSurface() &&
+	    // Dist2 (points.Get(i).P(), p0) <= xh2 &&
+	    invpindex[i] <= 0)
+	  {
+	    invpindex[i] = locpoints.Append (points[i].P());
+	    pindex.Append(i);
+	  }
+      }
+    /*
+    double xh2 = xh*xh;
+    for (i = 1; i <= points.Size(); i++)
+      {
+	if (points.Get(i).Valid() && 
+	    points.Get(i).OnSurface() &&
+	    Dist2 (points.Get(i).P(), p0) <= xh2 &&
+	    invpindex.Get(i) <= 0)
+	  {
+	    invpindex.Elem(i) =
+	      locpoints.Append (points.Get(i).P());
+	    pindex.Append(i);
+	  }
+      }
+    */
+
+    pgeominfo.SetSize (locpoints.Size());
+    for (int i = 0; i < pgeominfo.Size(); i++)
+      pgeominfo[i].Init();
+
+
+    for (int i = 0; i < loclines.Size(); i++)
+      for (int j = 0; j < 2; j++)
+	{
+	  int lpi = loclines[i][j];
+	
+	  const PointGeomInfo & gi = 
+	    lines[lindex[i]].GetGeomInfo (j+1);
+	  pgeominfo.Elem(lpi).AddPointGeomInfo (gi);
+	
+	  /*
+	    if (pgeominfo.Elem(lpi).cnt == MULTIPOINTGEOMINFO_MAX)
+	    break;
+
+	    const PointGeomInfo & gi = 
+	    lines.Get(lindex.Get(i)).GetGeomInfo (j);
+	
+	    PointGeomInfo * pgi = pgeominfo.Elem(lpi).mgi;
+
+	    int found = 0;
+	    for (k = 0; k < pgeominfo.Elem(lpi).cnt; k++)
+	    if (pgi[k].trignum == gi.trignum)
+	    found = 1;
+
+	    if (!found)
+	    {
+	    pgi[pgeominfo.Elem(lpi).cnt] = gi;
+	    pgeominfo.Elem(lpi).cnt++;
+	    }
+	  */
+	}
+
+    for (int i = 0; i < locpoints.Size(); i++)
+      {
+	int pi = pindex[i];
+      
+	if (points[pi].mgi)
+	  for (int j = 1; j <= points[pi].mgi->GetNPGI(); j++)
+	    pgeominfo[i].AddPointGeomInfo (points[pi].mgi->GetPGI(j));
+      }
+   
+    if (loclines.Size() == 1)
+      {
+	cout << "loclines.Size = 1" << endl;
+	(*testout) << "loclines.size = 1" << endl
+		   << " h = " << xh << endl
+		   << " nearline.size = " << nearlines.Size() << endl
+		   << " p0 = " << p0 << endl;
+      }
+
+    return lines[baselineindex].LineClass();
+  }
+
+
+
+  void AdFront2 :: SetStartFront ()
+  {
+    for (int i = lines.Begin(); i < lines.End(); i++)
+      if (lines[i].Valid())
+	for (int j = 1; j <= 2; j++)
+	  points[lines[i].L().I(j)].DecFrontNr(0);
+  }
+
+
+  void AdFront2 :: Print (ostream & ost) const
+  {
+    ost << points.Size() << " Points: " << endl;
+    for (int i = points.Begin(); i < points.End(); i++)
+      if (points[i].Valid())
+	ost << i << "  " << points[i].P() << endl;
+
+    ost << nfl << " Lines: " << endl;
+    for (int i = lines.Begin(); i < lines.End(); i++)
+      if (lines[i].Valid())
+	ost << lines[i].L().I1() << " - " << lines[i].L().I2() << endl;
+
+    ost << flush;
+  }
+
+
+  bool AdFront2 :: Inside (const Point<2> & p) const
+  {
+    int cnt;
+    Vec<2> n;
+    Vec<3> v1;
+    DenseMatrix a(2), ainv(2);
+    Vector b(2), u(2);
+    
+    // random numbers:
+    n(0) = 0.123871;
+    n(1) = 0.15432;
+    
+    cnt = 0;
+    for (int i = 0; i < lines.Size(); i++)
+      if (lines[i].Valid())
+	{
+	  const Point<3> & p1 = points[lines[i].L().I1()].P();
+	  const Point<3> & p2 = points[lines[i].L().I2()].P();
+	  
+	  v1 = p2 - p1;
+	  
+	  a(0, 0) = v1(0);
+	  a(1, 0) = v1(1);
+	  
+	  a(0, 1) = -n(0);
+	  a(1, 1) = -n(1);
+
+	  b(0) = p(0) - p1(0);
+	  b(1) = p(1) - p1(1);
+	  
+	  CalcInverse (a, ainv);
+	  ainv.Mult (b, u);
+	  
+	  if (u(0) >= 0 && u(0) <= 1 && u(1) > 0)
+	    cnt++;
+	}
+    
+    return ((cnt % 2) != 0);
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/adfront2.hpp b/contrib/Netgen/libsrc/meshing/adfront2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6a8158b974202c7426b83ff337eb7d2424fce0a8
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/adfront2.hpp
@@ -0,0 +1,282 @@
+#ifndef FILE_ADFRONT2
+#define FILE_ADFRONT2
+
+/**************************************************************************/
+/* File:   adfront2.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+
+/**
+
+    Advancing front class for surfaces
+
+*/
+
+  ///
+  class FrontPoint2
+  {
+    /// coordinates
+    Point<3> p;            
+    /// global node index
+    PointIndex globalindex;   
+    /// number of front lines connected to point 
+    int nlinetopoint;    
+    /// distance to original boundary
+    int frontnr;          
+
+    bool onsurface;
+
+  public:
+    ///
+    MultiPointGeomInfo * mgi;
+
+    ///
+    FrontPoint2 ()
+    {
+      globalindex = -1;
+      nlinetopoint = 0;
+      frontnr = INT_MAX-10;    // attention: overflow on calculating  INT_MAX + 1
+      mgi = NULL;
+      onsurface = true;
+    }
+
+    ///
+    FrontPoint2 (const Point<3> & ap, PointIndex agi,
+		 MultiPointGeomInfo * amgi, bool aonsurface = true);
+    ///
+    ~FrontPoint2 () { ; }
+
+    ///
+    const Point<3> & P () const { return p; }
+    ///
+    operator const Point<3> & () const { return p; }
+    ///
+    PointIndex GlobalIndex () const { return globalindex; }
+
+    ///
+    void AddLine () { nlinetopoint++; }
+    ///
+    void RemoveLine ()
+    {
+      nlinetopoint--;
+      if (nlinetopoint == 0)
+	nlinetopoint = -1;
+    }
+
+    ///
+    bool Valid () const
+    { return nlinetopoint >= 0; }
+
+    ///
+    bool OnSurface() const
+    { return onsurface; }
+
+    ///
+    void DecFrontNr (int afrontnr)
+    {
+      if (frontnr > afrontnr) frontnr = afrontnr;
+    }
+    
+    ///
+    int FrontNr () const { return frontnr; }
+  };
+
+  
+  ///
+  class FrontLine
+  {
+  private:
+    /// Point Indizes
+    INDEX_2 l;            
+    /// quality class 
+    int lineclass;      
+    /// geometry specific data
+    PointGeomInfo geominfo[2];
+  public:
+
+    FrontLine ()
+    {
+      lineclass = 1;
+    }
+
+    ///
+    FrontLine (const INDEX_2 & al)
+    {
+      l = al;
+      lineclass = 1;
+    }
+
+
+    ///
+    const INDEX_2 & L () const
+    {
+      return l;
+    }
+
+    ///
+    int LineClass() const
+    {
+      return lineclass;
+    }
+
+    ///
+    void IncrementClass ()
+    {
+      lineclass++;
+    }
+    ///
+    void ResetClass ()
+    {
+      lineclass = 1;
+    }
+
+    ///
+    bool Valid () const
+    {
+      return l.I1() != -1;
+    }
+    ///
+    void Invalidate ()
+    {
+      l.I1() = -1;
+      l.I2() = -1;
+      lineclass = 1000;
+    }
+
+    void SetGeomInfo (const PointGeomInfo & gi1, const PointGeomInfo & gi2)
+      {
+	geominfo[0] = gi1;
+	geominfo[1] = gi2;
+      }
+
+    const PointGeomInfo * GetGeomInfo () const
+    { return geominfo; }
+    
+    const PointGeomInfo & GetGeomInfo (int endp) const
+    { return geominfo[endp-1]; }
+
+    friend class AdFront2;
+  };
+
+
+class AdFront2
+{
+
+  ///
+  Array<FrontPoint2> points;  /// front points
+  Array<FrontLine> lines;     /// front lines
+
+  Box3d boundingbox;
+  Box3dTree linesearchtree;       /// search tree for lines
+  Point3dTree pointsearchtree;    /// search tree for points
+  Point3dTree cpointsearchtree;   /// search tree for cone points (not used ???)
+
+  Array<int> delpointl;     /// list of deleted front points
+  Array<int> dellinel;      /// list of deleted front lines
+
+  int nfl;                  /// number of front lines;
+  INDEX_2_HASHTABLE<int> * allflines; /// all front lines ever have been
+
+  Array<int> invpindex;
+
+  int minval;
+  int starti;
+
+public:
+  ///
+  //  AdFront2 ();
+  AdFront2 (const Box3d & aboundingbox);
+  ///
+  ~AdFront2 ();
+
+  ///
+  // void GetPoints (Array<Point<3> > & apoints) const;
+  ///
+  void Print (ostream & ost) const;
+
+  ///
+  bool Empty () const
+  {
+    return nfl == 0;
+  }
+  ///
+  int GetNFL () const { return nfl; }
+
+  const FrontLine & GetLine (int nr) { return lines[nr]; }
+  const FrontPoint2 & GetPoint (int nr) { return points[nr]; }
+
+
+  ///
+  int SelectBaseLine (Point<3> & p1, Point<3> & p2, 
+		      const PointGeomInfo *& geominfo1,
+		      const PointGeomInfo *& geominfo2,
+		      int & qualclass);
+
+  ///
+  int GetLocals (int baseline, 
+		 Array<Point3d> & locpoints,
+		 Array<MultiPointGeomInfo> & pgeominfo,
+                 Array<INDEX_2> & loclines,   // local index
+                 Array<int> & pindex,
+                 Array<int> & lindex,
+                 double xh);
+
+  ///
+  void DeleteLine (int li);
+  ///
+  int AddPoint (const Point<3> & p, PointIndex globind, 
+                MultiPointGeomInfo * mgi = NULL,
+                bool pointonsurface = true);
+  ///
+  int AddLine (int pi1, int pi2, 
+               const PointGeomInfo & gi1, const PointGeomInfo & gi2);
+  ///
+  int ExistsLine (int gpi1, int gpi2);
+
+  ///
+  void IncrementClass (int li)
+  {
+    lines[li].IncrementClass();
+  }
+
+  ///
+  void ResetClass (int li)
+  {
+    lines[li].ResetClass();
+  }
+
+  ///
+  const PointGeomInfo & GetLineGeomInfo (int li, int lend) const
+    { return lines[li].GetGeomInfo (lend); }
+  ///
+
+  PointIndex GetGlobalIndex (int pi) const
+  {
+    return points[pi].GlobalIndex();
+  }
+
+
+  /// is Point p inside Surface (flat geometry only)
+  bool Inside (const Point<2> & p) const;
+
+  bool SameSide (const Point<2> & lp1, const Point<2> & lp2, 
+                 const Array<int> * /* testfaces */ = NULL) const
+  {
+    return Inside (lp1) == Inside (lp2);
+  }
+
+
+  ///
+  void SetStartFront ();
+  ///
+  void PrintOpenSegments (ostream & ost) const;
+};
+
+
+
+#endif
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/adfront3.cpp b/contrib/Netgen/libsrc/meshing/adfront3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8863d9f42bb48e6a0f0502f0cf564b59db2cf47
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/adfront3.cpp
@@ -0,0 +1,868 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+/* ********************** FrontPoint ********************** */
+
+namespace netgen
+{
+
+FrontPoint3 :: FrontPoint3 () 
+{ 
+  globalindex = -1;
+  nfacetopoint = 0; 
+  frontnr = 1000; 
+  cluster = 0;
+}
+
+
+FrontPoint3 :: FrontPoint3 (const Point<3> & ap, PointIndex agi)
+{ 
+  p = ap; 
+  globalindex = agi;
+  nfacetopoint = 0; 
+  frontnr = 1000; 
+  cluster = 0;
+}
+
+
+
+/* ********************** FrontFace ********************** */
+
+FrontFace :: FrontFace () 
+{ 
+  qualclass = 1; 
+  oldfront = 0; 
+  hashvalue = 0;
+  cluster = 0;
+}
+
+FrontFace :: FrontFace (const MiniElement2d & af)
+{ 
+  f = af; 
+  oldfront = 0; 
+  qualclass = 1; 
+  hashvalue = 0;
+}
+
+void FrontFace :: Invalidate ()
+{ 
+  f.Delete(); 
+  oldfront = 0; 
+  qualclass = 1000; 
+}
+
+
+
+
+/* ********************** AddFront ********************** */
+ 
+
+AdFront3 :: AdFront3 ()
+{
+  nff = 0;
+  nff4 = 0;
+  vol = 0;
+
+  hashon = 1;
+  hashcreated = 0;
+  if (hashon) 
+    hashtable.Init(&points, &faces);
+
+  facetree = NULL;
+  connectedpairs = NULL;
+
+  rebuildcounter = -1;
+  lasti = 0;
+  minval = -1;
+}
+
+
+AdFront3 :: ~AdFront3 ()
+{
+  delete facetree;
+  delete connectedpairs;
+}
+
+void AdFront3 :: GetPoints (Array<Point<3> > & apoints) const
+{
+  for (PointIndex pi = PointIndex::BASE; 
+       pi < points.Size()+PointIndex::BASE; pi++)
+    
+    apoints.Append (points[pi].P());
+}
+
+
+PointIndex AdFront3 :: AddPoint (const Point<3> & p, PointIndex globind)
+{
+  if (delpointl.Size())
+    {
+      PointIndex pi = delpointl.Last();
+      delpointl.DeleteLast ();
+      
+      points[pi] = FrontPoint3 (p, globind);
+      return pi;
+    }
+  else
+    {
+      points.Append (FrontPoint3 (p, globind));
+      return points.Size()-1+PointIndex::BASE;
+    }
+}
+
+
+INDEX AdFront3 :: AddFace (const MiniElement2d & aface)
+{
+  int i, minfn;
+
+  nff++;
+
+  for (i = 0; i < aface.GetNP(); i++)
+    points[aface[i]].AddFace();
+
+  const Point3d & p1 = points[aface[0]].P();
+  const Point3d & p2 = points[aface[1]].P();
+  const Point3d & p3 = points[aface[2]].P();
+
+  vol += 1.0/6.0 * (p1.X() + p2.X() + p3.X()) *
+    ( (p2.Y() - p1.Y()) * (p3.Z() - p1.Z()) -
+      (p2.Z() - p1.Z()) * (p3.Y() - p1.Y()) );
+
+  if (aface.GetNP() == 4)
+    {
+      nff4++;
+      const Point3d & p4 = points[aface[3]].P();      
+      vol += 1.0/6.0 * (p1.X() + p3.X() + p4.X()) *
+	( (p3.Y() - p1.Y()) * (p4.Z() - p1.Z()) -
+	  (p3.Z() - p1.Z()) * (p4.Y() - p1.Y()) );
+    }
+
+
+  minfn = 1000;
+  for (i = 0; i < aface.GetNP(); i++)
+    {
+      int fpn = points[aface[i]].FrontNr();
+      if (i == 0 || fpn < minfn)
+	minfn = fpn;
+    }
+
+
+  int cluster = 0;
+  for (i = 1; i <= aface.GetNP(); i++)
+    {
+      if (points[aface.PNum(i)].cluster)
+	cluster = points[aface.PNum(i)].cluster;
+    }
+  for (i = 1; i <= aface.GetNP(); i++)
+    points[aface.PNum(i)].cluster = cluster;
+
+
+  for (i = 1; i <= aface.GetNP(); i++)
+    points[aface.PNum(i)].DecFrontNr (minfn+1);
+
+  int nfn = faces.Append(FrontFace (aface));
+  faces.Elem(nfn).cluster = cluster;
+
+  if (hashon && hashcreated) 
+    hashtable.AddElem(aface, nfn);
+
+  return nfn;
+}
+
+
+
+void AdFront3 :: DeleteFace (INDEX fi)
+{
+  nff--;
+
+  for (int i = 1; i <= faces.Get(fi).Face().GetNP(); i++)
+    {
+      PointIndex pi = faces.Get(fi).Face().PNum(i);
+      points[pi].RemoveFace();
+      if (!points[pi].Valid())
+	delpointl.Append (pi);
+    }
+
+  const MiniElement2d & face = faces.Get(fi).Face();
+  const Point3d & p1 = points[face.PNum(1)].P();
+  const Point3d & p2 = points[face.PNum(2)].P();
+  const Point3d & p3 = points[face.PNum(3)].P();
+
+  vol -= 1.0/6.0 * (p1.X() + p2.X() + p3.X()) *
+    ( (p2.Y() - p1.Y()) * (p3.Z() - p1.Z()) -
+      (p2.Z() - p1.Z()) * (p3.Y() - p1.Y()) );
+
+  if (face.GetNP() == 4)
+    {
+      const Point3d & p4 = points[face.PNum(4)].P();      
+      vol -= 1.0/6.0 * (p1.X() + p3.X() + p4.X()) *
+	( (p3.Y() - p1.Y()) * (p4.Z() - p1.Z()) -
+	  (p3.Z() - p1.Z()) * (p4.Y() - p1.Y()) );
+
+      nff4--;
+    }
+
+  faces.Elem(fi).Invalidate();
+}
+
+
+INDEX AdFront3 :: AddConnectedPair (const INDEX_2 & apair)
+{
+  if (!connectedpairs)
+    connectedpairs = new TABLE<int, PointIndex::BASE> (GetNP());
+
+  connectedpairs->Add (apair.I1(), apair.I2());
+  connectedpairs->Add (apair.I2(), apair.I1());
+
+  return 0;
+}
+
+
+void AdFront3 :: CreateTrees ()
+{
+  int i, j;
+  PointIndex pi;
+  Point3d pmin, pmax;
+
+  for (pi = PointIndex::BASE; 
+       pi < GetNP()+PointIndex::BASE; pi++)
+    {
+      const Point<3> & p = GetPoint(pi);
+      if (pi == PointIndex::BASE)
+	{
+	  pmin = p;
+	  pmax = p;
+	}
+      else
+	{
+	  pmin.SetToMin (p);
+	  pmax.SetToMax (p);
+	}
+    }
+
+  pmax = pmax + 0.5 * (pmax - pmin);
+  pmin = pmin + 0.5 * (pmin - pmax);
+
+  delete facetree;
+  facetree = new Box3dTree (pmin, pmax);
+  
+  for (i = 1; i <= GetNF(); i++)
+    {
+      const MiniElement2d & el = GetFace(i);
+      pmin = GetPoint (el[0]);
+      pmax = pmin;
+      for (j = 1; j < 3; j++)
+	{
+	  const Point<3> & p = GetPoint (el[j]);
+	  pmin.SetToMin (p);
+	  pmax.SetToMax (p);
+	}
+      pmax = pmax + 0.01 * (pmax - pmin);
+      pmin = pmin + 0.01 * (pmin - pmax);
+      //      (*testout) << "insert " << i << ": " << pmin << " - " << pmax << "\n";
+      facetree -> Insert (pmin, pmax, i);
+    }
+}
+
+
+void AdFront3 :: GetIntersectingFaces (const Point<3> & pmin, const Point<3> & pmax, 
+				       Array<int> & ifaces) const
+{
+  facetree -> GetIntersecting (pmin, pmax, ifaces);
+}
+
+void AdFront3 :: GetFaceBoundingBox (int i, Box3d & box) const
+{
+  const FrontFace & face = faces.Get(i);
+  box.SetPoint (points[face.f[0]].p);
+  box.AddPoint (points[face.f[1]].p);
+  box.AddPoint (points[face.f[2]].p);
+}
+
+void AdFront3 :: RebuildInternalTables ()
+{
+  static int timer_a = NgProfiler::CreateTimer ("Adfront3::RebuildInternal A");
+  static int timer_b = NgProfiler::CreateTimer ("Adfront3::RebuildInternal B");
+  static int timer_c = NgProfiler::CreateTimer ("Adfront3::RebuildInternal C");
+  static int timer_d = NgProfiler::CreateTimer ("Adfront3::RebuildInternal D");
+
+
+  NgProfiler::StartTimer (timer_a);	  
+  int hi = 0;
+  for (int i = 1; i <= faces.Size(); i++)
+    if (faces.Get(i).Valid())
+      {
+	hi++;
+	if (hi < i)
+	  faces.Elem(hi) = faces.Get(i);
+      }
+  
+  faces.SetSize (nff);
+
+  int np = points.Size();
+
+  for (int i = PointIndex::BASE; 
+       i < np+PointIndex::BASE; i++)
+    points[i].cluster = i;
+  
+  NgProfiler::StopTimer (timer_a);	  
+  NgProfiler::StartTimer (timer_b);	  
+
+  int change;
+  do
+    {
+      change = 0;
+      for (int i = 1; i <= faces.Size(); i++)
+	{
+	  const MiniElement2d & el = faces.Get(i).Face();
+
+	  int mini = points[el.PNum(1)].cluster;
+	  int maxi = mini;
+	  
+	  for (int j = 2; j <= 3; j++)
+	    {
+	      int ci = points[el.PNum(j)].cluster;
+	      if (ci < mini) mini = ci;
+	      if (ci > maxi) maxi = ci;
+	    }
+
+	  if (mini < maxi)
+	    {
+	      change = 1;
+	      for (int j = 1; j <= 3; j++)
+		points[el.PNum(j)].cluster = mini;
+	    }
+	}
+    }
+  while (change);
+
+
+  NgProfiler::StopTimer (timer_b);	  
+  NgProfiler::StartTimer (timer_c);	  
+
+
+
+
+  BitArrayChar<PointIndex::BASE> usecl(np);
+  usecl.Clear();
+  for (int i = 1; i <= faces.Size(); i++)
+    {
+      usecl.Set (points[faces.Get(i).Face().PNum(1)].cluster);
+      faces.Elem(i).cluster =
+	points[faces.Get(i).Face().PNum(1)].cluster;
+    }
+  int cntcl = 0;
+  for (int i = PointIndex::BASE; 
+       i < np+PointIndex::BASE; i++)
+    if (usecl.Test(i))
+      cntcl++;
+
+  Array<double, PointIndex::BASE> clvol (np);
+  clvol = 0.0;
+
+  for (int i = 1; i <= faces.Size(); i++)
+    {
+      const MiniElement2d & face = faces.Get(i).Face();
+
+      const Point3d p1 = points[face.PNum(1)].P();      
+      const Point3d p2 = points[face.PNum(2)].P();      
+      const Point3d p3 = points[face.PNum(3)].P();      
+      
+      double vi = 1.0/6.0 * (p1.X() + p2.X() + p3.X()) *
+	( (p2.Y() - p1.Y()) * (p3.Z() - p1.Z()) -
+	  (p2.Z() - p1.Z()) * (p3.Y() - p1.Y()) );
+      
+      if (face.GetNP() == 4)
+	{
+	  const Point3d p4 = points[face.PNum(4)].P();      
+	  vi += 1.0/6.0 * (p1.X() + p3.X() + p4.X()) *
+	    ( (p3.Y() - p1.Y()) * (p4.Z() - p1.Z()) -
+	      (p3.Z() - p1.Z()) * (p4.Y() - p1.Y()) );
+	}
+     
+      clvol[faces.Get(i).cluster] += vi;
+    }
+
+  NgProfiler::StopTimer (timer_c);	  
+  NgProfiler::StartTimer (timer_d);	  
+
+
+
+  int negvol = 0;
+  for (int i = PointIndex::BASE; 
+       i < clvol.Size()+PointIndex::BASE; i++)
+    {
+      if (clvol[i] < 0)
+	negvol = 1;
+    }
+
+  if (negvol)
+    {
+      for (int i = 1; i <= faces.Size(); i++)
+	faces.Elem(i).cluster = 1;
+      for (int i = PointIndex::BASE; 
+	   i < points.Size()+PointIndex::BASE; i++)
+	points[i].cluster = 1;
+    }
+
+  if (hashon) 
+    hashtable.Create();
+
+  NgProfiler::StopTimer (timer_d);	  
+}
+
+
+
+int AdFront3 :: SelectBaseElement ()
+{
+  int i, hi, fstind;
+
+  /*
+  static int minval = -1;
+  static int lasti = 0;
+  static int counter = 0;
+  */
+  if (rebuildcounter <= 0)
+    {
+      RebuildInternalTables();
+      rebuildcounter = nff / 10 + 1;
+      
+      lasti = 0;
+    }
+  rebuildcounter--;
+
+  /*
+  if (faces.Size() > 2 * nff)
+    {
+      // compress facelist
+
+      RebuildInternalTables ();
+      lasti = 0;
+    }
+    */
+  
+  fstind = 0;
+
+  for (i = lasti+1; i <= faces.Size() && !fstind; i++)
+    if (faces.Elem(i).Valid())
+      {
+	hi = faces.Get(i).QualClass() +
+	  points[faces.Get(i).Face().PNum(1)].FrontNr() +
+	  points[faces.Get(i).Face().PNum(2)].FrontNr() +
+	  points[faces.Get(i).Face().PNum(3)].FrontNr();
+	
+	if (hi <= minval)
+	  {
+	    minval = hi;
+	    fstind = i;
+	    lasti = fstind;
+	  }
+      }
+  
+  if (!fstind)
+    {
+      minval = INT_MAX;
+      for (i = 1; i <= faces.Size(); i++)
+	if (faces.Elem(i).Valid())
+	  {
+	    hi = faces.Get(i).QualClass() +
+	      points[faces.Get(i).Face().PNum(1)].FrontNr() +
+	      points[faces.Get(i).Face().PNum(2)].FrontNr() +
+	      points[faces.Get(i).Face().PNum(3)].FrontNr();
+	    
+	    if (hi <= minval)
+	      {
+		minval = hi;
+		fstind = i;
+		lasti = 0;
+	      }
+	  }
+    }
+
+
+  return fstind;
+}
+
+
+
+int AdFront3 :: GetLocals (int fstind,
+			   Array<Point3d > & locpoints,
+			   Array<MiniElement2d> & locfaces,   // local index
+			   Array<PointIndex> & pindex,
+			   Array<INDEX> & findex,
+			   INDEX_2_HASHTABLE<int> & getconnectedpairs,
+			   float xh,
+			   float relh,
+			   INDEX& facesplit)
+{
+  static int timer = NgProfiler::CreateTimer ("AdFront3::GetLocals");
+  NgProfiler::RegionTimer reg (timer);
+
+
+  if (hashon && faces.Size() < 500) { hashon=0; }
+  if (hashon && !hashcreated) 
+    {
+      hashtable.Create(); 
+      hashcreated=1;
+    }
+
+  INDEX i, j;
+  PointIndex pstind;
+  INDEX pi;
+  Point3d midp, p0;
+
+  //  static Array<int, PointIndex::BASE> invpindex;
+  
+  Array<MiniElement2d> locfaces2;           //all local faces in radius xh
+  Array<int> locfaces3;           // all faces in outer radius relh
+  Array<INDEX> findex2;
+
+  locfaces2.SetSize(0);
+  locfaces3.SetSize(0);
+  findex2.SetSize(0);
+
+  int cluster = faces.Get(fstind).cluster;
+
+  pstind = faces.Get(fstind).Face().PNum(1);
+  p0 = points[pstind].P();
+  
+  locfaces2.Append(faces.Get(fstind).Face());
+  findex2.Append(fstind);
+
+
+  Box3d b1 (p0 - Vec3d(xh, xh, xh), p0 + Vec3d (xh, xh, xh));
+
+  if (hashon)
+    {
+      hashtable.GetLocals(locfaces2, findex2, fstind, p0, xh);
+    }
+  else
+    {
+      for (i = 1; i <= faces.Size(); i++)
+	{
+	  const MiniElement2d & face = faces.Get(i).Face();
+	  if (faces.Get(i).cluster == cluster && faces.Get(i).Valid() && i != fstind)
+	    {
+	      Box3d b2;
+	      b2.SetPoint (points[face[0]].P());
+	      b2.AddPoint (points[face[1]].P());
+	      b2.AddPoint (points[face[2]].P());
+
+	      if (b1.Intersect (b2))
+		{
+		  locfaces2.Append(faces.Get(i).Face());
+		  findex2.Append(i);
+		}
+	    }
+	}
+    }
+
+  //local faces for inner radius:
+  for (i = 1; i <= locfaces2.Size(); i++)
+    {
+      const MiniElement2d & face = locfaces2.Get(i);
+      const Point3d & p1 = points[face[0]].P();
+      const Point3d & p2 = points[face[1]].P();
+      const Point3d & p3 = points[face[2]].P();
+
+      midp = Center (p1, p2, p3);
+
+      if (Dist2 (midp, p0) <= relh * relh || i == 1)
+	{
+          locfaces.Append(locfaces2.Get(i));
+	  findex.Append(findex2.Get(i));
+	}
+      else
+	locfaces3.Append (i);
+    }
+  
+  facesplit=locfaces.Size();
+  
+  
+  //local faces for outer radius:
+  for (i = 1; i <= locfaces3.Size(); i++)
+    {
+      locfaces.Append (locfaces2.Get(locfaces3.Get(i)));
+      findex.Append (findex2.Get(locfaces3.Get(i)));
+    }
+
+
+  invpindex.SetSize (points.Size());
+  for (i = 1; i <= locfaces.Size(); i++)
+    for (j = 1; j <= locfaces.Get(i).GetNP(); j++)
+      {
+	pi = locfaces.Get(i).PNum(j);
+	invpindex[pi] = -1;
+      }
+
+  for (i = 1; i <= locfaces.Size(); i++)
+    {
+      for (j = 1; j <= locfaces.Get(i).GetNP(); j++)
+	{
+	  pi = locfaces.Get(i).PNum(j);
+	  if (invpindex[pi] == -1)
+	    {
+	      pindex.Append (pi);
+	      invpindex[pi] = pindex.Size();  // -1+PointIndex::BASE;
+	      locfaces.Elem(i).PNum(j) = locpoints.Append (points[pi].P());
+	    }
+	  else
+	    locfaces.Elem(i).PNum(j) = invpindex[pi];
+
+	}
+    }
+
+
+
+  if (connectedpairs)
+    {
+      for (i = 1; i <= locpoints.Size(); i++)
+	{
+	  int pind = pindex.Get(i);
+	  if (pind >= 1 && pind <= connectedpairs->Size ())
+	    {
+	      for (j = 1; j <= connectedpairs->EntrySize(pind); j++)
+		{
+		  int oi = connectedpairs->Get(pind, j);
+		  int other = invpindex.Get(oi);
+		  if (other >= 1 && other <= pindex.Size() && 
+		      pindex.Get(other) == oi)
+		    {
+		      // INDEX_2 coned(i, other);
+		      // coned.Sort();
+		      // (*testout) << "connected: " << locpoints.Get(i) << "-" << locpoints.Get(other) << endl;
+		      getconnectedpairs.Set (INDEX_2::Sort (i, other), 1);
+		    }
+		}
+	    }
+	}
+    }
+  
+
+  /*
+    // add isolated points
+  for (i = 1; i <= points.Size(); i++)
+    if (points.Elem(i).Valid() && Dist (points.Elem(i).P(), p0) <= xh)
+      {
+	if (!invpindex.Get(i))
+	  {
+	    locpoints.Append (points.Get(i).P());
+	    pindex.Append (i);
+	    invpindex.Elem(i) = pindex.Size();
+	  }
+      }
+      */
+  return faces.Get(fstind).QualClass();
+}
+
+
+// returns all points connected with fi
+void AdFront3 :: GetGroup (int fi,
+			   Array<MeshPoint> & grouppoints,
+			   Array<MiniElement2d> & groupelements,
+			   Array<PointIndex> & pindex,
+			   Array<INDEX> & findex) 
+{
+  // static Array<char> pingroup;
+  int i, j, changed;
+
+  pingroup.SetSize(points.Size());
+
+  pingroup = 0;
+  for (j = 1; j <= 3; j++)
+    pingroup.Elem (faces.Get(fi).Face().PNum(j)) = 1;
+
+  do
+    {
+      changed = 0;
+
+      for (i = 1; i <= faces.Size(); i++)
+	if (faces.Get(i).Valid())
+	  {
+	    const MiniElement2d & face = faces.Get(i).Face();
+
+	    int fused = 0;
+	    for (j = 1; j <= 3; j++)
+	      if (pingroup.Elem(face.PNum(j))) 
+		fused++;
+            
+	    if (fused >= 2)
+	      for (j = 1; j <= 3; j++)
+		if (!pingroup.Elem(face.PNum(j)))
+		  {
+		    pingroup.Elem(face.PNum(j)) = 1;
+		    changed = 1;
+		  }
+	  }
+
+    }
+  while (changed);
+
+
+  //  static Array<int> invpindex;
+  invpindex.SetSize (points.Size());
+  
+
+  for (i = 1; i <= points.Size(); i++)
+    if (points.Get(i).Valid())
+      {
+	grouppoints.Append (points.Get(i).P());
+	pindex.Append (i);
+	invpindex.Elem(i) = pindex.Size();
+      }
+
+  for (i = 1; i <= faces.Size(); i++)
+    if (faces.Get(i).Valid())
+      {
+	int fused = 0;
+	for (j = 1; j <= 3; j++)
+	  if (pingroup.Get(faces.Get(i).Face().PNum(j)))
+	    fused++;
+
+	if (fused >= 2)
+	  {
+	    groupelements.Append (faces.Get(i).Face());
+	    findex.Append (i);
+	  }
+      }
+      
+  for (i = 1; i <= groupelements.Size(); i++)
+    for (j = 1; j <= 3; j++)
+      {
+	groupelements.Elem(i).PNum(j) =
+	  invpindex.Get(groupelements.Elem(i).PNum(j));
+      }
+
+  /*
+  for (i = 1; i <= groupelements.Size(); i++)
+    for (j = 1; j <= 3; j++)
+      for (k = 1; k <= grouppoints.Size(); k++)
+        if (pindex.Get(k) == groupelements.Get(i).PNum(j))
+          {
+	    groupelements.Elem(i).PNum(j) = k;
+	    break;
+          }
+  */          
+}
+
+
+void AdFront3 :: SetStartFront (int /* baseelnp */)
+{
+  INDEX i;
+  int j;
+
+  for (i = 1; i <= faces.Size(); i++)
+    if (faces.Get(i).Valid())
+      {
+	const MiniElement2d & face = faces.Get(i).Face();
+	for (j = 1; j <= 3; j++)
+	  points[face.PNum(j)].DecFrontNr(0);
+      }
+
+  /*
+  if (baseelnp)
+    {
+      for (i = 1; i <= faces.Size(); i++)
+	if (faces.Get(i).Valid() && faces.Get(i).Face().GetNP() != baseelnp)
+	  faces.Elem(i).qualclass = 1000;
+    }
+    */
+}
+
+
+bool AdFront3 :: Inside (const Point<3> & p) const
+{
+  int cnt;
+  Vec3d n, v1, v2;
+  DenseMatrix a(3), ainv(3);
+  Vector b(3), u(3);
+
+  // random numbers:
+  n.X() = 0.123871;
+  n.Y() = 0.15432;
+  n.Z() = -0.43989;
+
+  cnt = 0;
+  for (int i = 1; i <= faces.Size(); i++)
+    if (faces.Get(i).Valid())
+      {
+	const Point<3> & p1 = points[faces.Get(i).Face().PNum(1)].P();
+	const Point<3> & p2 = points[faces.Get(i).Face().PNum(2)].P();
+	const Point<3> & p3 = points[faces.Get(i).Face().PNum(3)].P();
+
+	v1 = p2 - p1;
+	v2 = p3 - p1;
+
+	a(0, 0) = v1.X();
+	a(1, 0) = v1.Y();
+	a(2, 0) = v1.Z();
+	a(0, 1) = v2.X();
+	a(1, 1) = v2.Y();
+	a(2, 1) = v2.Z();
+	a(0, 2) = -n.X();
+	a(1, 2) = -n.Y();
+	a(2, 2) = -n.Z();
+
+	b(0) = p(0) - p1(0);
+	b(1) = p(1) - p1(1);
+	b(2) = p(2) - p1(2);
+
+	CalcInverse (a, ainv);
+	ainv.Mult (b, u);
+
+	if (u(0) >= 0 && u(1) >= 0 && u(0)+u(1) <= 1 &&
+	    u(2) > 0)
+	  {
+	    cnt++;
+	  }
+      }
+
+  return ((cnt % 2) != 0);
+}
+
+
+
+
+
+int AdFront3 :: SameSide (const Point<3> & lp1, const Point<3> & lp2,
+			  const Array<int> * testfaces) const
+{
+  const Point<3> *line[2];
+  line[0] = &lp1;
+  line[1] = &lp2;
+
+
+  Point3d pmin(lp1);
+  Point3d pmax(lp1);
+  pmin.SetToMin (lp2);
+  pmax.SetToMax (lp2);
+  
+  ArrayMem<int, 100> aprif;
+  aprif.SetSize(0);
+  
+  if (!testfaces)
+    facetree->GetIntersecting (pmin, pmax, aprif);
+  else
+    for (int i = 1; i <= testfaces->Size(); i++)
+      aprif.Append (testfaces->Get(i));
+
+  int cnt = 0;
+  for (int ii = 1; ii <= aprif.Size(); ii++)
+    {
+      int i = aprif.Get(ii);
+      
+      if (faces.Get(i).Valid())
+	{
+	  const Point<3> *tri[3];
+	  tri[0] = &points[faces.Get(i).Face().PNum(1)].P();
+	  tri[1] = &points[faces.Get(i).Face().PNum(2)].P();
+	  tri[2] = &points[faces.Get(i).Face().PNum(3)].P();
+	  	  
+	  if (IntersectTriangleLine (&tri[0], &line[0]))
+	    cnt++;
+	}
+    }
+
+  return ((cnt+1) % 2);
+}
+}
diff --git a/contrib/Netgen/libsrc/meshing/adfront3.hpp b/contrib/Netgen/libsrc/meshing/adfront3.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..60c6aa108a11a39660cce62f7502af00216d320d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/adfront3.hpp
@@ -0,0 +1,320 @@
+#ifndef FILE_ADFRONT3
+#define FILE_ADFRONT3
+
+/**************************************************************************/
+/* File:   adfront3.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+/*
+  Advancing front class for volume meshing
+*/
+
+
+
+/// Point in advancing front
+class FrontPoint3
+{
+  /// coordinates
+Point<3> p;           
+  /// global node index
+PointIndex globalindex;   
+  /// number of faces connected to point 
+int nfacetopoint;    
+  /// distance to original boundary
+int frontnr;
+  /// 
+int cluster;
+public:
+  ///
+  FrontPoint3 ();
+  ///
+  FrontPoint3 (const Point<3> & ap, PointIndex agi);
+  
+  ///
+  const Point<3> & P () const
+  { return p; }
+  ///
+  PointIndex GlobalIndex () const
+  { return globalindex; }
+  
+  ///
+  void AddFace ()
+  { nfacetopoint++; }
+
+  ///
+  void RemoveFace()
+  { 
+    nfacetopoint--;
+    if (nfacetopoint == 0) nfacetopoint = -1;
+  }
+  
+  ///
+  int Valid () const
+  { return nfacetopoint >= 0; }
+
+  ///
+  void DecFrontNr (int afrontnr)
+  {
+    if (frontnr > afrontnr) frontnr = afrontnr;
+  }
+  
+  ///
+  int FrontNr () const
+  { return frontnr; }
+
+  ///
+  friend class AdFront3;
+};
+
+
+
+class MiniElement2d
+{
+protected:
+  int np;
+  PointIndex pnum[4];
+  bool deleted;
+public:
+  MiniElement2d ()
+  { np = 3; deleted = 0; }
+  MiniElement2d (int anp)
+  { np = anp; deleted = 0; }
+
+  int GetNP() const { return np; }
+  PointIndex & operator[] (int i) { return pnum[i]; }
+  const PointIndex operator[] (int i) const { return pnum[i]; }
+
+  const PointIndex PNum (int i) const { return pnum[i-1]; }
+  PointIndex & PNum (int i) { return pnum[i-1]; }
+  const PointIndex PNumMod (int i) const { return pnum[(i-1)%np]; }
+
+  void Delete () { deleted = 1; pnum[0] = pnum[1] = pnum[2] = pnum[3] = PointIndex::BASE-1; }
+  bool IsDeleted () const { return deleted; }
+};
+
+
+inline ostream & operator<<(ostream  & s, const MiniElement2d & el)
+{
+  s << "np = " << el.GetNP();
+  for (int j = 0; j < el.GetNP(); j++)
+    s << " " << el[j];
+  return s;
+}
+
+
+
+
+/// Face in advancing front
+class FrontFace
+{
+private:
+  ///
+  MiniElement2d f;
+  ///
+  int qualclass;
+  ///
+  char oldfront;
+  ///
+  int hashvalue;
+  ///
+  int cluster;
+
+public:
+  ///
+  FrontFace ();
+  ///
+  FrontFace (const MiniElement2d & af);
+  ///
+  const MiniElement2d & Face () const
+  { return f; }
+  
+  ///
+  int QualClass () const
+  { return qualclass; }
+
+  ///
+  void IncrementQualClass ()
+  { qualclass++; }
+
+  ///
+  void ResetQualClass ()
+  {
+    if (qualclass > 1)
+      {
+	qualclass = 1;
+	oldfront = 0;
+      }
+  }
+  
+  ///
+  bool Valid () const
+  { return !f.IsDeleted(); }
+
+  ///
+  void Invalidate ();
+
+  ///
+  int HashValue() const 
+  { return hashvalue; }
+
+  ///
+  void SetHashValue(int hv) 
+  { hashvalue = hv; }
+
+  ///
+  friend class AdFront3;
+
+  int Cluster () const { return cluster; }
+};  
+
+
+
+
+/// Advancing front, 3D.
+class AdFront3
+{
+  ///
+Array<FrontPoint3, PointIndex::BASE> points;
+  ///
+Array<FrontFace> faces;
+  ///
+Array<PointIndex> delpointl;
+
+  /// which points are connected to pi ?
+TABLE<int, PointIndex::BASE> * connectedpairs;
+  
+  /// number of total front faces;
+int nff;
+  /// number of quads in front
+int nff4; 
+  
+  ///
+double vol;
+
+  ///
+GeomSearch3d hashtable;
+
+  /// 
+int hashon;
+
+  ///
+int hashcreated;
+
+  /// counter for rebuilding internal tables
+int rebuildcounter;
+  /// last base element
+int lasti;
+  /// minimal selection-value of baseelements
+int minval;
+  Array<int, PointIndex::BASE> invpindex;
+  Array<char> pingroup;
+  
+  ///
+class Box3dTree * facetree;
+public:
+
+  ///
+  AdFront3 ();
+  ///
+  ~AdFront3 ();
+  ///
+  void GetPoints (Array<Point<3> > & apoints) const;
+  ///
+  int GetNP() const 
+  { return points.Size(); }
+  ///
+  const Point<3> & GetPoint (PointIndex pi) const
+  { return points[pi].P(); }
+  ///
+  int GetNF() const
+  { return nff; }
+  ///
+  const MiniElement2d & GetFace (int i) const
+  { return faces.Get(i).Face(); }
+  ///
+  void Print () const;
+  ///
+  bool Empty () const
+  { return nff == 0; }
+  ///
+  bool Empty (int elnp) const
+  {
+    if (elnp == 4)
+      return (nff4 == 0);
+    return (nff - nff4 == 0);
+  }
+  ///
+  int SelectBaseElement ();
+
+  ///
+  void CreateTrees ();
+
+  ///
+  void GetIntersectingFaces (const Point<3> & pmin, const Point<3> & pmax, 
+			     Array<int> & ifaces) const;
+
+  ///
+  void GetFaceBoundingBox (int i, Box3d & box) const;
+
+  ///
+  int GetLocals (int baseelement,
+		 Array<Point3d > & locpoints,
+                 Array<MiniElement2d> & locfaces,   // local index
+                 Array<PointIndex> & pindex,
+                 Array<INDEX> & findex,
+		 INDEX_2_HASHTABLE<int> & connectedpairs,
+                 float xh,
+		 float relh,
+		 INDEX& facesplit);
+  
+  ///
+  void GetGroup (int fi,
+                 Array<MeshPoint> & grouppoints,
+                 Array<MiniElement2d> & groupelements,
+                 Array<PointIndex> & pindex,
+                 Array<INDEX> & findex);
+
+  ///
+  void DeleteFace (INDEX fi);
+  ///
+  PointIndex AddPoint (const Point<3> & p, PointIndex globind);
+  ///
+  INDEX AddFace (const MiniElement2d & e);
+  ///
+  INDEX AddConnectedPair (const INDEX_2 & pair);
+  ///
+  void IncrementClass (INDEX fi)
+  { faces.Elem(fi).IncrementQualClass(); }
+
+  ///
+  void ResetClass (INDEX fi)
+  { faces.Elem(fi).ResetQualClass(); }
+
+  ///
+  void SetStartFront (int baseelnp = 0);
+
+  /// is Point p inside Surface ?
+  bool Inside (const Point<3> & p) const;
+  /// both points on same side ?
+  int SameSide (const Point<3> & lp1, const Point<3> & lp2, 
+		const Array<int> * testfaces = NULL) const;
+
+
+  ///
+  PointIndex GetGlobalIndex (PointIndex pi) const
+  { return points[pi].GlobalIndex(); }
+  ///
+  double Volume () const
+  { return vol; }
+
+
+private:
+  void RebuildInternalTables();
+};
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/basegeom.cpp b/contrib/Netgen/libsrc/meshing/basegeom.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c8d642e29d7e5d87782f766b5e21ced9b88ab676
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/basegeom.cpp
@@ -0,0 +1,66 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+  DLL_HEADER Array<GeometryRegister*> geometryregister;
+
+  GeometryRegister :: ~GeometryRegister()
+  { ; }
+
+
+
+
+  
+  int NetgenGeometry :: GenerateMesh (Mesh*& mesh, MeshingParameters & mparam,
+				      int perfstepsstart, int perfstepsend)
+  {
+    if (!mesh) return 1;
+
+    if (perfstepsstart <= MESHCONST_MESHVOLUME)
+      {
+	multithread.task = "Volume meshing";
+	
+	MESHING3_RESULT res =
+	  MeshVolume (mparam, *mesh);
+	
+	if (res != MESHING3_OK) return 1;
+	
+	if (multithread.terminate) return 0;
+	
+	RemoveIllegalElements (*mesh);
+	if (multithread.terminate) return 0;
+
+	MeshQuality3d (*mesh);
+      }
+
+    
+    if (multithread.terminate || perfstepsend <= MESHCONST_MESHVOLUME)
+      return 0;
+
+
+    if (perfstepsstart <= MESHCONST_OPTVOLUME)
+      {
+	multithread.task = "Volume optimization";
+	
+	OptimizeVolume (mparam, *mesh);
+	if (multithread.terminate) return 0;
+      }
+    
+    return 0;
+  }    
+  
+
+  const Refinement & NetgenGeometry :: GetRefinement () const
+  {
+    return *new Refinement;;
+  }
+
+
+  void NetgenGeometry :: Save (string filename) const
+  {
+    throw NgException("Cannot save geometry - no geometry available");
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/basegeom.hpp b/contrib/Netgen/libsrc/meshing/basegeom.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5f866d0738f595223c7a9de51c286ecfb2e469bc
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/basegeom.hpp
@@ -0,0 +1,50 @@
+#ifndef FILE_BASEGEOM
+#define FILE_BASEGEOM
+
+/**************************************************************************/
+/* File:   basegeom.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   23. Aug. 09                                                    */
+/**************************************************************************/
+
+
+struct Tcl_Interp;
+
+namespace netgen
+{
+
+  class DLL_HEADER NetgenGeometry
+  {
+  public:
+    virtual ~NetgenGeometry () { ; }
+
+    virtual int GenerateMesh (Mesh*& mesh, MeshingParameters & mparam, 
+			      int perfstepsstart, int perfstepsend);
+
+    virtual const Refinement & GetRefinement () const;
+
+    virtual void Save (string filename) const;
+    virtual void SaveToMeshFile (ostream & /* ost */) const { ; }
+  };
+
+
+
+
+
+  class DLL_HEADER GeometryRegister
+  {
+  public:
+    virtual ~GeometryRegister();
+    virtual NetgenGeometry * Load (string filename) const = 0;
+    virtual NetgenGeometry * LoadFromMeshFile (istream & /* ist */) const { return NULL; }
+    virtual class VisualScene * GetVisualScene (const NetgenGeometry * /* geom */) const
+    { return NULL; }
+    virtual void SetParameters (Tcl_Interp * /* interp */) { ; }
+  };
+
+  extern DLL_HEADER Array<GeometryRegister*> geometryregister; 
+}
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/bcfunctions.cpp b/contrib/Netgen/libsrc/meshing/bcfunctions.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1bab1b75cc359454dd6ebd0c26b00e0185cd8211
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/bcfunctions.cpp
@@ -0,0 +1,468 @@
+
+#include <mystdlib.h>
+#include <meshing.hpp>
+
+
+namespace netgen
+{
+   // Default colour to be used for boundary condition number "0"
+   #define DEFAULT_R       0.0
+   #define DEFAULT_G       1.0
+   #define DEFAULT_B       0.0
+
+   // Boundary condition number to use if a face does not have a 
+   // colour assigned to it, or if the colour is the above defined 
+   // default colour
+   #define DEFAULT_BCNUM   1
+
+   // Default tolerance for colour matching (using Euclidean distance)
+   #define DEFAULT_EPS     2.5e-05
+
+
+
+
+   /*! Philippose - 11/07/2009
+       Function to check if two RGB colours are equal
+
+       Note#1: Currently uses unweighted Euclidean Distance 
+       for colour matching.
+
+       Note#2: The tolerance used for deciding whether two 
+       colours match is defined as "eps" and is currently 
+       2.5e-5 (for square of distance)
+   */
+   bool ColourMatch(Vec3d col1, Vec3d col2, double eps = DEFAULT_EPS)
+   {
+      if(eps <= 0.0) eps = DEFAULT_EPS;
+      
+      bool colmatch = false;
+
+      if(Dist2(col1,col2) < eps) colmatch = true;
+
+      return colmatch;
+   }
+      
+
+
+
+
+   /*! Philippose - 11/07/2009
+       Function to create a list of all the unique colours 
+       available in a given mesh
+   */
+   void GetFaceColours(Mesh & mesh, Array<Vec3d> & face_colours)
+   {
+      face_colours.SetSize(1);
+      face_colours.Elem(1) = mesh.GetFaceDescriptor(1).SurfColour();
+      
+      for(int i = 1; i <= mesh.GetNFD(); i++)
+      {
+         Vec3d face_colour = mesh.GetFaceDescriptor(i).SurfColour();
+         bool col_found = false;
+         
+         for(int j = 1; j <= face_colours.Size(); j++)
+         {
+            if(ColourMatch(face_colours.Elem(j),face_colour))
+            {
+               col_found = true;
+               break;
+            }
+         }
+         
+         if(!col_found) face_colours.Append(face_colour);
+      }
+
+      if(printmessage_importance >= 3)
+      {
+         cout << endl << "-------- Face Colours --------" << endl;
+         for( int i = 1; i <= face_colours.Size(); i++)
+         {
+            cout << face_colours.Elem(i) << endl;
+         }
+         cout << "------------------------------" << endl;
+      }
+   }
+
+
+
+
+
+
+   /*! Philippose - 11/07/2009
+       Assign boundary condition numbers based on a user defined 
+       colour profile file.
+
+       The default profile file is "netgen.ocf"
+
+       If the mesh contains colours not defined in the profile,
+       netgen automatically starts assigning each new colour a 
+       new boundary condition number starting from the highest 
+       boundary condition number specified in the profile file.
+   */
+   void AutoColourAlg_UserProfile(Mesh & mesh, ifstream & ocf)
+   {
+      char ocf_inp[100];
+      bool header_found = false;
+
+      // Number of colour specifications in the 
+      // user profile file
+      int numentries = 0;
+      while((ocf.good()) && (!header_found))
+      {
+         ocf >> ocf_inp;
+         if(strcmp(ocf_inp,"boundary_colours") == 0) header_found = true;
+      }
+
+      if(!header_found)
+      {
+         ocf.close();
+         throw NgException("AutoColourAlg_UserProfile: Invalid or empty Boundary Colour Profile file\n");
+         return;
+      }
+
+      // Read in the number of entries from file
+      ocf >> numentries;
+      if(numentries > 0)
+      {
+         if(!ocf.good())
+         {
+            ocf.close();
+            throw NgException("AutoColourAlg_UserProfile: Invalid or empty Boundary Colour Profile file\n");
+            return;
+         }
+
+         PrintMessage(3, "Number of colour entries: ", numentries);
+      }
+      else
+      {
+         ocf.close();
+         PrintMessage(3, "AutoColourAlg_UserProfile: No Boundary Colour entries found.... no changes made!");
+         return;
+      }
+
+      // Arrays to hold the specified RGB colour triplets as well 
+      // as the associated boundary condition number
+      Array<Vec3d> bc_colours(numentries);
+      Array<int> bc_num(numentries);
+      Array<bool> bc_used(numentries);
+      
+      // Actually read in the data from the file
+      for(int i = 1; i <= numentries; i++)
+      {
+         int bcnum;
+         // double col_red, col_green, col_blue;
+
+         ocf >> bcnum;
+         // Boundary condition number DEFAULT_BCNUM is reserved for 
+         // faces which have the default colour Green (0.0,1.0,0.0)
+         // To prevent confusion, no boundary numbery below this default 
+         // are permitted
+         if(bcnum < (DEFAULT_BCNUM + 1)) bcnum = DEFAULT_BCNUM+1;
+
+         bc_num.Elem(i) = bcnum;
+         bc_used.Elem(i) = false;
+         ocf >> bc_colours.Elem(i).X() 
+             >> bc_colours.Elem(i).Y() 
+             >> bc_colours.Elem(i).Z();
+
+         if(!ocf.good())
+         {
+            ocf.close();
+            throw NgException("Boundary Colour file error: Number of entries do not match specified list size!!\n");
+            return;
+         }
+
+         // Bound checking of the values
+         // The RGB values should be between 0.0 and 1.0
+         if(bc_colours.Elem(bcnum).X() < 0.0) bc_colours.Elem(bcnum).X() = 0.0;
+         if(bc_colours.Elem(bcnum).X() > 1.0) bc_colours.Elem(bcnum).X() = 1.0;
+         if(bc_colours.Elem(bcnum).Y() < 0.0) bc_colours.Elem(bcnum).X() = 0.0;
+         if(bc_colours.Elem(bcnum).Y() > 1.0) bc_colours.Elem(bcnum).X() = 1.0;
+         if(bc_colours.Elem(bcnum).Z() < 0.0) bc_colours.Elem(bcnum).X() = 0.0;
+         if(bc_colours.Elem(bcnum).Z() > 1.0) bc_colours.Elem(bcnum).X() = 1.0;
+      }
+
+      PrintMessage(3, "Successfully loaded Boundary Colour Profile file....");
+      ocf.close();
+
+      // Find the highest boundary condition number in the list
+      // All colours in the geometry which are not specified in the 
+      // list will be given boundary condition numbers higher than this 
+      // number
+      int max_bcnum = DEFAULT_BCNUM;
+      for(int i = 1; i <= bc_num.Size();i++)
+      {
+         if(bc_num.Elem(i) > max_bcnum) max_bcnum = bc_num.Elem(i);
+      }
+
+      PrintMessage(3, "Highest boundary number in list = ",max_bcnum);
+
+      Array<Vec3d> all_colours;
+      
+      // Extract all the colours to see how many there are
+      GetFaceColours(mesh,all_colours);
+      PrintMessage(3,"\nNumber of colours defined in Mesh: ", all_colours.Size());
+
+      if(all_colours.Size() == 0)
+      {
+         PrintMessage(3,"No colour data detected in Mesh... no changes made!");
+         return;
+      }
+
+      int nfd = mesh.GetNFD();
+
+      for(int face_index = 1; face_index <= nfd; face_index++)
+      {
+         // Temporary container for individual face colours
+         Vec3d face_colour;
+
+         // Get the colour of the face being currently processed
+         face_colour = mesh.GetFaceDescriptor(face_index).SurfColour();
+         if(!ColourMatch(face_colour,Vec3d(DEFAULT_R,DEFAULT_G,DEFAULT_B)))
+         {
+            // Boolean variable to check if the boundary condition was applied 
+            // or not... not applied would imply that the colour of the face 
+            // does not exist in the list of colours in the profile file
+            bool bc_assigned = false;
+
+            for(int col_index = 1; col_index <= bc_colours.Size(); col_index++)
+            {
+               if((ColourMatch(face_colour,bc_colours.Elem(col_index))) && (!bc_assigned))
+               {
+                  mesh.GetFaceDescriptor(face_index).SetBCProperty(bc_num.Elem(col_index));
+                  bc_used.Elem(col_index) = true;
+                  bc_assigned = true;
+                  break;
+               }
+            }
+
+            // If the colour was not found in the list, add it to the list, and assign 
+            // the next free boundary condition number to it
+            if(!bc_assigned)
+            {
+               max_bcnum++;
+               bc_num.Append(max_bcnum);
+               bc_colours.Append(face_colour);
+               bc_used.Append(true);
+
+               mesh.GetFaceDescriptor(face_index).SetBCProperty(max_bcnum);
+            }
+         }
+         else
+         {
+            // Set the boundary condition number to the default one
+            mesh.GetFaceDescriptor(face_index).SetBCProperty(DEFAULT_BCNUM);
+         }
+      }
+
+      // User Information of the results of the operation
+      Vec3d ref_colour(0.0,1.0,0.0);
+      PrintMessage(3,"Colour based Boundary Condition Property details:");
+      for(int bc_index = 0; bc_index <= bc_num.Size(); bc_index++)
+      {
+         if(bc_index > 0) ref_colour = bc_colours.Elem(bc_index);
+
+         if(bc_index == 0) 
+         {
+            PrintMessage(3, "BC Property: ",DEFAULT_BCNUM);
+            PrintMessage(3, "   RGB Face Colour = ",ref_colour,"","\n");
+         }
+         else if(bc_used.Elem(bc_index))
+         {
+            PrintMessage(3, "BC Property: ",bc_num.Elem(bc_index));
+            PrintMessage(3, "   RGB Face Colour = ",ref_colour,"","\n");
+         }
+      }
+   }
+
+
+
+
+   
+   /*! Philippose - 11/07/2009
+       Assign boundary condition numbers based on the colours 
+       assigned to each face in the mesh using an automated 
+       algorithm.
+
+       The particular algorithm used has been briefly explained 
+       in the header file "occauxfunctions.hpp"
+   */
+   void AutoColourAlg_Sorted(Mesh & mesh)
+   {
+      Array<Vec3d> all_colours;
+      Array<int> faces_sorted;
+      Array<int> colours_sorted;
+
+      // Extract all the colours to see how many there are
+      GetFaceColours(mesh,all_colours);
+
+      // Delete the default colour from the list since it will be accounted 
+      // for automatically
+      for(int i = 1; i <= all_colours.Size(); i++)
+      {
+         if(ColourMatch(all_colours.Elem(i),Vec3d(DEFAULT_R,DEFAULT_G,DEFAULT_B)))
+         {
+            all_colours.DeleteElement(i);
+            break;
+         }
+      }
+      PrintMessage(3,"\nNumber of colours defined in Mesh: ", all_colours.Size());
+
+      if(all_colours.Size() == 0)
+      {
+         PrintMessage(3,"No colour data detected in Mesh... no changes made!");
+         return;
+      }
+
+      // One more slot than the number of colours are required, to 
+      // account for individual faces which have no colour data 
+      // assigned to them in the CAD software
+      faces_sorted.SetSize(all_colours.Size()+1);
+      colours_sorted.SetSize(all_colours.Size()+1);
+      faces_sorted = 0;
+      
+      // Slave Array to identify the colours the faces were assigned to, 
+      // after the bubble sort routine to sort the automatic boundary 
+      // identifiers according to the number of surface mesh elements 
+      // of a given colour
+      for(int i = 0; i <= all_colours.Size(); i++) colours_sorted[i] = i;
+
+      // Used to hold the number of surface elements without any OCC 
+      // colour definition
+      int no_colour_faces = 0;
+
+      // Index in the faces array assigned to faces without any 
+      // or the default colour definition
+      int no_colour_index = 0;
+
+      int nfd = mesh.GetNFD();
+
+      // Extract the number of surface elements having a given colour
+      // And save this number into an array for later sorting
+      for(int face_index = 1; face_index <= nfd; face_index++)
+      {
+         Array<SurfaceElementIndex> se_face;
+
+         mesh.GetSurfaceElementsOfFace(face_index, se_face);
+
+         Vec3d face_colour;
+
+         face_colour = mesh.GetFaceDescriptor(face_index).SurfColour();
+         if(!ColourMatch(face_colour,Vec3d(DEFAULT_R,DEFAULT_G,DEFAULT_B)))
+         {
+            for(int i = 1; i <= all_colours.Size(); i++)
+            {
+               if(ColourMatch(face_colour, all_colours.Elem(i)))
+               {
+                  faces_sorted[i] = faces_sorted[i] + se_face.Size();
+               }
+            }
+         }
+         else
+         {
+            // Add the number of surface elements without any colour 
+            // definition separately
+            no_colour_faces = no_colour_faces + se_face.Size();
+         }
+      }
+
+      // Sort the face colour indices according to the number of surface 
+      // mesh elements which have a specific colour
+      BubbleSort(faces_sorted,colours_sorted);
+
+      // Now update the array position assigned for surface elements 
+      // without any colour definition with the number of elements
+      faces_sorted[no_colour_index] = no_colour_faces;
+
+      // Now actually assign the BC Property to the respective faces
+      for(int face_index = 1; face_index <= nfd; face_index++)
+      {
+         Vec3d face_colour;
+
+         face_colour = mesh.GetFaceDescriptor(face_index).SurfColour();
+         if(!ColourMatch(face_colour,Vec3d(DEFAULT_R,DEFAULT_G,DEFAULT_B)))
+         {
+            for(int i = 0; i < colours_sorted.Size(); i++)
+            {
+               Vec3d ref_colour;
+               if(i != no_colour_index) ref_colour = all_colours.Elem(colours_sorted[i]);
+
+               if(ColourMatch(face_colour, ref_colour))
+               {
+                  mesh.GetFaceDescriptor(face_index).SetBCProperty(i + DEFAULT_BCNUM);
+               }
+            }
+         }
+         else
+         {
+            mesh.GetFaceDescriptor(face_index).SetBCProperty(DEFAULT_BCNUM);
+         }
+
+         PrintMessage(4,"Face number: ",face_index," ; BC Property = ",mesh.GetFaceDescriptor(face_index).BCProperty());
+      }
+
+      // User Information of the results of the operation
+      Vec3d ref_colour(0.0,1.0,0.0);
+      PrintMessage(3,"Colour based Boundary Condition Property details:");
+      for(int i = 0; i < faces_sorted.Size(); i++)
+      {
+         if(colours_sorted[i] > 0) ref_colour = all_colours.Elem(colours_sorted[i]);
+
+         PrintMessage(3, "BC Property: ",i + DEFAULT_BCNUM);
+         PrintMessage(3, "   Nr. of Surface Elements = ", faces_sorted[i]);
+         PrintMessage(3, "   Colour Index = ", colours_sorted[i]);
+         PrintMessage(3, "   RGB Face Colour = ",ref_colour,"","\n");
+      }
+   }
+
+
+
+
+
+   /*! Philippose - 13/07/2009
+       Main function implementing automated assignment of 
+       Boundary Condition numbers based on face colours
+
+       This functionality is currently implemtented at the mesh 
+       level, and hence allows colour based assignment of boundary 
+       conditions for any geometry type within netgen which 
+       supports face colours
+   */
+   void AutoColourBcProps(Mesh & mesh, const char * bccolourfile)
+   {
+      // Go directly to the alternate algorithm if no colour profile file was specified
+      if(!bccolourfile)
+      {
+         PrintMessage(1,"AutoColourBcProps: Using Automatic Colour based boundary property assignment algorithm");
+         AutoColourAlg_Sorted(mesh);
+      }
+      else
+      {
+         ifstream ocf(bccolourfile);
+
+         // If there was an error opening the Colour profile file, jump to the alternate 
+         // algorithm after printing a message
+         if(!ocf)
+         {
+            PrintMessage(1,"AutoColourBcProps: Error loading Boundary Colour Profile file ", 
+                         bccolourfile, " ....","Switching to Automatic Assignment algorithm!");
+
+            AutoColourAlg_Sorted(mesh);
+         }
+         // If the file opens successfully, call the function which assigns boundary conditions 
+         // based on the colour profile file
+         else
+         {
+            PrintMessage(1, "AutoColourBcProps: Using Boundary Colour Profile file: ");
+            PrintMessage(1, "  ", bccolourfile);
+            AutoColourAlg_UserProfile(mesh, ocf);
+
+            // Make sure the file is closed before exiting the function
+            if(ocf.is_open())
+            {
+               ocf.close();
+            }
+         }
+      }
+   }
+}
diff --git a/contrib/Netgen/libsrc/meshing/bcfunctions.hpp b/contrib/Netgen/libsrc/meshing/bcfunctions.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..593838afe83a6ef85f85962030d97bb47831c04f
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/bcfunctions.hpp
@@ -0,0 +1,53 @@
+#ifndef FILE_BCFUNCTIONS
+#define FILE_BCFUNCTIONS
+
+// Philippose - 14/03/2009
+// Auxiliary functions for OCC Geometry
+// Use this file and the corresponding ".cpp" 
+// file to add miscellaneous functionality 
+// to the OpenCascade Geometry support in Netgen
+namespace netgen
+{
+   /*! \brief Automatically assign boundary conditions for meshes
+
+       This function allows the boundary condition numbers of a 
+       mesh created in Netgen to be automatically assigned based on 
+       the colours of each face.
+
+       Currently, two algorithms are utilised to assign the BC Properties:
+       1. Automatic assignment using a user defined colour profile file 
+          which defines which RGB colours are to be assigned to which 
+          BC Property number
+          - A default profile file exists in the Netgen folder called 
+            "netgen.ocf"
+       
+       2. The second algorithm uses the following automated algorithm:
+          - Extract all the colours present in the mesh
+          - Use colour index 0 (zero) for all faces with no colour defined
+          - Calculate the number of faces of the surface mesh for each colour
+          - Sort the number of surface elements in ascending order, with the 
+            colour indices as a slave
+          - Use the indices of the sorted array as the BC property number
+
+          Example: If there are 3 colours, present in the mesh and the number 
+          of surface elements for each colour are:
+          - Colour 0: 8500
+          - Colour 1: 120
+          - Colour 2: 2200
+          - Colour 3: 575
+
+          The above is sorted in ascending order and assigned as BC Properties:
+          - BC Prop 0: 120  : Colour 1
+          - BC Prop 1: 575  : Colour 3
+          - BC Prop 2: 2200 : Colour 2
+          - BC Prop 3: 8500 : Colour 0 (no colour defined)
+   */
+   //extern void OCCAutoColourBcProps(Mesh & mesh, OCCGeometry & occgeometry, const char *occcolourfile);
+   extern void AutoColourBcProps(Mesh & mesh, const char *bccolourfile);
+
+   extern void GetFaceColours(Mesh & mesh, Array<Vec3d> & face_colours);
+
+   extern bool ColourMatch(Vec3d col1, Vec3d col2, double eps = 2.5e-05);
+}
+#endif
+
diff --git a/contrib/Netgen/libsrc/meshing/bisect.cpp b/contrib/Netgen/libsrc/meshing/bisect.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..afa3400dacdf205f458fc6fc9d2bd92d308a42ce
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/bisect.cpp
@@ -0,0 +1,4071 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+#define noDEBUG
+
+
+namespace netgen
+{
+  //#include "../interface/writeuser.hpp"
+  class MarkedTet;
+  class MarkedPrism;
+  class MarkedIdentification;
+  class MarkedTri;
+  class MarkedQuad;
+  
+  typedef MoveableArray<MarkedTet> T_MTETS;
+  typedef MoveableArray<MarkedPrism> T_MPRISMS;
+  typedef MoveableArray<MarkedIdentification> T_MIDS;
+  typedef MoveableArray<MarkedTri> T_MTRIS;
+  typedef MoveableArray<MarkedQuad> T_MQUADS;
+
+  
+  
+  class MarkedTet
+  {
+  public:
+    /// pnums of tet
+    PointIndex pnums[4];
+    /// material number
+    int matindex;
+    /// element marked for refinement
+    /// marked = 1: marked by element marker, marked = 2 due to closure
+    unsigned int marked:2;
+    /// flag of Arnold-Mukherjee algorithm
+    unsigned int flagged:1;
+    /// tetedge (local coordinates 0..3)
+    unsigned int tetedge1:3;
+    unsigned int tetedge2:3;
+    // marked edge of faces
+    // face_j : face without node j,
+    // mark_k : edge without node k
+    
+    char faceedges[4];
+    // unsigned char faceedges[4];
+    bool incorder;
+    unsigned int order:6;
+
+    MarkedTet()
+    { 
+      for (int i = 0; i < 4; i++) { faceedges[i] = 255; }
+    }
+  };
+
+  ostream & operator<< (ostream & ost, const MarkedTet & mt)
+  {
+    for(int i=0; i<4; i++)
+      ost << mt.pnums[i] << " ";
+
+    ost << mt.matindex << " " << int(mt.marked) << " " << int(mt.flagged) << " " << int(mt.tetedge1) << " " << int(mt.tetedge2) << " ";
+    
+    ost << "faceedges = ";
+    for(int i=0; i<4; i++)
+      ost << int(mt.faceedges[i]) << " ";
+
+    ost << " order = ";
+    ost << mt.incorder << " " << int(mt.order) << "\n";
+    return ost;
+  }
+  istream & operator>> (istream & ost, MarkedTet & mt)
+  {
+    for(int i=0; i<4; i++)
+      ost >> mt.pnums[i];
+
+    ost >> mt.matindex;
+
+    int auxint;
+    ost >> auxint;
+    mt.marked = auxint;
+    ost >> auxint;
+    mt.flagged = auxint;
+    ost >> auxint;
+    mt.tetedge1 = auxint;
+    ost >> auxint;
+    mt.tetedge2 = auxint;
+    
+    char auxchar;
+
+    for(int i=0; i<4; i++)
+      {
+	ost >> auxchar;
+	mt.faceedges[i] = auxchar;
+      }
+
+    ost >> mt.incorder;
+    ost >> auxint;
+    mt.order = auxint;
+    return ost;
+  }
+
+  class MarkedPrism
+  {
+  public:
+    /// 6 point numbers
+    PointIndex pnums[6];
+    /// material number
+    int matindex;
+    /// marked for refinement
+    int marked;
+    /// edge without node k (0,1,2)
+    int markededge;
+
+    bool incorder;
+    unsigned int order:6;
+  };
+
+  
+  ostream & operator<< (ostream & ost, const MarkedPrism & mp)
+  {
+    for(int i=0; i<6; i++)
+      ost << mp.pnums[i] << " ";
+
+    ost << mp.matindex << " " << mp.marked << " " << mp.markededge << " " << mp.incorder << " " << int(mp.order) << "\n";
+    return ost;
+  }
+  istream & operator>> (istream & ist, MarkedPrism & mp)
+  {
+    for(int i=0; i<6; i++)
+      ist >> mp.pnums[i];
+
+    ist >> mp.matindex >> mp.marked >> mp.markededge >> mp.incorder;
+    int auxint;
+    ist >> auxint;
+    mp.order = auxint;
+    return ist;
+  }
+
+
+  class MarkedIdentification
+  {
+  public:
+    // number of points of one face (3 or 4)
+    int np;
+    /// 6 or 8 point numbers
+    PointIndex pnums[8];
+    /// marked for refinement
+    int marked;
+    /// edge starting with node k (0,1,2, or 3)
+    int markededge;
+
+    bool incorder;
+    unsigned int order:6;
+  };
+    
+  
+  ostream & operator<< (ostream & ost, const MarkedIdentification & mi)
+  {
+    ost << mi.np << " ";
+    for(int i=0; i<2*mi.np; i++)
+      ost << mi.pnums[i] << " ";
+    ost << mi.marked << " " << mi.markededge << " " << mi.incorder << " " << int(mi.order) << "\n";
+    return ost;
+  }
+  istream & operator>> (istream & ist, MarkedIdentification & mi)
+  {
+    ist >> mi.np;
+    for(int i=0; i<2*mi.np; i++)
+      ist >> mi.pnums[i];
+    ist >> mi.marked >> mi.markededge >> mi.incorder;
+    int auxint;
+    ist >> auxint;
+    mi.order = auxint;
+    return ist;
+  }
+  
+
+
+
+
+  class MarkedTri
+  {
+  public:
+    /// three point numbers
+    PointIndex pnums[3];
+    /// three geominfos
+    PointGeomInfo pgeominfo[3];
+    /// marked for refinement
+    int marked;
+    /// edge without node k
+    int markededge;
+    /// surface id
+    int surfid;
+
+    bool incorder;
+    unsigned int order:6;
+  };
+  
+  ostream & operator<< (ostream & ost, const MarkedTri & mt)
+  {
+    for(int i=0; i<3; i++)
+      ost << mt.pnums[i] << " ";
+    for(int i=0; i<3; i++)
+      ost << mt.pgeominfo[i] << " ";
+    ost << mt.marked << " " << mt.markededge << " " << mt.surfid << " " << mt.incorder << " " << int(mt.order) << "\n";
+    return ost;
+  } 
+  istream & operator>> (istream & ist, MarkedTri & mt)
+  {
+    for(int i=0; i<3; i++)
+      ist >> mt.pnums[i];
+    for(int i=0; i<3; i++)
+      ist >> mt.pgeominfo[i];
+    ist >> mt.marked >> mt.markededge >> mt.surfid >> mt.incorder;
+    int auxint;
+    ist >> auxint;
+    mt.order = auxint;
+    return ist;
+  }
+    
+
+
+  class MarkedQuad
+  {
+  public:
+    /// point numbers
+    PointIndex pnums[4];
+    ///
+    PointGeomInfo pgeominfo[4];
+    /// marked for refinement
+    int marked;
+    /// marked edge: 0/2 = vertical, 1/3 = horizontal
+    int markededge;
+    /// surface id
+    int surfid;
+
+    bool incorder;
+    unsigned int order:6;
+  };
+
+  ostream & operator<< (ostream & ost, const MarkedQuad & mt)
+  {
+    for(int i=0; i<4; i++)
+      ost << mt.pnums[i] << " ";
+    for(int i=0; i<4; i++)
+      ost << mt.pgeominfo[i] << " ";
+    ost << mt.marked << " " << mt.markededge << " " << mt.surfid << " " << mt.incorder << " " << int(mt.order) << "\n";
+    return ost;
+  } 
+  istream & operator>> (istream & ist, MarkedQuad & mt)
+  {
+    for(int i=0; i<4; i++)
+      ist >> mt.pnums[i];
+    for(int i=0; i<4; i++)
+      ist >> mt.pgeominfo[i];
+    ist >> mt.marked >> mt.markededge >> mt.surfid >> mt.incorder;
+    int auxint;
+    ist >> auxint;
+    mt.order = auxint;
+    return ist;
+  }
+
+
+
+
+  void PrettyPrint(ostream & ost, const MarkedTet & mt)
+  {
+    int te1 = mt.tetedge1;
+    int te2 = mt.tetedge2;
+    int order = mt.order;
+
+    ost << "MT: " << mt.pnums[0] << " - " << mt.pnums[1] << " - " 
+	<< mt.pnums[2] << " - " << mt.pnums[3] << endl
+	<< "marked edge: " << te1 << " - " << te2
+	<< ", order = " << order << endl;
+    //for (int k = 0; k < 4; k++)
+    //  ost << int(mt.faceedges[k]) << "  ";
+    for (int k = 0; k < 4; k++)
+      {
+	ost << "face";
+	for (int j=0; j<4; j++)
+	  if(j != k)
+	    ost << " " << mt.pnums[j];
+	for(int i=0; i<3; i++)
+	  for(int j=i+1; j<4; j++)
+	    if(i != k && j != k && int(mt.faceedges[k]) == 6-k-i-j)
+	      ost << " marked edge " << mt.pnums[i] << " " << mt.pnums[j] << endl;
+      }
+    ost << endl;
+  }
+
+
+
+
+  int BTSortEdges (const Mesh & mesh,
+		   const Array< Array<int,PointIndex::BASE>* > & idmaps,
+		   INDEX_2_CLOSED_HASHTABLE<int> & edgenumber)
+  {
+    PrintMessage(4,"sorting ... ");
+
+    //  if (mesh.PureTetMesh())
+    if (1)
+      {
+	// new, fast version
+      
+	Array<INDEX_2> edges;
+	Array<int> eclasses;
+      
+	int i, j, k;
+	int cntedges = 0;
+	int go_on;
+	int ned(0);
+      
+	// enumerate edges:
+	for (i = 1; i <= mesh.GetNE(); i++)
+	  {
+	    const Element & el = mesh.VolumeElement (i);
+	    static int tetedges[6][2] =
+	      { { 1, 2 },
+		{ 1, 3 },
+		{ 1, 4 },
+		{ 2, 3 },
+		{ 2, 4 },
+		{ 3, 4 } } ;
+	    static int prismedges[9][2] =
+	      { { 1, 2 },
+		{ 1, 3 },
+		{ 2, 3 },
+		{ 4, 5 },
+		{ 4, 6 },
+		{ 5, 6 },
+		{ 1, 4 },
+		{ 2, 5 },
+		{ 3, 6 } };
+	    int pyramidedges[6][2] =
+	      { { 1, 2 },
+		{ 3, 4 },
+		{ 1, 5 },
+		{ 2, 5 },
+		{ 3, 5 },
+		{ 4, 5 } };
+	  
+	    int (*tip)[2] = NULL;
+	  
+	    switch (el.GetType())
+	      {
+	      case TET:
+	      case TET10:
+		{
+		  tip = tetedges;
+		  ned = 6;
+		  break;
+		}
+	      case PRISM:
+	      case PRISM12:
+		{
+		  tip = prismedges;
+		  ned = 6;
+		  break;
+		}
+	      case PYRAMID:
+		{
+		  tip = pyramidedges;
+		  ned = 6;
+		  break;
+		}
+              default:
+                throw NgException("Bisect, element type not handled in switch");
+	      }
+	      
+	    for (j = 0; j < ned; j++)
+	      {
+		INDEX_2 i2(el.PNum(tip[j][0]), el.PNum(tip[j][1]));
+		i2.Sort();
+		//(*testout) << "edge " << i2 << endl;
+		if (!edgenumber.Used(i2))
+		  {
+		    cntedges++;
+		    edges.Append (i2);
+		    edgenumber.Set(i2, cntedges);
+		  }
+	      }
+	  }
+      
+	// additional surface edges:
+	for (i = 1; i <= mesh.GetNSE(); i++)
+	  {
+	    const Element2d & el = mesh.SurfaceElement (i);
+	    static int trigedges[3][2] =
+	      { { 1, 2 },
+		{ 2, 3 },
+		{ 3, 1 } };
+
+	    static int quadedges[4][2] =
+	      { { 1, 2 },
+		{ 2, 3 },
+		{ 3, 4 },
+		{ 4, 1 } };
+
+
+	    int (*tip)[2] = NULL;
+	  
+	    switch (el.GetType())
+	      {
+	      case TRIG:
+	      case TRIG6:
+		{
+		  tip = trigedges;
+		  ned = 3;
+		  break;
+		}
+	      case QUAD:
+	      case QUAD6:
+		{
+		  tip = quadedges;
+		  ned = 4;
+		  break;
+		}
+	      default:
+		{
+		  cerr << "Error: Sort for Bisect, SE has " << el.GetNP() << " points" << endl;
+		  ned = 0;
+		}
+	      }
+	      
+	    for (j = 0; j < ned; j++)
+	      {
+		INDEX_2 i2(el.PNum(tip[j][0]), el.PNum(tip[j][1]));
+		i2.Sort();
+		if (!edgenumber.Used(i2))
+		  {
+		    cntedges++;
+		    edges.Append (i2);
+		    edgenumber.Set(i2, cntedges);
+		  }
+	      }
+	  }
+
+
+
+
+
+	eclasses.SetSize (cntedges);
+	for (i = 1; i <= cntedges; i++)
+	  eclasses.Elem(i) = i;
+
+	// identify edges in element stack
+	do
+	  {
+	    go_on = 0;
+	    for (i = 1; i <= mesh.GetNE(); i++)
+	      {
+		const Element & el = mesh.VolumeElement (i);	     
+	      
+		if (el.GetType() != PRISM &&
+		    el.GetType() != PRISM12 &&
+		    el.GetType() != PYRAMID)
+		  continue;
+
+		int prismpairs[3][4] =
+		  { { 1, 2, 4, 5 },
+		    { 2, 3, 5, 6 },
+		    { 1, 3, 4, 6 } };
+	      
+		int pyramidpairs[3][4] =
+		  { { 1, 2, 4, 3 },
+		    { 1, 5, 4, 5 },
+		    { 2, 5, 3, 5 } };
+                
+		int (*pairs)[4] = NULL;
+		switch (el.GetType())
+		  {
+		  case PRISM:
+		  case PRISM12:
+		    {
+		      pairs = prismpairs;
+		      break;
+		    }
+		  case PYRAMID:
+		    {
+		      pairs = pyramidpairs;
+		      break;
+		    }
+                  default:
+                    throw NgException("Bisect, element type not handled in switch, 2");
+		  }
+
+		for (j = 0; j < 3; j++)
+		  {
+		    INDEX_2 e1 (el.PNum(pairs[j][0]), 
+				el.PNum(pairs[j][1]));
+		    INDEX_2 e2 (el.PNum(pairs[j][2]), 
+				el.PNum(pairs[j][3]));
+		    e1.Sort();
+		    e2.Sort();
+		      
+		    int eclass1 = edgenumber.Get (e1);
+		    int eclass2 = edgenumber.Get (e2);
+
+		    //		  (*testout) << "identify edges " << eclass1 << "-" << eclass2 << endl;
+
+		    if (eclasses.Get(eclass1) >
+			eclasses.Get(eclass2))
+		      {
+			eclasses.Elem(eclass1) = 
+			  eclasses.Get(eclass2);
+			go_on = 1;
+		      }
+		    else if (eclasses.Get(eclass2) >
+			     eclasses.Get(eclass1))
+		      {
+			eclasses.Elem(eclass2) = 
+			  eclasses.Get(eclass1);
+			go_on = 1;
+		      }
+		  }
+	      }
+
+	    for(SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++)
+	      {
+		const Element2d & el2d = mesh[sei];
+
+		for(i = 0; i < el2d.GetNP(); i++)
+		  {
+		    INDEX_2 e1(el2d[i], el2d[(i+1) % el2d.GetNP()]);
+		    e1.Sort();
+		    INDEX_2 e2;
+		    
+		    for(k = 0; k < idmaps.Size(); k++)
+		      {
+			e2.I1() = (*idmaps[k])[e1.I1()];
+			e2.I2() = (*idmaps[k])[e1.I2()];
+			
+			if(e2.I1() == 0 || e2.I2() == 0 ||
+			   e1.I1() == e2.I1() || e1.I2() == e2.I2())
+			  continue;
+			
+			e2.Sort();
+			if(!edgenumber.Used(e2))
+			  continue;
+			
+
+			int eclass1 = edgenumber.Get (e1);
+			int eclass2 = edgenumber.Get (e2);
+			
+			if (eclasses.Get(eclass1) >
+			    eclasses.Get(eclass2))
+			  {
+			    eclasses.Elem(eclass1) = 
+			      eclasses.Get(eclass2);
+
+
+			    go_on = 1;
+			  }
+			else if (eclasses.Get(eclass2) >
+				 eclasses.Get(eclass1))
+			  {
+			    eclasses.Elem(eclass2) = 
+			      eclasses.Get(eclass1);
+			    go_on = 1;
+			  }
+		      }		      
+		  }
+		
+	      }
+
+	  }
+	while (go_on);
+
+// 	for (i = 1; i <= cntedges; i++)
+// 	  {
+// 	    (*testout) << "edge " << i << ": " 
+// 		       << edges.Get(i).I1() << "-" << edges.Get(i).I2()
+// 		       << ", class = " << eclasses.Get(i) << endl;
+// 	  }
+	
+	// compute classlength:
+	Array<double> edgelength(cntedges);
+
+	/*
+	for (i = 1; i <= cntedges; i++)
+	  edgelength.Elem(i) = 1e20;
+	*/
+
+	for (i = 1; i <= cntedges; i++)
+	  {
+	    INDEX_2 edge = edges.Get(i);
+	    double elen = Dist (mesh.Point(edge.I1()),
+				mesh.Point(edge.I2()));
+	    edgelength.Elem (i) = elen;
+	  }
+
+	/*
+	  for (i = 1; i <= mesh.GetNE(); i++)
+	  {
+	  const Element & el = mesh.VolumeElement (i);
+	  
+	  if (el.GetType() == TET)
+	  {
+	  for (j = 1; j <= 3; j++)
+	  for (k = j+1; k <= 4; k++)
+	  {
+	  INDEX_2 i2(el.PNum(j), el.PNum(k));
+	  i2.Sort();
+		    
+	  int enr = edgenumber.Get(i2);
+	  double elen = Dist (mesh.Point (i2.I1()), mesh.Point (i2.I2()));
+	  if (elen < edgelength.Get(enr))
+	  edgelength.Set (enr, elen);
+	  }
+	  }
+	  else if (el.GetType() == PRISM)
+	  {
+	  for (j = 1; j <= 3; j++)
+	  {
+	  k = (j % 3) + 1;
+		  
+	  INDEX_2 i2(el.PNum(j), el.PNum(k));
+	  i2.Sort();
+		  
+	  int enr = edgenumber.Get(i2);
+	  double elen = Dist (mesh.Point (i2.I1()), mesh.Point (i2.I2()));
+	  if (elen < edgelength.Get(enr))
+	  edgelength.Set (enr, elen);
+		  
+	  i2 = INDEX_2(el.PNum(j+3), el.PNum(k+3));
+	  i2.Sort();
+		  
+	  enr = edgenumber.Get(i2);
+	  elen = Dist (mesh.Point (i2.I1()), mesh.Point (i2.I2()));
+	  if (elen < edgelength.Get(enr))
+	  edgelength.Set (enr, elen);
+		  
+	  if (!edgenumber.Used(i2))
+	  {
+	  cntedges++;
+	  edgenumber.Set(i2, cntedges);
+	  }
+	  i2 = INDEX_2(el.PNum(j), el.PNum(j+3));
+	  i2.Sort();
+		  
+	  enr = edgenumber.Get(i2);
+	  elen = Dist (mesh.Point (i2.I1()), mesh.Point (i2.I2()));
+	  if (elen < edgelength.Get(enr))
+	  edgelength.Set (enr, elen);
+	  }
+	  }
+	  }
+	*/
+
+      
+	for (i = 1; i <= cntedges; i++)
+	  {
+	    if (eclasses.Get(i) != i)
+	      {
+		if (edgelength.Get(i) < edgelength.Get(eclasses.Get(i)))
+		  edgelength.Elem(eclasses.Get(i)) = edgelength.Get(i);
+		edgelength.Elem(i) = 1e20;
+	      }
+	  }
+
+
+	TABLE<int> eclasstab(cntedges);
+	for (i = 1; i <= cntedges; i++)
+	  eclasstab.Add1 (eclasses.Get(i), i);
+
+
+	// sort edges:
+	Array<int> sorted(cntedges);
+      
+	QuickSort (edgelength, sorted);
+      
+	int cnt = 0;
+	for (i = 1; i <= cntedges; i++)
+	  {
+	    int ii = sorted.Get(i);
+	    for (j = 1; j <= eclasstab.EntrySize(ii); j++)
+	      {
+		cnt++;
+		edgenumber.Set (edges.Get(eclasstab.Get(ii, j)), cnt); 
+	      }
+	  }
+	return cnt;
+      }
+
+    else
+    
+      {
+	// old version
+      
+	int i, j;
+	int cnt = 0;
+	int found;
+	double len2, maxlen2;
+	INDEX_2 ep;
+      
+	// sort edges by length, parallel edges (on prisms)
+	// are added in blocks
+      
+	do
+	  {
+	    found = 0;
+	    maxlen2 = 1e30;
+	  
+	    for (i = 1; i <= mesh.GetNE(); i++)
+	      {
+		const Element & el = mesh.VolumeElement (i);
+		int ned;
+		int tetedges[6][2] =
+		  { { 1, 2 },
+		    { 1, 3 },
+		    { 1, 4 },
+		    { 2, 3 },
+		    { 2, 4 },
+		    { 3, 4 } };
+		int prismedges[6][2] =
+		  { { 1, 2 },
+		    { 1, 3 },
+		    { 2, 4 },
+		    { 4, 5 },
+		    { 4, 6 },
+		    { 5, 6 } };
+		int pyramidedges[6][2] =
+		  { { 1, 2 },
+		    { 3, 4 },
+		    { 1, 5 },
+		    { 2, 5 },
+		    { 3, 5 },
+		    { 4, 5 } };
+
+		int (*tip)[2];
+
+		switch (el.GetType())
+		  {
+		  case TET:
+		    {
+		      tip = tetedges;
+		      ned = 6;
+		      break;
+		    }
+		  case PRISM:
+		    {
+		      tip = prismedges;
+		      ned = 6;
+		      break;
+		    }
+		  case PYRAMID:
+		    {
+		      tip = pyramidedges;
+		      ned = 6;
+		      break;
+		    }
+                  default:
+                    throw NgException("Bisect, element type not handled in switch, 3");
+		  }
+	      
+		for (j = 0; j < ned; j++)
+		  {
+		    INDEX_2 i2(el.PNum(tip[j][0]), el.PNum(tip[j][1]));
+		    i2.Sort();
+		    if (!edgenumber.Used(i2))
+		      {
+			len2 = Dist (mesh.Point (i2.I1()),
+				     mesh.Point (i2.I2()));
+			if (len2 < maxlen2)
+			  {
+			    maxlen2 = len2;
+			    ep = i2;
+			    found = 1;
+			  }
+		      }
+		  }
+	      }
+	    if (found)
+	      {
+		cnt++;
+		edgenumber.Set (ep, cnt);
+	      
+	      
+		// find connected edges:
+		int go_on = 0;
+		do
+		  {
+		    go_on = 0;
+		    for (i = 1; i <= mesh.GetNE(); i++)
+		      {
+			const Element & el = mesh.VolumeElement (i);	      
+			if (el.GetNP() != 6) continue;
+
+			int prismpairs[3][4] =
+			  { { 1, 2, 4, 5 },
+			    { 2, 3, 5, 6 },
+			    { 1, 3, 4, 6 } };
+
+			int pyramidpairs[3][4] =
+			  { { 1, 2, 4, 3 },
+			    { 1, 5, 4, 5 },
+			    { 2, 5, 3, 5 } };
+		      
+			int (*pairs)[4];
+			switch (el.GetType())
+			  {
+			  case PRISM:
+			    {
+			      pairs = prismpairs;
+			      break;
+			    }
+			  case PYRAMID:
+			    {
+			      pairs = pyramidpairs;
+			      break;
+			    }
+                          default:
+                            throw NgException("Bisect, element type not handled in switch, 3a");
+			  }
+
+			for (j = 0; j < 3; j++)
+			  {
+			    INDEX_2 e1 (el.PNum(pairs[j][0]), 
+					el.PNum(pairs[j][1]));
+			    INDEX_2 e2 (el.PNum(pairs[j][2]), 
+					el.PNum(pairs[j][3]));
+			    e1.Sort();
+			    e2.Sort();
+			  
+			    int used1 = edgenumber.Used (e1);
+			    int used2 = edgenumber.Used (e2);
+			  
+			    if (used1 && !used2)
+			      {
+				cnt++;
+				edgenumber.Set (e2, cnt);
+				go_on = 1;
+			      }
+			    if (used2 && !used1)
+			      {
+				cnt++;
+				edgenumber.Set (e1, cnt);
+				go_on = 1;
+			      }
+			  }
+		      }
+		  }
+		while (go_on);
+	      }
+	  }
+	while (found);
+
+	return cnt;
+      }
+  }
+
+
+
+
+  void BTDefineMarkedTet (const Element & el,
+			  INDEX_2_CLOSED_HASHTABLE<int> & edgenumber,
+			  MarkedTet & mt)
+  {
+    int i, j, k;
+    for (i = 0; i < 4; i++)
+      mt.pnums[i] = el[i];
+
+    mt.marked = 0;
+    mt.flagged = 0;
+
+    mt.incorder = 0;
+    mt.order = 1;
+  
+    int val = 0;
+    // find marked edge of tet:
+    for (i = 0; i < 3; i++)
+      for (j = i+1; j < 4; j++)
+	{
+	  INDEX_2 i2(mt.pnums[i], mt.pnums[j]);
+	  i2.Sort();
+	  int hval = edgenumber.Get(i2);
+	  if (hval > val)
+	    {
+	      val = hval;
+	      mt.tetedge1 = i;
+	      mt.tetedge2 = j;    
+	    }
+	}
+
+
+    // find marked edges of faces:
+    for (k = 0; k < 4; k++)
+      {
+	val = 0;
+	for (i = 0; i < 3; i++)
+	  for (j = i+1; j < 4; j++)
+	    if (i != k && j != k)
+	      {
+		INDEX_2 i2(mt.pnums[i], mt.pnums[j]);
+		i2.Sort();
+		int hval = edgenumber.Get(i2);
+		if (hval > val)
+		  {
+		    val = hval;
+                    int hi = 6 - k - i - j;
+                    mt.faceedges[k] = char(hi);
+		  }
+	      }
+      }
+  }
+
+
+
+
+  void BTDefineMarkedPrism (const Element & el,
+			    INDEX_2_CLOSED_HASHTABLE<int> & edgenumber,
+			    MarkedPrism & mp)
+  {
+    int i, j;
+
+    if (el.GetType() == PRISM ||
+	el.GetType() == PRISM12)
+      {
+	for (i = 0; i < 6; i++)
+	  mp.pnums[i] = el[i];
+      }
+    else if (el.GetType() == PYRAMID)
+      {
+	static int map[6] = 
+	  { 1, 2, 5, 4, 3, 5 };
+	for (i = 0; i < 6; i++)
+	  mp.pnums[i] = el.PNum(map[i]);
+      }
+    else if (el.GetType() == TET ||
+	     el.GetType() == TET10)
+      {
+	static int map[6] = 
+	  { 1, 4, 3, 2, 4, 3 };
+	for (i = 0; i < 6; i++)
+	  mp.pnums[i] = el.PNum(map[i]);
+      
+      }
+    else
+      {
+	PrintSysError ("Define marked prism called for non-prism and non-pyramid");
+      }
+  
+
+
+    mp.marked = 0;
+
+    mp.incorder = 0;
+    mp.order = 1;
+
+    int val = 0;
+    for (i = 0; i < 2; i++)
+      for (j = i+1; j < 3; j++)
+	{
+	  INDEX_2 i2(mp.pnums[i], mp.pnums[j]);
+	  i2.Sort();
+	  int hval = edgenumber.Get(i2);
+	  if (hval > val)
+	    {
+	      val = hval;
+	      mp.markededge = 3 - i - j;
+	    }
+	}
+  }
+
+
+
+  bool BTDefineMarkedId(const Element2d & el, 
+			INDEX_2_CLOSED_HASHTABLE<int> & edgenumber, 
+			const Array<int,PointIndex::BASE> & idmap,
+			MarkedIdentification & mi)
+  {
+
+    bool identified = true;
+    mi.np = el.GetNP();
+    int min1(0),min2(0);
+    for(int j = 0; identified && j < mi.np; j++)
+      {
+	mi.pnums[j] = el[j];
+	mi.pnums[j+mi.np] = idmap[el[j]];
+
+	if(j == 0 || el[j] < min1)
+	  min1 = el[j];
+	if(j == 0 || mi.pnums[j+mi.np] < min2)
+	  min2 = mi.pnums[j+mi.np];
+
+	identified = (mi.pnums[j+mi.np] != 0 && mi.pnums[j+mi.np] != mi.pnums[j]);
+      }
+
+    identified = identified && (min1 < min2);
+
+    if(identified)
+      {
+	mi.marked = 0;
+	
+	mi.incorder = 0;
+	mi.order = 1;
+
+	int val = 0;
+	for (int i = 0; i < mi.np; i++)
+	  {
+	    INDEX_2 i2(mi.pnums[i], mi.pnums[(i+1)%mi.np]);
+	    i2.Sort();
+	    int hval = edgenumber.Get(i2);
+	    if (hval > val)
+	      {
+		val = hval;
+		mi.markededge = i;
+	      }
+	  }
+      }
+
+    return identified;
+  }
+
+
+  void BTDefineMarkedTri (const Element2d & el,
+			  INDEX_2_CLOSED_HASHTABLE<int> & edgenumber,
+			  MarkedTri & mt)
+  {
+    int i, j;
+    for (i = 0; i < 3; i++)
+      {
+	mt.pnums[i] = el[i];
+	mt.pgeominfo[i] = el.GeomInfoPi (i+1);
+      }
+
+    mt.marked = 0;
+    mt.surfid = el.GetIndex();
+
+    mt.incorder = 0;
+    mt.order = 1;
+
+    int val = 0;
+    for (i = 0; i < 2; i++)
+      for (j = i+1; j < 3; j++)
+	{
+	  INDEX_2 i2(mt.pnums[i], mt.pnums[j]);
+	  i2.Sort();
+	  int hval = edgenumber.Get(i2);
+	  if (hval > val)
+	    {
+	      val = hval;
+	      mt.markededge = 3 - i - j;
+	    }
+	}
+  }
+  
+
+  
+  void PrettyPrint(ostream & ost, const MarkedTri & mt)
+  {
+    ost << "MarkedTrig: " << endl;
+    ost << "  pnums = "; for (int i=0; i<3; i++) ost << mt.pnums[i] << " "; ost << endl; 
+    ost << "  marked = " << mt.marked << ", markededge=" << mt.markededge << endl;
+    for(int i=0; i<2; i++)
+      for(int j=i+1; j<3; j++)
+	if(mt.markededge == 3-i-j)
+	  ost << "  marked edge pnums = " << mt.pnums[i] << " " << mt.pnums[j] << endl;
+  }
+
+
+  void PrettyPrint(ostream & ost, const MarkedQuad & mq)
+  {
+    ost << "MarkedQuad: " << endl;
+    ost << "  pnums = "; for (int i=0; i<4; i++) ost << mq.pnums[i] << " "; ost << endl; 
+    ost << "  marked = " << mq.marked << ", markededge=" << mq.markededge << endl;
+  }
+
+
+
+
+
+  void BTDefineMarkedQuad (const Element2d & el,
+			   INDEX_2_CLOSED_HASHTABLE<int> & edgenumber,
+			   MarkedQuad & mq)
+  {
+    int i;
+    for (i = 0; i < 4; i++)
+      mq.pnums[i] = el[i];
+    Swap (mq.pnums[2], mq.pnums[3]);  
+
+    mq.marked = 0;
+    mq.markededge = 0;
+    mq.surfid = el.GetIndex();
+  }
+
+
+
+
+  // mark elements due to local h
+  int BTMarkTets (T_MTETS & mtets,
+		  T_MPRISMS & mprisms,
+		  const Mesh & mesh)
+  {
+    int marked = 0;
+
+    int np = mesh.GetNP();
+    Vector hv(np);
+    for (int i = 0; i < np; i++)
+      hv(i) = mesh.GetH (mesh.Point(i+1));
+
+    double hfac = 1;
+  
+    for (int step = 1; step <= 2; step++)
+      {
+	for (int i = 1; i <= mtets.Size(); i++)
+	  {
+	    double h = 0;
+	  
+	    for (int j = 0; j < 3; j++)
+	      for (int k = j+1; k < 4; k++)
+		{
+		  const Point<3> & p1 = mesh.Point (mtets.Get(i).pnums[j]);
+		  const Point<3> & p2 = mesh.Point (mtets.Get(i).pnums[k]);
+		  double hh = Dist2 (p1, p2);
+		  if (hh > h) h = hh;
+		}
+	    h = sqrt (h);
+	  
+	    double hshould = 1e10;
+	    for (int j = 0; j < 4; j++)
+	      {
+		double hi = hv (mtets.Get(i).pnums[j]-1);
+		if (hi < hshould)
+		  hshould = hi;
+	      }
+	  
+	
+	    if (step == 1)
+	      {
+		if (h / hshould > hfac)
+		  hfac = h / hshould;
+	      }
+	    else
+	      {
+		if (h > hshould * hfac)
+		  {
+		    mtets.Elem(i).marked = 1;
+		    marked = 1;
+		  }
+		else
+		  mtets.Elem(i).marked = 0;
+	      }
+	  
+	  }
+	for (int i = 1; i <= mprisms.Size(); i++)
+	  {
+	    double h = 0;
+	  
+	    for (int j = 0; j < 2; j++)
+	      for (int k = j+1; k < 3; k++)
+		{
+		  const Point<3> & p1 = mesh.Point (mprisms.Get(i).pnums[j]);
+		  const Point<3> & p2 = mesh.Point (mprisms.Get(i).pnums[k]);
+		  double hh = Dist2 (p1, p2);
+		  if (hh > h) h = hh;
+		}
+	    h = sqrt (h);
+	  
+	    double hshould = 1e10;
+	    for (int j = 0; j < 6; j++)
+	      {
+		double hi = hv (mprisms.Get(i).pnums[j]-1);
+		if (hi < hshould)
+		  hshould = hi;
+	      }
+	  
+	
+	    if (step == 1)
+	      {
+		if (h / hshould > hfac)
+		  hfac = h / hshould;
+	      }
+	    else
+	      {
+		if (h > hshould * hfac)
+		  {
+		    mprisms.Elem(i).marked = 1;
+		    marked = 1;
+		  }
+		else
+		  mprisms.Elem(i).marked = 0;
+	      }
+	  
+	  }
+
+
+
+	if (step == 1)
+	  {
+	    if (hfac > 2)
+	      hfac /= 2;
+	    else
+	      hfac = 1;
+	  }
+
+      }
+    return marked;
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  void BTBisectTet (const MarkedTet & oldtet, int newp, 
+		    MarkedTet & newtet1, MarkedTet & newtet2)
+  {
+#ifdef DEBUG
+    *testout << "bisect tet " << oldtet << endl;
+#endif    
+    
+    int i, j, k;
+  
+  
+    // points vis a vis from tet-edge
+    int vis1, vis2;
+    vis1 = 0;
+    while (vis1 == oldtet.tetedge1 || vis1 == oldtet.tetedge2)
+      vis1++;
+    vis2 = 6 - vis1 - oldtet.tetedge1 - oldtet.tetedge2;
+
+
+    
+
+
+    // is tet of type P ?
+    int istypep = 0;
+    for (i = 0; i < 4; i++)
+      {
+	int cnt = 0;
+	for (j = 0; j < 4; j++)
+	  if (oldtet.faceedges[j] == i)
+	    cnt++;
+	if (cnt == 3)
+	  istypep = 1;
+      }
+
+
+  
+    for (i = 0; i < 4; i++)
+      {
+	newtet1.pnums[i] = oldtet.pnums[i];
+	newtet2.pnums[i] = oldtet.pnums[i];
+      }
+    newtet1.flagged = istypep && !oldtet.flagged;
+    newtet2.flagged = istypep && !oldtet.flagged;
+
+    int nm = oldtet.marked - 1;
+    if (nm < 0) nm = 0;
+    newtet1.marked = nm;
+    newtet2.marked = nm;
+
+#ifdef DEBUG
+    *testout << "newtet1,before = " << newtet1 << endl;
+    *testout << "newtet2,before = " << newtet2 << endl;
+#endif
+
+    for (i = 0; i < 4; i++)
+      {
+	if (i == oldtet.tetedge1)
+	  {
+	    newtet2.pnums[i] = newp;
+	    newtet2.faceedges[i] = oldtet.faceedges[i];  // inherited face
+	    newtet2.faceedges[vis1] = i;        // cut faces
+	    newtet2.faceedges[vis2] = i;
+
+	    j = 0;
+	    while (j == i || j == oldtet.faceedges[i])
+	      j++;
+	    k = 6 - i - oldtet.faceedges[i] - j;
+	    newtet2.tetedge1 = j;                        // tet-edge
+	    newtet2.tetedge2 = k;         
+
+	    // new face:
+	    if (istypep && oldtet.flagged)
+              {
+                int hi = 6 - oldtet.tetedge1 - j - k;
+                newtet2.faceedges[oldtet.tetedge2] = char(hi);
+              }
+	    else
+	      newtet2.faceedges[oldtet.tetedge2] = oldtet.tetedge1;
+            
+#ifdef DEBUG
+            *testout << "i = " << i << ", j = " << j << " k = " << k 
+                     << " oldtet.tetedge1 = " << oldtet.tetedge1 
+                     << " oldtet.tetedge2 = " << oldtet.tetedge2
+                     << "   6-oldtet.tetedge1-j-k = " <<  6 - oldtet.tetedge1 - j - k 
+                     << "   6-oldtet.tetedge1-j-k = " <<  short(6 - oldtet.tetedge1 - j - k)
+                     << endl;
+            *testout << "vis1 = " << vis1 << ", vis2 = " << vis2 << endl;
+            for (int j = 0; j < 4; j++)
+              if (newtet2.faceedges[j] > 3)
+                {
+                  *testout << "ERROR1" << endl;
+                }
+#endif
+	  }
+
+	if (i == oldtet.tetedge2)
+	  {
+	    newtet1.pnums[i] = newp;
+	    newtet1.faceedges[i] = oldtet.faceedges[i];  // inherited face
+	    newtet1.faceedges[vis1] = i;
+	    newtet1.faceedges[vis2] = i;
+	    j = 0;
+	    while (j == i || j == oldtet.faceedges[i])
+	      j++;
+	    k = 6 - i - oldtet.faceedges[i] - j;
+	    newtet1.tetedge1 = j;        
+	    newtet1.tetedge2 = k;
+
+	    // new face:
+	    if (istypep && oldtet.flagged)
+              {
+                int hi = 6 - oldtet.tetedge2 - j - k;
+                newtet1.faceedges[oldtet.tetedge1] = char(hi);
+              }
+	    else
+	      newtet1.faceedges[oldtet.tetedge1] = oldtet.tetedge2;
+
+#ifdef DEBUG
+            for (int j = 0; j < 4; j++)
+              if (newtet2.faceedges[j] > 3)
+                {
+                  *testout << "ERROR2" << endl;
+                }
+#endif
+	  }
+      }
+
+    newtet1.matindex = oldtet.matindex;
+    newtet2.matindex = oldtet.matindex;
+    newtet1.incorder = 0;
+    newtet1.order = oldtet.order;
+    newtet2.incorder = 0;
+    newtet2.order = oldtet.order;
+
+    *testout << "newtet1 =  " << newtet1 << endl;
+    *testout << "newtet2 =  " << newtet2 << endl;
+  }
+
+
+  
+
+  void BTBisectPrism (const MarkedPrism & oldprism, int newp1, int newp2,
+		      MarkedPrism & newprism1, MarkedPrism & newprism2)
+  {
+    int i;
+
+    for (i = 0; i < 6; i++)
+      {
+	newprism1.pnums[i] = oldprism.pnums[i];
+	newprism2.pnums[i] = oldprism.pnums[i];
+      }  
+    
+    int pe1, pe2;
+    pe1 = 0;
+    if (pe1 == oldprism.markededge)
+      pe1++;
+    pe2 = 3 - oldprism.markededge - pe1;
+
+    newprism1.pnums[pe2] = newp1;
+    newprism1.pnums[pe2+3] = newp2;
+    newprism1.markededge = pe2;
+    newprism2.pnums[pe1] = newp1;
+    newprism2.pnums[pe1+3] = newp2;
+    newprism2.markededge = pe1;
+
+    newprism1.matindex = oldprism.matindex;
+    newprism2.matindex = oldprism.matindex;
+
+    int nm = oldprism.marked - 1;
+    if (nm < 0) nm = 0;
+    newprism1.marked = nm;
+    newprism2.marked = nm;
+
+    newprism1.incorder = 0;
+    newprism1.order = oldprism.order;
+    newprism2.incorder = 0;
+    newprism2.order = oldprism.order;
+  }
+
+
+  void BTBisectIdentification (const MarkedIdentification & oldid,
+			       Array<int> & newp,
+			       MarkedIdentification & newid1,
+			       MarkedIdentification & newid2)
+  {
+    for(int i=0; i<2*oldid.np; i++)
+      {
+	newid1.pnums[i] = oldid.pnums[i];
+	newid2.pnums[i] = oldid.pnums[i];
+      }
+    newid1.np = newid2.np = oldid.np;
+
+    if(oldid.np == 3)
+      {
+	newid1.pnums[(oldid.markededge+1)%3] = newp[0];
+	newid1.pnums[(oldid.markededge+1)%3+3] = newp[1];
+	newid1.markededge = (oldid.markededge+2)%3;
+
+	newid2.pnums[oldid.markededge] = newp[0];
+	newid2.pnums[oldid.markededge+3] = newp[1];
+	newid2.markededge = (oldid.markededge+1)%3;
+      }
+    else if(oldid.np == 4)
+      {
+	newid1.pnums[(oldid.markededge+1)%4] = newp[0];
+	newid1.pnums[(oldid.markededge+2)%4] = newp[2];
+	newid1.pnums[(oldid.markededge+1)%4+4] = newp[1];
+	newid1.pnums[(oldid.markededge+2)%4+4] = newp[3];
+	newid1.markededge = (oldid.markededge+3)%4;
+
+	newid2.pnums[oldid.markededge] = newp[0];
+	newid2.pnums[(oldid.markededge+3)%4] = newp[2];
+	newid2.pnums[oldid.markededge+4] = newp[1];
+	newid2.pnums[(oldid.markededge+3)%4+4] = newp[3];
+	newid2.markededge = (oldid.markededge+1)%4;
+      }
+
+    
+    int nm = oldid.marked - 1;
+    if (nm < 0) nm = 0;
+    newid1.marked = newid2.marked = nm;
+
+    newid1.incorder = newid2.incorder = 0;
+    newid1.order = newid2.order = oldid.order;
+  }
+
+
+
+  void BTBisectTri (const MarkedTri & oldtri, int newp, const PointGeomInfo & newpgi,
+		    MarkedTri & newtri1, MarkedTri & newtri2)
+  {
+    int i;
+
+    for (i = 0; i < 3; i++)
+      {
+	newtri1.pnums[i] = oldtri.pnums[i];
+	newtri1.pgeominfo[i] = oldtri.pgeominfo[i];
+	newtri2.pnums[i] = oldtri.pnums[i];
+	newtri2.pgeominfo[i] = oldtri.pgeominfo[i];
+      }  
+
+    int pe1, pe2;
+    pe1 = 0;
+    if (pe1 == oldtri.markededge)
+      pe1++;
+    pe2 = 3 - oldtri.markededge - pe1;
+
+    newtri1.pnums[pe2] = newp;
+    newtri1.pgeominfo[pe2] = newpgi;
+    newtri1.markededge = pe2;
+
+    newtri2.pnums[pe1] = newp;
+    newtri2.pgeominfo[pe1] = newpgi;
+    newtri2.markededge = pe1;
+
+
+    newtri1.surfid = oldtri.surfid;
+    newtri2.surfid = oldtri.surfid;
+
+    int nm = oldtri.marked - 1;
+    if (nm < 0) nm = 0;
+    newtri1.marked = nm;
+    newtri2.marked = nm;
+
+    newtri1.incorder = 0;
+    newtri1.order = oldtri.order;
+    newtri2.incorder = 0;
+    newtri2.order = oldtri.order;
+    
+    
+  }
+
+
+  void BTBisectQuad (const MarkedQuad & oldquad, 
+		     int newp1, const PointGeomInfo & npgi1, 
+		     int newp2, const PointGeomInfo & npgi2, 
+		     MarkedQuad & newquad1, MarkedQuad & newquad2)
+  {
+    int i;
+
+    for (i = 0; i < 4; i++)
+      {
+	newquad1.pnums[i] = oldquad.pnums[i];
+	newquad1.pgeominfo[i] = oldquad.pgeominfo[i];
+	newquad2.pnums[i] = oldquad.pnums[i];
+	newquad2.pgeominfo[i] = oldquad.pgeominfo[i];
+      }  
+
+/*    if (oldquad.marked==1) // he/sz: 2d quads or 3d prism
+    {   
+      newquad1.pnums[1] = newp1;
+      newquad1.pgeominfo[1] = npgi1;
+      newquad1.pnums[3] = newp2;
+      newquad1.pgeominfo[3] = npgi2;
+
+      newquad2.pnums[0] = newp1;
+      newquad2.pgeominfo[0] = npgi1;
+      newquad2.pnums[2] = newp2;
+      newquad2.pgeominfo[2] = npgi2;
+    }
+      
+    else if (oldquad.marked==2) // he/sz: 2d quads only
+    {
+      newquad1.pnums[0] = newp1;
+      newquad1.pnums[1] = newp2;
+      newquad1.pnums[3] = oldquad.pnums[2];  
+      newquad1.pnums[2] = oldquad.pnums[0]; 
+      newquad1.pgeominfo[0] = npgi1;
+      newquad1.pgeominfo[1] = npgi2;
+      newquad1.pgeominfo[3] = oldquad.pgeominfo[2]; 
+      newquad1.pgeominfo[2] = oldquad.pgeominfo[0];
+
+      newquad2.pnums[0] = newp2;
+      newquad2.pnums[1] = newp1;
+      newquad2.pnums[3] = oldquad.pnums[1];  
+      newquad2.pnums[2] = oldquad.pnums[3]; 
+      newquad2.pgeominfo[0] = npgi2;
+      newquad2.pgeominfo[1] = npgi1;
+      newquad2.pgeominfo[3] = oldquad.pgeominfo[1]; 
+      newquad2.pgeominfo[2] = oldquad.pgeominfo[3];
+    }
+      
+    */
+      
+    if (oldquad.markededge==0 || oldquad.markededge==2)
+    {
+      newquad1.pnums[1] = newp1;
+      newquad1.pgeominfo[1] = npgi1;
+      newquad1.pnums[3] = newp2;
+      newquad1.pgeominfo[3] = npgi2;
+
+      newquad2.pnums[0] = newp1;
+      newquad2.pgeominfo[0] = npgi1;
+      newquad2.pnums[2] = newp2;
+      newquad2.pgeominfo[2] = npgi2;
+    }
+    else // 1 || 3 
+    {
+      newquad1.pnums[2] = newp1;
+      newquad1.pgeominfo[2] = npgi1;
+      newquad1.pnums[3] = newp2;
+      newquad1.pgeominfo[3] = npgi2;
+
+      newquad2.pnums[0] = newp1;
+      newquad2.pgeominfo[0] = npgi1;
+      newquad2.pnums[1] = newp2;
+      newquad2.pgeominfo[1] = npgi2;
+    }
+    newquad1.surfid = oldquad.surfid;
+    newquad2.surfid = oldquad.surfid;
+
+    int nm = oldquad.marked - 1;
+    if (nm < 0) nm = 0;
+
+    newquad1.marked = nm;
+    newquad2.marked = nm;
+    
+    if (nm==1)
+    {
+      newquad1.markededge=1;
+      newquad2.markededge=1;
+    }
+    else
+    {
+      newquad1.markededge=0;
+      newquad2.markededge=0;
+    }
+    
+  }
+
+
+  int MarkHangingIdentifications(T_MIDS & mids, 
+				 const INDEX_2_CLOSED_HASHTABLE<int> & cutedges)
+  {
+    int i, j;
+    
+    int hanging = 0;
+    for (i = 1; i <= mids.Size(); i++)
+      {
+	if (mids.Elem(i).marked)
+	  {
+	    hanging = 1;
+	    continue;
+	  }
+
+	const int np = mids.Get(i).np;
+
+	for(j = 0; j < np; j++)
+	  {
+	    INDEX_2 edge1(mids.Get(i).pnums[j],
+			  mids.Get(i).pnums[(j+1) % np]);
+	    INDEX_2 edge2(mids.Get(i).pnums[j+np],
+			  mids.Get(i).pnums[((j+1) % np) + np]);
+
+	    edge1.Sort();
+	    edge2.Sort();
+	    if (cutedges.Used (edge1) ||
+		cutedges.Used (edge2))
+	      {
+		mids.Elem(i).marked = 1;
+		hanging = 1;
+	      }
+	  }
+      }
+
+    return hanging;
+  }
+
+
+  /*
+  void IdentifyCutEdges(Mesh & mesh,
+			INDEX_2_CLOSED_HASHTABLE<int> & cutedges)
+  {
+    int i,j,k;
+
+    Array< Array<int,PointIndex::BASE>* > idmaps;
+    for(i=1; i<=mesh.GetIdentifications().GetMaxNr(); i++)
+      {
+	idmaps.Append(new Array<int,PointIndex::BASE>);
+	mesh.GetIdentifications().GetMap(i,*idmaps.Last());
+      }
+
+
+    
+    for(SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++)
+      {
+	const Element2d & el2d = mesh[sei];
+	
+	for(i = 0; i < el2d.GetNP(); i++)
+	  {
+	    INDEX_2 e1(el2d[i], el2d[(i+1) % el2d.GetNP()]);
+	    e1.Sort();
+
+	    if(!cutedges.Used(e1))
+	      continue;
+
+	    
+	    for(k = 0; k < idmaps.Size(); k++)
+	      {
+		INDEX_2 e2((*idmaps[k])[e1.I1()],
+			   (*idmaps[k])[e1.I2()]);
+		
+		if(e2.I1() == 0 || e2.I2() == 0 ||
+		   e1.I1() == e2.I1() || e1.I2() == e2.I2())
+		  continue;
+		
+		e2.Sort();
+
+		if(cutedges.Used(e2))
+		  continue;
+
+		Point3d np = Center(mesh.Point(e2.I1()),
+				    mesh.Point(e2.I2()));
+		int newp = mesh.AddPoint(np);
+		cutedges.Set(e2,newp);
+		(*testout) << "DAAA" << endl;
+	      }
+	  }
+      }
+
+    
+    for(i=0; i<idmaps.Size(); i++)
+      delete idmaps[i];
+    idmaps.DeleteAll();
+  }
+  */
+
+
+  int MarkHangingTets (T_MTETS & mtets, 
+		       const INDEX_2_CLOSED_HASHTABLE<int> & cutedges)
+  {
+    int i, j, k;
+
+    int hanging = 0;
+    for (i = 1; i <= mtets.Size(); i++)
+      {
+	MarkedTet & teti = mtets.Elem(i);
+
+	if (teti.marked)
+	  {
+	    hanging = 1;
+	    continue;
+	  }
+
+	for (j = 0; j < 3; j++)
+	  for (k = j+1; k < 4; k++)
+	    {
+	      INDEX_2 edge(teti.pnums[j],
+			   teti.pnums[k]);
+	      edge.Sort();
+	      if (cutedges.Used (edge))
+		{
+		  teti.marked = 1;
+		  hanging = 1;
+		}
+	    }
+      }
+
+    return hanging;
+  }
+
+
+
+  int MarkHangingPrisms (T_MPRISMS & mprisms, 
+			 const INDEX_2_CLOSED_HASHTABLE<int> & cutedges)
+  {
+    int i, j, k;
+
+    int hanging = 0;
+    for (i = 1; i <= mprisms.Size(); i++)
+      {
+	if (mprisms.Elem(i).marked)
+	  {
+	    hanging = 1;
+	    continue;
+	  }
+
+	for (j = 0; j < 2; j++)
+	  for (k = j+1; k < 3; k++)
+	    {
+	      INDEX_2 edge1(mprisms.Get(i).pnums[j],
+			    mprisms.Get(i).pnums[k]);
+	      INDEX_2 edge2(mprisms.Get(i).pnums[j+3],
+			    mprisms.Get(i).pnums[k+3]);
+	      edge1.Sort();
+	      edge2.Sort();
+	      if (cutedges.Used (edge1) ||
+		  cutedges.Used (edge2))
+		{
+		  mprisms.Elem(i).marked = 1;
+		  hanging = 1;
+		}
+	    }
+      }
+    return hanging;
+  }
+
+
+
+  int MarkHangingTris (T_MTRIS & mtris, 
+		       const INDEX_2_CLOSED_HASHTABLE<int> & cutedges)
+  {
+    int i, j, k;
+
+    int hanging = 0;
+    for (i = 1; i <= mtris.Size(); i++)
+      {
+	if (mtris.Get(i).marked)
+	  {
+	    hanging = 1;
+	    continue;
+	  }
+	for (j = 0; j < 2; j++)
+	  for (k = j+1; k < 3; k++)
+	    {
+	      INDEX_2 edge(mtris.Get(i).pnums[j],
+			   mtris.Get(i).pnums[k]);
+	      edge.Sort();
+	      if (cutedges.Used (edge))
+		{
+		  mtris.Elem(i).marked = 1;
+		  hanging = 1;
+                }
+	    }
+      }  
+      return hanging;
+  }
+
+
+
+  int MarkHangingQuads (T_MQUADS & mquads, 
+			const INDEX_2_CLOSED_HASHTABLE<int> & cutedges)
+  {
+    int i;
+
+    int hanging = 0;
+    for (i = 1; i <= mquads.Size(); i++)
+      {
+	if (mquads.Elem(i).marked)
+	  {
+	    hanging = 1;
+	    continue;
+	  }
+
+	INDEX_2 edge1(mquads.Get(i).pnums[0],
+		      mquads.Get(i).pnums[1]);
+	INDEX_2 edge2(mquads.Get(i).pnums[2],
+		      mquads.Get(i).pnums[3]);
+	edge1.Sort();
+	edge2.Sort();
+	if (cutedges.Used (edge1) ||
+	    cutedges.Used (edge2))
+	  {
+	    mquads.Elem(i).marked = 1;
+            mquads.Elem(i).markededge = 0;
+	    hanging = 1;
+            continue;
+	  }
+          
+        // he/sz: second case: split horizontally
+        INDEX_2 edge3(mquads.Get(i).pnums[1],
+                      mquads.Get(i).pnums[3]);
+        INDEX_2 edge4(mquads.Get(i).pnums[2],
+                      mquads.Get(i).pnums[0]);
+
+        edge3.Sort();
+        edge4.Sort();
+        if (cutedges.Used (edge3) ||
+            cutedges.Used (edge4))
+        {
+          mquads.Elem(i).marked = 1;
+          mquads.Elem(i).markededge = 1;
+          hanging = 1; 
+          continue; 
+        }
+    
+      }
+    return hanging;
+  }
+
+
+
+  void ConnectToNodeRec (int node, int tonode, 
+			 const TABLE<int> & conto, Array<int> & connecttonode)
+  {
+    int i, n2;
+    //  (*testout) << "connect " << node << " to " << tonode << endl;
+    for (i = 1; i <= conto.EntrySize(node); i++)
+      {
+	n2 = conto.Get(node, i);
+	if (!connecttonode.Get(n2))
+	  {
+	    connecttonode.Elem(n2) = tonode;
+	    ConnectToNodeRec (n2, tonode, conto, connecttonode);
+	  }
+      }
+  }
+
+
+
+
+  T_MTETS mtets;
+  T_MPRISMS mprisms;
+  T_MIDS mids;
+  T_MTRIS mtris;
+  T_MQUADS mquads;
+
+
+  void WriteMarkedElements(ostream & ost)
+  {
+    ost << "Marked Elements\n";
+
+    ost << mtets.Size() << "\n";
+    for(int i=0; i<mtets.Size(); i++)
+      ost << mtets[i];
+
+    ost << mprisms.Size() << "\n";
+    for(int i=0; i<mprisms.Size(); i++)
+      ost << mprisms[i];
+
+    ost << mids.Size() << "\n";
+    for(int i=0; i<mids.Size(); i++)
+      ost << mids[i];
+
+    ost << mtris.Size() << "\n";
+    for(int i=0; i<mtris.Size(); i++)
+      ost << mtris[i];
+
+    ost << mquads.Size() << "\n";
+    for(int i=0; i<mquads.Size(); i++)
+      ost << mquads[i];
+    ost << endl;
+  }
+
+  bool ReadMarkedElements(istream & ist, const Mesh & mesh)
+  {
+    string auxstring("");
+    if(ist)
+      ist >> auxstring;
+
+    if(auxstring != "Marked")
+      return false;
+
+    if(ist)
+      ist >> auxstring;
+
+    if(auxstring != "Elements")
+      return false;
+
+    int size;
+
+    ist >> size;
+    mtets.SetSize(size);
+    for(int i=0; i<size; i++)
+      {
+        ist >> mtets[i];
+        if(mtets[i].pnums[0] > mesh.GetNV() || 
+           mtets[i].pnums[1] > mesh.GetNV() || 
+           mtets[i].pnums[2] > mesh.GetNV() || 
+           mtets[i].pnums[3] > mesh.GetNV())
+          return false;
+      }
+
+    ist >> size;
+    mprisms.SetSize(size);
+    for(int i=0; i<size; i++)
+      ist >> mprisms[i];
+
+    ist >> size;
+    mids.SetSize(size);
+    for(int i=0; i<size; i++)
+      ist >> mids[i];
+
+    ist >> size;
+    mtris.SetSize(size);
+    for(int i=0; i<size; i++)
+      ist >> mtris[i];
+
+    ist >> size;
+    mquads.SetSize(size);
+    for(int i=0; i<size; i++)
+      ist >> mquads[i];
+
+    return true;
+  }
+
+
+
+
+
+  void BisectTetsCopyMesh (Mesh & mesh, const class CSGeometry *,
+			   BisectionOptions & opt,
+			   const Array< Array<int,PointIndex::BASE>* > & idmaps,
+			   const string & refinfofile)
+  {
+    mtets.SetName ("bisection, tets");
+    mprisms.SetName ("bisection, prisms");
+    mtris.SetName ("bisection, trigs");
+    mquads.SetName ("bisection, quads");
+    mids.SetName ("bisection, identifications");
+
+    //int np = mesh.GetNP();
+    int ne = mesh.GetNE();
+    int nse = mesh.GetNSE();
+    int i, j, k, l, m;
+
+    /*
+      if (mtets.Size() + mprisms.Size() == mesh.GetNE())
+      return;
+    */
+
+    bool readok = false;
+
+    if(refinfofile != "")
+      {
+	PrintMessage(3,"Reading marked-element information from \"",refinfofile,"\"");
+	ifstream ist(refinfofile.c_str());
+
+	readok = ReadMarkedElements(ist,mesh);
+
+	ist.close();
+      }
+
+    if(!readok)
+      {
+	PrintMessage(3,"resetting marked-element information");
+	mtets.SetSize(0);
+	mprisms.SetSize(0);
+	mids.SetSize(0);
+	mtris.SetSize(0);
+	mquads.SetSize(0);
+	
+	
+	INDEX_2_HASHTABLE<int> shortedges(100);
+	for (i = 1; i <= ne; i++)
+	  {
+	    const Element & el = mesh.VolumeElement(i);
+	    if (el.GetType() == PRISM ||
+		el.GetType() == PRISM12)
+	      {
+		for (j = 1; j <= 3; j++)
+		  {
+		    INDEX_2 se(el.PNum(j), el.PNum(j+3));
+		    se.Sort();
+		    shortedges.Set (se, 1);
+		  }
+	      }
+	  }
+	
+	
+	
+	// INDEX_2_HASHTABLE<int> edgenumber(np);
+	INDEX_2_CLOSED_HASHTABLE<int> edgenumber(9*ne+4*nse);  
+	
+	BTSortEdges (mesh, idmaps, edgenumber);
+	
+	
+	for (i = 1; i <= ne; i++)
+	  {
+	    const Element & el = mesh.VolumeElement(i);
+	    
+	    switch (el.GetType())
+	      {
+	      case TET:
+	      case TET10:
+		{
+		  // if tet has short edge, it is handled as degenerated prism
+		  
+		  int foundse = 0;
+		  for (j = 1; j <= 3; j++)
+		    for (k = j+1; k <= 4; k++)
+		      {
+			INDEX_2 se(el.PNum(j), el.PNum(k));
+			se.Sort();
+			if (shortedges.Used (se))
+			  {
+			    //		      cout << "tet converted to prism" << endl;
+			    
+			    foundse = 1;
+			    int p3 = 1;
+			    while (p3 == j || p3 == k)
+			      p3++;
+			    int p4 = 10 - j - k - p3;
+			    
+			    // even permutation ?
+			    int pi[4];
+			    pi[0] = j;
+			    pi[1] = k;
+			    pi[2] = p3;
+			    pi[3] = p4;
+			    int cnt = 0;
+			    for (l = 1; l <= 4; l++)
+			      for (m = 0; m < 3; m++)
+				if (pi[m] > pi[m+1])
+				  {
+				    Swap (pi[m], pi[m+1]);
+				    cnt++;
+				  }
+			    if (cnt % 2)
+			      Swap (p3, p4);
+			    
+			    Element hel = el;
+			    hel.PNum(1) = el.PNum(j);
+			    hel.PNum(2) = el.PNum(k);
+			    hel.PNum(3) = el.PNum(p3);
+			    hel.PNum(4) = el.PNum(p4);
+			    
+			    MarkedPrism mp;
+			    BTDefineMarkedPrism (hel, edgenumber, mp);
+			    mp.matindex = el.GetIndex();
+			    mprisms.Append (mp);
+			  }
+		      }
+		  if (!foundse)
+		    {
+		      MarkedTet mt;
+		      BTDefineMarkedTet (el, edgenumber, mt);
+		      mt.matindex = el.GetIndex();
+		      mtets.Append (mt);
+		    }
+		  break;
+		}
+	      case PYRAMID:
+		{
+		  // eventually rotate
+		  MarkedPrism mp;
+		  
+		  INDEX_2 se(el.PNum(1), el.PNum(2));
+		  se.Sort();
+		  if (shortedges.Used (se))
+		    {
+		      Element hel = el;
+		      hel.PNum(1) = el.PNum(2);
+		      hel.PNum(2) = el.PNum(3);
+		      hel.PNum(3) = el.PNum(4);
+		      hel.PNum(4) = el.PNum(1);
+		      BTDefineMarkedPrism (hel, edgenumber, mp);
+		    }
+		  else
+		    {
+		      BTDefineMarkedPrism (el, edgenumber, mp);
+		    }
+		  
+		  mp.matindex = el.GetIndex();
+		  mprisms.Append (mp);
+		  break;
+		}
+	      case PRISM:
+	      case PRISM12:
+		{
+		  MarkedPrism mp;
+		  BTDefineMarkedPrism (el, edgenumber, mp);
+		  mp.matindex = el.GetIndex();
+		  mprisms.Append (mp);
+		  break;
+		}
+              default:
+                throw NgException("Bisect, element type not handled in switch, 4");
+	      }
+	  }
+	
+	for (i = 1; i <= nse; i++)
+	  {
+	    const Element2d & el = mesh.SurfaceElement(i);
+	    if (el.GetType() == TRIG ||
+		el.GetType() == TRIG6)
+	      {
+		MarkedTri mt;
+		BTDefineMarkedTri (el, edgenumber, mt);
+		mtris.Append (mt);
+	      }
+	    else
+	      {
+		MarkedQuad mq;
+		BTDefineMarkedQuad (el, edgenumber, mq);
+		mquads.Append (mq);
+	      }
+	    
+	    MarkedIdentification mi;
+	    for(j=0; j<idmaps.Size(); j++)
+	      if(BTDefineMarkedId(el, edgenumber, *idmaps[j], mi))
+		mids.Append(mi);
+	  }
+      }
+	
+
+
+
+    mesh.mlparentelement.SetSize(ne);
+    for (i = 1; i <= ne; i++)
+      mesh.mlparentelement.Elem(i) = 0;
+    mesh.mlparentsurfaceelement.SetSize(nse);
+    for (i = 1; i <= nse; i++)
+      mesh.mlparentsurfaceelement.Elem(i) = 0;
+  
+    if (printmessage_importance>0)
+    {
+      ostringstream str1,str2;
+      str1 << "copied " << mtets.Size() << " tets, " << mprisms.Size() << " prisms";
+      str2 << "       " << mtris.Size() << " trigs, " << mquads.Size() << " quads";
+
+      PrintMessage(4,str1.str());
+      PrintMessage(4,str2.str());
+    }
+  }
+
+
+  /*
+  void UpdateEdgeMarks2(Mesh & mesh,
+			const Array< Array<int,PointIndex::BASE>* > & idmaps)
+  {
+    Array< Array<MarkedTet>*,PointIndex::BASE > mtets_old(mesh.GetNP());
+    Array< Array<MarkedPrism>*,PointIndex::BASE > mprisms_old(mesh.GetNP());
+    Array< Array<MarkedIdentification>*,PointIndex::BASE > mids_old(mesh.GetNP());
+    Array< Array<MarkedTri>*,PointIndex::BASE > mtris_old(mesh.GetNP());
+    Array< Array<MarkedQuad>*,PointIndex::BASE > mquads_old(mesh.GetNP());
+
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      mtets_old[i] = new Array<MarkedTet>;
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      mprisms_old[i] = new Array<MarkedPrism>;
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      mids_old[i] = new Array<MarkedIdentification>;
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      mtris_old[i] = new Array<MarkedTri>;
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      mquads_old[i] = new Array<MarkedQuad>;
+
+    for(int i=0; i<mtets.Size(); i++)
+      mtets_old[mtets[i].pnums[0]]->Append(mtets[i]);
+    for(int i=0; i<mprisms.Size(); i++)
+      mprisms_old[mprisms[i].pnums[0]]->Append(mprisms[i]);
+    for(int i=0; i<mids.Size(); i++)
+      mids_old[mids[i].pnums[0]]->Append(mids[i]);
+    for(int i=0; i<mtris.Size(); i++)
+      {
+	(*testout) << "i " << i << endl;
+	(*testout) << "mtris[i] " << mtris[i].pnums[0] << " " << mtris[i].pnums[1] << " " << mtris[i].pnums[2] << endl; 
+	mtris_old[mtris[i].pnums[0]]->Append(mtris[i]);
+      }
+    for(int i=0; i<mquads.Size(); i++)
+      mquads_old[mquads[i].pnums[0]]->Append(mquads[i]);
+
+   
+    
+    int np = mesh.GetNP();
+    int ne = mesh.GetNE();
+    int nse = mesh.GetNSE();
+    int i, j, k, l, m;
+
+
+//       if (mtets.Size() + mprisms.Size() == mesh.GetNE())
+//       return;
+
+    
+
+    mtets.SetSize(0);
+    mprisms.SetSize(0);
+    mids.SetSize(0);
+    mtris.SetSize(0);
+    mquads.SetSize(0);
+
+
+    INDEX_2_HASHTABLE<int> shortedges(100);
+    for (i = 1; i <= ne; i++)
+      {
+	const Element & el = mesh.VolumeElement(i);
+	if (el.GetType() == PRISM ||
+	    el.GetType() == PRISM12)
+	  {
+	    for (j = 1; j <= 3; j++)
+	      {
+		INDEX_2 se(el.PNum(j), el.PNum(j+3));
+		se.Sort();
+		shortedges.Set (se, 1);
+	      }
+	  }
+      }
+
+
+
+    // INDEX_2_HASHTABLE<int> edgenumber(np);
+    INDEX_2_CLOSED_HASHTABLE<int> edgenumber(9*ne+4*nse);  
+
+    BTSortEdges (mesh, idmaps, edgenumber);
+
+
+    for (i = 1; i <= ne; i++)
+      {
+	const Element & el = mesh.VolumeElement(i);
+	  
+	switch (el.GetType())
+	  {
+	  case TET:
+	  case TET10:
+	    {
+	      // if tet has short edge, it is handled as degenerated prism
+
+	      int foundse = 0;
+	      for (j = 1; j <= 3; j++)
+		for (k = j+1; k <= 4; k++)
+		  {
+		    INDEX_2 se(el.PNum(j), el.PNum(k));
+		    se.Sort();
+		    if (shortedges.Used (se))
+		      {
+//		      cout << "tet converted to prism" << endl;
+
+			foundse = 1;
+			int p3 = 1;
+			while (p3 == j || p3 == k)
+			  p3++;
+			int p4 = 10 - j - k - p3;
+
+			// even permutation ?
+			int pi[4];
+			pi[0] = j;
+			pi[1] = k;
+			pi[2] = p3;
+			pi[3] = p4;
+			int cnt = 0;
+			for (l = 1; l <= 4; l++)
+			  for (m = 0; m < 3; m++)
+			    if (pi[m] > pi[m+1])
+			      {
+				Swap (pi[m], pi[m+1]);
+				cnt++;
+			      }
+			if (cnt % 2)
+			  Swap (p3, p4);
+
+			Element hel = el;
+			hel.PNum(1) = el.PNum(j);
+			hel.PNum(2) = el.PNum(k);
+			hel.PNum(3) = el.PNum(p3);
+			hel.PNum(4) = el.PNum(p4);
+
+			MarkedPrism mp;
+
+			BTDefineMarkedPrism (hel, edgenumber, mp);
+			mp.matindex = el.GetIndex();
+			mprisms.Append (mp);
+		      }
+		  }
+	      if (!foundse)
+		{
+		  MarkedTet mt;
+		  
+		  int oldind = -1;
+		  for(l = 0; oldind < 0 && l<mtets_old[el[0]]->Size(); l++)
+		    if(el[1] == (*mtets_old[el[0]])[l].pnums[1] &&
+		       el[2] == (*mtets_old[el[0]])[l].pnums[2] &&
+		       el[3] == (*mtets_old[el[0]])[l].pnums[3])
+		      oldind = l;
+
+		  if(oldind >= 0)
+		    mtets.Append((*mtets_old[el[0]])[oldind]);
+		  else
+		    {
+		      BTDefineMarkedTet (el, edgenumber, mt);
+		      mt.matindex = el.GetIndex();
+		      mtets.Append (mt);
+		    }
+		}
+	      break;
+	    }
+	  case PYRAMID:
+	    {
+	      // eventually rotate
+	      MarkedPrism mp;
+	    
+	      INDEX_2 se(el.PNum(1), el.PNum(2));
+	      se.Sort();
+	      if (shortedges.Used (se))
+		{
+		  Element hel = el;
+		  hel.PNum(1) = el.PNum(2);
+		  hel.PNum(2) = el.PNum(3);
+		  hel.PNum(3) = el.PNum(4);
+		  hel.PNum(4) = el.PNum(1);
+		  BTDefineMarkedPrism (hel, edgenumber, mp);
+		}
+	      else
+		{
+		  BTDefineMarkedPrism (el, edgenumber, mp);
+		}
+
+	      mp.matindex = el.GetIndex();
+	      mprisms.Append (mp);
+	      break;
+	    }
+	  case PRISM:
+	  case PRISM12:
+	    {
+	      MarkedPrism mp;
+	      BTDefineMarkedPrism (el, edgenumber, mp);
+	      mp.matindex = el.GetIndex();
+	      mprisms.Append (mp);
+	      break;
+	    }
+	  }
+      }
+
+    for (i = 1; i <= nse; i++)
+      {
+	const Element2d & el = mesh.SurfaceElement(i);
+	if (el.GetType() == TRIG ||
+	    el.GetType() == TRIG6)
+	  {
+	    MarkedTri mt;
+	    BTDefineMarkedTri (el, edgenumber, mt);
+	    mtris.Append (mt);
+	  }
+	else
+	  {
+	    MarkedQuad mq;
+	    BTDefineMarkedQuad (el, edgenumber, mq);
+	    mquads.Append (mq);
+	  }
+	
+	MarkedIdentification mi;
+
+	
+
+	for(j=0; j<idmaps.Size(); j++)
+	  if(BTDefineMarkedId(el, edgenumber, *idmaps[j], mi))
+	    {
+	      mids.Append(mi);
+		
+	      int oldind = -1;
+	      for(l = 0; oldind < 0 && l<mids_old[mi.pnums[0]]->Size(); l++)
+		{
+		  bool equal = true;
+		  for(int m = 1; equal && m < mi.np; m++)
+		    equal = (mi.pnums[m] == (*mids_old[el[0]])[l].pnums[m]);
+		  if(equal)
+		    oldind = l;
+		}
+
+	      if(oldind >= 0)
+		mids.Last() = (*mids_old[mi.pnums[0]])[oldind];
+	    }
+
+      }
+
+
+
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      delete mtets_old[i];
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      delete mprisms_old[i];
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      delete mids_old[i];
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      delete mtris_old[i];
+    for(int i=PointIndex::BASE; i<mesh.GetNP()+PointIndex::BASE; i++)
+      delete mquads_old[i];
+  }
+*/
+
+  
+  void UpdateEdgeMarks (Mesh & mesh,
+			const Array< Array<int,PointIndex::BASE>* > & idmaps)
+  //const Array < Array<Element>* > & elements_before,
+  //const Array < Array<int>* > & markedelts_num,
+  //		const Array < Array<Element2d>* > & surfelements_before,
+  //		const Array < Array<int>* > & markedsurfelts_num)
+  {
+    T_MTETS mtets_old; mtets_old.Copy(mtets);
+    T_MPRISMS mprisms_old; mprisms_old.Copy(mprisms);
+    T_MIDS mids_old; mids_old.Copy(mids);
+    T_MTRIS mtris_old; mtris_old.Copy(mtris);
+    T_MQUADS mquads_old; mquads_old.Copy(mquads);
+
+
+
+    
+    mtets.SetSize(0);
+    mprisms.SetSize(0);
+    mids.SetSize(0);
+    mtris.SetSize(0);
+    mquads.SetSize(0);
+
+    //int nv = mesh.GetNV();
+
+
+    INDEX_2_CLOSED_HASHTABLE<int> edgenumber(9*mesh.GetNE()+4*mesh.GetNSE());  
+    
+    int maxnum = BTSortEdges (mesh, idmaps, edgenumber);
+
+    for(int m = 0; m < mtets_old.Size(); m++)
+      {
+	MarkedTet & mt = mtets_old[m];
+
+	//(*testout) << "old mt " << mt;
+	
+	INDEX_2 edge (mt.pnums[mt.tetedge1],mt.pnums[mt.tetedge2]);
+	edge.Sort();
+	if(edgenumber.Used(edge))
+	  {
+	    int val = edgenumber.Get(edge);
+	    //(*testout) << "set voledge " << edge << " from " << val;
+	    if(val <= maxnum)
+	      {
+		val += 2*maxnum;
+		edgenumber.Set(edge,val);
+	      }
+	    else if(val <= 2*maxnum)
+	      {
+		val += maxnum;
+		edgenumber.Set(edge,val);
+	      }
+	    //(*testout) << " to " << val << endl;
+	  }
+	
+	for(int k=0; k<4; k++)
+	  for(int i=0; i<3; i++)
+	    for(int j=i+1; i != k && j<4; j++)
+	      if(j != k && int(mt.faceedges[k]) == 6-k-i-j)
+		{
+		  edge[0] = mt.pnums[i];
+		  edge[1] = mt.pnums[j];
+		  edge.Sort();
+		  if(edgenumber.Used(edge))
+		    {
+		      int val = edgenumber.Get(edge);
+		      //(*testout) << "set faceedge " << edge << " from " << val;
+		      if(val <= maxnum)
+			{
+			  val += maxnum;
+			  edgenumber.Set(edge,val);
+			}
+		      //(*testout) << " to " << val << endl;
+		    }		      
+		}
+      }
+
+	
+    
+    
+    for(ElementIndex ei = 0; ei < mesh.GetNE(); ei++)
+      {
+	const Element & el = mesh[ei];
+	
+	//int pos = elements_before[el[0]]->Pos(el);
+	//int elnum = (pos >= 0) ? (*markedelts_num[el[0]])[pos] : -1;
+	 
+	switch (el.GetType())
+	  {
+	  case TET:
+	  case TET10:
+	    {
+	      //if(elnum >= 0)
+	      // {
+	      //   mtets.Append(mtets_old[elnum]);
+	      // } 
+	      //else
+	      // {
+	      MarkedTet mt;
+	      BTDefineMarkedTet (el, edgenumber, mt);
+	      mt.matindex = el.GetIndex();
+	      
+	      mtets.Append (mt);
+	    
+	      //(*testout) << "mtet " << mtets.Last() << endl;
+	      break;
+	    }
+	  case PYRAMID:
+	    {
+	      cerr << "Refinement :: UpdateEdgeMarks not yet implemented for pyramids"
+		   << endl;
+	      break;
+	    }
+	    
+	  case PRISM:
+	  case PRISM12:
+	    {
+	      cerr << "Refinement :: UpdateEdgeMarks not yet implemented for prisms"
+		   << endl;
+	      break;
+	    }
+          default:
+            throw NgException("Bisect, element type not handled in switch, 6");
+	  }
+	
+      }
+    
+
+    
+     for(SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++)
+       {
+	 const Element2d & el = mesh[sei];
+
+	 /*
+	 for(int k=0; k<3; k++)
+	   auxind3[k] = el[k];
+
+	 auxind3.Sort();
+	 
+	 int pos = oldfaces[auxind3[0]]->Pos(auxind3);
+	 if(pos < 0)
+	   cout << "UIUIUI" << endl;
+	 */	 
+	 
+	 switch (el.GetType())
+	   {
+	   case TRIG:
+	   case TRIG6:
+	     {
+	       MarkedTri mt;
+	       BTDefineMarkedTri (el, edgenumber, mt);
+	       mtris.Append (mt);
+	       break;
+	     }
+	     
+	   case QUAD:
+	   case QUAD6:
+	     {
+	       MarkedQuad mt;
+	       BTDefineMarkedQuad (el, edgenumber, mt);
+	       mquads.Append (mt);
+	       break;
+	     }
+           default:
+             throw NgException("Bisect, element type not handled in switch, 5");
+	   }
+
+	 
+	MarkedIdentification mi;
+	for(int j=0; j<idmaps.Size(); j++)
+	  if(BTDefineMarkedId(el, edgenumber, *idmaps[j], mi))
+	    mids.Append(mi);
+
+
+	 /*
+	 int pos = surfelements_before[el[0]]->Pos(el);
+	 int elnum = (pos >= 0) ? (*markedsurfelts_num[el[0]])[pos] : -1;
+	 
+	 
+	 switch (el.GetType())
+	   {
+	   case TRIG:
+	   case TRIG6:
+	     {
+	       if(elnum >= 0)
+		 mtris.Append(mtris_old[elnum]);
+	       else
+		 {
+		   MarkedTri mt;
+		   BTDefineMarkedTri (el, edgenumber, mt);
+		   mtris.Append (mt);
+		   (*testout) << "(new) ";
+		 }
+	       (*testout) << "mtri " << mtris.Last();
+	       break;
+	     }
+	     
+	   case QUAD:
+	   case QUAD6:
+	     {
+	       if(elnum >= 0)
+		 mquads.Append(mquads_old[elnum]);
+	       else
+		 {
+		   MarkedQuad mt;
+		   BTDefineMarkedQuad (el, edgenumber, mt);
+		   mquads.Append (mt);
+		 }
+	       break;
+	     }
+	   }
+	 */
+       }
+     
+     /*
+     for(int i=0; i<oldfaces.Size(); i++)
+       {
+	 delete oldfaces[i];
+	 delete oldmarkededges[i];
+       }
+     */
+     
+  }
+				      
+
+
+
+  void Refinement :: Bisect (Mesh & mesh, 
+			     BisectionOptions & opt,
+			     Array<double> * quality_loss) const
+  {
+    PrintMessage(1,"Mesh bisection");
+    PushStatus("Mesh bisection");
+
+    static int timer = NgProfiler::CreateTimer ("Bisect");
+    NgProfiler::RegionTimer reg1 (timer);
+    
+    
+
+    static int localizetimer = NgProfiler::CreateTimer("localize edgepoints");
+    NgProfiler::RegionTimer * loct = new NgProfiler::RegionTimer(localizetimer);   
+    LocalizeEdgePoints(mesh);
+    delete loct;
+
+    Array< Array<int,PointIndex::BASE>* > idmaps;
+    for(int i=1; i<=mesh.GetIdentifications().GetMaxNr(); i++)
+      {
+	if(mesh.GetIdentifications().GetType(i) == Identifications::PERIODIC)
+	  {
+	    idmaps.Append(new Array<int,PointIndex::BASE>);
+	    mesh.GetIdentifications().GetMap(i,*idmaps.Last(),true);
+	  }
+      }
+
+    
+    string refelementinfofileread = "";
+    string refelementinfofilewrite = "";
+
+    if(opt.refinementfilename)
+      {
+	ifstream inf(opt.refinementfilename);
+	string st;
+	inf >> st;
+	if(st == "refinementinfo")
+	  {
+	    while(inf)
+	      {
+		while(inf && st != "markedelementsfile")
+		  inf >> st;
+		
+		if(inf)
+		  inf >> st;
+		
+		if(st == "read" && inf)
+		  ReadEnclString(inf,refelementinfofileread,'\"');
+		else if(st == "write" && inf)
+		  ReadEnclString(inf,refelementinfofilewrite,'\"');
+	      }
+	  }
+	inf.close();
+      }
+	
+
+
+    if (mesh.mglevels == 1 || idmaps.Size() > 0)
+      BisectTetsCopyMesh(mesh, NULL, opt, idmaps, refelementinfofileread);
+
+
+    mesh.ComputeNVertices();
+  
+    int np = mesh.GetNV();
+    mesh.SetNP(np);
+
+    // int ne = mesh.GetNE();
+    // int nse = mesh.GetNSE();
+    int i, j, l;
+
+    // int initnp = np;
+    //  int maxsteps = 3;
+
+    mesh.mglevels++;
+
+    /*
+      if (opt.refinementfilename || opt.usemarkedelements)
+      maxsteps = 3;
+    */
+
+
+    if (opt.refine_p)   
+      {
+	int ne = mesh.GetNE();
+	int nse = mesh.GetNSE();
+	int ox,oy,oz; 
+	for (ElementIndex ei = 0; ei < ne; ei++)
+	  if (mesh[ei].TestRefinementFlag())
+	    {
+	      mesh[ei].GetOrder(ox,oy,oz);
+	      mesh[ei].SetOrder (ox+1,oy+1,oz+1);
+	      if (mesh[ei].TestStrongRefinementFlag())
+		mesh[ei].SetOrder (ox+2,oy+2,oz+2);
+	    }
+	for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+	  if (mesh[sei].TestRefinementFlag())
+	    {
+	      mesh[sei].GetOrder(ox,oy);
+	      mesh[sei].SetOrder(ox+1,oy+1);
+	      if (mesh[sei].TestStrongRefinementFlag())
+		mesh[sei].SetOrder(ox+2,oy+2);
+	    }
+
+	  #ifndef SABINE //Nachbarelemente mit ordx,ordy,ordz 
+      
+	  Array<int,PointIndex::BASE> v_order (mesh.GetNP());
+	  v_order = 0;
+
+	  for (ElementIndex ei = 0; ei < ne; ei++)
+	  for (j = 0; j < mesh[ei].GetNP(); j++)
+	  if (mesh[ei].GetOrder() > v_order[mesh[ei][j]])
+	  v_order[mesh[ei][j]] = mesh[ei].GetOrder();
+
+	  for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+	  for (j = 0; j < mesh[sei].GetNP(); j++)
+	  if (mesh[sei].GetOrder() > v_order[mesh[sei][j]])
+	  v_order[mesh[sei][j]] = mesh[sei].GetOrder();
+
+	  for (ElementIndex ei = 0; ei < ne; ei++)
+	  for (j = 0; j < mesh[ei].GetNP(); j++)
+	  if (mesh[ei].GetOrder() < v_order[mesh[ei][j]]-1)
+	  mesh[ei].SetOrder(v_order[mesh[ei][j]]-1);
+
+	  for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+	  for (j = 0; j < mesh[sei].GetNP(); j++)
+	  if (mesh[sei].GetOrder() < v_order[mesh[sei][j]]-1)
+	  mesh[sei].SetOrder(v_order[mesh[sei][j]]-1);
+	    
+	  #endif
+
+	PopStatus();
+	return;
+      }
+
+
+
+    // INDEX_2_HASHTABLE<int> cutedges(10 + 5 * (mtets.Size()+mprisms.Size()+mtris.Size()+mquads.Size()));
+    INDEX_2_CLOSED_HASHTABLE<int> cutedges(10 + 9 * (mtets.Size()+mprisms.Size()+mtris.Size()+mquads.Size()));
+
+    bool noprojection = false;
+
+    for (l = 1; l <= 1; l++)
+      {
+	int marked = 0;
+	if (opt.refinementfilename)
+	  {
+	    ifstream inf(opt.refinementfilename);
+	    PrintMessage(3,"load refinementinfo from file ",opt.refinementfilename);
+
+	    string st;
+	    inf >> st;
+	    if(st == "refinementinfo")
+	      // new version
+	      {
+		for(i=1; i<=mtets.Size(); i++)
+		  mtets.Elem(i).marked = 0;
+		for(i=1; i<=mprisms.Size(); i++)
+		  mprisms.Elem(i).marked = 0;
+		for(i=1; i<=mtris.Size(); i++)
+		  mtris.Elem(i).marked = 0;
+		for(i=1; i<=mquads.Size(); i++)
+		  mquads.Elem(i).marked = 0;
+		for(i=1; i<=mprisms.Size(); i++)
+		  mids.Elem(i).marked = 0;
+
+		inf >> st;
+		while(inf)
+		  {
+		    if(st[0] == '#')
+		      {
+			inf.ignore(10000,'\n');
+			inf >> st;
+		      }
+		    else if(st == "markedelementsfile")
+		      {
+			inf >> st;
+			ReadEnclString(inf,st,'\"');
+			inf >> st;
+		      }
+		    else if(st == "noprojection")
+		      {
+			noprojection = true;
+			inf >> st;
+		      }
+		    else if(st == "refine")
+		      {
+			inf >> st;
+			if(st == "elements")
+			  {
+			    inf >> st;
+			    bool isint = true;
+				for(string::size_type ii=0; isint && ii<st.size(); ii++)
+			      isint = (isdigit(st[ii]) != 0);
+			    
+			    while(inf && isint)
+			      {
+				mtets.Elem(atoi(st.c_str())).marked = 3;
+				marked = 1;
+
+				inf >> st;
+				isint = true;
+				for(string::size_type ii=0; isint && ii<st.size(); ii++)
+				  isint = (isdigit(st[ii]) != 0);
+			      }
+			  }
+			else if(st == "orthobrick")
+			  {
+			    double bounds[6];
+			    for(i=0; i<6; i++)
+			      inf >> bounds[i];
+			    
+			    int cnt = 0;
+
+			    for(ElementIndex ei = 0; ei < mesh.GetNE(); ei++)
+			      {
+				const Element & el = mesh[ei];
+				
+				//
+				Point<3> center(0,0,0);
+				for(i=0; i<el.GetNP(); i++)
+				  {
+				    const MeshPoint & point = mesh[el[i]];
+				    center(0) += point(0);
+				    center(1) += point(1);
+				    center(2) += point(2);
+				  }
+				for(i=0; i<3; i++)
+				  center(i) *= 1./double(el.GetNP());
+				if(bounds[0] <= center(0) && center(0) <= bounds[3] &&
+				   bounds[1] <= center(1) && center(1) <= bounds[4] &&
+				   bounds[2] <= center(2) && center(2) <= bounds[5])
+				  {
+				    mtets[ei].marked = 3;
+				    cnt++;
+				  }
+				
+				  
+// 				bool contained = false;
+// 				for(int i=0; !contained && i<el.GetNP(); i++)
+// 				  {
+// 				    const MeshPoint & point = mesh[el[i]];
+// 				    contained = (bounds[0] <= point.X() && point.X() <= bounds[3] &&
+// 						 bounds[1] <= point.Y() && point.Y() <= bounds[4] &&
+// 						 bounds[2] <= point.Z() && point.Z() <= bounds[5]);
+// 				  }
+// 				if(contained)
+// 				  {
+// 				    mtets[ei].marked = 3;
+// 				    cnt++;
+// 				  }
+			      }
+
+
+			    ostringstream strstr;
+			    strstr.precision(2);
+			    strstr << "marked " << float(cnt)/float(mesh.GetNE())*100. 
+#ifdef WIN32
+				   << "%%"
+#else
+				   << "%"
+#endif
+				   <<" of the elements";
+			    PrintMessage(4,strstr.str());
+
+			    if(cnt > 0)
+			      marked = 1;
+
+
+			    inf >> st;
+			  }
+			else
+			  {
+			    throw NgException("something wrong with refinementinfo file");
+			  }
+		      }
+		  }		
+	      }
+	    else
+	      {
+		inf.close();
+		inf.open(opt.refinementfilename);
+
+		char ch;
+		for (i = 1; i <= mtets.Size(); i++)
+		  {
+		    inf >> ch;
+		    if(!inf)
+		      throw NgException("something wrong with refinementinfo file (old format)");
+		    mtets.Elem(i).marked = (ch == '1');
+		  }
+		marked = 1;
+	      }
+	    inf.close();
+	  }
+
+	else if (opt.usemarkedelements)
+	  {
+	    int cntm = 0;
+
+	    // all in one !
+	    if (mprisms.Size())
+	      {
+		int cnttet = 0;
+		int cntprism = 0;
+		for (i = 1; i <= mesh.GetNE(); i++)
+		  {
+		    if (mesh.VolumeElement(i).GetType() == TET ||
+			mesh.VolumeElement(i).GetType() == TET10)
+		      {
+			cnttet++;
+			mtets.Elem(cnttet).marked =
+			  3 * mesh.VolumeElement(i).TestRefinementFlag();
+			if (mtets.Elem(cnttet).marked)
+			  cntm++;
+		      }
+		    else
+		      {
+			cntprism++;
+			mprisms.Elem(cntprism).marked =
+			  2 * mesh.VolumeElement(i).TestRefinementFlag();
+			if (mprisms.Elem(cntprism).marked)
+			  cntm++;
+		      }
+
+		  }
+	      }
+	    else
+	      for (i = 1; i <= mtets.Size(); i++)
+		{
+		  mtets.Elem(i).marked =
+		    3 * mesh.VolumeElement(i).TestRefinementFlag();
+		  if (mtets.Elem(i).marked)
+		    cntm++;
+		}
+
+	    // (*testout) << "mtets = " << mtets << endl;
+
+	    /*
+	      for (i = 1; i <= mtris.Size(); i++)
+	      mtris.Elem(i).marked = 0;
+	      for (i = 1; i <= mquads.Size(); i++)
+	      mquads.Elem(i).marked = 0;
+	    */
+
+	    if (printmessage_importance>0)
+	      {
+		ostringstream str;
+		str << "marked elements: " << cntm;
+		PrintMessage(4,str.str());
+	      }
+
+	    int cnttrig = 0;
+	    int cntquad = 0;
+	    for (i = 1; i <= mesh.GetNSE(); i++)
+	      {
+		if (mesh.SurfaceElement(i).GetType() == TRIG ||
+		    mesh.SurfaceElement(i).GetType() == TRIG6)
+		  {
+		    cnttrig++;
+		    mtris.Elem(cnttrig).marked =
+		      mesh.SurfaceElement(i).TestRefinementFlag() ? 2 : 0;
+		    // mtris.Elem(cnttrig).marked = 0;
+		    if (mtris.Elem(cnttrig).marked)
+		      cntm++;
+		  }
+		else
+		  {
+		    cntquad++;
+                    // 2d: marked=2, 3d prisms: marked=1
+		    mquads.Elem(cntquad).marked =
+                        mesh.SurfaceElement(i).TestRefinementFlag() ? 4-mesh.GetDimension() : 0 ;
+		    // mquads.Elem(cntquad).marked = 0;
+		    if (mquads.Elem(cntquad).marked)
+		      cntm++;
+		  }
+	      }
+
+              if (printmessage_importance>0)
+		{
+		  ostringstream str;
+		  str << "with surface-elements: " << cntm;
+		  PrintMessage(4,str.str());
+		}
+
+            // he/sz: das wird oben schon richtig gemacht.
+            // hier sind die quads vergessen!
+            /*
+	    if (mesh.GetDimension() == 2)
+	      {
+		cntm = 0;
+		for (i = 1; i <= mtris.Size(); i++)
+		  {
+		    mtris.Elem(i).marked =
+		      2 * mesh.SurfaceElement(i).TestRefinementFlag();
+		    //		  mtris.Elem(i).marked = 2;
+		    if (mtris.Elem(i).marked)
+		      cntm++;
+		  }
+
+		if (!cntm)
+		  {
+		    for (i = 1; i <= mtris.Size(); i++)
+		      {
+			mtris.Elem(i).marked = 2;
+			cntm++;
+		      }
+		  }
+		cout << "trigs: " << mtris.Size() << " ";
+		cout << "marked: " << cntm << endl;
+	      }
+            */ 
+            
+	    marked = (cntm > 0);
+	  }
+	else
+	  {
+	    marked = BTMarkTets (mtets, mprisms, mesh);
+	  }
+
+	if (!marked) break;
+        
+
+	//(*testout) << "mtets " << mtets << endl;
+
+	if (opt.refine_p)
+	  {
+	    PrintMessage(3,"refine p");
+
+	    for (i = 1; i <= mtets.Size(); i++)
+	      mtets.Elem(i).incorder = mtets.Elem(i).marked ? 1 : 0;
+
+	    for (i = 1; i <= mtets.Size(); i++)
+	      if (mtets.Elem(i).incorder)
+		mtets.Elem(i).marked = 0;
+
+
+	    for (i = 1; i <= mprisms.Size(); i++)
+	      mprisms.Elem(i).incorder = mprisms.Elem(i).marked ? 1 : 0;
+
+	    for (i = 1; i <= mprisms.Size(); i++)
+	      if (mprisms.Elem(i).incorder)
+		mprisms.Elem(i).marked = 0;
+
+
+	    for (i = 1; i <= mtris.Size(); i++)
+	      mtris.Elem(i).incorder = mtris.Elem(i).marked ? 1 : 0;
+
+	    for (i = 1; i <= mtris.Size(); i++)
+	      {
+		if (mtris.Elem(i).incorder)
+		  mtris.Elem(i).marked = 0;
+	      }
+	  }
+
+	if (opt.refine_hp)
+	  {
+	    PrintMessage(3,"refine hp");
+	    BitArray singv(np);
+	    singv.Clear();
+
+	    if (mesh.GetDimension() == 3)
+	      {
+		for (i = 1; i <= mesh.GetNSeg(); i++)
+		  {
+		    const Segment & seg = mesh.LineSegment(i);
+		    singv.Set (seg[0]);
+		    singv.Set (seg[1]);
+		  }
+		/*
+		  for ( i=1; i<= mesh.GetNSE(); i++)
+		  {
+		  const Element2d & sel = mesh.SurfaceElement(i);
+		  for(int j=1; j<=sel.GetNP(); j++)
+		  singv.Set(sel.PNum(j));
+		  }
+		*/
+	      }
+	    else
+	      {
+		// vertices with 2 different bnds
+		Array<int> bndind(np);
+		bndind = 0;
+		for (i = 1; i <= mesh.GetNSeg(); i++)
+		  {
+		    const Segment & seg = mesh.LineSegment(i);
+		    for (j = 0; j < 2; j++)
+		      {
+			int pi = (j == 0) ? seg[0] : seg[1];
+			if (bndind.Elem(pi) == 0)
+			  bndind.Elem(pi) = seg.edgenr;
+			else if (bndind.Elem(pi) != seg.edgenr)
+			  singv.Set (pi);
+		      }
+		  }
+	      }
+
+
+
+	    for (i = 1; i <= mtets.Size(); i++)
+	      mtets.Elem(i).incorder = 1;
+	    for (i = 1; i <= mtets.Size(); i++)
+	      {
+		if (!mtets.Elem(i).marked)
+		  mtets.Elem(i).incorder = 0;
+		for (j = 0; j < 4; j++)
+		  if (singv.Test (mtets.Elem(i).pnums[j]))
+		    mtets.Elem(i).incorder = 0;
+	      }
+	    for (i = 1; i <= mtets.Size(); i++)
+	      if (mtets.Elem(i).incorder)
+		mtets.Elem(i).marked = 0;
+
+
+	    for (i = 1; i <= mprisms.Size(); i++)
+	      mprisms.Elem(i).incorder = 1;
+	    for (i = 1; i <= mprisms.Size(); i++)
+	      {
+		if (!mprisms.Elem(i).marked)
+		  mprisms.Elem(i).incorder = 0;
+		for (j = 0; j < 6; j++)
+		  if (singv.Test (mprisms.Elem(i).pnums[j]))
+		    mprisms.Elem(i).incorder = 0;
+	      }
+	    for (i = 1; i <= mprisms.Size(); i++)
+	      if (mprisms.Elem(i).incorder)
+		mprisms.Elem(i).marked = 0;
+
+
+	    for (i = 1; i <= mtris.Size(); i++)
+	      mtris.Elem(i).incorder = 1;
+	    for (i = 1; i <= mtris.Size(); i++)
+	      {
+		if (!mtris.Elem(i).marked)
+		  mtris.Elem(i).incorder = 0;
+		for (j = 0; j < 3; j++)
+		  if (singv.Test (mtris.Elem(i).pnums[j]))
+		    mtris.Elem(i).incorder = 0;
+	      }
+	    for (i = 1; i <= mtris.Size(); i++)
+	      {
+		if (mtris.Elem(i).incorder)
+		  mtris.Elem(i).marked = 0;
+	      }
+	  }
+
+
+
+	int hangingvol, hangingsurf, hangingedge;
+
+	//cout << "write?" << endl;
+	//string yn;
+	//cin >> yn;
+
+	(*testout) << "refine volume elements" << endl;
+	do
+	  {
+	    // refine volume elements
+
+	    int nel = mtets.Size();
+	    for (i = 1; i <= nel; i++)
+	      if (mtets.Elem(i).marked)
+		{
+		  MarkedTet oldtet;
+		  MarkedTet newtet1, newtet2;
+		  int newp;
+
+
+		  oldtet = mtets.Get(i);
+		  //if(yn == "y")
+		  //  (*testout) << "bisected tet " << oldtet;
+		  INDEX_2 edge(oldtet.pnums[oldtet.tetedge1],
+			       oldtet.pnums[oldtet.tetedge2]);
+		  edge.Sort();
+		  if (cutedges.Used (edge))
+		    {
+		      newp = cutedges.Get(edge);
+		    }
+		  else
+		    {
+		      Point<3> npt = Center (mesh.Point (edge.I1()),
+					   mesh.Point (edge.I2()));
+		      newp = mesh.AddPoint (npt);
+		      cutedges.Set (edge, newp);
+		    }
+
+		  BTBisectTet (oldtet, newp, newtet1, newtet2);
+
+		  mtets.Elem(i) = newtet1;
+		  mtets.Append (newtet2);
+
+#ifdef DEBUG
+                  *testout << "tet1 has elnr = " << i << ", tet2 has elnr = " << mtets.Size() << endl;
+#endif
+		  //if(yn == "y")
+		  //  (*testout) << "and got " << newtet1 << "and " << newtet2 << endl;
+
+		  mesh.mlparentelement.Append (i);
+		}
+
+	    int npr = mprisms.Size();
+	    for (i = 1; i <= npr; i++)
+	      if (mprisms.Elem(i).marked)
+		{
+		  MarkedPrism oldprism;
+		  MarkedPrism newprism1, newprism2;
+		  int newp1, newp2;
+
+		  oldprism = mprisms.Get(i);
+		  int pi1 = 0;
+		  if (pi1 == oldprism.markededge)
+		    pi1++;
+		  int pi2 = 3-pi1-oldprism.markededge;
+
+		  INDEX_2 edge1(oldprism.pnums[pi1],
+				oldprism.pnums[pi2]);
+		  INDEX_2 edge2(oldprism.pnums[pi1+3],
+				oldprism.pnums[pi2+3]);
+		  edge1.Sort();
+		  edge2.Sort();
+
+		  if (cutedges.Used (edge1))
+		    newp1 = cutedges.Get(edge1);
+		  else
+		    {
+		      Point<3> npt = Center (mesh.Point (edge1.I1()),
+					    mesh.Point (edge1.I2()));
+		      newp1 = mesh.AddPoint (npt);
+		      cutedges.Set (edge1, newp1);
+		    }
+		  if (cutedges.Used (edge2))
+		    newp2 = cutedges.Get(edge2);
+		  else
+		    {
+		      Point<3> npt = Center (mesh.Point (edge2.I1()),
+					    mesh.Point (edge2.I2()));
+		      newp2 = mesh.AddPoint (npt);
+		      cutedges.Set (edge2, newp2);
+		    }
+		
+
+		  BTBisectPrism (oldprism, newp1, newp2, newprism1, newprism2);
+		  //if(yn == "y")
+		  //  (*testout) << "bisected prism " << oldprism << "and got " << newprism1 << "and " << newprism2 << endl;
+		  mprisms.Elem(i) = newprism1;
+		  mprisms.Append (newprism2);
+		}
+
+	    int nid = mids.Size();
+	    for (i = 1; i <= nid; i++)
+	      if (mids.Elem(i).marked)
+		{
+		  MarkedIdentification oldid,newid1,newid2;
+		  Array<int> newp;
+
+		  oldid = mids.Get(i);
+		  
+		  Array<INDEX_2> edges;
+		  edges.Append(INDEX_2(oldid.pnums[oldid.markededge],
+				       oldid.pnums[(oldid.markededge+1)%oldid.np]));
+		  edges.Append(INDEX_2(oldid.pnums[oldid.markededge + oldid.np],
+				       oldid.pnums[(oldid.markededge+1)%oldid.np + oldid.np]));
+
+		  if(oldid.np == 4)
+		    {
+		      edges.Append(INDEX_2(oldid.pnums[(oldid.markededge+2)%oldid.np],
+					   oldid.pnums[(oldid.markededge+3)%oldid.np]));
+		      edges.Append(INDEX_2(oldid.pnums[(oldid.markededge+2)%oldid.np + oldid.np],
+					   oldid.pnums[(oldid.markededge+3)%oldid.np + oldid.np]));
+		    }
+		  for (j = 0; j < edges.Size(); j++)
+		    {
+		      edges[j].Sort();
+
+		      if(cutedges.Used(edges[j]))
+			newp.Append(cutedges.Get(edges[j]));
+		      else
+			{
+			  Point<3> npt = Center (mesh.Point (edges[j].I1()),
+						mesh.Point (edges[j].I2()));
+			  newp.Append(mesh.AddPoint(npt));
+			  cutedges.Set(edges[j],newp[j]);
+			}			 
+		    }
+		  
+		  BTBisectIdentification(oldid,newp,newid1,newid2);
+		  mids.Elem(i) = newid1;
+		  mids.Append(newid2);		  
+		}
+
+	    
+	    //IdentifyCutEdges(mesh, cutedges);
+
+
+	    hangingvol = 
+	      MarkHangingTets (mtets, cutedges) +
+	      MarkHangingPrisms (mprisms, cutedges) +
+	      MarkHangingIdentifications (mids, cutedges);
+
+
+	    int nsel = mtris.Size();
+
+	    for (i = 1; i <= nsel; i++)
+	      if (mtris.Elem(i).marked)
+		{
+		  MarkedTri oldtri;
+		  MarkedTri newtri1, newtri2;
+		  PointIndex newp;
+		
+		  oldtri = mtris.Get(i);
+		  int oldpi1 = oldtri.pnums[(oldtri.markededge+1)%3];
+		  int oldpi2 = oldtri.pnums[(oldtri.markededge+2)%3];
+		  INDEX_2 edge(oldpi1, oldpi2);
+		  edge.Sort();
+
+		  //		cerr << "edge = " << edge.I1() << "-" << edge.I2() << endl;
+
+		  if (cutedges.Used (edge))
+		    {
+		      newp = cutedges.Get(edge);
+		    }
+		  else
+		    {
+		      Point<3> npt = Center (mesh.Point (edge.I1()),
+					    mesh.Point (edge.I2()));
+		      newp = mesh.AddPoint (npt);
+                      cutedges.Set (edge, newp);
+		    }
+		  //		newp = cutedges.Get(edge);
+		
+		  int si = mesh.GetFaceDescriptor (oldtri.surfid).SurfNr();
+		  //  geom->GetSurface(si)->Project (mesh.Point(newp));
+		  PointGeomInfo npgi;
+		
+//                   cerr << "project point " << newp << " old: " << mesh.Point(newp);
+                  if (mesh[newp].Type() != EDGEPOINT)
+                    PointBetween (mesh.Point (oldpi1), mesh.Point (oldpi2),
+                                  0.5, si,
+                                  oldtri.pgeominfo[(oldtri.markededge+1)%3],
+                                  oldtri.pgeominfo[(oldtri.markededge+2)%3],
+                                  mesh.Point (newp), npgi);
+//                   cerr << " new: " << mesh.Point(newp) << endl;
+		
+		  BTBisectTri (oldtri, newp, npgi, newtri1, newtri2);
+		  //if(yn == "y")
+		  //  (*testout) << "bisected tri " << oldtri << "and got " << newtri1 << "and " << newtri2 << endl;
+		
+		
+		  mtris.Elem(i) = newtri1;
+		  mtris.Append (newtri2);
+		  mesh.mlparentsurfaceelement.Append (i);
+		}
+	  
+	    int nquad = mquads.Size();
+	    for (i = 1; i <= nquad; i++)
+	      if (mquads.Elem(i).marked)
+		{
+		  MarkedQuad oldquad;
+		  MarkedQuad newquad1, newquad2;
+		  int newp1, newp2;
+		
+		  oldquad = mquads.Get(i);
+                  /*
+		  INDEX_2 edge1(oldquad.pnums[0],
+				oldquad.pnums[1]);
+		  INDEX_2 edge2(oldquad.pnums[2],
+				oldquad.pnums[3]);
+                  */
+                  INDEX_2 edge1, edge2;
+                  PointGeomInfo pgi11, pgi12, pgi21, pgi22;
+                  if (oldquad.markededge==0 || oldquad.markededge==2)
+                  {
+                    edge1.I1()=oldquad.pnums[0]; pgi11=oldquad.pgeominfo[0];
+                    edge1.I2()=oldquad.pnums[1]; pgi12=oldquad.pgeominfo[1];
+                    edge2.I1()=oldquad.pnums[2]; pgi21=oldquad.pgeominfo[2];
+                    edge2.I2()=oldquad.pnums[3]; pgi22=oldquad.pgeominfo[3];
+                  }
+                  else // 3 || 1
+                  {
+                    edge1.I1()=oldquad.pnums[0]; pgi11=oldquad.pgeominfo[0];
+                    edge1.I2()=oldquad.pnums[2]; pgi12=oldquad.pgeominfo[2];
+                    edge2.I1()=oldquad.pnums[1]; pgi21=oldquad.pgeominfo[1];
+                    edge2.I2()=oldquad.pnums[3]; pgi22=oldquad.pgeominfo[3];
+                  }
+                  
+                  edge1.Sort();
+		  edge2.Sort();
+
+		  if (cutedges.Used (edge1))
+		    {
+		      newp1 = cutedges.Get(edge1);
+		    }
+		  else
+		    {
+		      Point<3> np1 = Center (mesh.Point (edge1.I1()),
+					   mesh.Point (edge1.I2()));
+		      newp1 = mesh.AddPoint (np1);
+		      cutedges.Set (edge1, newp1);
+                    }
+
+		  if (cutedges.Used (edge2))
+		    {
+		      newp2 = cutedges.Get(edge2);
+		    }
+		  else
+		    {
+		      Point<3> np2 = Center (mesh.Point (edge2.I1()),
+					   mesh.Point (edge2.I2()));
+		      newp2 = mesh.AddPoint (np2);
+		      cutedges.Set (edge2, newp2);
+                    }
+
+		  PointGeomInfo npgi1, npgi2;
+		
+		  int si = mesh.GetFaceDescriptor (oldquad.surfid).SurfNr();
+		  //		geom->GetSurface(si)->Project (mesh.Point(newp1));
+		  //		geom->GetSurface(si)->Project (mesh.Point(newp2));
+
+//                   (*testout)
+//                   cerr << "project point 1 " << newp1 << " old: " << mesh.Point(newp1);
+                  PointBetween (mesh.Point (edge1.I1()), mesh.Point (edge1.I2()),
+				0.5, si,
+				pgi11,
+				pgi12,
+				mesh.Point (newp1), npgi1);
+// 		  (*testout)
+//                   cerr << " new: " << mesh.Point(newp1) << endl;
+
+		
+//                   cerr << "project point 2 " << newp2 << " old: " << mesh.Point(newp2);
+                  PointBetween (mesh.Point (edge2.I1()), mesh.Point (edge2.I2()),
+				0.5, si,
+				pgi21,
+				pgi22,
+				mesh.Point (newp2), npgi2);
+//                   cerr << " new: " << mesh.Point(newp2) << endl;
+		
+
+		  BTBisectQuad (oldquad, newp1, npgi1, newp2, npgi2,
+				newquad1, newquad2);
+                  
+		  mquads.Elem(i) = newquad1;
+		  mquads.Append (newquad2);
+		}
+	  
+
+	    hangingsurf = 
+	      MarkHangingTris (mtris, cutedges) +
+	      MarkHangingQuads (mquads, cutedges);
+
+	    hangingedge = 0;
+	  
+	    int nseg = mesh.GetNSeg ();
+	    for (i = 1; i <= nseg; i++)
+	      {
+		Segment & seg = mesh.LineSegment (i);
+		INDEX_2 edge(seg[0], seg[1]);
+		edge.Sort();
+		if (cutedges.Used (edge))
+		  {
+		    hangingedge = 1;
+		    Segment nseg1 = seg;
+		    Segment nseg2 = seg;
+		  
+		    int newpi = cutedges.Get(edge);
+		  
+		    nseg1[1] = newpi;
+		    nseg2[0] = newpi;
+		  
+		    EdgePointGeomInfo newepgi;
+		  
+ 
+//                     
+//                     cerr << "move edgepoint " << newpi << " from " << mesh.Point(newpi);
+		    PointBetween (mesh.Point (seg[0]), mesh.Point (seg[1]),
+				  0.5, seg.surfnr1, seg.surfnr2, 
+				  seg.epgeominfo[0], seg.epgeominfo[1],
+				  mesh.Point (newpi), newepgi);
+// 		    cerr << " to " << mesh.Point (newpi) << endl;
+
+		    
+		    nseg1.epgeominfo[1] = newepgi;
+		    nseg2.epgeominfo[0] = newepgi;
+		  
+		    mesh.LineSegment (i) = nseg1;
+		    mesh.AddSegment (nseg2);
+		  }
+	      }
+	  }
+	while (hangingvol || hangingsurf || hangingedge);
+        
+	/*
+        if (printmessage_importance>0)
+	  {
+	    ostringstream strstr;
+	    strstr << mtets.Size() << " tets" << endl
+		   << mtris.Size() << " trigs" << endl;
+	    if (mprisms.Size())
+	      {
+		strstr << mprisms.Size() << " prisms" << endl
+		       << mquads.Size() << " quads" << endl;
+	      }
+	    strstr << mesh.GetNP() << " points";
+	    PrintMessage(4,strstr.str());
+	  }
+	*/
+	PrintMessage (4, mtets.Size(), " tets");
+	PrintMessage (4, mtris.Size(), " trigs");
+	if (mprisms.Size())
+	  {
+	    PrintMessage (4, mprisms.Size(), " prisms");
+	    PrintMessage (4, mquads.Size(), " quads");
+	  }
+	PrintMessage (4, mesh.GetNP(), " points");
+      }
+
+
+    // (*testout) << "mtets = " << mtets << endl;
+
+    if (opt.refine_hp)
+      {
+	//
+	Array<int> v_order (mesh.GetNP());
+	v_order = 0;
+	if (mesh.GetDimension() == 3)
+	  {
+	    for (i = 1; i <= mtets.Size(); i++)
+	      if (mtets.Elem(i).incorder)
+		mtets.Elem(i).order++;
+      
+	    for (i = 0; i < mtets.Size(); i++)
+	      for (j = 0; j < 4; j++)
+		if (int(mtets[i].order) > v_order.Elem(mtets[i].pnums[j]))
+		  v_order.Elem(mtets[i].pnums[j]) = mtets[i].order;
+	    for (i = 0; i < mtets.Size(); i++)
+	      for (j = 0; j < 4; j++)
+		if (int(mtets[i].order) < v_order.Elem(mtets[i].pnums[j])-1)
+		  mtets[i].order = v_order.Elem(mtets[i].pnums[j])-1;
+	  }
+	else
+	  {
+	    for (i = 1; i <= mtris.Size(); i++)
+	      if (mtris.Elem(i).incorder)
+		{
+		  mtris.Elem(i).order++;
+		}
+
+	    for (i = 0; i < mtris.Size(); i++)
+	      for (j = 0; j < 3; j++)
+		if (int(mtris[i].order) > v_order.Elem(mtris[i].pnums[j]))
+		  v_order.Elem(mtris[i].pnums[j]) = mtris[i].order;
+	    for (i = 0; i < mtris.Size(); i++)
+	      {
+		for (j = 0; j < 3; j++)
+		  if (int(mtris[i].order) < v_order.Elem(mtris[i].pnums[j])-1)
+		    mtris[i].order = v_order.Elem(mtris[i].pnums[j])-1;
+	      }
+	  }
+      }
+  
+    mtets.SetAllocSize (mtets.Size());
+    mprisms.SetAllocSize (mprisms.Size());
+    mids.SetAllocSize (mids.Size());
+    mtris.SetAllocSize (mtris.Size());
+    mquads.SetAllocSize (mquads.Size());
+  
+    
+    mesh.ClearVolumeElements();
+    mesh.VolumeElements().SetAllocSize (mtets.Size()+mprisms.Size());
+    for (i = 1; i <= mtets.Size(); i++)
+      {
+	Element el(TET);
+	el.SetIndex (mtets.Get(i).matindex);
+	for (j = 1; j <= 4; j++)
+	  el.PNum(j) = mtets.Get(i).pnums[j-1];
+	el.SetOrder (mtets.Get(i).order);
+	mesh.AddVolumeElement (el);
+      }
+    for (i = 1; i <= mprisms.Size(); i++)
+      {
+	Element el(PRISM);
+	el.SetIndex (mprisms.Get(i).matindex);
+	for (j = 1; j <= 6; j++)
+	  el.PNum(j) = mprisms.Get(i).pnums[j-1];
+	el.SetOrder (mprisms.Get(i).order);
+
+	// degenerated prism ?
+	static const int map1[] = { 3, 2, 5, 6, 1 };
+	static const int map2[] = { 1, 3, 6, 4, 2 };
+	static const int map3[] = { 2, 1, 4, 5, 3 };
+      
+
+	const int * map = NULL;
+	int deg1 = 0, deg2 = 0, deg3 = 0;
+	// int deg = 0;
+	if (el.PNum(1) == el.PNum(4)) { map = map1; deg1 = 1; }
+	if (el.PNum(2) == el.PNum(5)) { map = map2; deg2 = 1; }
+	if (el.PNum(3) == el.PNum(6)) { map = map3; deg3 = 1; }
+	  
+	switch (deg1+deg2+deg3)
+	  {
+	  case 1:
+	    {
+	      for (j = 1; j <= 5; j++)
+		el.PNum(j) = mprisms.Get(i).pnums[map[j-1]-1];
+	    
+	      el.SetType (PYRAMID);
+	      break;
+	    }
+	  case 2:
+	    {
+	      static const int tetmap1[] = { 1, 2, 3, 4 };
+	      static const int tetmap2[] = { 2, 3, 1, 5 };
+	      static const int tetmap3[] = { 3, 1, 2, 6 };
+	      if (!deg1) map = tetmap1;
+	      if (!deg2) map = tetmap2;
+	      if (!deg3) map = tetmap3; 
+	      for (j = 1; j <= 4; j++)
+		el.PNum(j) = mprisms.Get(i).pnums[map[j-1]-1];
+	      /*
+		if (!deg1) el.PNum(4) = el.PNum(4);
+		if (!deg2) el.PNum(4) = el.PNum(5);
+		if (!deg3) el.PNum(4) = el.PNum(6);
+	      */
+	      el.SetType(TET);
+	      break;
+	    }
+	  default:
+	    ;
+	  }
+	mesh.AddVolumeElement (el);
+      }
+  
+    mesh.ClearSurfaceElements();
+    for (i = 1; i <= mtris.Size(); i++)
+      {
+	Element2d el(TRIG);
+	el.SetIndex (mtris.Get(i).surfid);
+	el.SetOrder (mtris.Get(i).order);
+	for (j = 1; j <= 3; j++)
+	  {
+	    el.PNum(j) = mtris.Get(i).pnums[j-1];
+	    el.GeomInfoPi(j) = mtris.Get(i).pgeominfo[j-1];
+	  }
+	mesh.AddSurfaceElement (el);
+      }
+    for (i = 1; i <= mquads.Size(); i++)
+      {
+	Element2d el(QUAD);
+	el.SetIndex (mquads.Get(i).surfid);
+	for (j = 1; j <= 4; j++)
+	  el.PNum(j) = mquads.Get(i).pnums[j-1];
+	Swap (el.PNum(3), el.PNum(4));
+	mesh.AddSurfaceElement (el);
+      }
+
+
+      
+    // write multilevel hierarchy to mesh:
+    np = mesh.GetNP();
+    mesh.mlbetweennodes.SetSize(np);
+    if (mesh.mglevels <= 2)
+      {
+	PrintMessage(4,"RESETTING mlbetweennodes");
+	for (i = 1; i <= np; i++)
+	  {
+	    mesh.mlbetweennodes.Elem(i).I1() = 0;
+	    mesh.mlbetweennodes.Elem(i).I2() = 0;
+	  }
+      }
+
+    /*
+      for (i = 1; i <= cutedges.GetNBags(); i++)
+      for (j = 1; j <= cutedges.GetBagSize(i); j++)
+      {
+      INDEX_2 edge;
+      int newpi;
+      cutedges.GetData (i, j, edge, newpi);
+      mesh.mlbetweennodes.Elem(newpi) = edge;
+      }
+    */
+
+    BitArray isnewpoint(np);
+    isnewpoint.Clear();
+
+    for (i = 1; i <= cutedges.Size(); i++)
+      if (cutedges.UsedPos(i))
+	{
+	  INDEX_2 edge;
+	  int newpi;
+	  cutedges.GetData (i, edge, newpi);
+	  isnewpoint.Set(newpi);
+	  mesh.mlbetweennodes.Elem(newpi) = edge;
+	}
+
+
+    /*
+      mesh.PrintMemInfo (cout);
+      cout << "tets ";
+      mtets.PrintMemInfo (cout);
+      cout << "prims ";
+      mprisms.PrintMemInfo (cout);
+      cout << "tris ";
+      mtris.PrintMemInfo (cout);
+      cout << "quads ";
+      mquads.PrintMemInfo (cout);
+      cout << "cutedges ";
+      cutedges.PrintMemInfo (cout);
+    */
+
+
+    /*
+
+    // find connected nodes (close nodes)
+    TABLE<int> conto(np);
+    for (i = 1; i <= mprisms.Size(); i++)
+    for (j = 1; j <= 6; j++)
+    {
+    int n1 = mprisms.Get(i).pnums[j-1];
+    int n2 = mprisms.Get(i).pnums[(j+2)%6];
+    //	    if (n1 != n2)
+    {
+    int found = 0;
+    for (k = 1; k <= conto.EntrySize(n1); k++)
+    if (conto.Get(n1, k) == n2)
+    {
+    found = 1;
+    break;
+    }
+    if (!found)
+    conto.Add (n1, n2);
+    }
+    }
+    mesh.connectedtonode.SetSize(np);
+    for (i = 1; i <= np; i++)
+    mesh.connectedtonode.Elem(i) = 0;
+  
+
+    //       (*testout) << "connection table: " << endl;
+    //       for (i = 1; i <= np; i++)
+    //       {
+    //       (*testout) << "node " << i << ": ";
+    // 	  for (j = 1; j <= conto.EntrySize(i); j++)
+    // 	  (*testout) << conto.Get(i, j) << " ";
+    // 	  (*testout) << endl;
+    // 	}
+
+  
+    for (i = 1; i <= np; i++)
+    if (mesh.connectedtonode.Elem(i) == 0)
+    {
+    mesh.connectedtonode.Elem(i) = i;
+    ConnectToNodeRec (i, i, conto, mesh.connectedtonode);
+    }
+    */  
+
+    //  mesh.BuildConnectedNodes();
+
+    
+
+
+    mesh.ComputeNVertices();
+    mesh.RebuildSurfaceElementLists();
+  
+    
+    // update identification tables
+    for (i = 1; i <= mesh.GetIdentifications().GetMaxNr(); i++)
+      {
+	Array<int,PointIndex::BASE> identmap;
+
+	mesh.GetIdentifications().GetMap (i, identmap);
+
+
+	/*
+	  for (j = 1; j <= cutedges.GetNBags(); j++)
+	  for (k = 1; k <= cutedges.GetBagSize(j); k++)
+	  {
+	  INDEX_2 i2;
+	  int newpi;
+	  cutedges.GetData (j, k, i2, newpi);
+	  INDEX_2 oi2(identmap.Get(i2.I1()),
+	  identmap.Get(i2.I2()));
+	  oi2.Sort();
+	  if (cutedges.Used (oi2))
+	  {
+	  int onewpi = cutedges.Get(oi2);
+	  mesh.GetIdentifications().Add (newpi, onewpi, i);
+	  }
+	  }
+	*/
+
+	for (j = 1; j <= cutedges.Size(); j++)
+	  if (cutedges.UsedPos(j))
+	    {
+	      INDEX_2 i2;
+	      int newpi;
+	      cutedges.GetData (j, i2, newpi);
+	      INDEX_2 oi2(identmap.Get(i2.I1()),
+			  identmap.Get(i2.I2()));
+	      oi2.Sort();
+	      if (cutedges.Used (oi2))
+		{
+		  int onewpi = cutedges.Get(oi2);
+		  mesh.GetIdentifications().Add (newpi, onewpi, i);
+		}
+	    }
+      }
+
+
+    // Repair works only for tets!
+    bool do_repair = mesh.PureTetMesh ();
+
+    do_repair = false;   // JS, March 2009: multigrid crashes
+
+    //if(mesh.mglevels == 3)
+    //  noprojection = true;
+
+    //noprojection = true;
+
+    if(noprojection)
+      {
+	do_repair = false;
+	for(int ii=1; ii<=mesh.GetNP(); ii++)
+	  {
+	    if(isnewpoint.Test(ii) && mesh.mlbetweennodes[ii][0] > 0)
+	      {
+		mesh.Point(ii) = Center(mesh.Point(mesh.mlbetweennodes[ii][0]),
+                                        mesh.Point(mesh.mlbetweennodes[ii][1]));
+	      }
+	  }
+      }
+
+
+    // Check/Repair
+
+    static bool repaired_once;
+    if(mesh.mglevels == 1)
+      repaired_once = false;
+
+    //mesh.Save("before.vol");
+
+    static int reptimer = NgProfiler::CreateTimer("check/repair");
+	NgProfiler::RegionTimer * regt(NULL);
+    regt = new NgProfiler::RegionTimer(reptimer); 
+
+    Array<ElementIndex> bad_elts;
+    Array<double> pure_badness;
+   
+    if(do_repair || quality_loss != NULL)
+      {
+	pure_badness.SetSize(mesh.GetNP()+2);
+	GetPureBadness(mesh,pure_badness,isnewpoint);
+      }
+
+
+    if(do_repair)  // by Markus W
+      {
+	const double max_worsening = 1;
+	
+	const bool uselocalworsening = false;
+	
+	bool repaired = false;
+	
+	Validate(mesh,bad_elts,pure_badness,max_worsening,uselocalworsening);
+	
+        if (printmessage_importance>0)
+	  {
+	    ostringstream strstr;
+	    for(int ii=0; ii<bad_elts.Size(); ii++)
+	      strstr << "bad element " << bad_elts[ii] << "\n";
+	    PrintMessage(1,strstr.str());
+	  }
+	if(repaired_once || bad_elts.Size() > 0)
+	  {
+	    clock_t t1(clock());
+	    
+	    
+	    // update id-maps
+	    j=0;
+	    for(i=1; i<=mesh.GetIdentifications().GetMaxNr(); i++)
+	      {
+		if(mesh.GetIdentifications().GetType(i) == Identifications::PERIODIC)
+		  {
+		    mesh.GetIdentifications().GetMap(i,*idmaps[j],true);
+		    j++;
+		  }
+	      }
+
+    
+	    // do the repair
+	    try
+	      {
+		RepairBisection(mesh,bad_elts,isnewpoint,*this,
+				pure_badness,
+				max_worsening,uselocalworsening,
+				idmaps);
+		repaired = true;
+		repaired_once = true;
+	      }
+	    catch(NgException & ex)
+	      {
+		PrintMessage(1,string("Problem: ") + ex.What());
+	      }
+
+
+            if (printmessage_importance>0)
+            {
+	      ostringstream strstr;
+              strstr << "Time for Repair: " << double(clock() - t1)/double(CLOCKS_PER_SEC) << endl
+		     << "bad elements after repair: " << bad_elts << endl;
+	      PrintMessage(1,strstr.str());
+            }
+	    
+	    if(quality_loss != NULL)
+	      Validate(mesh,bad_elts,pure_badness,1e100,uselocalworsening,quality_loss);
+
+	    if(idmaps.Size() == 0)
+	      UpdateEdgeMarks(mesh,idmaps);
+	    
+	    /*
+	    if(1==1)
+	      UpdateEdgeMarks(mesh,idmaps);
+	    else
+	      mesh.mglevels = 1;
+	    */
+	    
+	    //mesh.ImproveMesh();
+	    
+	  }
+      }
+    delete regt;
+
+
+    
+    for(i=0; i<idmaps.Size(); i++)
+      delete idmaps[i];
+    idmaps.DeleteAll();
+
+    mesh.UpdateTopology();
+
+
+
+
+    if(refelementinfofilewrite != "")
+      {
+	PrintMessage(3,"writing marked-elements information to \"",refelementinfofilewrite,"\"");
+	ofstream ofst(refelementinfofilewrite.c_str());
+
+	WriteMarkedElements(ofst);
+
+	ofst.close();
+      }
+
+
+    mesh.CalcSurfacesOfNode();
+
+    PrintMessage (1, "Bisection done");
+
+    PopStatus();
+  }
+
+
+
+
+  BisectionOptions :: BisectionOptions ()
+  {
+    outfilename = NULL;
+    mlfilename = NULL;
+    refinementfilename = NULL;
+    femcode = NULL;
+    maxlevel = 50;
+    usemarkedelements = 0;
+    refine_hp = 0;
+    refine_p = 0;
+  }
+
+
+  Refinement :: Refinement ()
+  {
+    optimizer2d = NULL;
+  }
+
+  Refinement :: ~Refinement ()
+  {
+    ;
+  }
+
+
+  void Refinement :: PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+				   int surfi, 
+				   const PointGeomInfo & gi1, 
+				   const PointGeomInfo & gi2,
+				   Point<3> & newp, PointGeomInfo & newgi) const
+  {
+    newp = p1+secpoint*(p2-p1);
+  }
+
+  void Refinement :: PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+				   int surfi1, int surfi2, 
+				   const EdgePointGeomInfo & ap1, 
+				   const EdgePointGeomInfo & ap2,
+				   Point<3> & newp, EdgePointGeomInfo & newgi) const
+  {
+    cout << "base class edge point between" << endl;
+    newp = p1+secpoint*(p2-p1);
+  }
+
+
+  Vec<3> Refinement :: GetTangent (const Point<3> & p, int surfi1, int surfi2,
+                                   const EdgePointGeomInfo & ap1) const
+  {
+    cerr << "Refinement::GetTangent not overloaded" << endl;
+    return Vec<3> (0,0,0);
+  }
+
+  Vec<3> Refinement :: GetNormal (const Point<3> & p, int surfi1, 
+                                  const PointGeomInfo & gi) const
+  {
+    cerr << "Refinement::GetNormal not overloaded" << endl;
+    return Vec<3> (0,0,0);
+  }
+
+
+  void Refinement :: ProjectToSurface (Point<3> & p, int surfi) const
+  {
+    if (printmessage_importance>0)
+      cerr << "Refinement :: ProjectToSurface    ERROR: no geometry set" << endl;
+  };
+
+  void Refinement :: ProjectToEdge (Point<3> & p, int surfi1, int surfi2, const EdgePointGeomInfo & egi) const
+  {
+    cerr << "Refinement::ProjectToEdge not overloaded" << endl;
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/bisect.hpp b/contrib/Netgen/libsrc/meshing/bisect.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2da9b97e8d068b1ca3d096e7fc3eb49dd914ac78
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/bisect.hpp
@@ -0,0 +1,102 @@
+#ifndef BISECT
+#define BISECT
+
+class BisectionOptions
+{
+public:
+  const char * outfilename;
+  const char * mlfilename;
+  const char * refinementfilename;
+  const char * femcode;
+  int maxlevel;
+  int usemarkedelements;
+  bool refine_hp;
+  bool refine_p;
+  BisectionOptions ();
+};
+
+class ZRefinementOptions
+{
+public:
+  int minref;
+  ZRefinementOptions();
+};
+
+
+/*
+extern void BisectTets (Mesh &, const CSGeometry *,
+			BisectionOptions & opt);
+*/
+
+extern void BisectTetsCopyMesh (Mesh &, const class CSGeometry *,
+				BisectionOptions & opt);
+
+extern void ZRefinement (Mesh &, const class NetgenGeometry *,
+			 ZRefinementOptions & opt);
+
+
+
+
+
+class DLL_HEADER Refinement
+{
+  MeshOptimize2d * optimizer2d;
+
+public:
+  Refinement ();
+  virtual ~Refinement ();
+  
+  void Refine (Mesh & mesh) const;
+  void Refine (Mesh & mesh);
+  void Bisect (Mesh & mesh, class BisectionOptions & opt, Array<double> * quality_loss = NULL) const;
+
+  void MakeSecondOrder (Mesh & mesh) const;
+  void MakeSecondOrder (Mesh & mesh);
+
+  virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint, 
+			     int surfi, 
+			     const PointGeomInfo & gi1, 
+			     const PointGeomInfo & gi2,
+			     Point<3> & newp, PointGeomInfo & newgi) const;
+
+  virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+			     int surfi1, int surfi2, 
+			     const EdgePointGeomInfo & ap1, 
+			     const EdgePointGeomInfo & ap2,
+			     Point<3> & newp, EdgePointGeomInfo & newgi) const;
+
+  virtual Vec<3> GetTangent (const Point<3> & p, int surfi1, int surfi2,
+                             const EdgePointGeomInfo & egi) const;
+
+  virtual Vec<3> GetNormal (const Point<3> & p, int surfi1, 
+                            const PointGeomInfo & gi) const;
+
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi) const;
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi, const PointGeomInfo & /* gi */) const
+  {
+    ProjectToSurface (p, surfi);
+  }
+
+  virtual void ProjectToEdge (Point<3> & p, int surfi1, int surfi2, const EdgePointGeomInfo & egi) const;
+
+
+  void ValidateSecondOrder (Mesh & mesh);
+  void ValidateRefinedMesh (Mesh & mesh, 
+			    Array<INDEX_2> & parents);
+
+  MeshOptimize2d * Get2dOptimizer(void) const
+  {
+    return optimizer2d;
+  }
+  void Set2dOptimizer(MeshOptimize2d * opti)
+  {
+    optimizer2d = opti;
+  }
+
+  
+  virtual void LocalizeEdgePoints(Mesh & /* mesh */) const {;}
+};
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/boundarylayer.cpp b/contrib/Netgen/libsrc/meshing/boundarylayer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..94bfcbe0d0038f773c7923936679e79f8d8d4e5e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/boundarylayer.cpp
@@ -0,0 +1,610 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+   void InsertVirtualBoundaryLayer (Mesh & mesh)
+   {
+      cout << "Insert virt. b.l." << endl;
+
+      int surfid;
+
+      cout << "Boundary Nr:";
+      cin >> surfid;
+
+      int i, j;
+      int np = mesh.GetNP();
+
+      cout << "Old NP: " << mesh.GetNP() << endl;
+      cout << "Trigs: " << mesh.GetNSE() << endl;
+
+      BitArray bndnodes(np);
+      Array<int> mapto(np);
+
+      bndnodes.Clear();
+      for (i = 1; i <= mesh.GetNSeg(); i++)
+      {
+         int snr = mesh.LineSegment(i).edgenr;
+         cout << "snr = " << snr << endl;
+         if (snr == surfid)
+         {
+            bndnodes.Set (mesh.LineSegment(i)[0]);
+            bndnodes.Set (mesh.LineSegment(i)[1]);
+         }
+      }
+      for (i = 1; i <= mesh.GetNSeg(); i++)
+      {
+         int snr = mesh.LineSegment(i).edgenr;
+         if (snr != surfid)
+         {
+            bndnodes.Clear (mesh.LineSegment(i)[0]);
+            bndnodes.Clear (mesh.LineSegment(i)[1]);
+         }
+      }
+
+      for (i = 1; i <= np; i++)
+      {
+         if (bndnodes.Test(i))
+            mapto.Elem(i) = mesh.AddPoint (mesh.Point (i));
+         else
+            mapto.Elem(i) = 0;
+      }
+
+      for (i = 1; i <= mesh.GetNSE(); i++)
+      {
+         Element2d & el = mesh.SurfaceElement(i);
+         for (j = 1; j <= el.GetNP(); j++)
+            if (mapto.Get(el.PNum(j)))
+               el.PNum(j) = mapto.Get(el.PNum(j));
+      }
+
+
+      int nq = 0;
+      for (i = 1; i <= mesh.GetNSeg(); i++)
+      {
+         int snr = mesh.LineSegment(i).edgenr;
+         if (snr == surfid)
+         {
+            int p1 = mesh.LineSegment(i)[0];
+            int p2 = mesh.LineSegment(i)[1];
+            int p3 = mapto.Get (p1);
+            if (!p3) p3 = p1;
+            int p4 = mapto.Get (p2);
+            if (!p4) p4 = p2;
+
+            Element2d el(QUAD);
+            el.PNum(1) = p1;
+            el.PNum(2) = p2;
+            el.PNum(3) = p3;
+            el.PNum(4) = p4;
+            el.SetIndex (2);
+            mesh.AddSurfaceElement (el);
+            nq++;
+         }
+      }
+
+      cout << "New NP: " << mesh.GetNP() << endl;
+      cout << "Quads: " << nq << endl;
+   }
+
+
+
+
+
+/*
+   Philippose Rajan - 11 June 2009
+
+   Function to calculate the surface normal at a given 
+   vertex of a surface element, with respect to that 
+   surface element.
+
+   This function is used by the boundary layer generation 
+   function, in order to calculate the effective direction 
+   in which the prismatic layer should grow
+*/
+   void GetSurfaceNormal(Mesh & mesh, Element2d & el, int Vertex, Vec3d & SurfaceNormal)
+   {
+      int Vertex_A;
+      int Vertex_B;
+
+      Vertex_A = Vertex + 1;
+      if(Vertex_A > el.GetNP()) Vertex_A = 1;
+
+      Vertex_B = Vertex - 1;
+      if(Vertex_B <= 0) Vertex_B = el.GetNP();
+
+      Vec3d Vect_A,Vect_B;
+      
+      Vect_A = mesh.Point(el.PNum(Vertex_A)) - mesh.Point(el.PNum(Vertex));
+      Vect_B = mesh.Point(el.PNum(Vertex_B)) - mesh.Point(el.PNum(Vertex));
+
+      SurfaceNormal = Cross(Vect_A,Vect_B);
+      SurfaceNormal.Normalize();
+   }
+
+
+
+
+
+/*
+    Philippose Rajan - 11 June 2009
+    
+    Added an initial experimental function for 
+    generating prismatic boundary layers on 
+    a given set of surfaces.
+    
+    The number of layers, height of the first layer 
+    and the growth / shrink factor can be specified 
+    by the user
+
+    Currently, the layer height is calculated using:
+    height = h_first_layer * (growth_factor^(num_layers - 1))
+*/
+   void GenerateBoundaryLayer (Mesh & mesh, MeshingParameters & mp)
+   {
+      int i, j;
+
+      ofstream dbg("BndLayerDebug.log");
+
+      // Angle between a surface element and a growth-vector below which 
+      // a prism is project onto that surface as a quad
+      // (in degrees)
+      double angleThreshold = 5.0;
+      
+      cout << "Generate Prismatic Boundary Layers (Experimental)...." << endl;
+
+      // Use an array to support creation of boundary 
+      // layers for multiple surfaces in the future...
+      Array<int> surfid;
+      int surfinp = 0;
+      int prismlayers = 1;
+      double hfirst = 0.01;
+      double growthfactor = 1.0;
+
+      // Monitor and print out the number of prism and quad elements 
+      // added to the mesh
+      int numprisms = 0;
+      int numquads = 0;
+
+      while(surfinp >= 0)
+      {
+         cout << "Enter Surface ID (-1 to end list): ";
+         cin >> surfinp;
+         if(surfinp >= 0) surfid.Append(surfinp);
+      }
+
+      cout << "Number of surfaces entered = " << surfid.Size() << endl; 
+      cout << "Selected surfaces are:" << endl;
+
+      for(i = 1; i <= surfid.Size(); i++)
+      {
+         cout << "Surface " << i << ": " << surfid.Elem(i) << endl;
+      }
+      
+      cout << endl << "Enter number of prism layers: ";
+      cin >> prismlayers;
+      if(prismlayers < 1) prismlayers = 1;
+
+      cout << "Enter height of first layer: ";
+      cin >> hfirst;
+      if(hfirst <= 0.0) hfirst = 0.01;
+
+      cout << "Enter layer growth / shrink factor: ";
+      cin >> growthfactor;
+      if(growthfactor <= 0.0) growthfactor = 0.5;
+
+      cout << "Old NP: " << mesh.GetNP() << endl;
+      cout << "Old NSE: " << mesh.GetNSE() << endl;
+      
+      for(int layer = prismlayers; layer >= 1; layer--)
+      {
+         cout << "Generating layer: " << layer << endl;
+
+         const MeshTopology& meshtopo = mesh.GetTopology();
+         const_cast<MeshTopology &> (meshtopo).SetBuildEdges(true);
+         const_cast<MeshTopology &> (meshtopo).SetBuildFaces(true);
+         const_cast<MeshTopology &> (meshtopo).Update();
+
+         double layerht = hfirst;
+
+         if(growthfactor == 1)
+         {
+            layerht = layer * hfirst;
+         }
+         else
+         {
+            layerht = hfirst*(pow(growthfactor,(layer+1)) - 1)/(growthfactor - 1);
+         }
+
+         cout << "Layer Height = " << layerht << endl;
+
+         // Need to store the old number of points and 
+         // surface elements because there are new points and 
+         // surface elements being added during the process
+         int np = mesh.GetNP();
+         int nse = mesh.GetNSE();
+
+         // Safety measure to ensure no issues with mesh 
+         // consistency
+         int nseg = mesh.GetNSeg();
+
+         // Indicate which points need to be remapped
+         BitArray bndnodes(np);
+
+         // Map of the old points to the new points
+         Array<int> mapto(np);
+
+         // Growth vectors for the prismatic layer based on 
+         // the effective surface normal at a given point
+         Array<Vec3d> growthvectors(np);
+
+         // Bit array to identify all the points belonging 
+         // to the surface of interest
+         bndnodes.Clear();
+
+         // Run through all the surface elements and mark the points 
+         // belonging to those where a boundary layer has to be created.
+         // In addition, also calculate the effective surface normal 
+         // vectors at each of those points to determine the mesh motion 
+         // direction
+         cout << "Marking points for remapping...." << endl;
+
+         for (i = 1; i <= nse; i++)
+         {
+            int snr = mesh.SurfaceElement(i).GetIndex();
+            // cout << "snr = " << snr << endl;
+            if (surfid.Contains(snr))
+            {
+               Element2d & sel = mesh.SurfaceElement(i);
+               int selNP = sel.GetNP();
+               for(j = 1; j <= selNP; j++)
+               {
+                  // Set the bitarray to indicate that the 
+                  // point is part of the required set
+                  bndnodes.Set(sel.PNum(j));
+		  
+                  // Vec3d& surfacenormal = Vec3d();   ????
+                  Vec3d surfacenormal;
+
+                  // Calculate the surface normal at the current point 
+                  // with respect to the current surface element
+                  GetSurfaceNormal(mesh,sel,j,surfacenormal);
+                  
+                  // Add the surface normal to the already existent one 
+                  // (This gives the effective normal direction at corners 
+                  //  and curved areas)
+                  growthvectors.Elem(sel.PNum(j)) = growthvectors.Elem(sel.PNum(j)) 
+                                                    + surfacenormal;
+               }
+            }
+         }
+
+         // Add additional points into the mesh structure in order to 
+         // clone the surface elements.
+         // Also invert the growth vectors so that they point inwards, 
+         // and normalize them
+         cout << "Cloning points and calculating growth vectors...." << endl;
+
+         for (i = 1; i <= np; i++)
+         {
+            if (bndnodes.Test(i))
+            {
+               mapto.Elem(i) = mesh.AddPoint (mesh.Point (i));
+
+               growthvectors.Elem(i).Normalize();
+               growthvectors.Elem(i) *= -1.0;
+            }
+            else
+            {
+               mapto.Elem(i) = 0;
+               growthvectors.Elem(i) = Vec3d(0,0,0);
+            }
+         }
+
+
+         // Add quad surface elements at edges for surfaces which 
+         // dont have boundary layers
+
+         // Bit array to keep track of segments already processed
+         BitArray segsel(nseg);
+
+         // Set them all to "1" to initially activate all segments
+         segsel.Set();
+
+         cout << "Adding 2D Quad elements on required surfaces...." << endl;
+
+         for (i = 1; i <= nseg; i++)
+         {
+            int seg_p1 = mesh.LineSegment(i)[0];
+            int seg_p2 = mesh.LineSegment(i)[1];
+
+            // Only go in if the segment is still active, and if both its 
+            // surface index is part of the "hit-list"
+            if(segsel.Test(i) && surfid.Contains(mesh.LineSegment(i).si))
+            {
+               // clear the bit to indicate that this segment has been processed
+               segsel.Clear(i);
+
+               // Find matching segment pair on other surface
+               for(j = 1; j <= nseg; j++)
+               {
+                  int segpair_p1 = mesh.LineSegment(j)[1];
+                  int segpair_p2 = mesh.LineSegment(j)[0];
+
+                  // Find the segment pair on the neighbouring surface element
+                  // Identified by: seg1[0] = seg_pair[1] and seg1[1] = seg_pair[0]
+                  if(segsel.Test(j) && ((segpair_p1 == seg_p1) && (segpair_p2 == seg_p2)))
+                  {
+                     // clear bit to indicate that processing of this segment is done
+                     segsel.Clear(j);
+
+                     // Only worry about those surfaces which are not in the 
+                     // boundary layer list
+                     if(!surfid.Contains(mesh.LineSegment(j).si))
+                     {
+                        int pnt_commelem = 0;
+                        int pnum_commelem = 0;
+                        Array<int> pnt1_elems;
+                        Array<int> pnt2_elems;
+                       
+                            
+                        meshtopo.GetVertexSurfaceElements(segpair_p1,pnt1_elems);
+                        meshtopo.GetVertexSurfaceElements(segpair_p2,pnt2_elems);
+                        for(int k = 1; k <= pnt1_elems.Size(); k++)
+                        {
+                           Element2d pnt1_sel = mesh.SurfaceElement(pnt1_elems.Elem(k));
+                           for(int l = 1; l <= pnt2_elems.Size(); l++)
+                           {
+                              Element2d pnt2_sel = mesh.SurfaceElement(pnt2_elems.Elem(l));
+                              if((pnt1_sel.GetIndex() == mesh.LineSegment(j).si) 
+                                 && (pnt2_sel.GetIndex() == mesh.LineSegment(j).si)
+                                 && (pnt1_elems.Elem(k) == pnt2_elems.Elem(l)))
+                              {
+                                 pnt_commelem = pnt1_elems.Elem(k);
+                              }
+                           }
+                        }
+
+                        for(int k = 1; k <= mesh.SurfaceElement(pnt_commelem).GetNP(); k++)
+                        {
+                           if((mesh.SurfaceElement(pnt_commelem).PNum(k) != segpair_p1)
+                              && (mesh.SurfaceElement(pnt_commelem).PNum(k) != segpair_p2))
+                           {
+                              pnum_commelem = mesh.SurfaceElement(pnt_commelem).PNum(k);
+                           }
+                        }
+
+                        Vec3d surfelem_vect, surfelem_vect1;
+                        
+                        Element2d & commsel = mesh.SurfaceElement(pnt_commelem);
+
+                        dbg << "NP= " << commsel.GetNP() << " : ";
+
+                        for(int k = 1; k <= commsel.GetNP(); k++)
+                        {
+                           GetSurfaceNormal(mesh,commsel,k,surfelem_vect1);
+                           surfelem_vect += surfelem_vect1;
+                        }
+
+                        surfelem_vect.Normalize();
+
+                        double surfangle = Angle(growthvectors.Elem(segpair_p1),surfelem_vect);
+
+                        dbg << "V1= " << surfelem_vect1 
+                            << " : V2= " << surfelem_vect1
+                            << " : V= " << surfelem_vect
+                            << " : GV= " << growthvectors.Elem(segpair_p1)
+                            << " : Angle= " << surfangle * 180 / 3.141592;
+
+                  
+                        // remap the segments to the new points
+                        mesh.LineSegment(i)[0] = mapto.Get(seg_p1);
+                        mesh.LineSegment(i)[1] = mapto.Get(seg_p2);
+                        mesh.LineSegment(j)[1] = mapto.Get(seg_p1);
+                        mesh.LineSegment(j)[0] = mapto.Get(seg_p2);
+
+                        if((surfangle < (90 + angleThreshold) * 3.141592 / 180.0)
+                           && (surfangle > (90 - angleThreshold) * 3.141592 / 180.0))
+                        {
+                           dbg << " : quad\n";
+                           // Since the surface is lower than the threshold, change the effective 
+                           // prism growth vector to match with the surface vector, so that 
+                           // the Quad which is created lies on the original surface
+                           //growthvectors.Elem(segpair_p1) = surfelem_vect;
+
+                           // Add a quad element to account for the prism volume
+                           // element which is going to be added 
+                           Element2d sel(QUAD);
+                           sel.PNum(4) = mapto.Get(seg_p1);
+                           sel.PNum(3) = mapto.Get(seg_p2);
+                           sel.PNum(2) = segpair_p2;
+                           sel.PNum(1) = segpair_p1;
+                           sel.SetIndex(mesh.LineSegment(j).si);
+                           mesh.AddSurfaceElement(sel);
+                           numquads++;
+                        }
+                        else
+                        {
+                           dbg << "\n";
+                           for (int k = 1; k <= pnt1_elems.Size(); k++)
+                           {
+                              Element2d & pnt_sel = mesh.SurfaceElement(pnt1_elems.Elem(k));
+                              if(pnt_sel.GetIndex() == mesh.LineSegment(j).si)
+                              {
+                                 for(int l = 1; l <= pnt_sel.GetNP(); l++)
+                                 {
+                                    if(pnt_sel.PNum(l) == segpair_p1)
+                                    {
+                                       pnt_sel.PNum(l) = mapto.Get(seg_p1);
+                                    }
+                                    else if(pnt_sel.PNum(l) == segpair_p2)
+                                    {
+                                       pnt_sel.PNum(l) = mapto.Get(seg_p2);
+                                    }
+                                 }
+                              }
+                           }
+
+                           for (int k = 1; k <= pnt2_elems.Size(); k++)
+                           {
+                              Element2d & pnt_sel = mesh.SurfaceElement(pnt2_elems.Elem(k));
+                              if(pnt_sel.GetIndex() == mesh.LineSegment(j).si)
+                              {
+                                 for(int l = 1; l <= pnt_sel.GetNP(); l++)
+                                 {
+                                    if(pnt_sel.PNum(l) == segpair_p1)
+                                    {
+                                       pnt_sel.PNum(l) = mapto.Get(seg_p1);
+                                    }
+                                    else if(pnt_sel.PNum(l) == segpair_p2)
+                                    {
+                                       pnt_sel.PNum(l) = mapto.Get(seg_p2);
+                                    }
+                                 }
+                              }
+                           }
+                        }
+                     }
+                     else
+                     {
+                        // If the code comes here, it indicates that we are at 
+                        // a line segment pair which is at the intersection 
+                        // of two surfaces, both of which have to grow boundary 
+                        // layers.... here too, remapping the segments to the 
+                        // new points is required
+                        mesh.LineSegment(i)[0] = mapto.Get(seg_p1);
+                        mesh.LineSegment(i)[1] = mapto.Get(seg_p2);
+                        mesh.LineSegment(j)[1] = mapto.Get(seg_p1);
+                        mesh.LineSegment(j)[0] = mapto.Get(seg_p2);
+                     }
+                  }
+               }
+            }
+         }
+
+         // Add prismatic cells at the boundaries
+         cout << "Generating prism boundary layer volume elements...." << endl;
+
+         for (i = 1; i <= nse; i++)
+         {
+            Element2d & sel = mesh.SurfaceElement(i);
+            if(surfid.Contains(sel.GetIndex()))
+            {
+               Element el(PRISM);
+               for (j = 1; j <= sel.GetNP(); j++)
+               {
+                  // Check (Doublecheck) if the corresponding point has a 
+                  // copy available for remapping
+                  if (mapto.Get(sel.PNum(j)))
+                  {
+                     // Define the points of the newly added Prism cell
+                     el.PNum(j+3) = mapto.Get(sel.PNum(j));
+                     el.PNum(j) = sel.PNum(j);
+                  }
+               }
+
+               el.SetIndex(1);
+               el.Invert();
+               mesh.AddVolumeElement(el);
+               numprisms++;
+            }
+         }
+
+         // Finally switch the point indices of the surface elements 
+         // to the newly added ones
+         cout << "Transferring boundary layer surface elements to new vertex references...." << endl;
+
+         for (i = 1; i <= nse; i++)
+         {
+            Element2d & sel = mesh.SurfaceElement(i);
+            if(surfid.Contains(sel.GetIndex()))
+            {
+               for (j = 1; j <= sel.GetNP(); j++)
+               {
+                  // Check (Doublecheck) if the corresponding point has a 
+                  // copy available for remapping
+                  if (mapto.Get(sel.PNum(j)))
+                  {
+                     // Map the surface elements to the new points
+                     sel.PNum(j) = mapto.Get(sel.PNum(j));
+                  }
+               }
+            }
+         }
+
+         // Lock all the prism points so that the rest of the mesh can be 
+         // optimised without invalidating the entire mesh
+         for (i = 1; i <= np; i++)
+         {
+            if(bndnodes.Test(i)) mesh.AddLockedPoint(i);
+         }
+
+         // Now, actually pull back the old surface points to create 
+         // the actual boundary layers
+         cout << "Moving and optimising boundary layer points...." << endl;
+         
+         for (i = 1; i <= np; i++)
+         {
+            Array<int> vertelems;
+
+            if(bndnodes.Test(i))
+            {
+               MeshPoint pointtomove;
+
+               pointtomove = mesh.Point(i);
+
+               if(layer == prismlayers)
+               {
+                  mesh.Point(i).SetPoint(pointtomove + layerht * growthvectors.Elem(i));
+
+                  meshtopo.GetVertexElements(i,vertelems);
+
+                  for(j = 1; j <= vertelems.Size(); j++)
+                  {
+		    // double sfact = 0.9;
+                     Element volel = mesh.VolumeElement(vertelems.Elem(j));
+                     if(((volel.GetType() == TET) || (volel.GetType() == TET10)) && (!volel.IsDeleted()))
+                     {
+                        //while((volel.Volume(mesh.Points()) <= 0.0) && (sfact >= 0.0))
+                        //{
+                        //   mesh.Point(i).SetPoint(pointtomove + (sfact * layerht * growthvectors.Elem(i)));
+                        //   mesh.ImproveMesh();
+
+                        //   // Try to move the point back by one step but 
+                        //   // if the volume drops to below zero, double back
+                        //   mesh.Point(i).SetPoint(pointtomove + ((sfact + 0.1) * layerht * growthvectors.Elem(i)));
+                        //   if(volel.Volume(mesh.Points()) <= 0.0)
+                        //   {
+                        //      mesh.Point(i).SetPoint(pointtomove + (sfact * layerht * growthvectors.Elem(i)));
+                        //   }
+                        //   sfact -= 0.1;
+                        //}
+                        volel.Delete();
+                     }
+                  }
+
+                  mesh.Compress();
+               }
+               else
+               {
+                  mesh.Point(i).SetPoint(pointtomove + layerht * growthvectors.Elem(i));
+               }
+            }
+         }
+      }
+
+      // Optimise the tet part of the volume mesh after all the modifications 
+      // to the system are completed
+      //OptimizeVolume(mparam,mesh);
+
+      cout << "New NP: " << mesh.GetNP() << endl;
+      cout << "Num of Quads: " << numquads << endl;
+      cout << "Num of Prisms: " << numprisms << endl;
+      cout << "Boundary Layer Generation....Done!" << endl;
+
+      dbg.close();
+   }
+
+}
+
diff --git a/contrib/Netgen/libsrc/meshing/boundarylayer.hpp b/contrib/Netgen/libsrc/meshing/boundarylayer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4bd469848f05018993afebad2ed0a5b038d7ae88
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/boundarylayer.hpp
@@ -0,0 +1,13 @@
+#ifndef FILE_BOUNDARYLAYER
+#define FILE_BOUNDARYLAYER
+
+
+///
+extern void InsertVirtualBoundaryLayer (Mesh & mesh);
+
+/// Create a typical prismatic boundary layer on the given 
+/// surfaces
+extern void GenerateBoundaryLayer (Mesh & mesh, MeshingParameters & mp);
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/classifyhpel.hpp b/contrib/Netgen/libsrc/meshing/classifyhpel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c1555371992d1bee99277997a30711b214369495
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/classifyhpel.hpp
@@ -0,0 +1,1728 @@
+HPREF_ELEMENT_TYPE ClassifyTet(HPRefElement & el, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoint_dom, 
+                               BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int> & face_edges, 
+                               INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint)
+{
+  int ep1(0), ep2(0), ep3(0), ep4(0), cp1(0), cp2(0), cp3(0), cp4(0), fp1, fp2, fp3, fp4;
+  int isedge1(0), isedge2(0), isedge3(0), isedge4(0), isedge5(0), isedge6(0);
+  int isfedge1, isfedge2, isfedge3, isfedge4, isfedge5, isfedge6;
+  int isface1(0), isface2(0), isface3(0), isface4(0);
+
+  HPREF_ELEMENT_TYPE type = HP_NONE; 
+  
+
+  int debug = 0;
+  for (int j = 0;j < 4; j++)
+    {
+      if (el.pnums[j] == 444) debug++;
+      if (el.pnums[j] == 115) debug++;
+      if (el.pnums[j] == 382) debug++;
+      if (el.pnums[j] == 281) debug++;
+    }
+  if (debug < 4) debug = 0;
+  
+
+
+  for (int j = 0; j < 4; j++)
+    for (int k = 0; k < 4; k++)
+      {
+	if (j == k) continue;
+	if (type) break;
+	
+	int pi3 = 0;
+	while (pi3 == j || pi3 == k) pi3++;
+	int pi4 = 6 - j - k - pi3;
+	
+	// preserve orientation
+	int sort[4];
+	sort[0] = j; sort[1] = k; sort[2] = pi3; sort[3] = pi4;
+	int cnt = 0;
+	for (int jj = 0; jj < 4; jj++)
+	  for (int kk = 0; kk < 3; kk++)
+	    if (sort[kk] > sort[kk+1])
+	      {
+		cnt++;
+		Swap (sort[kk], sort[kk+1]); 
+	      }
+	if (cnt % 2 == 1) Swap (pi3, pi4);
+	
+	ep1 = edgepoint.Test (el.pnums[j]);
+	ep2 = edgepoint.Test (el.pnums[k]);
+	ep3 = edgepoint.Test (el.pnums[pi3]);
+	ep4 = edgepoint.Test (el.pnums[pi4]);
+	
+	cp1 = cornerpoint.Test (el.pnums[j]);
+	cp2 = cornerpoint.Test (el.pnums[k]);
+	cp3 = cornerpoint.Test (el.pnums[pi3]);
+	cp4 = cornerpoint.Test (el.pnums[pi4]);
+	
+	isedge1 = edges.Used (INDEX_2::Sort (el.pnums[j], el.pnums[k]));
+	isedge2 = edges.Used (INDEX_2::Sort (el.pnums[j], el.pnums[pi3]));
+	isedge3 = edges.Used (INDEX_2::Sort (el.pnums[j], el.pnums[pi4]));
+	isedge4 = edges.Used (INDEX_2::Sort (el.pnums[k], el.pnums[pi3]));
+	isedge5 = edges.Used (INDEX_2::Sort (el.pnums[k], el.pnums[pi4]));
+	isedge6 = edges.Used (INDEX_2::Sort (el.pnums[pi3], el.pnums[pi4]));
+	
+	if (debug)
+	  {
+	    cout << "debug" << endl;
+	    *testout  << "debug" << endl;
+	    *testout << "ep = " << ep1 << ep2 << ep3 << ep4 << endl;
+	    *testout << "cp = " << cp1 << cp2 << cp3 << cp4 << endl;
+	    *testout << "edge = " << isedge1 << isedge2 << isedge3 << isedge4 << isedge5 << isedge6 << endl;
+	  }
+
+
+	isface1 = isface2 = isface3 = isface4 = 0;
+	for (int l = 0; l < 4; l++)
+	  {
+	    INDEX_3 i3(0,0,0);
+	    switch (l)
+	      {
+              case 0: i3.I1() = el.pnums[k]; i3.I1() = el.pnums[pi3]; i3.I1() = el.pnums[pi4]; break;
+              case 1: i3.I1() = el.pnums[j]; i3.I1() = el.pnums[pi3]; i3.I1() = el.pnums[pi4]; break;
+              case 2: i3.I1() = el.pnums[j]; i3.I1() = el.pnums[k]; i3.I1() = el.pnums[pi4]; break;
+              case 3: i3.I1() = el.pnums[j]; i3.I1() = el.pnums[k]; i3.I1() = el.pnums[pi3]; break;
+	      }
+	    i3.Sort();
+	    if (faces.Used (i3))
+	      {
+		int domnr = faces.Get(i3);
+		if (domnr == -1 || domnr == el.GetIndex())
+		  {
+		    switch (l)
+		      {
+		      case 0: isface1 = 1; break;
+		      case 1: isface2 = 1; break;
+		      case 2: isface3 = 1; break;
+		      case 3: isface4 = 1; break;
+		      }
+		  }
+	      }
+	  }
+	/*
+	  isface1 = faces.Used (INDEX_3::Sort (el.pnums[k], el.pnums[pi3], el.pnums[pi4]));
+	  isface2 = faces.Used (INDEX_3::Sort (el.pnums[j], el.pnums[pi3], el.pnums[pi4]));
+	  isface3 = faces.Used (INDEX_3::Sort (el.pnums[j], el.pnums[k], el.pnums[pi4]));
+	  isface4 = faces.Used (INDEX_3::Sort (el.pnums[j], el.pnums[k], el.pnums[pi3]));
+	*/
+	
+	isfedge1 = isfedge2 = isfedge3 = isfedge4 = isfedge5 = isfedge6 = 0;
+	for (int l = 0; l < 6; l++)
+	  {
+	    INDEX_2 i2(0,0);
+	    switch (l)
+	      {
+              case 0: i2.I1() = el.pnums[j]; i2.I2() = el[k]; break;
+              case 1: i2.I1() = el.pnums[j]; i2.I2() = el.pnums[pi3]; break;
+              case 2: i2.I1() = el.pnums[j]; i2.I2() = el.pnums[pi4]; break;
+              case 3: i2.I1() = el.pnums[k]; i2.I2() = el.pnums[pi3]; break;
+              case 4: i2.I1() = el.pnums[k]; i2.I2() = el.pnums[pi4]; break;
+              case 5: i2.I1() = el.pnums[pi3]; i2.I2() = el.pnums[pi4]; break;
+	      }
+	    i2.Sort();
+	    if (face_edges.Used (i2))
+	      {
+		int domnr = face_edges.Get(i2);
+		if (domnr == -1 || domnr == el.GetIndex())
+		  {
+		    switch (l)
+		      {
+		      case 0: isfedge1 = 1; break;
+		      case 1: isfedge2 = 1; break;
+		      case 2: isfedge3 = 1; break;
+		      case 3: isfedge4 = 1; break;
+		      case 4: isfedge5 = 1; break;
+		      case 5: isfedge6 = 1; break;
+		      }
+		  }
+	      }
+	  }
+	/*
+	  isfedge1 = face_edges.Used (INDEX_2::Sort (el.pnums[j], el.pnums[k]));
+	  isfedge2 = face_edges.Used (INDEX_2::Sort (el.pnums[j], el.pnums[pi3]));
+	  isfedge3 = face_edges.Used (INDEX_2::Sort (el.pnums[j], el.pnums[pi4]));
+	  isfedge4 = face_edges.Used (INDEX_2::Sort (el.pnums[k], el.pnums[pi3]));
+	  isfedge5 = face_edges.Used (INDEX_2::Sort (el.pnums[k], el.pnums[pi4]));
+	  isfedge6 = face_edges.Used (INDEX_2::Sort (el.pnums[pi3], el.pnums[pi4]));
+	*/
+	
+	fp1 = fp2 = fp3 = fp4 = 0;
+	for (int l = 0; l < 4; l++)
+	  {
+	    int pti(0);
+	    switch (l)
+	      {
+	      case 0: pti = el.pnums[j]; break;
+	      case 1: pti = el.pnums[k]; break;
+	      case 2: pti = el.pnums[pi3]; break;
+	      case 3: pti = el.pnums[pi4]; break;
+	      }
+	    int domnr = facepoint[pti];
+	    if (domnr == -1 || domnr == el.GetIndex())
+	      {
+		switch (l)
+		  {
+		  case 0: fp1 = 1; break;
+		  case 1: fp2 = 1; break;
+		  case 2: fp3 = 1; break;
+		  case 3: fp4 = 1; break;
+		  }
+	      }
+	  }
+	
+	/*
+	  fp1 = facepoint[el.pnums[j]] != 0;
+	  fp2 = facepoint[el.pnums[k]] != 0;
+	  fp3 = facepoint[el.pnums[pi3]] != 0;
+	  fp4 = facepoint[el.pnums[pi4]] != 0;
+	*/
+	
+	
+	switch (isface1+isface2+isface3+isface4)
+	  {
+	  case 0:
+	    {
+	      isedge1 |= isfedge1;
+	      isedge2 |= isfedge2;
+	      isedge3 |= isfedge3;
+	      isedge4 |= isfedge4;
+	      isedge5 |= isfedge5;
+	      isedge6 |= isfedge6;
+	      
+	      ep1 |= fp1;
+	      ep2 |= fp2;
+	      ep3 |= fp3;
+	      ep4 |= fp4;
+	      
+	      switch (isedge1+isedge2+isedge3+isedge4+isedge5+isedge6)
+		{
+		case 0:
+		  {		
+		    if (!ep1 && !ep2 && !ep3 && !ep4)
+		      type = HP_TET;
+				
+		    if (ep1 && !ep2 && !ep3 && !ep4)
+		      type = HP_TET_0E_1V;
+		    
+		    if (ep1 && ep2 && !ep3 && !ep4)
+		      type = HP_TET_0E_2V;
+		    
+		    if (ep1 && ep2 && ep3 && !ep4)
+		      type = HP_TET_0E_3V;
+		    
+		    if (ep1 && ep2 && ep3 && ep4)
+		      type = HP_TET_0E_4V;
+		    
+		    break;
+		  }
+		  
+		case 1:
+		  {
+		    if (!isedge1) break;
+		    
+		    if (!cp1 && !cp2 && !ep3 && !ep4)
+		      type = HP_TET_1E_0V;
+		    
+		    if (cp1 && !cp2 && !ep3 && !ep4)
+		      type = HP_TET_1E_1VA;
+		    
+		    if (!cp1 && !cp2 && !ep3 && ep4)
+		      type = HP_TET_1E_1VB;
+		    
+		    if (cp1 && cp2 && !ep3 && !ep4)
+		      type = HP_TET_1E_2VA;
+		    
+		    if (cp1 && !cp2 && ep3 && !ep4)
+		      type = HP_TET_1E_2VB;
+		    
+		    if (cp1 && !cp2 && !ep3 && ep4)
+		      type = HP_TET_1E_2VC;
+		    
+		    if (!cp1 && !cp2 && ep3 && ep4)
+		      type = HP_TET_1E_2VD;
+		    
+		    if (cp1 && cp2 && ep3 && !ep4)
+		      type = HP_TET_1E_3VA;
+		    
+		    if (cp1 && !cp2 && ep3 && ep4)
+		      type = HP_TET_1E_3VB;
+		    
+		    if (cp1 && cp2 && ep3 && ep4)
+		      type = HP_TET_1E_4V;
+		    
+		    break;
+		  }
+		case 2:
+		  {
+		    if (isedge1 && isedge2)
+		      {
+			if (!cp2 && !cp3 && !ep4)
+			  type = HP_TET_2EA_0V;
+			
+			if (cp2 && !cp3 && !ep4)
+			  type = HP_TET_2EA_1VA;
+			if (!cp2 && cp3 && !ep4)
+			  type = HP_TET_2EA_1VB;
+			
+			if (!cp2 && !cp3 && ep4)
+			  type = HP_TET_2EA_1VC;
+			
+			if (cp2 && cp3 && !ep4)
+			  type = HP_TET_2EA_2VA;
+			if (cp2 && !cp3 && ep4)
+			  type = HP_TET_2EA_2VB;
+			if (!cp2 && cp3 && ep4)
+			  type = HP_TET_2EA_2VC;
+			
+			if (cp2 && cp3 && ep4)
+			  type = HP_TET_2EA_3V;
+		      }
+		    if (isedge1 && isedge6)
+		      {
+			if (!cp1 && !cp2 && !cp3 && !cp4)
+			  type = HP_TET_2EB_0V;
+			if (cp1 && !cp2 && !cp3 && !cp4)
+			  type = HP_TET_2EB_1V;
+			if (cp1 && cp2 && !cp3 && !cp4)
+			  type = HP_TET_2EB_2VA;
+			if (cp1 && !cp2 && cp3 && !cp4)
+			  type = HP_TET_2EB_2VB;
+			if (cp1 && !cp2 && !cp3 && cp4)
+			  type = HP_TET_2EB_2VC;
+			if (cp1 && cp2 && cp3 && !cp4)
+			  type = HP_TET_2EB_3V;
+			if (cp1 && cp2 && cp3 && cp4)
+			  type = HP_TET_2EB_4V;
+		      }
+		    break;
+		  }
+		case 3:
+		  {
+		    if (isedge1 && isedge2 && isedge3)
+		      {
+			if (!cp2 && !cp3 && !cp4)
+			  type = HP_TET_3EA_0V;
+			if (cp2 && !cp3 && !cp4)
+			  type = HP_TET_3EA_1V;
+			if (cp2 && cp3 && !cp4)
+			  type = HP_TET_3EA_2V;
+			if (cp2 && cp3 && cp4)
+			  type = HP_TET_3EA_3V;
+		      }
+		    if (isedge1 && isedge3 && isedge4)
+		      {
+			if (!cp3 && !cp4)
+			  type = HP_TET_3EB_0V;
+			if (cp3 && !cp4)
+                          type = HP_TET_3EB_1V;
+			if (cp3 && cp4)
+			  type = HP_TET_3EB_2V;
+		      }
+		    if (isedge1 && isedge2 && isedge5)
+		      {
+			if (!cp3 && !cp4)
+			  type = HP_TET_3EC_0V;
+			if (cp3 && !cp4)
+			  type = HP_TET_3EC_1V;
+			if (cp3 && cp4)
+			  type = HP_TET_3EC_2V;
+		      }
+		    break;
+		  }
+		}
+	      break;
+	    }
+	    
+	    
+	    
+	  case 1:  // one singular face
+	    {
+	      if (!isface1) break;
+	      
+	      switch (isfedge1+isfedge2+isfedge3+isedge4+isedge5+isedge6)
+		{
+		case 0:
+		  {
+		    if (!fp1 && !ep2 && !ep3 && !ep4)
+		      type = HP_TET_1F_0E_0V;
+		    if (fp1 && !ep2 && !ep3 && !ep4)
+		      type = HP_TET_1F_0E_1VB;
+		    if (!fp1 && ep2 && !ep3 & !ep4)
+		      type = HP_TET_1F_0E_1VA;
+		    break;
+		  }
+		case 1:
+		  {
+		    if (isfedge1)
+		      {
+			if (!ep1 && !ep3 && !ep4)
+			  type = HP_TET_1F_1EA_0V;
+		      }
+		    if (isedge4) // V1-V3
+		      {
+			if (!ep1 && !cp2 && !cp3 && !ep4)
+			  type = HP_TET_1F_1EB_0V;
+		      }
+		    break;
+		  }
+		}
+	      break;
+	    }
+	    
+	    
+	  case 2:  // two singular faces
+	    {
+	      if (!isface1 || !isface2) break;
+	      
+	      switch (isfedge1+isedge2+isedge3+isedge4+isedge5)
+		{
+		case 0:
+		  {
+		    if (!ep1 && !ep2 && !cp3 && !cp4)
+		      type = HP_TET_2F_0E_0V;
+		    break;
+		  }
+		}
+	      break;
+	    }
+	    
+	    
+	  }
+	
+	if (type != HP_NONE)
+	  {
+	    int pnums[4]; 
+	    pnums[0] = el.pnums[j];
+	    pnums[1] = el.pnums[k];
+	    pnums[2] = el.pnums[pi3];
+	    pnums[3] = el.pnums[pi4];
+	    for(k=0;k<4;k++) el.pnums[k] = pnums[k]; 
+	    break;
+	  }
+      }
+  
+  
+  if (debug) cout << "type = " << type << endl;
+
+  if (type == HP_NONE)
+    {
+      //     cnt_undef++;
+      (*testout) << "undefined element" << endl
+		 << "cp = " << cp1 << cp2 << cp3 << cp4 << endl
+		 << "ep = " << ep1 << ep2 << ep3 << ep4 << endl
+		 << "isedge = " << isedge1 << isedge2 << isedge3 
+		 << isedge4 << isedge5 << isedge6 << endl
+		 << "isface = " << isface1 << isface2 << isface3 << isface4 << endl;
+      cout << "undefined element !!! " << endl;
+
+      
+    }
+  return(type); 
+}
+
+
+
+HPREF_ELEMENT_TYPE ClassifyPrism(HPRefElement & el, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoint_dom, 
+                                 BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int> & face_edges, 
+                                 INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint)
+{
+
+  HPREF_ELEMENT_TYPE type = HP_NONE;
+  
+  int p[6];
+  for(int m=1;m<=6;m++)
+    {
+      int point_sing[6]={0,0,0,0,0,0}; 
+      int face_sing[5]={0,0,0,0,0};
+      int edge_sing[9]={0,0,0,0,0,0,0,0,0}; 
+      
+      if(m<4)
+	{ 
+	  p[0]= m; p[1]=m%3+1; p[2]=(m%3+1)%3+1;
+	  for(int l=3;l<6;l++) p[l]=p[l-3]+3;  
+	}
+      else
+	{
+	  p[0] = m; p[1]=(m%3+1)%3+4; p[2]=m%3+4;
+	  for(int l=3;l<6;l++) p[l]=p[l-3]-3; 
+	}
+      
+      for(int j=0;j<6;j++) 
+	{ 
+	  if(cornerpoint.Test(el.PNum(p[j])))  { point_sing[p[j]-1]=3;}
+	  else if(edgepoint.Test(el.PNum(p[j]))) point_sing[p[j]-1]=2;
+	  else if (facepoint[el.PNum(p[j])] == -1 || facepoint[el.PNum(p[j])] == el.GetIndex())
+	    point_sing[p[j]-1] = 1;  
+	}
+      
+      const ELEMENT_EDGE * eledges = MeshTopology::GetEdges1 (PRISM);
+      for(int k=0;k<9;k++)
+	{
+	  INDEX_2 i2 = INDEX_2 :: Sort(el.PNum(p[eledges[k][0]-1]),el.PNum(p[eledges[k][1]-1])); 
+	  if (edges.Used(i2)) edge_sing[k] = 2;
+	  else edge_sing[k] = face_edges.Used(i2);
+	}
+      
+      const ELEMENT_FACE * elfaces  = MeshTopology::GetFaces1 (PRISM);
+      for (int k=0;k<5;k++)
+	{
+	  INDEX_3 i3; 
+	  
+	  if(k<2) 
+	    i3 = INDEX_3::Sort(el.pnums[p[elfaces[k][0]-1]-1], el.pnums[p[elfaces[k][1]-1]-1], 
+			       el.pnums[p[elfaces[k][2]-1]-1]); 
+	  else 
+	    { 
+	      INDEX_4  i4 = INDEX_4(el.pnums[p[elfaces[k][0]-1]-1], el.pnums[p[elfaces[k][1]-1]-1], el.pnums[p[elfaces[k][2]-1]-1],el.pnums[p[elfaces[k][3]-1]-1]); 
+	      i4.Sort();
+	      i3 = INDEX_3(i4.I1(), i4.I2(), i4.I3()); 
+	    }
+	  
+	  if (faces.Used (i3))
+	    {
+	      int domnr = faces.Get(i3); 
+	      if (domnr == -1 || domnr == el.GetIndex())
+		face_sing[k] = 1; 
+	      
+	    } 
+	} 
+      if (face_sing[1] > face_sing[0]) {m=m+2; continue;}  
+      
+      
+      //int cp = 0;  
+      
+      int qfsing = face_sing[2] + face_sing[3] + face_sing[4];
+      int tfsing = face_sing[0] + face_sing[1]; 
+      int evsing = edge_sing[6] + edge_sing[7] + edge_sing[8];
+      int ehsing = edge_sing[0] + edge_sing[1] + edge_sing[2] + edge_sing[3] + edge_sing[4] + edge_sing[5];
+      
+      if (qfsing + tfsing + evsing + ehsing == 0)  
+	{ type = HP_PRISM;  break;}
+      
+      HPREF_ELEMENT_TYPE types[] = {HP_NONE,HP_NONE,HP_NONE};   
+      
+      int fb = (1-face_sing[4])* face_sing[3] * (face_sing[2] + face_sing[3]) + 3*face_sing[4]*face_sing[3]*face_sing[2];  
+      int sve[3] = {edge_sing[7] , edge_sing[8], edge_sing[6]}; 
+      
+            
+      if(fb!=qfsing) continue; 
+      
+      
+      switch(fb)
+	{ 
+	case 0: 
+	  if (evsing == 0 && ehsing==3*tfsing) 
+	    {
+	      types[0] = HP_PRISM; 
+	      types[1] = HP_PRISM_1FA_0E_0V;   
+	      types[2] = HP_PRISM_2FA_0E_0V; 
+	    } 
+	  if(evsing > 0 &&  sve[0] == evsing) // 1 vertical edge 1-4 
+	    { 
+	      types[0] = HP_PRISM_SINGEDGE;
+	      types[1] = HP_PRISM_1FA_1E_0V;
+	      types[2] = HP_PRISM_2FA_1E_0V;   
+	    }
+	  
+	  if(sve[0] > 0 && sve[1] > 0 && sve[2] == 0)
+	    {
+	      types[0] = HP_PRISM_SINGEDGE_V12;
+	      types[1] = HP_PRISM_1FA_2E_0V; 
+	      types[2] = HP_PRISM_2FA_2E_0V; 
+	    }
+	  if(sve[0] > 0 && sve[1] > 0 && sve[2] > 0) 
+	    {
+	      types[0] = HP_PRISM_3E_0V;
+	      types[1] = HP_PRISM_1FA_3E_0V;
+	      types[2] = HP_PRISM_2FA_3E_0V;
+	      
+	      if ( edge_sing[0] > 1 && edge_sing[2] > 1 &&  
+		   edge_sing[4] > 1 && edge_sing[5] > 1 && tfsing==0)
+		types[0] = HP_PRISM_3E_4EH; 
+	    }
+	  
+	  break;
+	case 1:
+	  if(sve[0] <= 1 && sve[1] <= 1)  
+            {
+              if(sve[2]==0)
+                { 
+                  types[0] = HP_PRISM_1FB_0E_0V;
+                  types[1] = HP_PRISM_1FA_1FB_0E_0V;
+                  types[2] = HP_PRISM_2FA_1FB_0E_0V;  
+                }
+              else
+                { 
+                  types[0] = HP_PRISM_1FB_1EC_0V;
+                  types[1] = HP_PRISM_1FA_1FB_1EC_0V;
+                  types[2] = HP_PRISM_2FA_1FB_1EC_0V; 
+                }
+            }
+
+	  if(sve[0] > 1 && sve[2] >= 1 && sve[1] <= 1)
+	    { 
+	      types[0] = HP_PRISM_1FB_2EB_0V;  
+	      types[1] = HP_PRISM_1FA_1FB_2EB_0V;
+	      types[2] = HP_PRISM_2FA_1FB_2EB_0V; 
+	    }
+	  
+	  if(sve[0] > 1 && sve[1] <= 1 && sve[2] == 0) // ea && !eb  
+	    {
+	      types[0] = HP_PRISM_1FB_1EA_0V;
+	      types[1] = HP_PRISM_1FA_1FB_1EA_0V;
+	      types[2] = HP_PRISM_2FA_1FB_1EA_0V; 
+	    } 
+	  
+	  if(sve[0] <= 1 && sve[1] > 1 && sve[2] == 0)
+	    types[1] = HP_PRISM_1FA_1FB_1EB_0V; 
+	  
+	  if(sve[0] > 1 && sve[1]>1) 
+	    if(sve[2] == 0)  // ea && eb 
+	      {
+		types[0] = HP_PRISM_1FB_2EA_0V;
+		types[1] = HP_PRISM_1FA_1FB_2EA_0V;
+		types[2] = HP_PRISM_2FA_1FB_2EA_0V; 
+	      }
+	  if(sve[0] <= 1 && sve[1] > 1 && sve[2] >0)
+	    types[1] = HP_PRISM_1FA_1FB_2EC_0V; 
+	  
+	  if(sve[0] > 1 && sve[1] > 1 && sve[2] >= 1) //sve[2] can also be a face-edge  
+	    {
+	      types[0] = HP_PRISM_1FB_3E_0V;  
+	      types[1] = HP_PRISM_1FA_1FB_3E_0V; 
+	      types[2] = HP_PRISM_2FA_1FB_3E_0V; 
+	    } 
+	  
+	  break;  
+	  
+	case 2:
+	  if(sve[0] <= 1) 
+	    cout << " **** WARNING: Edge between to different singular faces should be marked singular " << endl; 
+		      
+	  if(sve[1] <= 1)   
+	    if(sve[2] <=1) 
+	      { 
+		types[0] = HP_PRISM_2FB_0E_0V; 
+		types[1] = HP_PRISM_1FA_2FB_0E_0V;
+		types[2] = HP_PRISM_2FA_2FB_0E_0V;
+	      }
+	    else
+	      { 
+		types[0] = HP_PRISM_2FB_1EC_0V; 
+		types[1] = HP_PRISM_1FA_2FB_1EC_0V; 
+		types[2] = HP_PRISM_2FA_2FB_1EC_0V;   
+	      }
+	  else
+	    if(sve[2] <= 1) 
+	      types[1] = HP_PRISM_1FA_2FB_1EB_0V; 
+	    else
+	      { 
+		types[0] = HP_PRISM_2FB_3E_0V; 
+		types[1] = HP_PRISM_1FA_2FB_3E_0V; 
+		types[2] = HP_PRISM_2FA_2FB_3E_0V; 
+	      }
+	  
+	  break;
+	  
+	case 3: 
+	  types[0] = HP_PRISM_3FB_0V; 
+	  types[1] = HP_PRISM_1FA_3FB_0V; 
+	  types[2] = HP_PRISM_2FA_3FB_0V; 
+	  break;
+	}
+      type = types[tfsing];
+      
+         
+      if(type != HP_NONE)  
+	break;
+    }
+	 
+  /*
+   *testout << " Prism with pnums " << endl; 
+   for(int j=0;j<6;j++) *testout << el.pnums[j] << "\t"; 
+   *testout << endl; 
+   */
+  
+  if(type != HP_NONE) 
+    {
+      int pnums[6]; 
+      for(int j=0;j<6;j++) pnums[j] = el.PNum (p[j]);
+      for(int k=0;k<6;k++) el.pnums[k] = pnums[k]; 
+    }
+
+  /* *testout << " Classified Prism with pnums " << endl; 
+     for(int j=0;j<6;j++) *testout << el.pnums[j] << "\t"; 
+     *testout << endl; 
+     */ 
+  return(type); 
+}
+
+
+// #ifdef SABINE 
+HPREF_ELEMENT_TYPE ClassifyTrig(HPRefElement & el, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoint_dom, 
+                                BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int>  & face_edges, 
+				INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint, int dim, const FaceDescriptor & fd)
+
+{
+  HPREF_ELEMENT_TYPE type = HP_NONE; 
+  
+  int pnums[3]; 
+  int p[3];   
+  
+  INDEX_3 i3 (el.pnums[0], el.pnums[1], el.pnums[2]);
+  i3.Sort();
+  bool sing_face = faces.Used (i3);
+  
+  // *testout << " facepoint " << facepoint << endl;  
+      
+
+  // Try all rotations of the trig 
+  for (int j=0;j<3;j++) 
+    {
+      int point_sing[3] = {0,0,0}; 
+      int edge_sing[3] = {0,0,0}; 
+      // *testout << " actual rotation of trig points " ;  
+      for(int m=0;m<3;m++) 
+	{ 
+	  p[m] = (j+m)%3 +1; // local vertex number
+	  pnums[m] = el.PNum(p[m]); // global vertex number 
+	  // *testout << pnums[m] << " \t "; 
+	}
+      // *testout << endl ; 
+      
+      if(dim == 3) 
+	{
+	  // face point 
+	  for(int k=0;k<3;k++)
+	    if(!sing_face)
+	      { 
+		//	*testout << " fp [" << k << "] = " << facepoint[pnums[k]] << endl;   
+		//	*testout << " fd.DomainIn()" <<  fd.DomainIn() << endl; 
+		//	*testout  << " fd.DomainOut()" <<  fd.DomainOut() << endl; 
+		if( facepoint[pnums[k]]  && (facepoint[pnums[k]] ==-1 || 
+					     facepoint[pnums[k]] == fd.DomainIn() ||   facepoint[pnums[k]] == fd.DomainOut()))
+		  point_sing[p[k]-1] = 1; 
+	      } 
+	  // if point is on face_edge in next step sing = 2 
+
+	  /*	  *testout << " pointsing NACH FACEPOints ... FALLS EDGEPOINT UMSETZEN" ; 
+            for (int k=0;k<3;k++) *testout << "\t" << point_sing[p[k]-1] ;
+            *testout << endl; */
+	}
+      
+      const ELEMENT_EDGE * eledges = MeshTopology::GetEdges1(TRIG); 
+      
+      if(dim==3)
+	{
+	  for(int k=0;k<3;k++) 
+	    { 
+	      int ep1=p[eledges[k][0]-1];  
+	      int ep2=p[eledges[k][1]-1];  
+	      INDEX_2 i2(el.PNum(ep1),el.PNum(ep2)); 
+	      
+	      if(edges.Used(i2)) 
+		{
+		  
+		  edge_sing[k]=2;
+		  point_sing[ep1-1] = 2; 
+		  point_sing[ep2-1] = 2; 
+		}
+	      else // face_edge? 
+		{	  
+		  i2.Sort();  
+		  if(surf_edges.Used(i2) && surf_edges.Get(i2) != fd.SurfNr()+1)  // edge not face_edge acc. to surface in which trig lies
+		    {
+		      if(face_edges.Get(i2)==-1 ||face_edges.Get(i2) == fd.DomainIn() || face_edges.Get(i2) == fd.DomainOut() )
+			{ 
+			  edge_sing[k]=1;
+			} 
+		      else
+			{ 
+			  point_sing[ep1-1] = 0; // set to edge_point 
+			  point_sing[ep2-1] = 0; // set to edge_point
+			} 
+		    }
+		}
+	      
+	      /*  *testout << " pointsing NACH edges UND FACEEDGES UMSETZEN ... " ; 
+                  for (int k=0;k<3;k++) *testout << "\t" << point_sing[p[k]-1] ; 
+                  *testout << endl;          
+                  */
+	    }
+	}
+      /*
+       *testout << " dim " << dim << endl; 
+       *testout << " edgepoint_dom " << edgepoint_dom << endl; 
+       */
+      if(dim==2)
+	{
+	  for(int k=0;k<3;k++) 
+	    { 
+	      int ep1=p[eledges[k][0]-1];  
+	      int ep2=p[eledges[k][1]-1];  
+	     
+	      INDEX_2 i2(el.PNum(ep1),el.PNum(ep2));  
+	     
+	      if(edges.Used(i2)) 
+		{
+		  
+		  if(edgepoint_dom.Used(INDEX_2(fd.SurfNr(),pnums[ep1-1])) || 
+		     edgepoint_dom.Used(INDEX_2(-1,pnums[ep1-1])) || 
+		     edgepoint_dom.Used(INDEX_2(fd.SurfNr(),pnums[ep2-1])) || 
+		     edgepoint_dom.Used(INDEX_2(-1,pnums[ep2-1]))) 
+		    {
+		      edge_sing[k]=2;
+		      point_sing[ep1-1] = 2;
+		      point_sing[ep2-1] = 2; 
+		    }
+		}
+	     
+	    }
+	}
+
+     
+	 
+      for(int k=0;k<3;k++) 
+	if(edgepoint.Test(pnums[k])) //edgepoint, but not member of sing_edge on trig -> cp 
+	  {
+	    INDEX_2 i2a=INDEX_2::Sort(el.PNum(p[k]), el.PNum(p[(k+1)%3])); 
+	    INDEX_2 i2b=INDEX_2::Sort(el.PNum(p[k]), el.PNum(p[(k+2)%3])); 
+	    
+	    if(!edges.Used(i2a) && !edges.Used(i2b)) 
+	      point_sing[p[k]-1] = 3; 	
+	  } 
+      
+      for(int k=0;k<3;k++) 
+	if(cornerpoint.Test(el.PNum(p[k]))) 
+	  point_sing[p[k]-1] = 3; 
+      
+      *testout << "point_sing = " << point_sing[0] << point_sing[1] << point_sing[2] << endl;
+
+      if(edge_sing[0] + edge_sing[1] + edge_sing[2] == 0) 
+        { 
+          int ps = point_sing[0] + point_sing[1] + point_sing[2]; 
+	 
+          if(ps==0) 
+            type = HP_TRIG; 
+          else if(point_sing[p[0]-1]  && !point_sing[p[1]-1] && !point_sing[p[2]-1])
+            type = HP_TRIG_SINGCORNER;
+          else if(point_sing[p[0]-1] && point_sing[p[1]-1] && !point_sing[p[2]-1]) 
+            type = HP_TRIG_SINGCORNER12; 
+          else if(point_sing[p[0]-1] && point_sing[p[1]-1] && point_sing[p[2]-1]) 
+            { 
+              if(dim==2) type = HP_TRIG_SINGCORNER123_2D; 
+              else type = HP_TRIG_SINGCORNER123; 
+            } 
+        } 
+      else
+        if (edge_sing[2] && !edge_sing[0] && !edge_sing[1]) //E[2]=(1,2) 
+          { 
+            int code = 0; 
+            if(point_sing[p[0]-1] > edge_sing[2]) code+=1; 
+            if(point_sing[p[1]-1] > edge_sing[2]) code+=2; 
+            if(point_sing[p[2]-1]) code+=4; 
+	
+            HPREF_ELEMENT_TYPE types[] =
+              {
+                HP_TRIG_SINGEDGE, 
+                HP_TRIG_SINGEDGECORNER1, 
+                HP_TRIG_SINGEDGECORNER2,
+                HP_TRIG_SINGEDGECORNER12, 
+                HP_TRIG_SINGEDGECORNER3, 
+                HP_TRIG_SINGEDGECORNER13, 
+                HP_TRIG_SINGEDGECORNER23, 
+                HP_TRIG_SINGEDGECORNER123, 
+              };
+            type = types[code]; 
+	
+          }  // E[0] = [0,2], E[1] =[1,2], E[2] = [0,1]
+        else 
+          if(edge_sing[2] && !edge_sing[1] && edge_sing[0])
+            {
+              if(point_sing[p[2]-1] <= edge_sing[0] ) 
+                { 
+                  if(point_sing[p[1]-1]<= edge_sing[2]) type = HP_TRIG_SINGEDGES; 
+                  else type = HP_TRIG_SINGEDGES2; 
+                } 
+              else 
+                {
+                  if(point_sing[p[1]-1]<= edge_sing[2]) 
+                    type = HP_TRIG_SINGEDGES3; 
+                  else type = HP_TRIG_SINGEDGES23; 
+                }
+            }
+          else if (edge_sing[2] && edge_sing[1] && edge_sing[0])
+            type = HP_TRIG_3SINGEDGES; 
+     
+      //  cout << " run for " <<  j << " gives type " << type << endl; 
+      //*testout << " run for " <<  j << " gives type " << type << endl; 
+
+      if(type!=HP_NONE) break;
+    }
+
+  *testout << "type = " << type << endl;
+    
+  for(int k=0;k<3;k++) el[k] = pnums[k]; 
+  /*if(type != HP_NONE) 
+    {
+     
+    cout << " TRIG with pnums " << pnums[0] << "\t"  << 
+    pnums[1] << "\t"  << pnums[2] << endl; 
+    cout << " type "  << type << endl; 
+    }
+  */
+      return(type);
+}
+#ifdef HPREF_OLD 
+HPREF_ELEMENT_TYPE ClassifyTrig(HPRefElement & el, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoint_dom, 
+				BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int> & face_edges, 
+				INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint, int dim, const FaceDescriptor & fd)
+{
+  HPREF_ELEMENT_TYPE type = HP_NONE; 
+  
+  int pnums[3]; 
+	      
+  INDEX_3 i3 (el.pnums[0], el.pnums[1], el.pnums[2]);
+  i3.Sort();
+  bool sing_face = faces.Used (i3);
+   
+  
+  for (int j = 1; j <= 3; j++)
+    {
+      int ep1 = edgepoint.Test (el.PNumMod (j));
+      int ep2 = edgepoint.Test (el.PNumMod (j+1));
+      int ep3 = edgepoint.Test (el.PNumMod (j+2));
+      
+      if (dim == 2)
+	{
+	  // JS, Dec 11
+	  ep1 = edgepoint_dom.Used (INDEX_2 (fd.SurfNr(), el.PNumMod(j))) ||
+	    edgepoint_dom.Used (INDEX_2 (-1, el.PNumMod(j)));
+	  ep2 = edgepoint_dom.Used (INDEX_2 (fd.SurfNr(), el.PNumMod(j+1))) ||
+	    edgepoint_dom.Used (INDEX_2 (-1, el.PNumMod(j+1)));
+	  ep3 = edgepoint_dom.Used (INDEX_2 (fd.SurfNr(), el.PNumMod(j+2))) ||
+	    edgepoint_dom.Used (INDEX_2 (-1, el.PNumMod(j+2)));
+	  /*
+            ep1 = edgepoint_dom.Used (INDEX_2 (el.index, el.PNumMod(j)));
+            ep2 = edgepoint_dom.Used (INDEX_2 (el.index, el.PNumMod(j+1)));
+            ep3 = edgepoint_dom.Used (INDEX_2 (el.index, el.PNumMod(j+2)));
+	  */
+	  // ep3 = edgepoint_dom.Used (INDEX_2 (mesh.SurfaceElement(i).GetIndex(), el.PNumMod(j+2)));
+	}
+      
+      
+      
+      int cp1 = cornerpoint.Test (el.PNumMod (j));
+      int cp2 = cornerpoint.Test (el.PNumMod (j+1));
+      int cp3 = cornerpoint.Test (el.PNumMod (j+2));
+      
+      ep1 |= cp1;
+      ep2 |= cp2;
+      ep3 |= cp3;
+      
+      
+      // (*testout) << "cp = " << cp1 << cp2 << cp3 << ", ep = " << ep1 << ep2 << ep3 << endl;
+
+      int p[3] = { el.PNumMod (j), el.PNumMod (j+1), el.PNumMod (j+2)};
+      if(ep1)
+	{
+	  INDEX_2 i2a=INDEX_2::Sort(p[0], p[1]); 
+	  INDEX_2 i2b=INDEX_2::Sort(p[0], p[2]); 
+	  if(!edges.Used(i2a) && !edges.Used(i2b)) 
+	    cp1 = 1; 
+	}
+      if(ep2)
+	{
+	  INDEX_2 i2a=INDEX_2::Sort(p[1], p[0]); 
+	  INDEX_2 i2b=INDEX_2::Sort(p[1], p[2]); 
+	  if(!edges.Used(i2a) && !edges.Used(i2b)) 
+	    cp2 = 1; 
+	}
+      if(ep3)
+	{
+	  INDEX_2 i2a=INDEX_2::Sort(p[2], p[0]); 
+	  INDEX_2 i2b=INDEX_2::Sort(p[2], p[1]); 
+	  if(!edges.Used(i2a) && !edges.Used(i2b)) 
+	    cp3= 1; 
+	}		      
+      
+      
+      int isedge1=0, isedge2=0, isedge3=0; 
+      if(dim == 3 )
+	{
+	  INDEX_2 i2;
+	  i2 = INDEX_2(el.PNumMod (j), el.PNumMod (j+1));
+	  isedge1 = edges.Used (i2);
+	  i2.Sort();
+	  if(surf_edges.Used(i2) &&  surf_edges.Get(i2)   != fd.SurfNr()+1 && 
+	     (face_edges.Get(i2) == -1 || face_edges.Get(i2) == fd.DomainIn() || face_edges.Get(i2) == fd.DomainOut()) ) 
+	    {
+	      isedge1=1;
+	      ep1 = 1; ep2=1;
+	    }
+	  
+	  i2 = INDEX_2(el.PNumMod (j+1), el.PNumMod (j+2));
+	  isedge2 = edges.Used (i2);
+	  i2.Sort();
+	  if(surf_edges.Used(i2) &&  surf_edges.Get(i2)   != fd.SurfNr()+1 &&
+	     (face_edges.Get(i2) == -1 || face_edges.Get(i2) == fd.DomainIn() || face_edges.Get(i2) == fd.DomainOut()) ) 
+	    {
+	      isedge2=1;
+	      ep2 = 1; ep3=1;
+	    }
+	  i2 = INDEX_2(el.PNumMod (j+2), el.PNumMod (j+3));
+	  isedge3 = edges.Used (i2);
+	  i2.Sort();
+	  if(surf_edges.Used(i2) &&  surf_edges.Get(i2)   != fd.SurfNr()+1 && 
+	     (face_edges.Get(i2) == -1 || face_edges.Get(i2) == fd.DomainIn() || face_edges.Get(i2) == fd.DomainOut()) ) 
+	    {
+	      isedge3=1;
+	      ep1 = 1; ep3=1;
+	    }
+	  
+	  // cout << " isedge " << isedge1 << " \t " << isedge2 << " \t " << isedge3 << endl;  
+	
+	  if (!sing_face)
+            {
+              /*
+                if (!isedge1)  { cp1 |= ep1; cp2 |= ep2; }
+                if (!isedge2)  { cp2 |= ep2; cp3 |= ep3; }
+                if (!isedge3)  { cp3 |= ep3; cp1 |= ep1; }
+              */
+              ep1 |= facepoint [el.PNumMod(j)] != 0;
+              ep2 |= facepoint [el.PNumMod(j+1)] != 0;
+              ep3 |= facepoint [el.PNumMod(j+2)] != 0;
+	  
+	  
+              isedge1 |= face_edges.Used (INDEX_2::Sort (el.PNumMod(j), el.PNumMod(j+1)));
+              isedge2 |= face_edges.Used (INDEX_2::Sort (el.PNumMod(j+1), el.PNumMod(j+2)));
+              isedge3 |= face_edges.Used (INDEX_2::Sort (el.PNumMod(j+2), el.PNumMod(j+3)));
+            }
+	}
+      
+      if(dim ==2) 
+	{ 
+	  INDEX_2 i2;
+	  i2 = INDEX_2(el.PNumMod (j), el.PNumMod (j+1));
+	  i2.Sort();
+	  isedge1 = edges.Used (i2);
+	  if(isedge1)
+	    {
+	      ep1 = 1; ep2=1;
+	    }
+	  
+	  i2 = INDEX_2(el.PNumMod (j+1), el.PNumMod (j+2));
+	  i2.Sort();
+	  isedge2 = edges.Used (i2);
+	  if(isedge2)
+	    {
+	      ep2 = 1; ep3=1;
+	    }
+	  i2 = INDEX_2(el.PNumMod (j+2), el.PNumMod (j+3));
+	  i2.Sort();
+	  isedge3 = edges.Used (i2);
+	  if(isedge3)
+	    {
+	      ep1 = 1; ep3=1;
+	    }
+	  
+	  
+	}
+      
+		  
+      /*
+        cout << " used " << face_edges.Used (INDEX_2::Sort (el.PNumMod(j), el.PNumMod(j+1))) << endl; 
+
+        cout << " isedge " << isedge1 << " \t " << isedge2 << " \t " << isedge3 << endl; 
+        cout << " ep " << ep1 << "\t" << ep2 << " \t " << ep3 << endl; 
+        cout << " cp " << cp1 << "\t" << cp2 << " \t " << cp3 << endl; 
+      */
+		  
+
+      
+      if (isedge1 + isedge2 + isedge3 == 0)
+	{
+	  if (!ep1 && !ep2 && !ep3)
+	    type = HP_TRIG;
+	  
+	  if (ep1 && !ep2 && !ep3)
+	    type = HP_TRIG_SINGCORNER;
+	  
+	  if (ep1 && ep2 && !ep3)
+	    type = HP_TRIG_SINGCORNER12;
+	  
+	  if (ep1 && ep2 && ep3)
+	    {
+	      if (dim == 2)
+                type = HP_TRIG_SINGCORNER123_2D;
+	      else
+		type = HP_TRIG_SINGCORNER123;
+	    }
+	  
+	  if (type != HP_NONE)
+	    {
+	      pnums[0] = el.PNumMod (j);
+	      pnums[1] = el.PNumMod (j+1);
+	      pnums[2] = el.PNumMod (j+2);
+	      break;
+	    }
+	}
+      
+      if (isedge1 && !isedge2 && !isedge3)
+	{
+	  int code = 0;
+	  if (cp1) code += 1;
+	  if (cp2) code += 2;
+	  if (ep3) code += 4;
+	  
+	  HPREF_ELEMENT_TYPE types[] =
+	    {
+	      HP_TRIG_SINGEDGE, 
+	      HP_TRIG_SINGEDGECORNER1, 
+	      HP_TRIG_SINGEDGECORNER2,
+	      HP_TRIG_SINGEDGECORNER12, 
+	      HP_TRIG_SINGEDGECORNER3, 
+	      HP_TRIG_SINGEDGECORNER13, 
+	      HP_TRIG_SINGEDGECORNER23, 
+	      HP_TRIG_SINGEDGECORNER123, 
+	    };
+	  type = types[code];
+	  pnums[0] = el.PNumMod (j);
+	  pnums[1] = el.PNumMod (j+1);
+	  pnums[2] = el.PNumMod (j+2);
+	  break;
+	}
+      
+      
+      if (isedge1 && !isedge2 && isedge3)
+	{
+	  if (!cp3)
+	    {
+	      if (!cp2) type = HP_TRIG_SINGEDGES;
+	      else      type = HP_TRIG_SINGEDGES2;
+	    }
+	  else
+	    { 
+	      if (!cp2) type = HP_TRIG_SINGEDGES3;
+	      else      type = HP_TRIG_SINGEDGES23;
+	    }
+	  
+	  pnums[0] = el.PNumMod (j);
+	  pnums[1] = el.PNumMod (j+1);
+	  pnums[2] = el.PNumMod (j+2);
+	  break;
+	}
+       
+      if (isedge1 && isedge2 && isedge3)
+	{
+	  type = HP_TRIG_3SINGEDGES;
+	  pnums[0] = el.PNumMod (j);
+	  pnums[1] = el.PNumMod (j+1);
+	  pnums[2] = el.PNumMod (j+2);
+	  break;
+	}
+    }
+  
+  for(int k=0;k<3;k++) el[k] = pnums[k]; 
+  /*if(type != HP_NONE) 
+    {
+     
+    cout << " TRIG with pnums " << pnums[0] << "\t"  << 
+    pnums[1] << "\t"  << pnums[2] << endl; 
+    cout << " type "  << type << endl; 
+    }
+  */
+  return(type);
+}
+#endif
+HPREF_ELEMENT_TYPE ClassifyQuad(HPRefElement & el, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoint_dom, 
+                                BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int>  & face_edges, 
+				INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint, int dim, const FaceDescriptor & fd)
+{
+  HPREF_ELEMENT_TYPE type = HP_NONE; 
+  
+  int ep1(-1), ep2(-1), ep3(-1), ep4(-1), cp1(-1), cp2(-1), cp3(-1), cp4(-1);
+  int isedge1, isedge2, isedge3, isedge4;
+
+  *testout << "edges = " << edges << endl;
+  
+  for (int j = 1; j <= 4; j++)
+    {
+      ep1 = edgepoint.Test (el.PNumMod (j));
+      ep2 = edgepoint.Test (el.PNumMod (j+1));
+      ep3 = edgepoint.Test (el.PNumMod (j+2));
+      ep4 = edgepoint.Test (el.PNumMod (j+3));
+
+      if (dim == 2)
+        {
+          ep1 = edgepoint_dom.Used (INDEX_2 (el.GetIndex(), el.PNumMod(j)));
+          ep2 = edgepoint_dom.Used (INDEX_2 (el.GetIndex(), el.PNumMod(j+1)));
+          ep3 = edgepoint_dom.Used (INDEX_2 (el.GetIndex(), el.PNumMod(j+2)));
+          ep4 = edgepoint_dom.Used (INDEX_2 (el.GetIndex(), el.PNumMod(j+3)));
+        }
+
+      cp1 = cornerpoint.Test (el.PNumMod (j));
+      cp2 = cornerpoint.Test (el.PNumMod (j+1));
+      cp3 = cornerpoint.Test (el.PNumMod (j+2));
+      cp4 = cornerpoint.Test (el.PNumMod (j+3));
+
+      ep1 |= cp1;
+      ep2 |= cp2;
+      ep3 |= cp3;
+      ep4 |= cp4;
+		
+      int p[4] = { el.PNumMod (j), el.PNumMod (j+1), el.PNumMod (j+2), el.PNumMod(j+4)};
+      //int epp[4] = { ep1, ep2, ep3, ep4}; 
+      int cpp[4] = { cp1, cp2, cp3, cp4};
+      for(int k=0;k<0;k++)
+        {
+          INDEX_2 i2a=INDEX_2::Sort(p[k], p[(k+1)%4]); 
+          INDEX_2 i2b=INDEX_2::Sort(p[k], p[(k-1)%4]); 
+          if(!edges.Used(i2a) && !edges.Used(i2b)) 
+            cpp[k] = 1; 
+        }
+      cp1= cpp[0]; cp2=cpp[1]; cp3=cpp[2]; cp4=cpp[3];
+		  
+
+      if(dim ==3) 
+        { 
+          INDEX_2 i2;
+          i2 = INDEX_2(el.PNumMod (j), el.PNumMod (j+1));
+          // i2.Sort();
+          isedge1 = edges.Used (i2);
+          i2.Sort();
+          if(surf_edges.Used(i2)  &&  surf_edges.Get(i2)   != fd.SurfNr()+1 && 
+             (face_edges.Get(i2) == -1 || face_edges.Get(i2) == fd.DomainIn() || face_edges.Get(i2) == fd.DomainOut()) ) 
+            {
+              isedge1=1;
+              ep1 = 1; ep2=1;
+            }
+          i2 = INDEX_2(el.PNumMod (j+1), el.PNumMod (j+2));
+          // i2.Sort();
+          isedge2 = edges.Used (i2);
+          i2.Sort();
+          if(surf_edges.Used(i2)  &&  surf_edges.Get(i2)   != fd.SurfNr()+1 && 
+             (face_edges.Get(i2) == -1 || face_edges.Get(i2) == fd.DomainIn() || face_edges.Get(i2) == fd.DomainOut()) ) 
+            {  
+              isedge2=1;
+              ep2=1; ep3=1;
+            }
+          i2 = INDEX_2(el.PNumMod (j+2), el.PNumMod (j+3));
+          // i2.Sort();
+          isedge3 = edges.Used (i2); 
+          i2.Sort();
+          if(surf_edges.Used(i2)   &&  surf_edges.Get(i2)   != fd.SurfNr()+1 && (face_edges.Get(i2) == -1 || face_edges.Get(i2) == fd.DomainIn() || face_edges.Get(i2) == fd.DomainOut()) ) 
+            {
+              isedge3=1;
+              ep3=1; ep4=1;
+            }
+          i2 = INDEX_2(el.PNumMod (j+3), el.PNumMod (j+4));
+          // i2.Sort();
+          isedge4 = edges.Used (i2);
+          i2.Sort();
+          if(surf_edges.Used(i2)  &&  surf_edges.Get(i2)   != fd.SurfNr()+1 && 
+             (face_edges.Get(i2) == -1 || face_edges.Get(i2) == fd.DomainIn() || face_edges.Get(i2) == fd.DomainOut()) ) 
+            { 
+              isedge4=1;
+              ep4=1; ep1=1;
+            } 
+		    
+
+          //MH***********************************************************************************************************
+          if(ep1)
+            if(edgepoint.Test(p[0]))
+              {
+                INDEX_2 i2a=INDEX_2::Sort(p[0], p[1]); 
+                INDEX_2 i2b=INDEX_2::Sort(p[0], p[3]); 
+                if(!edges.Used(i2a) && !edges.Used(i2b)) 
+                  cp1 = 1; 
+              }
+          if(ep2)
+            if(edgepoint.Test(p[1]))
+              {
+                INDEX_2 i2a=INDEX_2::Sort(p[0], p[1]); 
+                INDEX_2 i2b=INDEX_2::Sort(p[1], p[2]); 
+                if(!edges.Used(i2a) && !edges.Used(i2b)) 
+                  cp2 = 1; 
+              }
+          if(ep3)
+            if(edgepoint.Test(p[2]))
+              {
+                INDEX_2 i2a=INDEX_2::Sort(p[2], p[1]); 
+                INDEX_2 i2b=INDEX_2::Sort(p[3], p[2]); 
+                if(!edges.Used(i2a) && !edges.Used(i2b)) 
+                  cp3 = 1; 
+              }
+          if(ep4)
+            if(edgepoint.Test(p[3]))
+              {
+                INDEX_2 i2a=INDEX_2::Sort(p[0], p[3]); 
+                INDEX_2 i2b=INDEX_2::Sort(p[3], p[2]); 
+                if(!edges.Used(i2a) && !edges.Used(i2b)) 
+                  cp4 = 1; 
+              }
+          //MH*****************************************************************************************************************************
+        }
+      else
+        { 
+          INDEX_2 i2;
+          i2 = INDEX_2(el.PNumMod (j), el.PNumMod (j+1));
+          i2.Sort();
+          isedge1 = edges.Used (i2);
+          if(isedge1)
+            {
+              ep1 = 1; ep2=1;
+            }
+          i2 = INDEX_2(el.PNumMod (j+1), el.PNumMod (j+2));
+          i2.Sort();
+          isedge2 = edges.Used (i2);
+          if(isedge2)
+            {  
+              ep2=1; ep3=1;
+            }
+          i2 = INDEX_2(el.PNumMod (j+2), el.PNumMod (j+3));
+          i2.Sort();
+          isedge3 = edges.Used (i2); 
+		      
+          if(isedge3)
+            {
+              ep3=1; ep4=1;
+            }
+          i2 = INDEX_2(el.PNumMod (j+3), el.PNumMod (j+4));
+          i2.Sort();
+          isedge4 = edges.Used (i2);
+          if(isedge4)
+            { 
+              ep4=1; ep1=1;
+            } 
+        }
+
+      int sumcp = cp1 + cp2 + cp3 + cp4;
+      int sumep = ep1 + ep2 + ep3 + ep4;
+      int sumedge = isedge1 + isedge2 + isedge3 + isedge4;
+
+      *testout << "isedge = " << isedge1 << isedge2 << isedge3 << isedge4 << endl;
+      *testout << "iscp = " << cp1 << cp2 << cp3 << cp4 << endl;
+      *testout << "isep = " << ep1 << ep2 << ep3 << ep4 << endl;
+
+      switch (sumedge)
+        {
+        case 0:
+          {
+            switch (sumep)
+              {
+              case 0: 
+                type = HP_QUAD; 
+                break;
+              case 1: 
+                if (ep1) type = HP_QUAD_SINGCORNER;
+                break; 
+              case 2:
+                {
+                  if (ep1 && ep2) type = HP_QUAD_0E_2VA;
+                  if (ep1 && ep3) type = HP_QUAD_0E_2VB;
+                  break;
+                }
+              case 3: 
+                if (!ep4) type = HP_QUAD_0E_3V; 
+                break; 
+              case 4: 
+                type = HP_QUAD_0E_4V; 
+                break; 
+              }
+            break;
+          }
+        case 1:
+          {
+            if (isedge1)
+              {
+                switch (cp1+cp2+ep3+ep4)
+                  {
+                  case 0: 
+                    type = HP_QUAD_SINGEDGE; 
+                    break;
+                  case 1:
+                    {
+                      if (cp1) type = HP_QUAD_1E_1VA;
+                      if (cp2) type = HP_QUAD_1E_1VB;
+                      if (ep3) type = HP_QUAD_1E_1VC;
+                      if (ep4) type = HP_QUAD_1E_1VD; 
+                      break; 
+                    }
+                  case 2:
+                    {
+                      if (cp1 && cp2) type = HP_QUAD_1E_2VA; 
+                      if (cp1 && ep3) type = HP_QUAD_1E_2VB; 
+                      if (cp1 && ep4) type = HP_QUAD_1E_2VC; 
+                      if (cp2 && ep3) type = HP_QUAD_1E_2VD; 
+                      if (cp2 && ep4) type = HP_QUAD_1E_2VE; 
+                      if (ep3 && ep4) type = HP_QUAD_1E_2VF; 
+                      break; 
+                    }
+                  case 3:
+                    {
+                      if (cp1 && cp2 && ep3) type = HP_QUAD_1E_3VA;
+                      if (cp1 && cp2 && ep4) type = HP_QUAD_1E_3VB;
+                      if (cp1 && ep3 && ep4) type = HP_QUAD_1E_3VC;
+                      if (cp2 && ep3 && ep4) type = HP_QUAD_1E_3VD;
+                      break;
+                    }
+                  case 4:
+                    {
+                      type = HP_QUAD_1E_4V; 
+                      break;
+                    }
+                  }
+              }
+            break;
+          }
+        case 2:
+          {
+            if (isedge1 && isedge4)
+              {
+                if (!cp2 && !ep3 && !cp4)
+                  type = HP_QUAD_2E;
+			  
+                if (cp2 && !ep3 && !cp4)
+                  type = HP_QUAD_2E_1VA;
+                if (!cp2 && ep3 && !cp4)
+                  type = HP_QUAD_2E_1VB;
+                if (!cp2 && !ep3 && cp4)
+                  type = HP_QUAD_2E_1VC;
+
+                if (cp2 && ep3 && !cp4)
+                  type = HP_QUAD_2E_2VA;
+                if (cp2 && !ep3 && cp4)
+                  type = HP_QUAD_2E_2VB;
+                if (!cp2 && ep3 && cp4)
+                  type = HP_QUAD_2E_2VC;
+
+                if (cp2 && ep3 && cp4)
+                  type = HP_QUAD_2E_3V;
+              }
+            if (isedge1 && isedge3)
+              {
+                switch (sumcp)
+                  {
+                  case 0: 
+                    type = HP_QUAD_2EB_0V; break;
+                  case 1:
+                    {
+                      if (cp1) type = HP_QUAD_2EB_1VA; 
+                      if (cp2) type = HP_QUAD_2EB_1VB; 
+                      break;
+                    }
+                  case 2:
+                    {
+                      if (cp1 && cp2) { type = HP_QUAD_2EB_2VA; }
+                      if (cp1 && cp3) { type = HP_QUAD_2EB_2VB; }
+                      if (cp1 && cp4) { type = HP_QUAD_2EB_2VC; }
+                      if (cp2 && cp4) { type = HP_QUAD_2EB_2VD; }
+                      break;
+                    }
+                  case 3:
+                    {
+                      if (cp1 && cp2 && cp3) { type = HP_QUAD_2EB_3VA; }
+                      if (cp1 && cp2 && cp4) { type = HP_QUAD_2EB_3VB; }
+                      break;
+                    }
+                  case 4:
+                    {
+                      type = HP_QUAD_2EB_4V; break;
+                    }
+                  }
+              }
+            break;
+          }
+
+        case 3:
+          {
+            if (isedge1 && isedge2 && isedge4)
+              {
+                if (!cp3 && !cp4) type = HP_QUAD_3E;
+                if (cp3 && !cp4) type = HP_QUAD_3E_3VA;
+                if (!cp3 && cp4) type = HP_QUAD_3E_3VB;
+                if (cp3 && cp4) type = HP_QUAD_3E_4V;
+              }
+            break;
+          }
+
+        case 4:
+          {
+            type = HP_QUAD_4E;
+            break;
+          }
+        }
+
+      if (type != HP_NONE)
+        {
+          int pnums[4]; 
+          pnums[0] = el.PNumMod (j); 
+          pnums[1] = el.PNumMod (j+1);
+          pnums[2] = el.PNumMod (j+2); 
+          pnums[3] = el.PNumMod (j+3);
+          for (int k=0;k<4;k++) el[k] = pnums[k]; 
+	
+          /*  cout << " QUAD with pnums " << pnums[0] << "\t"  << 
+              pnums[1] << "\t"  << pnums[2] << "\t"  << pnums[3] 
+              << endl << " of type " << type << endl; */
+		   		      
+          break;
+        }
+    }
+  if (type == HP_NONE)
+    {
+      (*testout) << "undefined element" << endl
+                 << "cp = " << cp1 << cp2 << cp3 << cp4 << endl
+                 << "ep = " << ep1 << ep2 << ep3 << ep4 << endl
+                 << "isedge = " << isedge1 << isedge2 << isedge3 
+                 << isedge4 << endl;
+    }
+	    
+  *testout << "quad type = " << type << endl;
+
+  return type;  
+}	    
+
+
+HPREF_ELEMENT_TYPE ClassifyHex(HPRefElement & el, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoint_dom, 
+                               BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int> & face_edges, 
+                               INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint)
+{
+  HPREF_ELEMENT_TYPE type = HP_NONE;
+  
+  // implementation only for HP_HEX_1F_0E_0V
+  //                         HP_HEX_1FA_1FB_0E_0V
+  //                         HP_HEX 
+  // up to now other cases are refine dummies 
+  
+  // indices of bot,top-faces combinations
+  int index[6][2] = {{0,1},{1,0},{2,4},{4,2},{3,5},{5,3}}; 
+  int p[8]; 
+  const ELEMENT_FACE * elfaces  = MeshTopology::GetFaces1 (HEX);
+  const ELEMENT_EDGE * eledges = MeshTopology::GetEdges1 (HEX);
+  
+  for(int m=0;m<6 && type == HP_NONE;m++) 
+    for(int j=0;j<4 && type == HP_NONE;j++) 
+      { 
+	int point_sing[8]={0,0,0,0,0,0,0,0}; 
+	int face_sing[6] = {0,0,0,0,0,0};
+	int edge_sing[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+	int spoint=0, sface=0, sedge=0; 
+	for(int l=0;l<4;l++) 
+	  {
+	    p[l] = elfaces[index[m][0]][(4-j-l)%4]; 
+	    p[l+4] = elfaces[index[m][1]][(j+l)%4];
+	  }
+	
+	for(int l=0;l<8;l++) 
+	  if(cornerpoint.Test(el.PNum(p[l])))  
+	    { 
+	      point_sing[p[l]-1]=3;
+	      spoint++; 
+	    }
+	  else if(edgepoint.Test(el.PNum(p[l]))) point_sing[p[l]-1]=2;
+	  else if (facepoint[el.PNum(p[l])] == -1 || facepoint[el.PNum(p[l])] == el.GetIndex())
+	    point_sing[p[l]-1] = 1;   
+	
+	for(int k=0;k<12;k++)
+          {
+            INDEX_2 i2 = INDEX_2 :: Sort(el.PNum(p[eledges[k][0]-1]),el.PNum(p[eledges[k][1]-1])); 
+            if (edges.Used(i2)) 
+              { 
+                edge_sing[k] = 2;
+                sedge++; 
+              }
+            else edge_sing[k] = face_edges.Used(i2);
+          }
+	
+	for (int k=0;k<6;k++)
+          {
+            INDEX_3 i3; 
+	  
+	
+            INDEX_4  i4 = INDEX_4(el.pnums[p[elfaces[k][0]-1]-1], el.pnums[p[elfaces[k][1]-1]-1], el.pnums[p[elfaces[k][2]-1]-1],el.pnums[p[elfaces[k][3]-1]-1]); 
+            i4.Sort();
+            i3 = INDEX_3(i4.I1(), i4.I2(), i4.I3()); 
+	  
+            if (faces.Used (i3))
+              {
+	      
+                int domnr = faces.Get(i3); 
+                if (domnr == -1 || domnr == el.GetIndex())
+                  {
+                    face_sing[k] = 1;
+                    sface++;
+                  }
+	      
+              } 
+          } 
+	
+	if(!sface && !sedge && !spoint) type = HP_HEX; 
+	if(!sedge && !spoint) 
+	  {
+	    if(face_sing[0] && face_sing[2] && sface==2) 
+	      type = HP_HEX_1FA_1FB_0E_0V; 
+	    if (face_sing[0] && sface==1)  
+	      type = HP_HEX_1F_0E_0V; 
+	  }
+	
+	el.type=type; 
+
+	if(type != HP_NONE) 
+	  {
+	    int pnums[8]; 
+	    for(int l=0;l<8;l++) pnums[l] = el[p[l]-1];
+	    for(int l=0;l<8;l++) el[l] = pnums[l];
+	    /* cout << " HEX with pnums " << pnums[0] << "\t"  << 
+               pnums[1] << "\t"  << pnums[2] << "\t"  << pnums[3] << "\t"  << 
+               pnums[4] << "\t"  <<  pnums[5] << endl << " of type " << type << endl; */
+	    break; 
+	  }
+      }
+  
+  return (type); 
+
+}
+
+HPREF_ELEMENT_TYPE ClassifySegm(HPRefElement & hpel, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoint_dom, 
+                                BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int> & face_edges, 
+                                INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint)
+{
+  
+  int cp1 = cornerpoint.Test (hpel[0]);
+  int cp2 = cornerpoint.Test (hpel[1]);
+  
+  INDEX_2 i2;
+  i2 = INDEX_2(hpel[0], hpel[1]);
+  i2.Sort();
+  if (!edges.Used (i2))
+    {
+      cp1 = edgepoint.Test (hpel[0]);
+      cp2 = edgepoint.Test (hpel[1]);
+    }
+  
+  if(!edges.Used(i2) && !face_edges.Used(i2))
+    {
+      if(facepoint[hpel[0]]!=0) cp1=1; 
+      if(facepoint[hpel[1]]!=0) cp2=1; 
+    }
+  
+  if(edges.Used(i2) && !face_edges.Used(i2))
+    {
+      if(facepoint[hpel[0]]) cp1 = 1; 
+      if(facepoint[hpel[1]]) cp2 = 1; 
+    }
+  
+  if (!cp1 && !cp2)
+    hpel.type = HP_SEGM;
+  else if (cp1 && !cp2)
+    hpel.type = HP_SEGM_SINGCORNERL;
+  else if (!cp1 && cp2)
+    hpel.type = HP_SEGM_SINGCORNERR;
+  else
+    hpel.type = HP_SEGM_SINGCORNERS;
+  
+  // cout << " SEGM found with " << hpel[0] << " \t" << hpel[1] << endl << " of type " << hpel.type << endl; 
+  return(hpel.type) ; 
+} 
+
+
+HPREF_ELEMENT_TYPE ClassifyPyramid(HPRefElement & el, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoint_dom, 
+                                   BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int> & face_edges, 
+                                   INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint)
+{
+  HPREF_ELEMENT_TYPE type = HP_NONE;
+  
+  // implementation only for HP_PYRAMID
+  //                         HP_PYRAMID_0E_1V
+  //                         HP_PYRAMID_EDGES
+  //                         HP_PYRAMID_1FB_0E_1VA
+  // up to now other cases are refine dummies 
+  
+  // indices of bot,top-faces combinations
+  // int index[6][2] = {{0,1},{1,0},{2,4},{4,2},{3,5},{5,3}}; 
+
+  const ELEMENT_FACE * elfaces  = MeshTopology::GetFaces1 (PYRAMID);
+  const ELEMENT_EDGE * eledges = MeshTopology::GetEdges1 (PYRAMID);
+  
+  int point_sing[5]={0,0,0,0,0}; 
+  int face_sing[5] = {0,0,0,0,0};
+  int edge_sing[8] = {0,0,0,0,0,0,0,0};
+  
+  int spoint=0, sedge=0, sface=0; 
+   
+  for(int m=0;m<4 && type == HP_NONE;m++) 
+    {
+      int p[5] = {m%4, m%4+1, m%4+2, m%4+3, 4}; 
+
+      for(int l=0;l<5;l++) 
+	{
+	  if(cornerpoint.Test(el.pnums[p[l]]))  
+	    point_sing[l]=3;
+	  
+          else if(edgepoint.Test(el.pnums[p[l]]))
+	    point_sing[l]=2;
+	  
+	  else if (facepoint[el.pnums[p[l]]] == -1 || facepoint[el.pnums[p[l]]] == el.GetIndex())
+	    point_sing[l] = 1;   
+	  
+	  spoint += point_sing[l]; 
+	}
+      
+      for(int k=0;k<8;k++)
+	{
+	  INDEX_2 i2 = INDEX_2 :: Sort(el.pnums[p[eledges[k][0]-1]],
+				       el.pnums[p[eledges[k][1]-1]]); 
+	  if (edges.Used(i2)) 
+	    edge_sing[k] = 2;
+	  else 
+	    edge_sing[k] = face_edges.Used(i2);
+	  
+	  sedge += edge_sing[k]; 
+	}
+  
+      for (int k=0;k<5;k++)
+	{
+	  INDEX_3 i3; 
+	  INDEX_4  i4 = INDEX_4(el.pnums[p[elfaces[k][0]-1]], el.pnums[p[elfaces[k][1]-1]], el.pnums[p[elfaces[k][2]-1]],
+				el.pnums[p[elfaces[k][3]-1]]); 
+	  i4.Sort();
+	  i3 = INDEX_3(i4.I1(), i4.I2(), i4.I3()); 
+	  
+	  if (faces.Used (i3))
+	    {
+	      
+	      int domnr = faces.Get(i3); 
+	      if (domnr == -1 || domnr == el.GetIndex())
+		face_sing[k] = 1;
+	    } 
+	  sface +=face_sing[k]; 
+	} 
+  
+      if(!sface && !spoint && !sedge) return(HP_PYRAMID); 
+      
+      if(!sface && !sedge && point_sing[p[0]] == spoint) 
+	type = HP_PYRAMID_0E_1V; 
+      
+      if(!sface && edge_sing[0] + edge_sing[2] == sedge && 
+	 spoint == point_sing[0] + point_sing[1] + point_sing[3]) 
+	type = HP_PYRAMID_EDGES; 
+      
+      if(sface && sface == face_sing[0] && spoint == point_sing[4] + 2)
+	type = HP_PYRAMID_1FB_0E_1VA; 
+      
+      
+      if(type != HP_NONE) 
+	{ 
+	  int pnums[8]; 
+	  for(int l=0;l<5;l++) pnums[l] = el[p[l]];
+	  for(int l=0;l<5;l++) el[l] = pnums[l];
+	  el.type=type; 
+	  break; 
+	} 
+    }
+  
+  return (type); 
+  
+}
diff --git a/contrib/Netgen/libsrc/meshing/clusters.cpp b/contrib/Netgen/libsrc/meshing/clusters.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e89def6a8594a6a9acfc163a45b15b7d310f5da9
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/clusters.cpp
@@ -0,0 +1,267 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+  AnisotropicClusters ::  AnisotropicClusters (const Mesh & amesh)
+    : mesh(amesh)
+  {
+    ;
+  }
+
+  AnisotropicClusters ::  ~AnisotropicClusters ()
+  {
+    ;
+  }
+
+  void AnisotropicClusters ::  Update()
+  {
+    int i, j, k;
+
+    const MeshTopology & top = mesh.GetTopology();
+
+    bool hasedges = top.HasEdges();
+    bool hasfaces = top.HasFaces();
+
+    if (!hasedges || !hasfaces) return;
+
+    if (id == 0)
+      PrintMessage (3, "Update Clusters");
+
+    nv = mesh.GetNV();
+    ned = top.GetNEdges();
+    nfa = top.GetNFaces();
+    ne = mesh.GetNE();
+    int nse = mesh.GetNSE();
+
+    cluster_reps.SetSize (nv+ned+nfa+ne);
+  
+    Array<int> nnums, ednums, fanums;
+    int changed;
+
+    for (i = 1; i <= cluster_reps.Size(); i++)
+      cluster_reps.Elem(i) = -1;
+
+  
+    for (i = 1; i <= ne; i++)
+      {
+	const Element & el = mesh.VolumeElement(i);
+	ELEMENT_TYPE typ = el.GetType();
+      
+	top.GetElementEdges (i, ednums);
+	top.GetElementFaces (i, fanums);
+      
+	int elnv = top.GetNVertices (typ);
+	int elned = ednums.Size();
+	int elnfa = fanums.Size();
+	  
+	nnums.SetSize(elnv+elned+elnfa+1);
+	for (j = 1; j <= elnv; j++)
+	  nnums.Elem(j) = el.PNum(j);
+	for (j = 1; j <= elned; j++)
+	  nnums.Elem(elnv+j) = nv+ednums.Elem(j);
+	for (j = 1; j <= elnfa; j++)
+	  nnums.Elem(elnv+elned+j) = nv+ned+fanums.Elem(j);
+	nnums.Elem(elnv+elned+elnfa+1) = nv+ned+nfa+i;
+
+	for (j = 0; j < nnums.Size(); j++)
+	  cluster_reps.Elem(nnums[j]) = nnums[j];
+      }
+
+  
+
+    for (i = 1; i <= nse; i++)
+      {
+	const Element2d & el = mesh.SurfaceElement(i);
+	ELEMENT_TYPE typ = el.GetType();
+      
+	top.GetSurfaceElementEdges (i, ednums);
+	int fanum = top.GetSurfaceElementFace (i);
+      
+	int elnv = top.GetNVertices (typ);
+	int elned = ednums.Size();
+	  
+	nnums.SetSize(elnv+elned+1);
+	for (j = 1; j <= elnv; j++)
+	  nnums.Elem(j) = el.PNum(j);
+	for (j = 1; j <= elned; j++)
+	  nnums.Elem(elnv+j) = nv+ednums.Elem(j);
+	nnums.Elem(elnv+elned+1) = fanum;
+
+	for (j = 0; j < nnums.Size(); j++)
+	  cluster_reps.Elem(nnums[j]) = nnums[j];
+      }
+
+    static const int hex_cluster[] =
+      { 
+	1, 2, 3, 4, 1, 2, 3, 4, 
+	5, 6, 7, 8, 5, 6, 7, 8, 1, 2, 3, 4, 
+	9, 9, 5, 8, 6, 7, 
+	9
+      };
+
+    static const int prism_cluster[] =
+      { 
+	1, 2, 3, 1, 2, 3,
+	4, 5, 6, 4, 5, 6, 3, 1, 2,
+	7, 7, 4, 5, 6, 
+	7
+      };
+
+    static const int pyramid_cluster[] =
+      { 
+	1, 2, 2, 1, 3,
+	4, 2, 1, 4, 6, 5, 5, 6,
+	7, 5, 7, 6, 4, 
+	7
+      };
+    static const int tet_cluster14[] =
+      { 1, 2, 3, 1,   1, 4, 5, 4, 5, 6,   7, 5, 4, 7, 7 };
+  
+    static const int tet_cluster12[] =
+      { 1, 1, 2, 3,   4, 4, 5, 1, 6, 6,   7, 7, 4, 6, 7 };
+
+    static const int tet_cluster13[] =
+      { 1, 2, 1, 3,   4, 6, 4, 5, 1, 5,   7, 4, 7, 5, 7 };
+
+    static const int tet_cluster23[] =
+      { 2, 1, 1, 3, 6, 5, 5, 4, 4, 1, 5, 7, 7, 4, 7 };
+
+    static const int tet_cluster24[] =
+      { 2, 1, 3, 1, 4, 1, 5, 4, 6, 5, 5, 7, 4, 7, 7 };
+
+    static const int tet_cluster34[] =
+      { 2, 3, 1, 1, 4, 5, 1, 6, 4, 5, 5, 4, 7, 7, 7 };
+
+    int cnt = 0;
+
+    do
+      {
+
+	cnt++;
+	changed = 0;
+      
+	for (i = 1; i <= ne; i++)
+	  {
+	    const Element & el = mesh.VolumeElement(i);
+	    ELEMENT_TYPE typ = el.GetType();
+	  
+	    top.GetElementEdges (i, ednums);
+	    top.GetElementFaces (i, fanums);
+	  
+	    int elnv = top.GetNVertices (typ);
+	    int elned = ednums.Size();
+	    int elnfa = fanums.Size();
+	  
+	    nnums.SetSize(elnv+elned+elnfa+1);
+	    for (j = 1; j <= elnv; j++)
+	      nnums.Elem(j) = el.PNum(j);
+	    for (j = 1; j <= elned; j++)
+	      nnums.Elem(elnv+j) = nv+ednums.Elem(j);
+	    for (j = 1; j <= elnfa; j++)
+	      nnums.Elem(elnv+elned+j) = nv+ned+fanums.Elem(j);
+	    nnums.Elem(elnv+elned+elnfa+1) = nv+ned+nfa+i;
+
+	  
+	    const int * clustertab = NULL;
+	    switch (typ)
+	      {
+	      case PRISM:
+	      case PRISM12:
+		clustertab = prism_cluster;
+		break;
+	      case HEX: 
+		clustertab = hex_cluster; 
+		break; 
+	      case PYRAMID:
+		clustertab = pyramid_cluster;
+		break;
+	      case TET:
+	      case TET10:
+		if (cluster_reps.Get(el.PNum(1)) == 
+		    cluster_reps.Get(el.PNum(2)))
+		  clustertab = tet_cluster12;
+		else if (cluster_reps.Get(el.PNum(1)) == 
+			 cluster_reps.Get(el.PNum(3)))
+		  clustertab = tet_cluster13;
+		else if (cluster_reps.Get(el.PNum(1)) == 
+			 cluster_reps.Get(el.PNum(4)))
+		  clustertab = tet_cluster14;
+		else if (cluster_reps.Get(el.PNum(2)) == 
+			 cluster_reps.Get(el.PNum(3)))
+		  clustertab = tet_cluster23;
+		else if (cluster_reps.Get(el.PNum(2)) == 
+			 cluster_reps.Get(el.PNum(4)))
+		  clustertab = tet_cluster24;
+		else if (cluster_reps.Get(el.PNum(3)) == 
+			 cluster_reps.Get(el.PNum(4)))
+		  clustertab = tet_cluster34;
+
+		else
+		  clustertab = NULL;
+		break;
+	      default:
+		clustertab = NULL;
+	      }
+	  
+	    if (clustertab)
+	      for (j = 0; j < nnums.Size(); j++)
+		for (k = 0; k < j; k++)
+		  if (clustertab[j] == clustertab[k])
+		    {
+		      int jj = nnums[j];
+		      int kk = nnums[k];
+		      if (cluster_reps.Get(jj) < cluster_reps.Get(kk))
+			{
+			  cluster_reps.Elem(kk) = cluster_reps.Get(jj);
+			  changed = 1;
+			}
+		      else if (cluster_reps.Get(kk) < cluster_reps.Get(jj))
+			{
+			  cluster_reps.Elem(jj) = cluster_reps.Get(kk);
+			  changed = 1;
+			}
+		    }
+
+	    /*
+	      if (clustertab)
+	      {
+	      if (typ == PYRAMID)
+	      (*testout) << "pyramid";
+	      else if (typ == PRISM || typ == PRISM12)
+	      (*testout) << "prism";
+	      else if (typ == TET || typ == TET10)
+	      (*testout) << "tet";
+	      else
+	      (*testout) << "unknown type" << endl;
+		
+	      (*testout) << ", nnums  = ";
+	      for (j = 0; j < nnums.Size(); j++)
+	      (*testout) << "node " << j << " = " << nnums[j] << ", rep = "
+	      << cluster_reps.Get(nnums[j]) << endl;
+	      }
+	    */
+	  }
+      }
+    while (changed);
+
+    /*
+      (*testout) << "cluster reps:" << endl;
+      for (i = 1; i <= cluster_reps.Size(); i++)
+      {
+      (*testout) << i << ": ";
+      if (i <= nv)
+      (*testout) << "v" << i << " ";
+      else if (i <= nv+ned)
+      (*testout) << "e" << i-nv << " ";
+      else if (i <= nv+ned+nfa)
+      (*testout) << "f" << i-nv-ned << " ";
+      else
+      (*testout) << "c" << i-nv-ned-nfa << " ";
+      (*testout) << cluster_reps.Get(i) << endl;
+      }
+    */
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/clusters.hpp b/contrib/Netgen/libsrc/meshing/clusters.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fa0de93a4af40d7789b2a1090c4cfc02840763a9
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/clusters.hpp
@@ -0,0 +1,42 @@
+#ifndef CLUSTERS
+#define CLUSTERS
+
+/**************************************************************************/
+/* File:   clusers.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   28. Apr. 01                                                    */
+/**************************************************************************/
+
+/*
+  Anisotropic clusters
+
+  nodes, edges, faces, elements
+*/
+
+
+class AnisotropicClusters
+{
+  const Mesh & mesh;
+
+  int nv, ned, nfa, ne;
+
+  // connected nodes, nodes = vertices, edges, faces, elements
+  Array<int> cluster_reps;
+
+public:
+  AnisotropicClusters (const Mesh & amesh);
+  ~AnisotropicClusters();
+
+  void Update();
+
+  int GetVertexRepresentant (int vnr) const
+  { return cluster_reps.Get(vnr); }
+  int GetEdgeRepresentant (int ednr) const
+  { return cluster_reps.Get(nv+ednr); }
+  int GetFaceRepresentant (int fnr) const
+  { return cluster_reps.Get(nv+ned+fnr); }
+  int GetElementRepresentant (int enr) const
+  { return cluster_reps.Get(nv+ned+nfa+enr); }
+};
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/curvedelems.cpp b/contrib/Netgen/libsrc/meshing/curvedelems.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..df17cf1917e902e091b45e225af0feaef5c9b2dd
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/curvedelems.cpp
@@ -0,0 +1,3581 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+
+#include "../general/autodiff.hpp"
+
+
+namespace netgen
+{
+  
+  //   bool rational = true;
+
+
+  
+  static void ComputeGaussRule (int n, Array<double> & xi, Array<double> & wi)
+  {
+    xi.SetSize (n);
+    wi.SetSize (n);
+    
+    int m = (n+1)/2;
+    double p1, p2, p3;
+    double pp, z, z1;
+    for (int i = 1; i <= m; i++)
+      {
+	z = cos ( M_PI * (i - 0.25) / (n + 0.5));
+	while(1)
+	  {
+	    p1 = 1; p2 = 0;
+	    for (int j = 1; j <= n; j++)
+	      {
+		p3 = p2; p2 = p1;
+		p1 = ((2 * j - 1) * z * p2 - (j - 1) * p3) / j;
+	      }
+	    // p1 is legendre polynomial
+	    
+	    pp = n * (z*p1-p2) / (z*z - 1);
+	    z1 = z;
+	    z = z1-p1/pp;
+	    
+	    if (fabs (z - z1) < 1e-14) break;
+	  }
+	
+	xi[i-1] = 0.5 * (1 - z);
+	xi[n-i] = 0.5 * (1 + z);
+	wi[i-1] = wi[n-i] = 1.0 / ( (1  - z * z) * pp * pp);
+      }
+  }
+  
+  
+
+  // compute edge bubbles up to order n, x \in (-1, 1)
+  static void CalcEdgeShape (int n, double x, double * shape)
+  {
+    double p1 = x, p2 = -1, p3 = 0;
+    for (int j=2; j<=n; j++)
+      {
+	p3=p2; p2=p1;
+	p1=( (2*j-3) * x * p2 - (j-3) * p3) / j;
+	shape[j-2] = p1;
+      } 
+  }
+
+  static void CalcEdgeDx (int n, double x, double * dshape)
+  {
+    double p1 = x, p2 = -1, p3 = 0;
+    double p1dx = 1, p2dx = 0, p3dx = 0;
+
+    for (int j=2; j<=n; j++)
+      {
+	p3=p2; p2=p1;
+	p3dx = p2dx; p2dx = p1dx;
+
+	p1=( (2*j-3) * x * p2 - (j-3) * p3) / j;
+	p1dx = ( (2*j-3) * (x * p2dx + p2) - (j-3) * p3dx) / j;
+
+	dshape[j-2] = p1dx;
+      }    
+  }
+
+  static void CalcEdgeShapeDx (int n, double x, double * shape, double * dshape)
+  {
+    double p1 = x, p2 = -1, p3 = 0;
+    double p1dx = 1, p2dx = 0, p3dx = 0;
+
+    for (int j=2; j<=n; j++)
+      {
+	p3=p2; p2=p1;
+	p3dx = p2dx; p2dx = p1dx;
+
+	p1=( (2*j-3) * x * p2 - (j-3) * p3) / j;
+	p1dx = ( (2*j-3) * (x * p2dx + p2) - (j-3) * p3dx) / j;
+
+	shape[j-2] = p1;
+	dshape[j-2] = p1dx;
+      }    
+  }
+
+  // compute L_i(x/t) * t^i
+  static void CalcScaledEdgeShape (int n, double x, double t, double * shape)
+  {
+    static bool init = false;
+    static double coefs[100][2];
+    if (!init)
+      {
+        for (int j = 0; j < 100; j++)
+          {
+            coefs[j][0] = double(2*j+1)/(j+2);
+            coefs[j][1] = -double(j-1)/(j+2);
+          }
+        init = true;
+      }
+
+    double p1 = x, p2 = -1, p3 = 0;
+    double tt = t*t;
+    for (int j=0; j<=n-2; j++)
+      {
+	p3=p2; p2=p1;
+        p1= coefs[j][0] * x * p2 + coefs[j][1] * tt*p3;
+	// p1=( (2*j+1) * x * p2 - t*t*(j-1) * p3) / (j+2);
+	shape[j] = p1;
+      }    
+  }
+
+  template <int DIST>
+  static void CalcScaledEdgeShapeDxDt (int n, double x, double t, double * dshape)
+  {
+    double p1 = x, p2 = -1, p3 = 0;
+    double p1dx = 1, p1dt = 0;
+    double p2dx = 0, p2dt = 0;
+    double p3dx = 0, p3dt = 0;
+     
+    for (int j=0; j<=n-2; j++)
+      {
+	p3=p2; p3dx=p2dx; p3dt = p2dt;
+	p2=p1; p2dx=p1dx; p2dt = p1dt;
+
+	p1   = ( (2*j+1) * x * p2 - t*t*(j-1) * p3) / (j+2);
+	p1dx = ( (2*j+1) * (x * p2dx + p2) - t*t*(j-1) * p3dx) / (j+2);
+	p1dt = ( (2*j+1) * x * p2dt - (j-1)* (t*t*p3dt+2*t*p3)) / (j+2);
+
+	// shape[j] = p1;
+	dshape[DIST*j  ] = p1dx;
+	dshape[DIST*j+1] = p1dt;
+      }    
+  }
+
+
+  template <class Tx, class Tres>
+  static void LegendrePolynomial (int n, Tx x, Tres * values)
+  {
+    switch (n)
+      {
+      case 0:
+	values[0] = 1;
+	break;
+      case 1:
+	values[0] = 1;
+	values[1] = x;
+	break;
+
+      default:
+
+	if (n < 0) return;
+
+	Tx p1 = 1.0, p2 = 0.0, p3;
+	
+	values[0] = 1.0;
+	for (int j=1; j<=n; j++)
+	  {
+	    p3 = p2; p2 = p1;
+	    p1 = ((2.0*j-1.0)*x*p2 - (j-1.0)*p3) / j;
+	    values[j] = p1;
+	  }
+      }
+  }
+
+  template <class Tx, class Tt, class Tres>
+  static void ScaledLegendrePolynomial (int n, Tx x, Tt t, Tres * values)
+  {
+    switch (n)
+      {
+      case 0:
+	values[0] = 1.0;
+	break;
+
+      case 1:
+	values[0] = 1.0;
+	values[1] = x;
+	break;
+
+      default:
+
+	if (n < 0) return;
+
+	Tx p1 = 1.0, p2 = 0.0, p3;
+	values[0] = 1.0;
+	for (int j=1; j<=n; j++)
+	  {
+	    p3 = p2; p2 = p1;
+	    p1 = ((2.0*j-1.0)*x*p2 - t*t*(j-1.0)*p3) / j;
+	    values[j] = p1;
+	  }
+      }
+  }
+
+
+
+  template <class S, class T>
+  inline void JacobiPolynomial (int n, S x, double alpha, double beta, T * values)
+  {
+    S p1 = 1.0, p2 = 0.0, p3;
+
+    if (n >= 0) 
+      p2 = values[0] = 1.0;
+    if (n >= 1) 
+      p1 = values[1] = 0.5 * (2*(alpha+1)+(alpha+beta+2)*(x-1));
+
+    for (int i  = 1; i < n; i++)
+      {
+        p3 = p2; p2=p1;
+        p1 =
+          1.0 / ( 2 * (i+1) * (i+alpha+beta+1) * (2*i+alpha+beta) ) *
+          ( 
+           ( (2*i+alpha+beta+1)*(alpha*alpha-beta*beta) + 
+             (2*i+alpha+beta)*(2*i+alpha+beta+1)*(2*i+alpha+beta+2) * x) 
+           * p2
+           - 2*(i+alpha)*(i+beta) * (2*i+alpha+beta+2) * p3
+           );
+        values[i+1] = p1;
+      }
+  }
+
+
+
+
+  template <class S, class St, class T>
+  inline void ScaledJacobiPolynomial (int n, S x, St t, double alpha, double beta, T * values)
+  {
+    /*
+      S p1 = 1.0, p2 = 0.0, p3;
+
+      if (n >= 0) values[0] = 1.0;
+    */
+
+    S p1 = 1.0, p2 = 0.0, p3;
+
+    if (n >= 0) 
+      p2 = values[0] = 1.0;
+    if (n >= 1) 
+      p1 = values[1] = 0.5 * (2*(alpha+1)*t+(alpha+beta+2)*(x-t));
+
+    for (int i=1; i < n; i++)
+      {
+        p3 = p2; p2=p1;
+        p1 =
+          1.0 / ( 2 * (i+1) * (i+alpha+beta+1) * (2*i+alpha+beta) ) *
+          ( 
+           ( (2*i+alpha+beta+1)*(alpha*alpha-beta*beta) * t + 
+             (2*i+alpha+beta)*(2*i+alpha+beta+1)*(2*i+alpha+beta+2) * x) 
+           * p2
+           - 2*(i+alpha)*(i+beta) * (2*i+alpha+beta+2) * t * t * p3
+           );
+        values[i+1] = p1;
+      }
+  }
+
+
+
+
+
+  // compute face bubbles up to order n, 0 < y, y-x < 1, x+y < 1
+  template <class Tx, class Ty, class Ts>
+  static void CalcTrigShape (int n, Tx x, Ty y, Ts * shape)
+  { 
+    if (n < 3) return;
+    Tx hx[50], hy[50*50];
+    // ScaledLegendrePolynomial (n-3, 2*x-1, 1-y, hx);
+    ScaledJacobiPolynomial (n-3, x, 1-y, 2, 2, hx);
+
+
+    // LegendrePolynomial (n-3, 2*y-1, hy);
+    for (int ix = 0; ix <= n-3; ix++)
+      //  JacobiPolynomial (n-3, 2*y-1, 0, 0, hy+50*ix);
+      JacobiPolynomial (n-3, 2*y-1, 2*ix+5, 2, hy+50*ix);
+
+    int ii = 0;
+    Tx bub = (1+x-y)*y*(1-x-y);
+    for (int iy = 0; iy <= n-3; iy++)
+      for (int ix = 0; ix <= n-3-iy; ix++)
+	shape[ii++] = bub * hx[ix]*hy[iy+50*ix];
+  }
+
+
+
+  static void CalcTrigShapeDxDy (int n, double x, double y, double * dshape)
+  { 
+    if (n < 3) return;
+
+    AutoDiff<2> adx(x, 0);
+    AutoDiff<2> ady(y, 1);
+    AutoDiff<2> res[2000];
+    CalcTrigShape (n, adx, ady, &res[0]);
+    int ndof = (n-1)*(n-2)/2;
+    for (int i = 0; i < ndof; i++)
+      {
+	dshape[2*i] = res[i].DValue(0);
+	dshape[2*i+1] = res[i].DValue(1);
+      }
+
+    /*    
+	  if (n < 3) return;
+    
+          int ndof = (n-1)*(n-2)/2;
+          double h1[1000], h2[1000];
+          double eps = 1e-4;
+  
+          CalcTrigShape (n, x+eps, y, h1);
+          CalcTrigShape (n, x-eps, y, h2);
+
+          for (int i = 0; i < ndof; i++)
+          dshape[2*i] = (h1[i]-h2[i])/(2*eps);
+
+          CalcTrigShape (n, x, y+eps, h1);
+          CalcTrigShape (n, x, y-eps, h2);
+
+          for (int i = 0; i < ndof; i++)
+          dshape[2*i+1] = (h1[i]-h2[i])/(2*eps);
+    */
+  }
+
+
+
+
+
+
+
+
+  // compute face bubbles up to order n, 0 < y, y-x < 1, x+y < 1
+  template <class Tx, class Ty, class Tt, class Tr>
+  static void CalcScaledTrigShape (int n, Tx x, Ty y, Tt t, Tr * shape)
+  {
+    if (n < 3) return;
+
+    Tx hx[50], hy[50*50];
+    // ScaledLegendrePolynomial (n-3, (2*x-1), t-y, hx);
+    ScaledJacobiPolynomial (n-3, x, t-y, 2, 2, hx);
+
+    // ScaledLegendrePolynomial (n-3, (2*y-1), t, hy);
+    for (int ix = 0; ix <= n-3; ix++)
+      ScaledJacobiPolynomial (n-3, 2*y-1, t, 2*ix+5, 2, hy+50*ix);
+
+
+    int ii = 0;
+    Tx bub = (t+x-y)*y*(t-x-y);
+    for (int iy = 0; iy <= n-3; iy++)
+      for (int ix = 0; ix <= n-3-iy; ix++)
+	shape[ii++] = bub * hx[ix]*hy[iy+50*ix];
+  }
+
+
+  // compute face bubbles up to order n, 0 < y, y-x < 1, x+y < 1
+  static void CalcScaledTrigShapeDxDyDt (int n, double x, double y, double t, double * dshape)
+  {
+    if (n < 3) return;
+    AutoDiff<3> adx(x, 0);
+    AutoDiff<3> ady(y, 1);
+    AutoDiff<3> adt(t, 2);
+    AutoDiff<3> res[2000];
+    CalcScaledTrigShape (n, adx, ady, adt, &res[0]);
+    int ndof = (n-1)*(n-2)/2;
+    for (int i = 0; i < ndof; i++)
+      {
+	dshape[3*i] = res[i].DValue(0);
+	dshape[3*i+1] = res[i].DValue(1);
+	dshape[3*i+2] = res[i].DValue(2);
+      }
+
+    /*
+      double dshape1[6000];
+      if (n < 3) return;
+      double hvl[1000], hvr[1000];
+      int nd = (n-1)*(n-2)/2;
+    
+      double eps = 1e-6;
+
+      CalcScaledTrigShape (n, x-eps, y, t, hvl);
+      CalcScaledTrigShape (n, x+eps, y, t, hvr);
+      for (int i = 0; i < nd; i++)
+      dshape[3*i] = (hvr[i]-hvl[i])/(2*eps);
+
+      CalcScaledTrigShape (n, x, y-eps, t, hvl);
+      CalcScaledTrigShape (n, x, y+eps, t, hvr);
+      for (int i = 0; i < nd; i++)
+      dshape[3*i+1] = (hvr[i]-hvl[i])/(2*eps);
+
+      CalcScaledTrigShape (n, x, y, t-eps, hvl);
+      CalcScaledTrigShape (n, x, y, t+eps, hvr);
+      for (int i = 0; i < nd; i++)
+      dshape[3*i+2] = (hvr[i]-hvl[i])/(2*eps);
+    */
+
+    /*
+      for (int i = 0; i < 3*nd; i++)
+      if (fabs (dshape[i]-dshape1[i]) > 1e-8 * fabs(dshape[i]) + 1e-6)
+      {
+      cerr
+      cerr << "big difference: " << dshape1[i] << " != " << dshape[i] << endl;
+      }
+    */
+  }
+
+    
+
+  
+
+  CurvedElements :: CurvedElements (const Mesh & amesh)
+    : mesh (amesh)
+  {
+    order = 1;
+    rational = 0;
+    ishighorder = 0;
+  }
+
+
+  CurvedElements :: ~CurvedElements()
+  {
+    ;
+  }
+
+
+  void CurvedElements :: BuildCurvedElements(const Refinement * ref, int aorder,
+                                             bool arational)
+  {
+    order = aorder;
+    ishighorder = 0;
+
+    if (mesh.coarsemesh)
+      {
+	mesh.coarsemesh->GetCurvedElements().BuildCurvedElements (ref, aorder, arational);
+        order = aorder;
+        rational = arational;
+        ishighorder = (order > 1);
+	return;
+      }
+
+
+#ifdef PARALLEL
+    if (id > 0)
+      {
+	Array<int> master_edgeorder;
+	Array<int> master_edgecoeffsindex;
+	Array<Vec<3> > master_edgecoeffs;
+	Array<int> master_faceorder;
+	Array<int> master_facecoeffsindex;
+	Array<Vec<3> > master_facecoeffs;
+	    
+	MyMPI_Bcast (master_edgeorder, 0, mesh_comm);
+	MyMPI_Bcast (master_edgecoeffsindex, 0, mesh_comm);
+	MyMPI_Bcast (master_edgecoeffs, 0, mesh_comm);
+
+	if (mesh.GetDimension() == 3)
+	  {
+	    MyMPI_Bcast (master_faceorder, 0, mesh_comm);
+	    MyMPI_Bcast (master_facecoeffsindex, 0, mesh_comm);
+	    MyMPI_Bcast (master_facecoeffs, 0, mesh_comm);
+	  }
+
+
+	const MeshTopology & top = mesh.GetTopology();
+	const ParallelMeshTopology & partop = mesh.GetParallelTopology ();
+	
+	edgeorder.SetSize (top.GetNEdges());
+	edgecoeffsindex.SetSize (top.GetNEdges()+1);
+	edgecoeffsindex[0] = 0;
+	for (int i = 0; i < top.GetNEdges(); i++)
+	  {
+	    int glob = partop.GetDistantEdgeNum (0, i+1);
+	    edgeorder[i] = master_edgeorder[glob-1];
+	    int ncoefs = master_edgecoeffsindex[glob]-master_edgecoeffsindex[glob-1];
+	    edgecoeffsindex[i+1] = edgecoeffsindex[i] + ncoefs;
+	  }
+	edgecoeffs.SetSize (edgecoeffsindex[top.GetNEdges()]);
+	
+	for (int i = 0; i < top.GetNEdges(); i++)
+	  {
+	    int glob = partop.GetDistantEdgeNum (0, i+1);
+	    int ncoefs = master_edgecoeffsindex[glob]-master_edgecoeffsindex[glob-1];
+	    for (int j = 0; j < ncoefs; j++)
+	      edgecoeffs[edgecoeffsindex[i]+j] = master_edgecoeffs[master_edgecoeffsindex[glob-1]+j];
+	  }
+
+	if (mesh.GetDimension() == 3)
+	  {
+	    faceorder.SetSize (top.GetNFaces());
+	    facecoeffsindex.SetSize (top.GetNFaces()+1);
+	    facecoeffsindex[0] = 0;
+	    for (int i = 0; i < top.GetNFaces(); i++)
+	      {
+		int glob = partop.GetDistantFaceNum (0, i+1);
+		faceorder[i] = master_faceorder[glob-1];
+		int ncoefs = master_facecoeffsindex[glob]-master_facecoeffsindex[glob-1];
+		facecoeffsindex[i+1] = facecoeffsindex[i] + ncoefs;
+	      }
+	    facecoeffs.SetSize (facecoeffsindex[top.GetNFaces()]);
+	    
+	    for (int i = 0; i < top.GetNFaces(); i++)
+	      {
+		int glob = partop.GetDistantFaceNum (0, i+1);
+		int ncoefs = master_facecoeffsindex[glob]-master_facecoeffsindex[glob-1];
+		for (int j = 0; j < ncoefs; j++)
+		  facecoeffs[facecoeffsindex[i]+j] = master_facecoeffs[master_facecoeffsindex[glob-1]+j];
+	      }
+	  }
+	else
+	  {
+	    faceorder.SetSize (top.GetNFaces());
+	    faceorder = 1;
+	    facecoeffsindex.SetSize (top.GetNFaces()+1);
+	    facecoeffsindex = 0;
+	  }
+
+	ishighorder = 1;
+	return;
+      }
+#endif
+
+    
+    PrintMessage (1, "Curve elements, order = ", aorder);
+    if (rational) PrintMessage (1, "curved elements with rational splines");
+
+    const_cast<Mesh&> (mesh).UpdateTopology();
+    const MeshTopology & top = mesh.GetTopology();
+
+    rational = arational;
+
+    Array<int> edgenrs;
+
+    edgeorder.SetSize (top.GetNEdges());
+    faceorder.SetSize (top.GetNFaces());
+
+    edgeorder = 1;
+    faceorder = 1;
+
+    if (rational)
+      {
+        edgeweight.SetSize (top.GetNEdges());
+        edgeweight = 1.0;
+      }
+
+    
+    if (aorder <= 1) 
+      {
+	for (ElementIndex ei = 0; ei < mesh.GetNE(); ei++)
+	  if (mesh[ei].GetType() == TET10)
+	    ishighorder = 1;
+	return;
+      }
+
+
+    if (rational) aorder = 2;
+
+
+    if (mesh.GetDimension() == 3)
+      for (SurfaceElementIndex i = 0; i < mesh.GetNSE(); i++)
+	{
+	  top.GetSurfaceElementEdges (i+1, edgenrs);
+	  for (int j = 0; j < edgenrs.Size(); j++)
+	    edgeorder[edgenrs[j]-1] = aorder;
+	  faceorder[top.GetSurfaceElementFace (i+1)-1] = aorder;
+	}
+    for (SegmentIndex i = 0; i < mesh.GetNSeg(); i++)
+      edgeorder[top.GetSegmentEdge (i+1)-1] = aorder;
+
+    if (rational)
+      {
+        edgeorder = 2;
+        faceorder = 1;
+      }
+
+
+    edgecoeffsindex.SetSize (top.GetNEdges()+1);
+    int nd = 0;
+    for (int i = 0; i < top.GetNEdges(); i++)
+      {
+	edgecoeffsindex[i] = nd;
+	nd += max (0, edgeorder[i]-1);
+      }
+    edgecoeffsindex[top.GetNEdges()] = nd;
+
+    edgecoeffs.SetSize (nd);
+    edgecoeffs = Vec<3> (0,0,0);
+    
+
+    facecoeffsindex.SetSize (top.GetNFaces()+1);
+    nd = 0;
+    for (int i = 0; i < top.GetNFaces(); i++)
+      {
+	facecoeffsindex[i] = nd;
+	if (top.GetFaceType(i+1) == TRIG)
+	  nd += max (0, (faceorder[i]-1)*(faceorder[i]-2)/2);
+	else
+	  nd += max (0, sqr(faceorder[i]-1));
+      }
+    facecoeffsindex[top.GetNFaces()] = nd;
+
+    facecoeffs.SetSize (nd);
+    facecoeffs = Vec<3> (0,0,0);
+
+
+    if (!ref || aorder <= 1) 
+      {
+        order = aorder;
+        return;
+      }
+    
+    Array<double> xi, weight;
+
+    ComputeGaussRule (aorder+4, xi, weight);  // on (0,1)
+
+    PrintMessage (3, "Curving edges");
+
+    if (mesh.GetDimension() == 3 || rational)
+      for (SurfaceElementIndex i = 0; i < mesh.GetNSE(); i++)
+        {
+	  SetThreadPercent(double(i)/mesh.GetNSE()*100.);
+          const Element2d & el = mesh[i];
+          top.GetSurfaceElementEdges (i+1, edgenrs);
+          for (int j = 0; j < edgenrs.Size(); j++)
+            edgenrs[j]--;
+          const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (el.GetType());
+
+          for (int i2 = 0; i2 < edgenrs.Size(); i2++)
+            {
+              PointIndex pi1 = edges[i2][0]-1;
+              PointIndex pi2 = edges[i2][1]-1;
+
+              bool swap = el[pi1] > el[pi2];
+
+              Point<3> p1 = mesh[el[pi1]];
+              Point<3> p2 = mesh[el[pi2]];
+
+              int order1 = edgeorder[edgenrs[i2]];
+              int ndof = max (0, order1-1);
+
+              if (rational && order1 >= 2)
+                {
+                  Point<3> pm = Center (p1, p2);
+
+                  int surfnr = mesh.GetFaceDescriptor(el.GetIndex()).SurfNr();
+
+                  Vec<3> n1 = ref -> GetNormal (p1, surfnr, el.GeomInfoPi(edges[i2][0]));
+                  Vec<3> n2 = ref -> GetNormal (p2, surfnr, el.GeomInfoPi(edges[i2][1]));
+
+                  // p3 = pm + alpha1 n1 + alpha2 n2
+                  
+                  Mat<2> mat, inv;
+                  Vec<2> rhs, sol;
+
+                  mat(0,0) = n1*n1;
+                  mat(0,1) = mat(1,0) = n1*n2;
+                  mat(1,1) = n2*n2;
+                  
+                  rhs(0) = n1 * (p1-pm);
+                  rhs(1) = n2 * (p2-pm);
+                  
+
+                  Point<3> p3;
+
+                  if (fabs (Det (mat)) > 1e-10)
+                    {
+                      CalcInverse (mat, inv);
+                      sol = inv * rhs;
+
+                      p3 = pm + sol(0) * n1 + sol(1) * n2;
+                    }
+                  else
+                    p3 = pm;
+
+                  edgecoeffs[edgecoeffsindex[edgenrs[i2]]] = Vec<3> (p3);
+
+
+                  double wold = 1, w = 1, dw = 0.1;
+                  double dold = 1e99;
+                  while (fabs (dw) > 1e-12)
+                    {
+                      Vec<3> v05 = 0.25 * Vec<3> (p1) + 0.5*w* Vec<3>(p3) + 0.25 * Vec<3> (p2);
+                      v05 /= 1 + (w-1) * 0.5;
+                      Point<3> p05 (v05), pp05(v05);
+                      ref -> ProjectToSurface (pp05, surfnr,  el.GeomInfoPi(edges[i2][0]));
+                      double d = Dist (pp05, p05);
+                      
+                      if (d < dold)
+                        {
+                          dold = d;
+                          wold = w;
+                          w += dw;
+                        }
+                      else
+                        {
+                          dw *= -0.7;
+                          w = wold + dw;
+                        }
+                    }
+                  
+                  edgeweight[edgenrs[i2]] = w;
+                  continue;
+                }
+	    
+              Vector shape(ndof);
+              DenseMatrix mat(ndof, ndof), inv(ndof, ndof),
+                rhs(ndof, 3), sol(ndof, 3);
+	    
+              rhs = 0.0;
+              mat = 0.0;
+              for (int j = 0; j < xi.Size(); j++)
+                {
+                  Point<3> p;
+                  Point<3> pp;
+                  PointGeomInfo ppgi;
+		
+                  if (swap)
+                    {
+                      p = p1 + xi[j] * (p2-p1);
+                      ref -> PointBetween (p1, p2, xi[j], 
+                                           mesh.GetFaceDescriptor(el.GetIndex()).SurfNr(),
+                                           el.GeomInfoPi(edges[i2][0]),
+                                           el.GeomInfoPi(edges[i2][1]),
+                                           pp, ppgi);
+                    }
+                  else
+                    {
+                      p = p2 + xi[j] * (p1-p2);
+                      ref -> PointBetween (p2, p1, xi[j], 
+                                           mesh.GetFaceDescriptor(el.GetIndex()).SurfNr(),
+                                           el.GeomInfoPi(edges[i2][1]),
+                                           el.GeomInfoPi(edges[i2][0]),
+                                           pp, ppgi);
+                    }
+		
+                  Vec<3> dist = pp - p;
+		
+                  CalcEdgeShape (order1, 2*xi[j]-1, &shape(0));
+		
+                  for (int k = 0; k < ndof; k++)
+                    for (int l = 0; l < ndof; l++)
+                      mat(k,l) += weight[j] * shape(k) * shape(l);
+		
+                  for (int k = 0; k < ndof; k++)
+                    for (int l = 0; l < 3; l++)
+                      rhs(k,l) += weight[j] * shape(k) * dist(l);
+                }
+	    
+              CalcInverse (mat, inv);
+              Mult (inv, rhs, sol);
+
+	      
+	    
+              int first = edgecoeffsindex[edgenrs[i2]];
+              for (int j = 0; j < ndof; j++)
+                for (int k = 0; k < 3; k++)
+                  edgecoeffs[first+j](k) = sol(j,k);
+            }
+        }
+
+
+    
+    for (SegmentIndex i = 0; i < mesh.GetNSeg(); i++)
+      {
+	SetThreadPercent(double(i)/mesh.GetNSeg()*100.);
+	const Segment & seg = mesh[i];
+	PointIndex pi1 = mesh[i][0];
+	PointIndex pi2 = mesh[i][1];
+
+	bool swap = (pi1 > pi2);
+
+	Point<3> p1 = mesh[pi1];
+	Point<3> p2 = mesh[pi2];
+
+	int segnr = top.GetSegmentEdge (i+1)-1;
+
+	int order1 = edgeorder[segnr];
+	int ndof = max (0, order1-1);
+
+
+        if (rational)
+          {
+            Vec<3> tau1 = ref -> GetTangent (p1, seg.surfnr2, seg.surfnr1, seg.epgeominfo[0]);
+            Vec<3> tau2 = ref -> GetTangent (p2, seg.surfnr2, seg.surfnr1, seg.epgeominfo[1]);
+            // p1 + alpha1 tau1 = p2 + alpha2 tau2;
+
+            Mat<3,2> mat;
+            Mat<2,3> inv;
+            Vec<3> rhs;
+            Vec<2> sol;
+            for (int j = 0; j < 3; j++)
+              {
+                mat(j,0) = tau1(j); 
+                mat(j,1) = -tau2(j); 
+                rhs(j) = p2(j)-p1(j); 
+              }
+            CalcInverse (mat, inv);
+            sol = inv * rhs;
+
+            Point<3> p3 = p1+sol(0) * tau1;
+            edgecoeffs[edgecoeffsindex[segnr]] = Vec<3> (p3);
+
+            
+            //             double dopt = 1e99;
+            //             double wopt = 0;
+            //             for (double w = 0; w <= 2; w += 0.0001)
+            //               {
+            //                 Vec<3> v05 = 0.25 * Vec<3> (p1) + 0.5*w* Vec<3>(p3) + 0.25 * Vec<3> (p2);
+            //                 v05 /= 1 + (w-1) * 0.5;
+            //                 Point<3> p05 (v05), pp05(v05);
+            //                 ref -> ProjectToEdge (pp05, seg.surfnr1, seg.surfnr2, seg.epgeominfo[0]);
+            //                 double d = Dist (pp05, p05);
+            //                 if (d < dopt)
+            //                   {
+            //                     wopt = w;
+            //                     dopt = d;
+            //                   }
+            //               }
+            
+            double wold = 1, w = 1, dw = 0.1;
+            double dold = 1e99;
+            while (fabs (dw) > 1e-12)
+              {
+                Vec<3> v05 = 0.25 * Vec<3> (p1) + 0.5*w* Vec<3>(p3) + 0.25 * Vec<3> (p2);
+                v05 /= 1 + (w-1) * 0.5;
+                Point<3> p05 (v05), pp05(v05);
+                ref -> ProjectToEdge (pp05, seg.surfnr1, seg.surfnr2, seg.epgeominfo[0]);
+                double d = Dist (pp05, p05);
+
+                if (d < dold)
+                  {
+                    dold = d;
+                    wold = w;
+                    w += dw;
+                  }
+                else
+                  {
+                    dw *= -0.7;
+                    w = wold + dw;
+                  }
+                // *testout << "w = " << w << ", dw = " << dw << endl;
+              }
+
+            // cout << "wopt = " << w << ", dopt = " << dold << endl;
+            edgeweight[segnr] = w;
+            
+            //             cout << "p1 = " << p1 << ", tau1 = " << tau1 << ", alpha1 = " << sol(0) << endl;
+            //             cout << "p2 = " << p2 << ", tau2 = " << tau2 << ", alpha2 = " << -sol(1) << endl;
+            //             cout << "p+alpha tau = " << p1 + sol(0) * tau1 
+            //                  << " =?= " << p2 +sol(1) * tau2 << endl;
+            
+          }
+
+        else
+          
+          {
+
+            Vector shape(ndof);
+            DenseMatrix mat(ndof, ndof), inv(ndof, ndof),
+              rhs(ndof, 3), sol(ndof, 3);
+
+            rhs = 0.0;
+            mat = 0.0;
+            for (int j = 0; j < xi.Size(); j++)
+              {
+                Point<3> p;
+
+                Point<3> pp;
+                EdgePointGeomInfo ppgi;
+	    
+                if (swap)
+                  {
+                    p = p1 + xi[j] * (p2-p1);
+                    ref -> PointBetween (p1, p2, xi[j], 
+                                         seg.surfnr2, seg.surfnr1, 
+                                         seg.epgeominfo[0], seg.epgeominfo[1],
+                                         pp, ppgi);
+                  }
+                else
+                  {
+                    p = p2 + xi[j] * (p1-p2);
+                    ref -> PointBetween (p2, p1, xi[j], 
+                                         seg.surfnr2, seg.surfnr1, 
+                                         seg.epgeominfo[1], seg.epgeominfo[0],
+                                         pp, ppgi);
+                  }
+	    
+                Vec<3> dist = pp - p;
+
+                CalcEdgeShape (order1, 2*xi[j]-1, &shape(0));
+
+                for (int k = 0; k < ndof; k++)
+                  for (int l = 0; l < ndof; l++)
+                    mat(k,l) += weight[j] * shape(k) * shape(l);
+
+                for (int k = 0; k < ndof; k++)
+                  for (int l = 0; l < 3; l++)
+                    rhs(k,l) += weight[j] * shape(k) * dist(l);
+              }
+
+
+            CalcInverse (mat, inv);
+            Mult (inv, rhs, sol);
+
+            int first = edgecoeffsindex[segnr];
+            for (int j = 0; j < ndof; j++)
+              for (int k = 0; k < 3; k++)
+                edgecoeffs[first+j](k) = sol(j,k);
+          }
+      }
+
+   
+
+    
+    PrintMessage (3, "Curving faces");
+
+    if (mesh.GetDimension() == 3)
+      for (SurfaceElementIndex i = 0; i < mesh.GetNSE(); i++)
+        {
+          SetThreadPercent(double(i)/mesh.GetNSE()*100.);
+          const Element2d & el = mesh[i];
+          int facenr = top.GetSurfaceElementFace (i+1)-1;
+
+          if (el.GetType() == TRIG && order >= 3)
+            {
+              int fnums[] = { 0, 1, 2 };
+              if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+              if (el[fnums[1]] > el[fnums[2]]) swap (fnums[1], fnums[2]);
+              if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+
+              int order1 = faceorder[facenr];
+              int ndof = max (0, (order1-1)*(order1-2)/2);
+	    
+              Vector shape(ndof);
+              DenseMatrix mat(ndof, ndof), inv(ndof, ndof),
+                rhs(ndof, 3), sol(ndof, 3);
+	    
+              rhs = 0.0;
+              mat = 0.0;
+
+              for (int jx = 0; jx < xi.Size(); jx++)
+                for (int jy = 0; jy < xi.Size(); jy++)
+                  {
+                    double y = xi[jy];
+                    double x = (1-y) * xi[jx];
+                    double lami[] = { x, y, 1-x-y };
+                    double wi = weight[jx]*weight[jy]*(1-y);
+
+                    Point<2> xii (x, y);
+                    Point<3> p1, p2;
+                    CalcSurfaceTransformation (xii, i, p1);
+                    p2 = p1;
+                    ref -> ProjectToSurface (p2, mesh.GetFaceDescriptor(el.GetIndex()).SurfNr());
+
+                    Vec<3> dist = p2-p1;
+		
+                    CalcTrigShape (order1, lami[fnums[1]]-lami[fnums[0]],
+                                   1-lami[fnums[1]]-lami[fnums[0]], &shape(0));
+
+                    for (int k = 0; k < ndof; k++)
+                      for (int l = 0; l < ndof; l++)
+                        mat(k,l) += wi * shape(k) * shape(l);
+		  
+                    for (int k = 0; k < ndof; k++)
+                      for (int l = 0; l < 3; l++)
+                        rhs(k,l) += wi * shape(k) * dist(l);
+                  }
+
+              // *testout << "mat = " << endl << mat << endl;
+              // CalcInverse (mat, inv);
+              // Mult (inv, rhs, sol);
+
+              for (int i = 0; i < ndof; i++)
+                for (int j = 0; j < 3; j++)
+                  sol(i,j) = rhs(i,j) / mat(i,i);   // Orthogonal basis !
+
+              int first = facecoeffsindex[facenr];
+              for (int j = 0; j < ndof; j++)
+                for (int k = 0; k < 3; k++)
+                  facecoeffs[first+j](k) = sol(j,k);
+            }
+        }
+    
+    PrintMessage (3, "Complete");
+
+
+    // compress edge and face tables
+    int newbase = 0;
+    for (int i = 0; i < edgeorder.Size(); i++)
+      {
+	bool curved = 0;
+	int oldbase = edgecoeffsindex[i];
+	nd = edgecoeffsindex[i+1] - edgecoeffsindex[i];
+
+	for (int j = 0; j < nd; j++)
+	  if (edgecoeffs[oldbase+j].Length() > 1e-12)
+	    curved = 1;
+        if (rational) curved = 1;
+
+	if (curved && newbase != oldbase)
+	  for (int j = 0; j < nd; j++)
+	    edgecoeffs[newbase+j] = edgecoeffs[oldbase+j];
+
+	edgecoeffsindex[i] = newbase;
+	if (!curved) edgeorder[i] = 1;
+	if (curved) newbase += nd;
+      }
+    edgecoeffsindex.Last() = newbase;
+
+
+    newbase = 0;
+    for (int i = 0; i < faceorder.Size(); i++)
+      {
+	bool curved = 0;
+	int oldbase = facecoeffsindex[i];
+	nd = facecoeffsindex[i+1] - facecoeffsindex[i];
+
+	for (int j = 0; j < nd; j++)
+	  if (facecoeffs[oldbase+j].Length() > 1e-12)
+	    curved = 1;
+
+	if (curved && newbase != oldbase)
+	  for (int j = 0; j < nd; j++)
+	    facecoeffs[newbase+j] = facecoeffs[oldbase+j];
+
+	facecoeffsindex[i] = newbase;
+	if (!curved) faceorder[i] = 1;
+	if (curved) newbase += nd;
+      }
+    facecoeffsindex.Last() = newbase;
+
+    ishighorder = (order > 1);
+    // (*testout) << "edgecoeffs = " << endl << edgecoeffs << endl;
+    // (*testout) << "facecoeffs = " << endl << facecoeffs << endl;
+
+
+#ifdef PARALLEL
+    if (ntasks > 1)
+      {
+	MyMPI_Bcast (edgeorder, 0, mesh_comm);
+	MyMPI_Bcast (edgecoeffsindex, 0, mesh_comm);
+	MyMPI_Bcast (edgecoeffs, 0, mesh_comm);
+	
+	if (mesh.GetDimension() == 3)
+	  {
+	    MyMPI_Bcast (faceorder, 0, mesh_comm);
+	    MyMPI_Bcast (facecoeffsindex, 0, mesh_comm);
+	    MyMPI_Bcast (facecoeffs, 0, mesh_comm);
+	  }
+      }
+#endif
+
+
+  }
+
+
+
+
+
+
+
+
+
+
+  // ***********************  Transform edges *****************************
+
+  
+  bool CurvedElements ::  IsSegmentCurved (SegmentIndex elnr) const
+  {
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	
+	return mesh.coarsemesh->GetCurvedElements().IsSegmentCurved (hpref_el.coarse_elnr);
+      }
+
+    SegmentInfo info;
+    info.elnr = elnr;
+    info.order = order;
+    info.ndof = info.nv = 2;
+    if (info.order > 1)
+      {
+	const MeshTopology & top = mesh.GetTopology();
+	info.edgenr = top.GetSegmentEdge (elnr+1)-1;	
+	info.ndof += edgeorder[info.edgenr]-1;
+      }
+
+    return (info.ndof > info.nv);
+  }
+
+
+ 
+  
+
+  void CurvedElements :: 
+  CalcSegmentTransformation (double xi, SegmentIndex elnr,
+			     Point<3> * x, Vec<3> * dxdxi, bool * curved)
+  {
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	
+	// xi umrechnen
+	double lami[2] = { xi, 1-xi };
+	double dlami[2] = { 1, -1 };
+
+	double coarse_xi = 0;
+	double trans = 0;
+	for (int i = 0; i < 2; i++)
+	  {
+	    coarse_xi += hpref_el.param[i][0] * lami[i];
+	    trans += hpref_el.param[i][0] * dlami[i];
+	  }
+
+	mesh.coarsemesh->GetCurvedElements().CalcSegmentTransformation (coarse_xi, hpref_el.coarse_elnr, x, dxdxi, curved);
+	if (dxdxi) *dxdxi *= trans;
+	
+	return;
+      }
+    
+
+    Vector shapes, dshapes;
+    Array<Vec<3> > coefs;
+
+    SegmentInfo info;
+    info.elnr = elnr;
+    info.order = order;
+    info.ndof = info.nv = 2;
+
+    if (info.order > 1)
+      {
+	const MeshTopology & top = mesh.GetTopology();
+	info.edgenr = top.GetSegmentEdge (elnr+1)-1;	
+	info.ndof += edgeorder[info.edgenr]-1;
+      }
+
+    CalcElementShapes (info, xi, shapes);
+    GetCoefficients (info, coefs);
+
+    *x = 0;
+    for (int i = 0; i < shapes.Size(); i++)
+      *x += shapes(i) * coefs[i];
+
+
+    if (dxdxi)
+      {
+	CalcElementDShapes (info, xi, dshapes);
+	
+	*dxdxi = 0;
+	for (int i = 0; i < shapes.Size(); i++)
+	  for (int j = 0; j < 3; j++)
+	    (*dxdxi)(j) += dshapes(i) * coefs[i](j);
+      }
+
+    if (curved)
+      *curved = (info.order > 1);
+
+    // cout << "Segment, |x| = " << Abs2(Vec<3> (*x) ) << endl;
+  }
+
+
+
+
+  void CurvedElements :: 
+  CalcElementShapes (SegmentInfo & info, double xi, Vector & shapes) const
+  {
+    if (rational && info.order == 2)
+      {
+        shapes.SetSize(3);
+        double w = edgeweight[info.edgenr];
+        shapes(0) = xi*xi;
+        shapes(1) = (1-xi)*(1-xi);
+        shapes(2) = 2*w*xi*(1-xi);
+        shapes *= 1.0 / (1 + (w-1) *2*xi*(1-xi));
+        return;
+      }
+
+
+    shapes.SetSize(info.ndof);
+    shapes(0) = xi;
+    shapes(1) = 1-xi;
+
+    if (info.order >= 2)
+      {
+	if (mesh[info.elnr][0] > mesh[info.elnr][1])
+	  xi = 1-xi;
+	CalcEdgeShape (edgeorder[info.edgenr], 2*xi-1, &shapes(2));
+      }
+  }
+
+  void CurvedElements :: 
+  CalcElementDShapes (SegmentInfo & info, double xi, Vector & dshapes) const
+  {
+    if (rational && info.order == 2)
+      {
+        dshapes.SetSize(3);
+        double wi = edgeweight[info.edgenr];
+        double shapes[3];
+        shapes[0] = xi*xi;
+        shapes[1] = (1-xi)*(1-xi);
+        shapes[2] = 2*wi*xi*(1-xi);
+        double w = 1 + (wi-1) *2*xi*(1-xi);
+        double dw = (wi-1) * (2 - 4*xi);
+        
+        dshapes(0) = 2*xi;
+        dshapes(1) = 2*(xi-1);
+        dshapes(2) = 2*wi*(1-2*xi);
+
+        for (int j = 0;j < 3; j++)
+          dshapes(j) = dshapes(j) / w - shapes[j] * dw / (w*w);
+        return;
+      }
+
+
+
+
+
+
+    dshapes.SetSize(info.ndof);
+    dshapes = 0;
+    dshapes(0) = 1;
+    dshapes(1) = -1;
+
+    // int order = edgeorder[info.edgenr];
+
+    if (info.order >= 2)
+      {
+        double fac = 2;
+	if (mesh[info.elnr][0] > mesh[info.elnr][1])
+          {
+            xi = 1-xi; 
+            fac *= -1;
+          }
+	CalcEdgeDx (edgeorder[info.edgenr], 2*xi-1, &dshapes(2));
+        for (int i = 2; i < dshapes.Size(); i++)
+          dshapes(i) *= fac;
+      }
+
+    // ??? not implemented ????
+  }
+
+  void CurvedElements :: 
+  GetCoefficients (SegmentInfo & info, Array<Vec<3> > & coefs) const
+  {
+    const Segment & el = mesh[info.elnr];
+
+    coefs.SetSize(info.ndof);
+
+    coefs[0] = Vec<3> (mesh[el[0]]);
+    coefs[1] = Vec<3> (mesh[el[1]]);
+
+    if (info.order >= 2)
+      {
+	int first = edgecoeffsindex[info.edgenr]; 
+	int next = edgecoeffsindex[info.edgenr+1]; 
+	for (int i = 0; i < next-first; i++)
+	  coefs[i+2] = edgecoeffs[first+i];
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+  // ********************** Transform surface elements *******************
+
+
+  bool CurvedElements :: IsSurfaceElementCurved (SurfaceElementIndex elnr) const
+  {
+    if (!IsHighOrder()) return false;
+
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	
+	return mesh.coarsemesh->GetCurvedElements().IsSurfaceElementCurved (hpref_el.coarse_elnr);
+      }
+
+    const Element2d & el = mesh[elnr];
+    ELEMENT_TYPE type = el.GetType();
+    
+    SurfaceElementInfo info;
+    info.elnr = elnr;
+    info.order = order;
+
+    switch (type)
+      {
+      case TRIG : info.nv = 3; break;
+      case QUAD : info.nv = 4; break;
+      case TRIG6: return true;
+      default:
+	cerr << "undef element in CalcSurfaceTrafo" << endl;
+      }
+    info.ndof = info.nv;
+
+    // info.ndof = info.nv = ( (type == TRIG) || (type == TRIG6) ) ? 3 : 4;
+    if (info.order > 1)
+      {
+	const MeshTopology & top = mesh.GetTopology();
+	
+	top.GetSurfaceElementEdges (elnr+1, info.edgenrs);
+	for (int i = 0; i < info.edgenrs.Size(); i++)
+	  info.edgenrs[i]--;
+	info.facenr = top.GetSurfaceElementFace (elnr+1)-1;
+
+	for (int i = 0; i < info.edgenrs.Size(); i++)
+	  info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]];
+	info.ndof += facecoeffsindex[info.facenr+1] - facecoeffsindex[info.facenr];
+      }
+
+    return (info.ndof > info.nv);
+  }
+  
+  void CurvedElements :: 
+  CalcSurfaceTransformation (Point<2> xi, SurfaceElementIndex elnr,
+			     Point<3> * x, Mat<3,2> * dxdxi, bool * curved)
+  {
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	
+        // xi umrechnen
+	double lami[4];
+	FlatVector vlami(4, lami);
+	vlami = 0;
+	mesh[elnr].GetShapeNew (xi, vlami);
+	
+	Mat<2,2> trans;
+	Mat<3,2> dxdxic;
+	if (dxdxi)
+	  {
+	    MatrixFixWidth<2> dlami(4);
+	    dlami = 0;
+	    mesh[elnr].GetDShapeNew (xi, dlami);	  
+	    
+	    trans = 0;
+	    for (int k = 0; k < 2; k++)
+	      for (int l = 0; l < 2; l++)
+		for (int i = 0; i < hpref_el.np; i++)
+		  trans(l,k) += hpref_el.param[i][l] * dlami(i, k);
+          }
+	
+	Point<2> coarse_xi(0,0);
+	for (int i = 0; i < hpref_el.np; i++)
+	  for (int j = 0; j < 2; j++)
+	    coarse_xi(j) += hpref_el.param[i][j] * lami[i];
+	
+	mesh.coarsemesh->GetCurvedElements().CalcSurfaceTransformation (coarse_xi, hpref_el.coarse_elnr, x, &dxdxic, curved);
+	
+	if (dxdxi)
+	  *dxdxi = dxdxic * trans;
+	
+	return;
+      }
+    
+
+
+    Vector shapes;
+    MatrixFixWidth<2> dshapes;
+    Array<Vec<3> > coefs;
+
+    const Element2d & el = mesh[elnr];
+    ELEMENT_TYPE type = el.GetType();
+
+    SurfaceElementInfo info;
+    info.elnr = elnr;
+    info.order = order;
+
+    switch (type)
+      {
+      case TRIG : info.nv = 3; break;
+      case QUAD : info.nv = 4; break;
+      case TRIG6: info.nv = 6; break;
+      default:
+	cerr << "undef element in CalcSurfaceTrafo" << endl;
+      }
+    info.ndof = info.nv;
+
+    if (info.order > 1)
+      {
+	const MeshTopology & top = mesh.GetTopology();
+	
+	top.GetSurfaceElementEdges (elnr+1, info.edgenrs);
+	for (int i = 0; i < info.edgenrs.Size(); i++)
+	  info.edgenrs[i]--;
+	info.facenr = top.GetSurfaceElementFace (elnr+1)-1;
+
+
+	bool firsttry = true;
+	bool problem = false;
+
+	while(firsttry || problem)
+	  {
+	    problem = false;
+
+	    for (int i = 0; !problem && i < info.edgenrs.Size(); i++)
+	      {
+		if(info.edgenrs[i]+1 >= edgecoeffsindex.Size())
+		  problem = true;
+		else
+		  info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]];
+	      }
+	    if(info.facenr+1 >= facecoeffsindex.Size())
+	      problem = true;
+	    else
+	      info.ndof += facecoeffsindex[info.facenr+1] - facecoeffsindex[info.facenr];
+
+	    if(problem && !firsttry)
+	      throw NgException("something wrong with curved elements");
+	    
+	    if(problem)
+	      BuildCurvedElements(NULL,order,rational);
+
+	    firsttry = false;
+	  }
+      }
+
+    CalcElementShapes (info, xi, shapes);
+    GetCoefficients (info, coefs);
+
+    *x = 0;
+    for (int i = 0; i < coefs.Size(); i++)
+      *x += shapes(i) * coefs[i];
+
+    if (dxdxi)
+      {
+	CalcElementDShapes (info, xi, dshapes);
+	
+	*dxdxi = 0;
+	for (int i = 0; i < coefs.Size(); i++)
+	  for (int j = 0; j < 3; j++)
+	    for (int k = 0; k < 2; k++)
+	      (*dxdxi)(j,k) += dshapes(i,k) * coefs[i](j);
+      }
+
+    if (curved)
+      *curved = (info.ndof > info.nv);
+  }
+
+
+
+
+  void CurvedElements :: 
+  CalcElementShapes (SurfaceElementInfo & info, const Point<2> & xi, Vector & shapes) const
+  {
+    const Element2d & el = mesh[info.elnr];
+
+    shapes.SetSize(info.ndof);
+    // shapes = 0;	  
+
+    if (rational && info.order >= 2)
+      {
+        shapes.SetSize(6);
+        double w = 1;
+        double lami[3] = { xi(0), xi(1), 1-xi(0)-xi(1) };
+        for (int j = 0; j < 3; j++)
+          shapes(j) = lami[j] * lami[j];
+
+        const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (TRIG);
+        for (int j = 0; j < 3; j++)
+          {
+            double wi = edgeweight[info.edgenrs[j]];
+            shapes(j+3) = 2 * wi * lami[edges[j][0]-1] * lami[edges[j][1]-1];
+            w += (wi-1) * 2 * lami[edges[j][0]-1] * lami[edges[j][1]-1];
+          }
+
+        shapes *= 1.0 / w;
+        return;
+      }
+
+    switch (el.GetType())
+      {
+      case TRIG:
+	{
+	  shapes(0) = xi(0);
+	  shapes(1) = xi(1);
+	  shapes(2) = 1-xi(0)-xi(1);
+
+	  if (info.order == 1) return;
+
+	  int ii = 3;
+	  const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (TRIG);
+	  
+	  for (int i = 0; i < 3; i++)
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = edges[i][0]-1, vi2 = edges[i][1]-1;
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  CalcScaledEdgeShape (eorder, shapes(vi1)-shapes(vi2), shapes(vi1)+shapes(vi2), &shapes(ii));
+		  ii += eorder-1;
+		}
+	    }
+
+	  int forder = faceorder[info.facenr];
+	  if (forder >= 3)
+	    {
+	      int fnums[] = { 0, 1, 2 };
+	      if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+	      if (el[fnums[1]] > el[fnums[2]]) swap (fnums[1], fnums[2]);
+	      if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+	      
+	      CalcTrigShape (forder, 
+			     shapes(fnums[1])-shapes(fnums[0]),
+			     1-shapes(fnums[1])-shapes(fnums[0]), &shapes(ii));
+	    }
+	  break;
+	}
+
+      case TRIG6:
+	{
+	  if (shapes.Size() == 3)
+	    {
+	      shapes(0) = xi(0);
+	      shapes(1) = xi(1);
+	      shapes(2) = 1-xi(0)-xi(1);
+	    }
+	  else
+	    {
+	      double x = xi(0);
+	      double y = xi(1);
+	      double lam3 = 1-x-y;
+	      
+	      shapes(0) = x * (2*x-1);
+	      shapes(1) = y * (2*y-1);
+	      shapes(2) = lam3 * (2*lam3-1);
+	      shapes(3) = 4 * y * lam3;
+	      shapes(4) = 4 * x * lam3;
+	      shapes(5) = 4 * x * y;
+	    }
+          break;
+        }
+
+      case QUAD:
+	{
+	  shapes(0) = (1-xi(0))*(1-xi(1));
+	  shapes(1) =    xi(0) *(1-xi(1));
+	  shapes(2) =    xi(0) *   xi(1) ;
+	  shapes(3) = (1-xi(0))*   xi(1) ;
+
+	  if (info.order == 1) return;
+	  
+	  double mu[4] = { 
+	    1 - xi(0) + 1 - xi(1), 
+            xi(0) + 1 - xi(1), 
+            xi(0) +     xi(1), 
+	    1 - xi(0) +     xi(1), 
+	  };
+	    
+	  int ii = 4;
+	  const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (QUAD);
+	  
+	  for (int i = 0; i < 4; i++)
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = edges[i][0]-1, vi2 = edges[i][1]-1;
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  CalcEdgeShape (eorder, mu[vi1]-mu[vi2], &shapes(ii));
+		  double lame = shapes(vi1)+shapes(vi2);
+		  for (int j = 0; j < order-1; j++)
+		    shapes(ii+j) *= lame;
+		  ii += eorder-1;
+		}
+	    }
+	  
+	  for (int i = ii; i < info.ndof; i++)
+	    shapes(i) = 0;
+
+	  break;
+	}
+        
+      default:
+        throw NgException("CurvedElements::CalcShape 2d, element type not handled");
+      };
+  }
+
+
+  void CurvedElements :: 
+  CalcElementDShapes (SurfaceElementInfo & info, const Point<2> & xi, MatrixFixWidth<2> & dshapes) const
+  {
+    const Element2d & el = mesh[info.elnr];
+    ELEMENT_TYPE type = el.GetType();
+
+    double lami[4];
+
+    dshapes.SetSize(info.ndof);
+    // dshapes = 0;	  
+
+    // *testout << "calcelementdshapes, info.ndof = " << info.ndof << endl;
+
+    if (rational && info.order >= 2)
+      {
+        double w = 1;
+        double dw[2] = { 0, 0 };
+
+
+        lami[0] = xi(0); lami[1] = xi(1); lami[2] = 1-xi(0)-xi(1);
+        double dlami[3][2] = { { 1, 0 }, { 0, 1 }, { -1, -1 }};
+        double shapes[6];
+
+        for (int j = 0; j < 3; j++)
+          {
+            shapes[j] = lami[j] * lami[j];
+            dshapes(j,0) = 2 * lami[j] * dlami[j][0];
+            dshapes(j,1) = 2 * lami[j] * dlami[j][1];
+          }
+
+        const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (TRIG);
+        for (int j = 0; j < 3; j++)
+          {
+            double wi = edgeweight[info.edgenrs[j]];
+
+            shapes[j+3] = 2 * wi * lami[edges[j][0]-1] * lami[edges[j][1]-1];
+            for (int k = 0; k < 2; k++)
+              dshapes(j+3,k) = 2*wi* (lami[edges[j][0]-1] * dlami[edges[j][1]-1][k] +
+                                      lami[edges[j][1]-1] * dlami[edges[j][0]-1][k]);
+
+            w += (wi-1) * 2 * lami[edges[j][0]-1] * lami[edges[j][1]-1];
+            for (int k = 0; k < 2; k++)
+              dw[k] += 2*(wi-1) * (lami[edges[j][0]-1] * dlami[edges[j][1]-1][k] +
+                                   lami[edges[j][1]-1] * dlami[edges[j][0]-1][k]);
+          }
+        // shapes *= 1.0 / w;
+        dshapes *= 1.0 / w;
+        for (int i = 0; i < 6; i++)
+          for (int j = 0; j < 2; j++)
+            dshapes(i,j) -= shapes[i] * dw[j] / (w*w);
+        return;
+      }
+
+
+
+
+
+    switch (type)
+      {
+      case TRIG:
+	{
+	  dshapes(0,0) = 1;
+          dshapes(0,1) = 0.0;
+          dshapes(1,0) = 0.0;
+	  dshapes(1,1) = 1;
+	  dshapes(2,0) = -1;
+	  dshapes(2,1) = -1;
+	  
+	  if (info.order == 1) return;
+
+          // *testout << "info.order = " << info.order << endl;
+
+
+	  lami[0] = xi(0);
+	  lami[1] = xi(1);
+	  lami[2] = 1-xi(0)-xi(1);
+
+	  int ii = 3;
+	  const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (TRIG);
+	  
+	  for (int i = 0; i < 3; i++)
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = edges[i][0]-1, vi2 = edges[i][1]-1;
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  CalcScaledEdgeShapeDxDt<2> (eorder, lami[vi1]-lami[vi2], lami[vi1]+lami[vi2], &dshapes(ii,0));
+
+		  Mat<2,2> trans;
+		  for (int j = 0; j < 2; j++)
+		    {
+		      trans(0,j) = dshapes(vi1,j)-dshapes(vi2,j);
+		      trans(1,j) = dshapes(vi1,j)+dshapes(vi2,j);
+		    }
+		  
+		  for (int j = 0; j < eorder-1; j++)
+		    {
+		      double ddx = dshapes(ii+j,0);
+		      double ddt = dshapes(ii+j,1);
+		      dshapes(ii+j,0) = ddx * trans(0,0) + ddt * trans(1,0);
+		      dshapes(ii+j,1) = ddx * trans(0,1) + ddt * trans(1,1);
+		    }
+
+		  ii += eorder-1;
+		}
+	    }
+
+	  int forder = faceorder[info.facenr];
+          // *testout << "forder = " << forder << endl;
+	  if (forder >= 3)
+	    {
+	      int fnums[] = { 0, 1, 2 };
+	      if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+	      if (el[fnums[1]] > el[fnums[2]]) swap (fnums[1], fnums[2]);
+	      if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+	      
+	      CalcTrigShapeDxDy (forder, 
+				 lami[fnums[1]]-lami[fnums[0]],
+				 1-lami[fnums[1]]-lami[fnums[0]], &dshapes(ii,0));
+
+	      int nd = (forder-1)*(forder-2)/2;
+	      Mat<2,2> trans;
+	      for (int j = 0; j < 2; j++)
+		{
+		  trans(0,j) = dshapes(fnums[1],j)-dshapes(fnums[0],j);
+		  trans(1,j) = -dshapes(fnums[1],j)-dshapes(fnums[0],j);
+		}
+
+	      for (int j = 0; j < nd; j++)
+		{
+		  double ddx = dshapes(ii+j,0);
+		  double ddt = dshapes(ii+j,1);
+		  dshapes(ii+j,0) = ddx * trans(0,0) + ddt * trans(1,0);
+		  dshapes(ii+j,1) = ddx * trans(0,1) + ddt * trans(1,1);
+		}
+	    }
+
+	  break;
+	}
+
+      case TRIG6:
+	{
+	  if (dshapes.Height() == 3)
+	    {
+	      dshapes = 0.0;
+	      dshapes(0,0) = 1;
+	      dshapes(1,1) = 1;
+	      dshapes(2,0) = -1;
+	      dshapes(2,1) = -1;	    
+	    }
+	  else
+	    {
+	      AutoDiff<2> x(xi(0), 0);
+	      AutoDiff<2> y(xi(1), 1);
+	      AutoDiff<2> lam3 = 1-x-y;
+	      AutoDiff<2> shapes[6];
+	      shapes[0] = x * (2*x-1);
+	      shapes[1] = y * (2*y-1);
+	      shapes[2] = lam3 * (2*lam3-1);
+	      shapes[3] = 4 * y * lam3;
+	      shapes[4] = 4 * x * lam3;
+	      shapes[5] = 4 * x * y;
+
+	      for (int i = 0; i < 6; i++)
+		{
+		  dshapes(i,0) = shapes[i].DValue(0);
+		  dshapes(i,1) = shapes[i].DValue(1);
+		}
+	      
+	    }
+          break;
+        }
+
+      case QUAD:
+	{
+	  dshapes(0,0) = -(1-xi(1));
+	  dshapes(0,1) = -(1-xi(0));
+	  dshapes(1,0) =  (1-xi(1));
+	  dshapes(1,1) =    -xi(0);
+	  dshapes(2,0) =     xi(1);
+	  dshapes(2,1) =     xi(0);
+	  dshapes(3,0) =    -xi(1);
+	  dshapes(3,1) =  (1-xi(0));
+
+	  if (info.order == 1) return;
+
+	  double shapes[4] = {
+	    (1-xi(0))*(1-xi(1)),
+            xi(0) *(1-xi(1)),
+            xi(0) *   xi(1) ,
+	    (1-xi(0))*   xi(1) 
+	  };
+
+	  double mu[4] = { 
+	    1 - xi(0) + 1 - xi(1), 
+            xi(0) + 1 - xi(1), 
+            xi(0) +     xi(1), 
+	    1 - xi(0) +     xi(1), 
+	  };
+
+	  double dmu[4][2] = {
+	    { -1, -1 },
+	    { 1, -1 },
+	    { 1, 1 },
+	    { -1, 1 } };
+	    
+	  // double hshapes[20], hdshapes[20];
+          ArrayMem<double, 20> hshapes(order+1), hdshapes(order+1);
+
+	  int ii = 4;
+	  const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (QUAD);
+	  
+	  for (int i = 0; i < 4; i++)
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = edges[i][0]-1, vi2 = edges[i][1]-1;
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  CalcEdgeShapeDx (eorder, mu[vi1]-mu[vi2], &hshapes[0], &hdshapes[0]);
+
+		  double lame = shapes[vi1]+shapes[vi2];
+		  double dlame[2] = {
+		    dshapes(vi1, 0) + dshapes(vi2, 0),
+		    dshapes(vi1, 1) + dshapes(vi2, 1) };
+		    
+		  for (int j = 0; j < eorder-1; j++)
+		    for (int k = 0; k < 2; k++)
+		      dshapes(ii+j, k) = 
+			lame * hdshapes[j] * (dmu[vi1][k]-dmu[vi2][k])
+			+ dlame[k] * hshapes[j];
+
+		  ii += eorder-1;
+		}
+	    }
+
+	  /*	  
+           *testout << "quad, dshape = " << endl << dshapes << endl;
+           for (int i = 0; i < 2; i++)
+           {
+           Point<2> xil = xi, xir = xi;
+           Vector shapesl(dshapes.Height()), shapesr(dshapes.Height());
+           xil(i) -= 1e-6;
+           xir(i) += 1e-6;
+           CalcElementShapes (info, xil, shapesl);
+           CalcElementShapes (info, xir, shapesr);
+	      
+           for (int j = 0; j < dshapes.Height(); j++)
+           dshapes(j,i) = 1.0 / 2e-6 * (shapesr(j)-shapesl(j));
+           }
+	  
+           *testout << "quad, num dshape = " << endl << dshapes << endl;
+           */
+	  break;
+	}
+      default:
+        throw NgException("CurvedElements::CalcDShape 2d, element type not handled");
+
+      };
+  }
+
+
+  template <int DIM_SPACE>
+  void CurvedElements :: 
+  GetCoefficients (SurfaceElementInfo & info, Array<Vec<DIM_SPACE> > & coefs) const
+  {
+    const Element2d & el = mesh[info.elnr];
+    coefs.SetSize (info.ndof);
+    // coefs = Vec<3> (0,0,0);
+    
+    for (int i = 0; i < info.nv; i++)
+      {
+        Vec<3> hv(mesh[el[i]]);
+        for (int j = 0; j < DIM_SPACE; j++)
+          coefs[i](j) = hv(j);
+      }
+    
+    if (info.order == 1) return;
+
+    int ii = info.nv;
+	  
+    for (int i = 0; i < info.edgenrs.Size(); i++)
+      {
+	int first = edgecoeffsindex[info.edgenrs[i]];
+	int next = edgecoeffsindex[info.edgenrs[i]+1];
+	for (int j = first; j < next; j++, ii++)
+          for (int k = 0; k < DIM_SPACE; k++)
+            coefs[ii](k) = edgecoeffs[j](k);
+      }
+    
+    int first = facecoeffsindex[info.facenr];
+    int next = facecoeffsindex[info.facenr+1];
+    for (int j = first; j < next; j++, ii++)
+      for (int k = 0; k < DIM_SPACE; k++)
+        coefs[ii](k) = facecoeffs[j](k);
+  }
+
+
+  template void CurvedElements :: 
+  GetCoefficients<2> (SurfaceElementInfo & info, Array<Vec<2> > & coefs) const;
+
+  template void CurvedElements :: 
+  GetCoefficients<3> (SurfaceElementInfo & info, Array<Vec<3> > & coefs) const;
+
+
+
+
+
+  // ********************** Transform volume elements *******************
+
+
+  bool CurvedElements :: IsElementCurved (ElementIndex elnr) const
+  {
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	
+	return mesh.coarsemesh->GetCurvedElements().IsElementCurved (hpref_el.coarse_elnr);
+      }
+
+    const Element & el = mesh[elnr];
+    ELEMENT_TYPE type = el.GetType();
+    
+    int nfaces = MeshTopology::GetNFaces (type);
+    if (nfaces > 4)
+      { // not a tet
+	const ELEMENT_FACE * faces = MeshTopology::GetFaces0 (type);
+	for (int j = 0; j < nfaces; j++)
+	  {
+	    if (faces[j][3] != -1)
+	      {  // a quad face
+		Point<3> pts[4];
+		for (int k = 0; k < 4; k++)
+		  pts[k] = mesh.Point(el[faces[j][k]]);
+		Vec<3> twist = (pts[1] - pts[0]) - (pts[2]-pts[3]);
+		if (twist.Length() > 1e-8 * (pts[1]-pts[0]).Length())
+		  return true;
+	      }
+	  }
+      }
+      
+    
+
+    ElementInfo info;
+    info.elnr = elnr;
+    info.order = order;
+    info.ndof = info.nv = MeshTopology::GetNPoints (type);
+    if (info.order > 1)
+      {
+	const MeshTopology & top = mesh.GetTopology();
+	
+	info.nedges = top.GetElementEdges (elnr+1, info.edgenrs, 0);
+	for (int i = 0; i < info.nedges; i++)
+	  info.edgenrs[i]--;
+
+	info.nfaces = top.GetElementFaces (elnr+1, info.facenrs, 0);
+	for (int i = 0; i < info.nfaces; i++)
+	  info.facenrs[i]--;
+
+	for (int i = 0; i < info.nedges; i++)
+	  info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]];
+	for (int i = 0; i < info.nfaces; i++)
+	  info.ndof += facecoeffsindex[info.facenrs[i]+1] - facecoeffsindex[info.facenrs[i]];
+      }
+
+    return (info.ndof > info.nv);
+  }
+
+
+
+
+
+
+  void CurvedElements :: 
+  CalcElementTransformation (Point<3> xi, ElementIndex elnr,
+			     Point<3> * x, Mat<3,3> * dxdxi, //  bool * curved,
+                             void * buffer, bool valid)
+  {
+    if (mesh.coarsemesh)
+      {
+        const HPRefElement & hpref_el =
+          (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	  
+        // xi umrechnen
+        double lami[8];
+        FlatVector vlami(8, lami);
+        vlami = 0;
+        mesh[elnr].GetShapeNew (xi, vlami);
+
+        Mat<3,3> trans, dxdxic;
+        if (dxdxi)
+          {
+            MatrixFixWidth<3> dlami(8);
+            dlami = 0;
+            mesh[elnr].GetDShapeNew (xi, dlami);	  
+	      
+            trans = 0;
+            for (int k = 0; k < 3; k++)
+              for (int l = 0; l < 3; l++)
+                for (int i = 0; i < hpref_el.np; i++)
+                  trans(l,k) += hpref_el.param[i][l] * dlami(i, k);
+          }
+
+        Point<3> coarse_xi(0,0,0);
+        for (int i = 0; i < hpref_el.np; i++)
+          for (int j = 0; j < 3; j++)
+            coarse_xi(j) += hpref_el.param[i][j] * lami[i];
+
+        mesh.coarsemesh->GetCurvedElements().CalcElementTransformation (coarse_xi, hpref_el.coarse_elnr, x, &dxdxic /* , curved */);
+
+        if (dxdxi)
+          *dxdxi = dxdxic * trans;
+
+        return;
+      }
+
+
+    Vector shapes;
+    MatrixFixWidth<3> dshapes;
+
+    const Element & el = mesh[elnr];
+    ELEMENT_TYPE type = el.GetType();
+
+    ElementInfo hinfo;
+    ElementInfo & info = (buffer) ? *static_cast<ElementInfo*> (buffer) : hinfo;
+    
+
+    if (!valid)
+      {
+        info.elnr = elnr;
+        info.order = order;
+        info.ndof = info.nv = MeshTopology::GetNPoints (type);
+        if (info.order > 1)
+          {
+            const MeshTopology & top = mesh.GetTopology();
+            
+            info.nedges = top.GetElementEdges (elnr+1, info.edgenrs, 0);
+            for (int i = 0; i < info.nedges; i++)
+              info.edgenrs[i]--;
+            
+            info.nfaces = top.GetElementFaces (elnr+1, info.facenrs, 0);
+            for (int i = 0; i < info.nfaces; i++)
+              info.facenrs[i]--;
+            
+            for (int i = 0; i < info.nedges; i++)
+              info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]];
+            for (int i = 0; i < info.nfaces; i++)
+              info.ndof += facecoeffsindex[info.facenrs[i]+1] - facecoeffsindex[info.facenrs[i]];
+          }
+      }
+
+    CalcElementShapes (info, xi, shapes);
+
+    Vec<3> * coefs =  (info.ndof <= 10) ? 
+      &info.hcoefs[0] : new Vec<3> [info.ndof];
+
+    if (info.ndof > 10 || !valid)
+      GetCoefficients (info, coefs);
+
+    if (x)
+      {
+        *x = 0;
+        for (int i = 0; i < shapes.Size(); i++)
+          *x += shapes(i) * coefs[i];
+      }
+
+    if (dxdxi)
+      {
+        if (valid && info.order == 1 && info.nv == 4)   // a linear tet
+          {
+            *dxdxi = info.hdxdxi;
+          }
+        else
+          {
+            CalcElementDShapes (info, xi, dshapes);
+            
+            *dxdxi = 0;
+            for (int i = 0; i < shapes.Size(); i++)
+              for (int j = 0; j < 3; j++)
+                for (int k = 0; k < 3; k++)
+                  (*dxdxi)(j,k) += dshapes(i,k) * coefs[i](j);
+            
+            info.hdxdxi = *dxdxi;
+          }
+      }
+
+    // *testout << "curved_elements, dshapes = " << endl << dshapes << endl;
+
+    //    if (curved) *curved = (info.ndof > info.nv);
+
+    if (info.ndof > 10) delete [] coefs;
+  }
+
+
+
+
+  void CurvedElements ::   CalcElementShapes (ElementInfo & info, const Point<3> & xi, Vector & shapes) const
+  {
+    const Element & el = mesh[info.elnr];
+
+    if (rational && info.order >= 2)
+      {
+        shapes.SetSize(10);
+        double w = 1;
+        double lami[4] = { xi(0), xi(1), xi(2), 1-xi(0)-xi(1)-xi(2) };
+        for (int j = 0; j < 4; j++)
+          shapes(j) = lami[j] * lami[j];
+
+        const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (TET);
+        for (int j = 0; j < 6; j++)
+          {
+            double wi = edgeweight[info.edgenrs[j]];
+            shapes(j+4) = 2 * wi * lami[edges[j][0]-1] * lami[edges[j][1]-1];
+            w += (wi-1) * 2 * lami[edges[j][0]-1] * lami[edges[j][1]-1];
+          }
+
+        shapes *= 1.0 / w;
+        return;
+      }
+
+    shapes.SetSize(info.ndof);
+    
+    switch (el.GetType())
+      {
+      case TET:
+	{
+	  shapes(0) = xi(0);
+	  shapes(1) = xi(1);
+	  shapes(2) = xi(2);
+	  shapes(3) = 1-xi(0)-xi(1)-xi(2);
+
+	  if (info.order == 1) return;
+
+	  int ii = 4;
+	  const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (TET);
+	  for (int i = 0; i < 6; i++)
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = edges[i][0]-1, vi2 = edges[i][1]-1;
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  CalcScaledEdgeShape (eorder, shapes(vi1)-shapes(vi2), shapes(vi1)+shapes(vi2), &shapes(ii));
+		  ii += eorder-1;
+		}
+	    }
+	  const ELEMENT_FACE * faces = MeshTopology::GetFaces1 (TET);
+	  for (int i = 0; i < 4; i++)
+	    {
+	      int forder = faceorder[info.facenrs[i]];
+	      if (forder >= 3)
+		{
+		  int fnums[] = { faces[i][0]-1, faces[i][1]-1, faces[i][2]-1 }; 
+		  if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+		  if (el[fnums[1]] > el[fnums[2]]) swap (fnums[1], fnums[2]);
+		  if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+
+		  CalcScaledTrigShape (forder, 
+				       shapes(fnums[1])-shapes(fnums[0]), shapes(fnums[2]), 
+				       shapes(fnums[0])+shapes(fnums[1])+shapes(fnums[2]), &shapes(ii));
+		  ii += (forder-1)*(forder-2)/2;
+		}
+	    }
+
+	  break;
+	}
+        
+      case TET10:
+        {
+          double x = xi(0);
+          double y = xi(1);
+          double z = xi(2);
+          double lam4 = 1 - x - y - z;
+          /*
+            shapes(0) = xi(0);
+            shapes(1) = xi(1);
+            shapes(2) = xi(2);
+            shapes(3) = 1-xi(0)-xi(1)-xi(2);
+          */
+          
+          shapes(0) = 2 * x * x - x;  
+          shapes(1) = 2 * y * y - y;
+          shapes(2) = 2 * z * z - z;
+          shapes(3) = 2 * lam4 * lam4 - lam4;
+          
+          shapes(4) = 4 * x * y;
+          shapes(5) = 4 * x * z;
+          shapes(6) = 4 * x * lam4;
+          shapes(7) = 4 * y * z;
+          shapes(8) = 4 * y * lam4;
+          shapes(9) = 4 * z * lam4;
+
+          break;
+        }
+
+      case PRISM:
+	{
+	  double lami[6] = { xi(0), xi(1), 1-xi(0)-xi(1), xi(0), xi(1), 1-xi(0)-xi(1) };
+	  double lamiz[6] = { 1-xi(2), 1-xi(2), 1-xi(2), xi(2), xi(2), xi(2) };
+	  for (int i = 0; i < 6; i++)
+	    shapes(i) = lami[i] * lamiz[i]; 
+	  for (int i = 6; i < info.ndof; i++)
+	    shapes(i) = 0;
+
+          if (info.order == 1) return;
+
+
+	  int ii = 6;
+ 	  const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (PRISM);
+	  for (int i = 0; i < 6; i++)    // horizontal edges
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = edges[i][0]-1, vi2 = edges[i][1]-1;
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  CalcScaledEdgeShape (eorder, lami[vi1]-lami[vi2], lami[vi1]+lami[vi2], &shapes(ii));
+		  double facz = (i < 3) ? (1-xi(2)) : xi(2);
+		  for (int j = 0; j < eorder-1; j++)
+		    shapes(ii+j) *= facz;
+
+		  ii += eorder-1;
+		}
+	    }
+
+	  for (int i = 6; i < 9; i++)    // vertical edges
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = edges[i][0]-1, vi2 = edges[i][1]-1;
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  double bubz = lamiz[vi1]*lamiz[vi2];
+		  double polyz = lamiz[vi1] - lamiz[vi2];
+		  double bubxy = lami[vi1];
+
+		  for (int j = 0; j < eorder-1; j++)
+		    {
+		      shapes(ii+j) = bubxy * bubz;
+		      bubz *= polyz;
+		    }
+		  ii += eorder-1;
+		}
+	    }
+
+	  // FACE SHAPES
+	  const ELEMENT_FACE * faces = MeshTopology::GetFaces1 (PRISM);
+	  for (int i = 0; i < 2; i++)
+	    {
+	      int forder = faceorder[info.facenrs[i]];
+	      if ( forder < 3 ) continue;
+	      int fav[3] = { faces[i][0]-1, faces[i][1]-1, faces[i][2]-1 };
+	      if(el[fav[0]] > el[fav[1]]) swap(fav[0],fav[1]); 
+	      if(el[fav[1]] > el[fav[2]]) swap(fav[1],fav[2]);
+	      if(el[fav[0]] > el[fav[1]]) swap(fav[0],fav[1]); 	
+
+	      CalcTrigShape (forder, 
+			     lami[fav[2]]-lami[fav[1]], lami[fav[0]],
+			     &shapes(ii));
+	      
+	      int ndf = (forder+1)*(forder+2)/2 - 3 - 3*(forder-1);
+	      for ( int j = 0; j < ndf; j++ )
+		shapes(ii+j) *= lamiz[fav[1]];
+	      ii += ndf;
+	    }
+	  break;
+	}
+
+      case PYRAMID:
+	{
+	  shapes = 0.0;
+	  double x = xi(0);
+	  double y = xi(1);
+	  double z = xi(2);
+	  
+	  if (z == 1.) z = 1-1e-10;
+	  shapes[0] = (1-z-x)*(1-z-y) / (1-z);
+	  shapes[1] = x*(1-z-y) / (1-z);
+	  shapes[2] = x*y / (1-z);
+	  shapes[3] = (1-z-x)*y / (1-z);
+	  shapes[4] = z;
+          
+          if (info.order == 1) return;
+
+	  int ii = 5;
+	  const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (PYRAMID);
+	  for (int i = 0; i < 4; i++)    // horizontal edges
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = (edges[i][0]-1), vi2 = (edges[i][1]-1);
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  CalcScaledEdgeShape (eorder, shapes[vi1]-shapes[vi2], shapes[vi1]+shapes[vi2], &shapes(ii));
+		  double fac = (shapes[vi1]+shapes[vi2]) / (1-z);
+		  for (int j = 0; j < eorder-1; j++)
+		    shapes(ii+j) *= fac;
+
+		  ii += eorder-1;
+		}
+	    }
+
+
+
+	  break;
+	}
+
+      case HEX:
+        {
+	  shapes = 0.0;
+	  double x = xi(0);
+	  double y = xi(1);
+	  double z = xi(2);
+	  
+	  shapes[0] = (1-x)*(1-y)*(1-z);
+	  shapes[1] =    x *(1-y)*(1-z);
+	  shapes[2] =    x *   y *(1-z);
+	  shapes[3] = (1-x)*   y *(1-z);
+	  shapes[4] = (1-x)*(1-y)*(z);
+	  shapes[5] =    x *(1-y)*(z);
+	  shapes[6] =    x *   y *(z);
+	  shapes[7] = (1-x)*   y *(z);
+          break;
+        }
+
+      default:
+        throw NgException("CurvedElements::CalcShape 3d, element type not handled");
+
+      };
+  }
+
+
+  void CurvedElements :: 
+  CalcElementDShapes (ElementInfo & info, const Point<3> & xi, MatrixFixWidth<3> & dshapes) const
+  {
+    const Element & el = mesh[info.elnr];
+
+    dshapes.SetSize(info.ndof);
+    dshapes = 0.0;
+
+
+
+    if (rational && info.order >= 2)
+      {
+        double w = 1;
+        double dw[3] = { 0, 0, 0 };
+
+        double lami[4] = { xi(0), xi(1), xi(2), 1-xi(0)-xi(1)-xi(2) };
+        double dlami[4][3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 }, { -1, -1, -1 }};
+        double shapes[10];
+
+        for (int j = 0; j < 4; j++)
+          {
+            shapes[j] = lami[j] * lami[j];
+            dshapes(j,0) = 2 * lami[j] * dlami[j][0];
+            dshapes(j,1) = 2 * lami[j] * dlami[j][1];
+            dshapes(j,2) = 2 * lami[j] * dlami[j][2];
+          }
+
+        const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (TET);
+        for (int j = 0; j < 6; j++)
+          {
+            double wi = edgeweight[info.edgenrs[j]];
+
+            shapes[j+4] = 2 * wi * lami[edges[j][0]-1] * lami[edges[j][1]-1];
+            for (int k = 0; k < 3; k++)
+              dshapes(j+4,k) = 2*wi* (lami[edges[j][0]-1] * dlami[edges[j][1]-1][k] +
+                                      lami[edges[j][1]-1] * dlami[edges[j][0]-1][k]);
+
+            w += (wi-1) * 2 * lami[edges[j][0]-1] * lami[edges[j][1]-1];
+            for (int k = 0; k < 3; k++)
+              dw[k] += 2*(wi-1) * (lami[edges[j][0]-1] * dlami[edges[j][1]-1][k] +
+                                   lami[edges[j][1]-1] * dlami[edges[j][0]-1][k]);
+          }
+        // shapes *= 1.0 / w;
+        dshapes *= 1.0 / w;
+        for (int i = 0; i < 10; i++)
+          for (int j = 0; j < 3; j++)
+            dshapes(i,j) -= shapes[i] * dw[j] / (w*w);
+        return;
+      }
+
+    switch (el.GetType())
+      {
+      case TET:
+	{
+	  dshapes(0,0) = 1;
+	  dshapes(1,1) = 1;
+	  dshapes(2,2) = 1;
+	  dshapes(3,0) = -1;
+	  dshapes(3,1) = -1;
+	  dshapes(3,2) = -1;
+
+	  if (info.order == 1) return;
+
+	  double lami[] = { xi(0), xi(1), xi(2), 1-xi(0)-xi(1)-xi(2) };
+	  int ii = 4;
+	  const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (TET);
+	  for (int i = 0; i < 6; i++)
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = edges[i][0]-1, vi2 = edges[i][1]-1;
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  CalcScaledEdgeShapeDxDt<3> (eorder, lami[vi1]-lami[vi2], lami[vi1]+lami[vi2], &dshapes(ii,0));
+
+		  Mat<2,3> trans;
+		  for (int j = 0; j < 3; j++)
+		    {
+		      trans(0,j) = dshapes(vi1,j)-dshapes(vi2,j);
+		      trans(1,j) = dshapes(vi1,j)+dshapes(vi2,j);
+		    }
+		  
+		  for (int j = 0; j < order-1; j++)
+		    {
+		      double ddx = dshapes(ii+j,0);
+		      double ddt = dshapes(ii+j,1);
+		      dshapes(ii+j,0) = ddx * trans(0,0) + ddt * trans(1,0);
+		      dshapes(ii+j,1) = ddx * trans(0,1) + ddt * trans(1,1);
+		      dshapes(ii+j,2) = ddx * trans(0,2) + ddt * trans(1,2);
+		    }
+
+		  ii += eorder-1;
+		}
+	    }
+
+	  const ELEMENT_FACE * faces = MeshTopology::GetFaces1 (TET);
+	  for (int i = 0; i < 4; i++)
+	    {
+	      int forder = faceorder[info.facenrs[i]];
+	      if (forder >= 3)
+		{
+		  int fnums[] = { faces[i][0]-1, faces[i][1]-1, faces[i][2]-1 }; 
+		  if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+		  if (el[fnums[1]] > el[fnums[2]]) swap (fnums[1], fnums[2]);
+		  if (el[fnums[0]] > el[fnums[1]]) swap (fnums[0], fnums[1]);
+
+		  CalcScaledTrigShapeDxDyDt (forder, 
+					     lami[fnums[1]]-lami[fnums[0]], 
+					     lami[fnums[2]], lami[fnums[0]]+lami[fnums[1]]+lami[fnums[2]],
+					     &dshapes(ii,0));
+
+		  Mat<3,3> trans;
+		  for (int j = 0; j < 3; j++)
+		    {
+		      trans(0,j) = dshapes(fnums[1],j)-dshapes(fnums[0],j);
+		      trans(1,j) = dshapes(fnums[2],j);
+		      trans(2,j) = dshapes(fnums[0],j)+dshapes(fnums[1],j)+dshapes(fnums[2],j);
+		    }
+		  
+		  int nfd = (forder-1)*(forder-2)/2;
+		  for (int j = 0; j < nfd; j++)
+		    {
+		      double ddx = dshapes(ii+j,0);
+		      double ddy = dshapes(ii+j,1);
+		      double ddt = dshapes(ii+j,2);
+		      dshapes(ii+j,0) = ddx * trans(0,0) + ddy * trans(1,0) + ddt * trans(2,0);
+		      dshapes(ii+j,1) = ddx * trans(0,1) + ddy * trans(1,1) + ddt * trans(2,1);
+		      dshapes(ii+j,2) = ddx * trans(0,2) + ddy * trans(1,2) + ddt * trans(2,2);
+		    }
+
+		  ii += nfd;
+		}
+	    }
+
+	  break;
+	}
+
+      case TET10:
+	{
+	  if (dshapes.Height() == 4)
+	    {
+	      dshapes = 0.0;
+
+              dshapes(0,0) = 1;
+              dshapes(1,1) = 1;
+              dshapes(2,2) = 1;
+              dshapes(3,0) = -1;
+              dshapes(3,1) = -1;
+              dshapes(3,2) = -1;
+	    }
+	  else
+	    {
+	      AutoDiff<3> x(xi(0), 0);
+	      AutoDiff<3> y(xi(1), 1);
+	      AutoDiff<3> z(xi(2), 2);
+	      AutoDiff<3> lam4 = 1-x-y-z;
+	      AutoDiff<3> shapes[10];
+              
+              shapes[0] = 2 * x * x - x;  
+              shapes[1] = 2 * y * y - y;
+              shapes[2] = 2 * z * z - z;
+              shapes[3] = 2 * lam4 * lam4 - lam4;
+              
+              shapes[4] = 4 * x * y;
+              shapes[5] = 4 * x * z;
+              shapes[6] = 4 * x * lam4;
+              shapes[7] = 4 * y * z;
+              shapes[8] = 4 * y * lam4;
+              shapes[9] = 4 * z * lam4;
+
+	      for (int i = 0; i < 10; i++)
+		{
+		  dshapes(i,0) = shapes[i].DValue(0);
+		  dshapes(i,1) = shapes[i].DValue(1);
+		  dshapes(i,2) = shapes[i].DValue(2);
+		}
+	      
+	    }
+          break;
+
+          break;
+        }
+
+
+      case PRISM:
+	{
+	  double lami[6] = { xi(0), xi(1), 1-xi(0)-xi(1), xi(0), xi(1), 1-xi(0)-xi(1)  };
+	  double lamiz[6] = { 1-xi(2), 1-xi(2), 1-xi(2), xi(2), xi(2), xi(2) };
+	  double dlamiz[6] = { -1, -1, -1, 1, 1, 1 };
+	  double dlami[6][2] = 
+	    { { 1, 0, },
+	      { 0, 1, },
+	      { -1, -1 },
+	      { 1, 0, },
+	      { 0, 1, },
+	      { -1, -1 } };
+	  for (int i = 0; i < 6; i++)
+	    {
+	      // shapes(i) = lami[i%3] * ( (i < 3) ? (1-xi(2)) : xi(2) );
+	      dshapes(i,0) = dlami[i%3][0] * ( (i < 3) ? (1-xi(2)) : xi(2) );
+	      dshapes(i,1) = dlami[i%3][1] * ( (i < 3) ? (1-xi(2)) : xi(2) );
+	      dshapes(i,2) = lami[i%3] * ( (i < 3) ? -1 : 1 );
+	    }
+
+	  int ii = 6;
+
+	  if (info.order == 1) return;
+
+
+	  const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (PRISM);
+	  for (int i = 0; i < 6; i++)    // horizontal edges
+	    {
+	      int order = edgeorder[info.edgenrs[i]];
+	      if (order >= 2)
+		{
+		  int vi1 = (edges[i][0]-1), vi2 = (edges[i][1]-1);
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+		  vi1 = vi1 % 3;
+		  vi2 = vi2 % 3;
+
+		  Vector shapei(order+1);
+		  CalcScaledEdgeShapeDxDt<3> (order, lami[vi1]-lami[vi2], lami[vi1]+lami[vi2], &dshapes(ii,0) );
+		  CalcScaledEdgeShape(order, lami[vi1]-lami[vi2], lami[vi1]+lami[vi2], &shapei(0) );
+
+		  Mat<2,2> trans;
+		  for (int j = 0; j < 2; j++)
+		    {
+		      trans(0,j) = dlami[vi1][j]-dlami[vi2][j];
+		      trans(1,j) = dlami[vi1][j]+dlami[vi2][j];
+		    }
+		  
+		  for (int j = 0; j < order-1; j++)
+		    {
+		      double ddx = dshapes(ii+j,0);
+		      double ddt = dshapes(ii+j,1);
+		      dshapes(ii+j,0) = ddx * trans(0,0) + ddt * trans(1,0);
+		      dshapes(ii+j,1) = ddx * trans(0,1) + ddt * trans(1,1);
+		    }
+
+
+
+		  double facz = (i < 3) ? (1-xi(2)) : xi(2);
+		  double dfacz = (i < 3) ? (-1) : 1;
+		  for (int j = 0; j < order-1; j++)
+		    {
+		      dshapes(ii+j,0) *= facz;
+		      dshapes(ii+j,1) *= facz;
+		      dshapes(ii+j,2) = shapei(j) * dfacz;
+		    }
+
+		  ii += order-1;
+		}
+	    }
+
+	  for (int i = 6; i < 9; i++)    // vertical edges
+	    {
+	      int eorder = edgeorder[info.edgenrs[i]];
+	      if (eorder >= 2)
+		{
+		  int vi1 = (edges[i][0]-1), vi2 = (edges[i][1]-1);
+		  if (el[vi1] > el[vi2]) swap (vi1, vi2);
+
+		  double bubz = lamiz[vi1] * lamiz[vi2];
+		  double dbubz = dlamiz[vi1]*lamiz[vi2] + lamiz[vi1]*dlamiz[vi2];
+		  double polyz = lamiz[vi1] - lamiz[vi2];
+		  double dpolyz = dlamiz[vi1] - dlamiz[vi2];
+		  double bubxy = lami[(vi1)%3];
+		  double dbubxydx = dlami[(vi1)%3][0];
+		  double dbubxydy = dlami[(vi1)%3][1];
+
+		  for (int j = 0; j < eorder-1; j++)
+		    {
+		      dshapes(ii+j,0) = dbubxydx * bubz;
+		      dshapes(ii+j,1) = dbubxydy * bubz;
+		      dshapes(ii+j,2) = bubxy * dbubz;
+
+		      dbubz = bubz * dpolyz + dbubz * polyz;
+		      bubz *= polyz;
+		    }
+		  ii += eorder-1;
+		}
+	    }
+
+
+	  if (info.order == 2) return;
+	  // FACE SHAPES
+	  const ELEMENT_FACE * faces = MeshTopology::GetFaces1 (PRISM);
+	  for (int i = 0; i < 2; i++)
+	    {
+	      int forder = faceorder[info.facenrs[i]];
+
+	      if ( forder < 3 ) continue;
+	      int ndf = (forder+1)*(forder+2)/2 - 3 - 3*(forder-1);
+
+	      int fav[3] = { faces[i][0]-1, faces[i][1]-1, faces[i][2]-1 };
+	      if(el[fav[0]] > el[fav[1]]) swap(fav[0],fav[1]); 
+	      if(el[fav[1]] > el[fav[2]]) swap(fav[1],fav[2]);
+	      if(el[fav[0]] > el[fav[1]]) swap(fav[0],fav[1]); 	
+
+	      MatrixFixWidth<2> dshapei(ndf);
+	      Vector shapei(ndf);
+
+	      CalcTrigShapeDxDy (forder, 
+				 lami[fav[2]]-lami[fav[1]], lami[fav[0]],
+				 &dshapei(0,0));
+	      CalcTrigShape (forder, lami[fav[2]]-lami[fav[1]], lami[fav[0]],
+                             &shapei(0));
+	      
+	      Mat<2,2> trans;
+	      for (int j = 0; j < 2; j++)
+		{
+		  trans(0,j) = dlami[fav[2]][j]-dlami[fav[1]][j];
+		  trans(1,j) = dlami[fav[0]][j];
+		}
+		  
+	      for (int j = 0; j < ndf; j++)
+		{
+		  // double ddx = dshapes(ii+j,0);
+		  // double ddt = dshapes(ii+j,1);
+		  double ddx = dshapei(j,0);
+		  double ddt = dshapei(j,1);
+		  dshapes(ii+j,0) = ddx * trans(0,0) + ddt * trans(1,0);
+		  dshapes(ii+j,1) = ddx * trans(0,1) + ddt * trans(1,1);
+		}
+
+	      for ( int j = 0; j < ndf; j++ )
+		{
+		  dshapes(ii+j,0) *= lamiz[fav[1]];
+		  dshapes(ii+j,1) *= lamiz[fav[1]];
+		  dshapes(ii+j,2) = shapei(j) * dlamiz[fav[1]];
+		}
+	      ii += ndf;
+	    }
+
+	  break;
+
+	}
+
+      case PYRAMID:
+	{
+	  dshapes = 0.0;
+	  double x = xi(0);
+	  double y = xi(1);
+	  double z = xi(2);
+	  
+	  if (z == 1.) z = 1-1e-10;
+	  double z1 = 1-z;
+	  double z2 = z1*z1;
+	  
+	  dshapes(0,0) = -(z1-y)/z1;
+	  dshapes(0,1) = -(z1-x)/z1;
+	  dshapes(0,2) = ((x+y+2*z-2)*z1+(z1-y)*(z1-x))/z2;
+
+	  dshapes(1,0) = (z1-y)/z1;
+	  dshapes(1,1) = -x/z1;
+	  dshapes(1,2) = (-x*z1+x*(z1-y))/z2;
+
+	  dshapes(2,0) = y/z1;
+	  dshapes(2,1) = x/z1;
+	  dshapes(2,2) = x*y/z2;
+
+	  dshapes(3,0) = -y/z1;
+	  dshapes(3,1) = (z1-x)/z1;
+	  dshapes(3,2) = (-y*z1+y*(z1-x))/z2;
+
+	  dshapes(4,0) = 0;
+	  dshapes(4,1) = 0;
+	  dshapes(4,2) = 1;
+          /* old:
+             vdshape[0] = Vec<3>( -(z1-y)/z1, -(z1-x)/z1, ((x+y+2*z-2)*z1+(z1-y)*(z1-x))/z2 );
+             vdshape[1] = Vec<3>( (z1-y)/z1,  -x/z1,      (-x*z1+x*(z1-y))/z2 );
+             vdshape[2] = Vec<3>( y/z1,       x/z1,       x*y/z2 );
+             vdshape[3] = Vec<3>( -y/z1,      (z1-x)/z1,  (-y*z1+y*(z1-x))/z2 );
+             vdshape[4] = Vec<3>( 0, 0, 1 );
+          */
+	  break;
+	}
+
+      case HEX:
+        {
+          dshapes = 0.0;
+
+	  double x = xi(0);
+	  double y = xi(1);
+	  double z = xi(2);
+
+	  // shapes[0] = (1-x)*(1-y)*(1-z);
+          dshapes(0,0) = - (1-y)*(1-z);
+          dshapes(0,1) = (1-x) * (-1) * (1-z);
+          dshapes(0,2) = (1-x) * (1-y) * (-1);
+
+	  // shapes[1] =    x *(1-y)*(1-z);
+          dshapes(1,0) = (1-y)*(1-z);
+          dshapes(1,1) = -x * (1-z);
+          dshapes(1,2) = -x * (1-y);
+
+	  // shapes[2] =    x *   y *(1-z);
+          dshapes(2,0) = y * (1-z);
+          dshapes(2,1) = x * (1-z);
+          dshapes(2,2) = -x * y;
+
+	  // shapes[3] = (1-x)*   y *(1-z);
+          dshapes(3,0) = -y * (1-z);
+          dshapes(3,1) = (1-x) * (1-z);
+          dshapes(3,2) = -(1-x) * y;
+
+	  // shapes[4] = (1-x)*(1-y)*z;
+          dshapes(4,0) = - (1-y)*z;
+          dshapes(4,1) = (1-x) * (-1) * z;
+          dshapes(4,2) = (1-x) * (1-y) * 1;
+
+	  // shapes[5] =    x *(1-y)*z;
+          dshapes(5,0) = (1-y)*z;
+          dshapes(5,1) = -x * z;
+          dshapes(5,2) = x * (1-y);
+
+	  // shapes[6] =    x *   y *z;
+          dshapes(6,0) = y * z;
+          dshapes(6,1) = x * z;
+          dshapes(6,2) = x * y;
+
+	  // shapes[7] = (1-x)*   y *z;
+          dshapes(7,0) = -y * z;
+          dshapes(7,1) = (1-x) * z;
+          dshapes(7,2) = (1-x) * y;
+
+          break;
+        }
+
+      default:
+        throw NgException("CurvedElements::CalcDShape 3d, element type not handled");
+      }
+    
+    /*
+      DenseMatrix dshapes2 (info.ndof, 3);
+      Vector shapesl(info.ndof); 
+      Vector shapesr(info.ndof); 
+    
+      double eps = 1e-6;
+      for (int i = 0; i < 3; i++)
+      {
+      Point<3> xl = xi;
+      Point<3> xr = xi;
+	
+      xl(i) -= eps;
+      xr(i) += eps;
+      CalcElementShapes (info, xl, shapesl);
+      CalcElementShapes (info, xr, shapesr);
+	
+      for (int j = 0; j < info.ndof; j++)
+      dshapes2(j,i) = (shapesr(j)-shapesl(j)) / (2*eps);
+      }
+      (*testout) << "dshapes = " << endl << dshapes << endl;
+      (*testout) << "dshapes2 = " << endl << dshapes2 << endl;
+      dshapes2 -= dshapes;
+      (*testout) << "diff = " << endl << dshapes2 << endl;
+    */
+  }
+
+
+
+  void CurvedElements :: 
+  GetCoefficients (ElementInfo & info, Vec<3> * coefs) const
+  {
+    const Element & el = mesh[info.elnr];
+
+    for (int i = 0; i < info.nv; i++)
+      coefs[i] = Vec<3> (mesh[el[i]]);
+
+    if (info.order == 1) return;
+
+    int ii = info.nv;
+	  
+    for (int i = 0; i < info.nedges; i++)
+      {
+	int first = edgecoeffsindex[info.edgenrs[i]];
+	int next = edgecoeffsindex[info.edgenrs[i]+1];
+	for (int j = first; j < next; j++, ii++)
+	  coefs[ii] = edgecoeffs[j];
+      }
+    for (int i = 0; i < info.nfaces; i++)
+      {
+	int first = facecoeffsindex[info.facenrs[i]];
+	int next = facecoeffsindex[info.facenrs[i]+1];
+	for (int j = first; j < next; j++, ii++)
+	  coefs[ii] = facecoeffs[j];
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  void CurvedElements :: 
+  CalcMultiPointSegmentTransformation (Array<double> * xi, SegmentIndex segnr,
+				       Array<Point<3> > * x,
+				       Array<Vec<3> > * dxdxi)
+  {
+    ;
+  }
+
+
+  template <int DIM_SPACE>
+  void CurvedElements :: 
+  CalcMultiPointSegmentTransformation (SegmentIndex elnr, int n,
+				       const double * xi, size_t sxi,
+				       double * x, size_t sx,
+				       double * dxdxi, size_t sdxdxi)
+  {
+    for (int ip = 0; ip < n; ip++)
+      {
+        Point<3> xg;
+        Vec<3> dx;
+
+        // mesh->GetCurvedElements().
+	CalcSegmentTransformation (xi[ip*sxi], elnr, xg, dx);
+      
+        if (x)
+          for (int i = 0; i < DIM_SPACE; i++)
+            x[ip*sx+i] = xg(i);
+	  
+        if (dxdxi)
+          for (int i=0; i<DIM_SPACE; i++)
+            dxdxi[ip*sdxdxi+i] = dx(i);
+      }
+  }
+
+  template void CurvedElements :: 
+  CalcMultiPointSegmentTransformation<2> (SegmentIndex elnr, int npts,
+                                          const double * xi, size_t sxi,
+                                          double * x, size_t sx,
+                                          double * dxdxi, size_t sdxdxi);
+
+  template void CurvedElements :: 
+  CalcMultiPointSegmentTransformation<3> (SegmentIndex elnr, int npts,
+                                          const double * xi, size_t sxi,
+                                          double * x, size_t sx,
+                                          double * dxdxi, size_t sdxdxi);
+
+
+
+  void CurvedElements :: 
+  CalcMultiPointSurfaceTransformation (Array< Point<2> > * xi, SurfaceElementIndex elnr,
+				       Array< Point<3> > * x,
+				       Array< Mat<3,2> > * dxdxi)
+  {
+    double * px = (x) ? &(*x)[0](0) : NULL;
+    double * pdxdxi = (dxdxi) ? &(*dxdxi)[0](0) : NULL;
+
+    CalcMultiPointSurfaceTransformation <3> (elnr, xi->Size(),
+					     &(*xi)[0](0), 2, 
+					     px, 3,
+					     pdxdxi, 6);
+					    
+    return;
+#ifdef OLD
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	
+        // xi umrechnen
+	double lami[4];
+	FlatVector vlami(4, lami);
+
+	ArrayMem<Point<2>, 50> coarse_xi (xi->Size());
+	
+	for (int pi = 0; pi < xi->Size(); pi++)
+	  {
+	    vlami = 0;
+	    mesh[elnr].GetShapeNew ( (*xi)[pi], vlami);
+	    
+	    Point<2> cxi(0,0);
+	    for (int i = 0; i < hpref_el.np; i++)
+	      for (int j = 0; j < 2; j++)
+		cxi(j) += hpref_el.param[i][j] * lami[i];
+
+	    coarse_xi[pi] = cxi;
+	  }
+
+	mesh.coarsemesh->GetCurvedElements().
+	  CalcMultiPointSurfaceTransformation (&coarse_xi, hpref_el.coarse_elnr, x, dxdxi);
+
+
+	Mat<2,2> trans;
+        Mat<3,2> dxdxic;
+	if (dxdxi)
+	  {
+	    MatrixFixWidth<2> dlami(4);
+	    dlami = 0;
+
+	    for (int pi = 0; pi < xi->Size(); pi++)
+	      {
+		mesh[elnr].GetDShapeNew ( (*xi)[pi], dlami);	  
+		
+		trans = 0;
+		for (int k = 0; k < 2; k++)
+		  for (int l = 0; l < 2; l++)
+		    for (int i = 0; i < hpref_el.np; i++)
+		      trans(l,k) += hpref_el.param[i][l] * dlami(i, k);
+		
+		dxdxic = (*dxdxi)[pi];
+		(*dxdxi)[pi] = dxdxic * trans;
+	      }
+	  }	
+
+	return;
+      }
+
+
+
+
+
+    Vector shapes;
+    MatrixFixWidth<2> dshapes;
+    Array<Vec<3> > coefs;
+
+
+    const Element2d & el = mesh[elnr];
+    ELEMENT_TYPE type = el.GetType();
+
+    SurfaceElementInfo info;
+    info.elnr = elnr;
+    info.order = order;
+    switch (type)
+      {
+      case TRIG : info.nv = 3; break;
+      case QUAD : info.nv = 4; break;
+      case TRIG6: info.nv = 6; break;
+      default:
+	cerr << "undef element in CalcMultPointSurfaceTrao" << endl;
+      }
+    info.ndof = info.nv;
+
+    if (info.order > 1)
+      {
+	const MeshTopology & top = mesh.GetTopology();
+	
+	top.GetSurfaceElementEdges (elnr+1, info.edgenrs);
+	for (int i = 0; i < info.edgenrs.Size(); i++)
+	  info.edgenrs[i]--;
+	info.facenr = top.GetSurfaceElementFace (elnr+1)-1;
+
+	for (int i = 0; i < info.edgenrs.Size(); i++)
+	  info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]];
+	info.ndof += facecoeffsindex[info.facenr+1] - facecoeffsindex[info.facenr];
+      }
+
+    GetCoefficients (info, coefs);
+    if (x)
+      {
+	for (int j = 0; j < xi->Size(); j++)
+	  {
+	    CalcElementShapes (info, (*xi)[j], shapes);
+	    Point<3> val(0,0,0);
+	    for (int i = 0; i < coefs.Size(); i++)
+	      val += shapes(i) * coefs[i];
+            (*x)[j] = val;
+	  }
+      }
+
+    if (dxdxi)
+      {
+	for (int ip = 0; ip < xi->Size(); ip++)
+	  {
+	    CalcElementDShapes (info, (*xi)[ip], dshapes);
+
+            /*
+              (*dxdxi)[ip] = 0;
+              for (int i = 0; i < coefs.Size(); i++)
+	      for (int j = 0; j < 3; j++)
+              for (int k = 0; k < 2; k++)
+              (*dxdxi)[ip](j,k) += dshapes(i,k) * coefs[i](j);
+            */
+
+	    Mat<3,2> ds;
+            ds = 0.0;
+	    for (int i = 0; i < coefs.Size(); i++)
+	      for (int j = 0; j < 3; j++)
+		for (int k = 0; k < 2; k++)
+                  ds(j,k) += dshapes(i,k) * coefs[i](j);
+            (*dxdxi)[ip] = ds;
+	  }
+      }
+#endif
+  }
+
+
+
+
+  template <int DIM_SPACE>
+  void CurvedElements :: 
+  CalcMultiPointSurfaceTransformation (SurfaceElementIndex elnr, int npts,
+				       const double * xi, size_t sxi,
+                                       double * x, size_t sx,
+                                       double * dxdxi, size_t sdxdxi)
+  {
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	
+        // xi umrechnen
+	double lami[4];
+	FlatVector vlami(4, lami);
+
+	ArrayMem<Point<2>, 50> coarse_xi (npts);
+	
+	for (int pi = 0; pi < npts; pi++)
+	  {
+	    vlami = 0;
+            Point<2> hxi(xi[pi*sxi], xi[pi*sxi+1]);
+	    mesh[elnr].GetShapeNew ( hxi, vlami);
+	    
+	    Point<2> cxi(0,0);
+	    for (int i = 0; i < hpref_el.np; i++)
+	      for (int j = 0; j < 2; j++)
+		cxi(j) += hpref_el.param[i][j] * lami[i];
+
+	    coarse_xi[pi] = cxi;
+	  }
+
+        mesh.coarsemesh->GetCurvedElements().
+          CalcMultiPointSurfaceTransformation<DIM_SPACE> (hpref_el.coarse_elnr, npts,
+                                                          &coarse_xi[0](0), &coarse_xi[1](0)-&coarse_xi[0](0), 
+                                                          x, sx, dxdxi, sdxdxi);
+
+        // Mat<3,2> dxdxic;
+	if (dxdxi)
+	  {
+	    MatrixFixWidth<2> dlami(4);
+	    dlami = 0;
+
+	    for (int pi = 0; pi < npts; pi++)
+	      {
+                Point<2> hxi(xi[pi*sxi], xi[pi*sxi+1]);
+		mesh[elnr].GetDShapeNew ( hxi, dlami);	  
+		
+		Mat<2,2> trans;
+		trans = 0;
+		for (int k = 0; k < 2; k++)
+		  for (int l = 0; l < 2; l++)
+		    for (int i = 0; i < hpref_el.np; i++)
+		      trans(l,k) += hpref_el.param[i][l] * dlami(i, k);
+		
+                Mat<DIM_SPACE,2> hdxdxic, hdxdxi;
+                for (int k = 0; k < 2*DIM_SPACE; k++)
+                  hdxdxic(k) = dxdxi[pi*sdxdxi+k];
+
+                hdxdxi = hdxdxic * trans;
+
+                for (int k = 0; k < 2*DIM_SPACE; k++)
+                  dxdxi[pi*sdxdxi+k] = hdxdxi(k);
+                    
+                // dxdxic = (*dxdxi)[pi];
+                // (*dxdxi)[pi] = dxdxic * trans;
+	      }
+	  }	
+
+	return;
+      }
+
+    Vector shapes;
+    MatrixFixWidth<2> dshapes;
+    Array<Vec<DIM_SPACE> > coefs;
+
+
+    const Element2d & el = mesh[elnr];
+    ELEMENT_TYPE type = el.GetType();
+
+    SurfaceElementInfo info;
+    info.elnr = elnr;
+    info.order = order;
+    switch (type)
+      {
+      case TRIG : info.nv = 3; break;
+      case QUAD : info.nv = 4; break;
+      case TRIG6: info.nv = 6; break;
+      default:
+	cerr << "undef element in CalcMultPointSurfaceTrao" << endl;
+      }
+    info.ndof = info.nv;
+
+    if (info.order > 1)
+      {
+	const MeshTopology & top = mesh.GetTopology();
+	
+	top.GetSurfaceElementEdges (elnr+1, info.edgenrs);
+	for (int i = 0; i < info.edgenrs.Size(); i++)
+	  info.edgenrs[i]--;
+	info.facenr = top.GetSurfaceElementFace (elnr+1)-1;
+
+	for (int i = 0; i < info.edgenrs.Size(); i++)
+	  info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]];
+	info.ndof += facecoeffsindex[info.facenr+1] - facecoeffsindex[info.facenr];
+      }
+
+    GetCoefficients (info, coefs);
+
+    if (x)
+      {
+	for (int j = 0; j < npts; j++)
+	  {
+            Point<2> vxi(xi[j*sxi], xi[j*sxi+1]);
+	    CalcElementShapes (info, vxi, shapes);
+
+	    Point<DIM_SPACE> val = 0.0;
+	    for (int i = 0; i < coefs.Size(); i++)
+	      val += shapes(i) * coefs[i];
+            
+            for (int k = 0; k < DIM_SPACE; k++)
+              x[j*sx+k] = val(k);
+	  }
+      }
+
+    if (dxdxi)
+      {
+	for (int j = 0; j < npts; j++)
+	  {
+            Point<2> vxi(xi[j*sxi], xi[j*sxi+1]);
+	    CalcElementDShapes (info, vxi, dshapes);
+
+	    Mat<DIM_SPACE,2> ds;
+            ds = 0.0;
+	    for (int i = 0; i < coefs.Size(); i++)
+	      for (int j = 0; j < DIM_SPACE; j++)
+		for (int k = 0; k < 2; k++)
+                  ds(j,k) += dshapes(i,k) * coefs[i](j);
+            // (*dxdxi)[ip] = ds;
+
+            for (int k = 0; k < 2*DIM_SPACE; k++)
+              dxdxi[j*sdxdxi+k] = ds(k);
+	  }
+      }
+  }
+
+
+
+  template void CurvedElements :: 
+  CalcMultiPointSurfaceTransformation<2> (SurfaceElementIndex elnr, int npts,
+                                          const double * xi, size_t sxi,
+                                          double * x, size_t sx,
+                                          double * dxdxi, size_t sdxdxi);
+
+  template void CurvedElements :: 
+  CalcMultiPointSurfaceTransformation<3> (SurfaceElementIndex elnr, int npts,
+                                          const double * xi, size_t sxi,
+                                          double * x, size_t sx,
+                                          double * dxdxi, size_t sdxdxi);
+
+
+
+
+
+
+
+
+
+
+
+
+  void CurvedElements :: 
+  CalcMultiPointElementTransformation (Array< Point<3> > * xi, ElementIndex elnr,
+				       Array< Point<3> > * x,
+				       Array< Mat<3,3> > * dxdxi)
+  {
+    double * px = (x) ? &(*x)[0](0) : NULL;
+    double * pdxdxi = (dxdxi) ? &(*dxdxi)[0](0) : NULL;
+
+    CalcMultiPointElementTransformation (elnr, xi->Size(),
+					 &(*xi)[0](0), 3, 
+					 px, 3,
+					 pdxdxi, 9);
+    
+    return;
+#ifdef OLD
+
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	
+        // xi umrechnen
+	double lami[8];
+	FlatVector vlami(8, lami);
+
+
+	ArrayMem<Point<3>, 50> coarse_xi (xi->Size());
+	
+	for (int pi = 0; pi < xi->Size(); pi++)
+	  {
+	    vlami = 0;
+	    mesh[elnr].GetShapeNew ( (*xi)[pi], vlami);
+	    
+	    Point<3> cxi(0,0,0);
+	    for (int i = 0; i < hpref_el.np; i++)
+	      for (int j = 0; j < 3; j++)
+		cxi(j) += hpref_el.param[i][j] * lami[i];
+
+	    coarse_xi[pi] = cxi;
+	  }
+
+	mesh.coarsemesh->GetCurvedElements().
+	  CalcMultiPointElementTransformation (&coarse_xi, hpref_el.coarse_elnr, x, dxdxi);
+
+
+	Mat<3,3> trans, dxdxic;
+	if (dxdxi)
+	  {
+	    MatrixFixWidth<3> dlami(8);
+	    dlami = 0;
+
+	    for (int pi = 0; pi < xi->Size(); pi++)
+	      {
+		mesh[elnr].GetDShapeNew ( (*xi)[pi], dlami);	  
+		
+		trans = 0;
+		for (int k = 0; k < 3; k++)
+		  for (int l = 0; l < 3; l++)
+		    for (int i = 0; i < hpref_el.np; i++)
+		      trans(l,k) += hpref_el.param[i][l] * dlami(i, k);
+		
+		dxdxic = (*dxdxi)[pi];
+		(*dxdxi)[pi] = dxdxic * trans;
+	      }
+	  }	
+
+	return;
+      }
+
+
+
+
+
+
+
+
+    Vector shapes;
+    MatrixFixWidth<3> dshapes;
+
+
+    const Element & el = mesh[elnr];
+    ELEMENT_TYPE type = el.GetType();
+
+    ElementInfo info;
+    info.elnr = elnr;
+    info.order = order;
+    info.ndof = info.nv = MeshTopology::GetNPoints (type);
+    if (info.order > 1)
+      {
+	const MeshTopology & top = mesh.GetTopology();
+	
+	info.nedges = top.GetElementEdges (elnr+1, info.edgenrs, 0);
+	for (int i = 0; i < info.nedges; i++)
+	  info.edgenrs[i]--;
+
+	info.nfaces = top.GetElementFaces (elnr+1, info.facenrs, 0);
+	for (int i = 0; i < info.nfaces; i++)
+	  info.facenrs[i]--;
+
+	for (int i = 0; i < info.nedges; i++)
+	  info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]];
+	for (int i = 0; i < info.nfaces; i++)
+	  info.ndof += facecoeffsindex[info.facenrs[i]+1] - facecoeffsindex[info.facenrs[i]];
+	// info.ndof += facecoeffsindex[info.facenr+1] - facecoeffsindex[info.facenr];
+      }
+
+    Array<Vec<3> > coefs(info.ndof);
+    GetCoefficients (info, &coefs[0]);
+    if (x)
+      {
+	for (int j = 0; j < xi->Size(); j++)
+	  {
+	    CalcElementShapes (info, (*xi)[j], shapes);
+	    (*x)[j] = 0;
+	    for (int i = 0; i < coefs.Size(); i++)
+	      (*x)[j] += shapes(i) * coefs[i];
+	  }
+      }
+
+    if (dxdxi)
+      {
+	if (info.order == 1 && type == TET)
+	  {
+	    if (xi->Size() > 0)
+	      {
+		CalcElementDShapes (info, (*xi)[0], dshapes);
+		Mat<3,3> ds;
+		ds = 0;
+		for (int i = 0; i < coefs.Size(); i++)
+		  for (int j = 0; j < 3; j++)
+		    for (int k = 0; k < 3; k++)
+		      ds(j,k) += dshapes(i,k) * coefs[i](j);
+	    
+		for (int ip = 0; ip < xi->Size(); ip++)
+		  (*dxdxi)[ip] = ds;
+	      }
+	  }
+	else
+	  for (int ip = 0; ip < xi->Size(); ip++)
+	    {
+	      CalcElementDShapes (info, (*xi)[ip], dshapes);
+	      
+	      Mat<3,3> ds;
+	      ds = 0;
+	      for (int i = 0; i < coefs.Size(); i++)
+		for (int j = 0; j < 3; j++)
+		  for (int k = 0; k < 3; k++)
+		    ds(j,k) += dshapes(i,k) * coefs[i](j);
+	      (*dxdxi)[ip] = ds;
+	    }
+      }
+#endif
+  }
+
+
+
+  void  CurvedElements :: 
+  CalcMultiPointElementTransformation (ElementIndex elnr, int n,
+                                       const double * xi, size_t sxi,
+                                       double * x, size_t sx,
+                                       double * dxdxi, size_t sdxdxi)
+  {
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [mesh[elnr].hp_elnr];
+	
+        // xi umrechnen
+	double lami[8];
+	FlatVector vlami(8, lami);
+
+
+	ArrayMem<double, 100> coarse_xi (3*n);
+	
+	for (int pi = 0; pi < n; pi++)
+	  {
+	    vlami = 0;
+            Point<3> pxi;
+            for (int j = 0; j < 3; j++)
+              pxi(j) = xi[pi*sxi+j];
+
+	    mesh[elnr].GetShapeNew ( pxi, vlami);
+	    
+	    Point<3> cxi(0,0,0);
+	    for (int i = 0; i < hpref_el.np; i++)
+	      for (int j = 0; j < 3; j++)
+		cxi(j) += hpref_el.param[i][j] * lami[i];
+
+            for (int j = 0; j < 3; j++)
+              coarse_xi[3*pi+j] = cxi(j);
+	  }
+
+	mesh.coarsemesh->GetCurvedElements().
+	  CalcMultiPointElementTransformation (hpref_el.coarse_elnr, n, 
+                                               &coarse_xi[0], 3, 
+                                               x, sx, 
+                                               dxdxi, sdxdxi);
+
+	Mat<3,3> trans, dxdxic;
+	if (dxdxi)
+	  {
+	    MatrixFixWidth<3> dlami(8);
+	    dlami = 0;
+
+	    for (int pi = 0; pi < n; pi++)
+	      {
+                Point<3> pxi;
+                for (int j = 0; j < 3; j++)
+                  pxi(j) = xi[pi*sxi+j];
+
+		mesh[elnr].GetDShapeNew (pxi, dlami);	  
+		
+		trans = 0;
+		for (int k = 0; k < 3; k++)
+		  for (int l = 0; l < 3; l++)
+		    for (int i = 0; i < hpref_el.np; i++)
+		      trans(l,k) += hpref_el.param[i][l] * dlami(i, k);
+
+                Mat<3> mat_dxdxic, mat_dxdxi;
+                for (int j = 0; j < 3; j++)
+                  for (int k = 0; k < 3; k++)
+                    mat_dxdxic(j,k) = dxdxi[pi*sdxdxi+3*j+k];
+		
+                mat_dxdxi = mat_dxdxic * trans;
+
+                for (int j = 0; j < 3; j++)
+                  for (int k = 0; k < 3; k++)
+                    dxdxi[pi*sdxdxi+3*j+k] = mat_dxdxi(j,k);
+
+		// dxdxic = (*dxdxi)[pi];
+		// (*dxdxi)[pi] = dxdxic * trans;
+	      }
+	  }	
+	return;
+      }
+
+
+
+
+
+
+    Vector shapes;
+    MatrixFixWidth<3> dshapes;
+
+
+    const Element & el = mesh[elnr];
+    ELEMENT_TYPE type = el.GetType();
+
+    ElementInfo info;
+    info.elnr = elnr;
+    info.order = order;
+    info.ndof = info.nv = MeshTopology::GetNPoints (type);
+    if (info.order > 1)
+      {
+	const MeshTopology & top = mesh.GetTopology();
+	
+	info.nedges = top.GetElementEdges (elnr+1, info.edgenrs, 0);
+	for (int i = 0; i < info.nedges; i++)
+	  info.edgenrs[i]--;
+
+	info.nfaces = top.GetElementFaces (elnr+1, info.facenrs, 0);
+	for (int i = 0; i < info.nfaces; i++)
+	  info.facenrs[i]--;
+
+	for (int i = 0; i < info.nedges; i++)
+	  info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]];
+	for (int i = 0; i < info.nfaces; i++)
+	  info.ndof += facecoeffsindex[info.facenrs[i]+1] - facecoeffsindex[info.facenrs[i]];
+	// info.ndof += facecoeffsindex[info.facenr+1] - facecoeffsindex[info.facenr];
+      }
+
+    Array<Vec<3> > coefs(info.ndof);
+    GetCoefficients (info, &coefs[0]);
+    if (x)
+      {
+	for (int j = 0; j < n; j++)
+	  {
+            Point<3> xij, xj;
+            for (int k = 0; k < 3; k++)
+              xij(k) = xi[j*sxi+k];
+
+	    CalcElementShapes (info, xij, shapes);
+	    xj = 0;
+	    for (int i = 0; i < coefs.Size(); i++)
+	      xj += shapes(i) * coefs[i];
+
+            for (int k = 0; k < 3; k++)
+              x[j*sx+k] = xj(k);
+	  }
+      }
+
+    if (dxdxi)
+      {
+	if (info.order == 1 && type == TET)
+	  {
+	    if (n > 0)
+	      {
+
+		Point<3> xij;
+		for (int k = 0; k < 3; k++)
+		  xij(k) = xi[k];
+		
+		CalcElementDShapes (info, xij, dshapes);
+		
+		Mat<3> dxdxij;
+		dxdxij = 0.0;
+		for (int i = 0; i < coefs.Size(); i++)
+		  for (int j = 0; j < 3; j++)
+		    for (int k = 0; k < 3; k++)
+		      dxdxij(j,k) += dshapes(i,k) * coefs[i](j);
+		
+		
+		for (int ip = 0; ip < n; ip++)
+		  for (int j = 0; j < 3; j++)
+		    for (int k = 0; k < 3; k++)
+		      dxdxi[ip*sdxdxi+3*j+k] = dxdxij(j,k);
+	      }
+	  }
+	else
+	  {
+	    for (int ip = 0; ip < n; ip++)
+	      {
+		Point<3> xij;
+		for (int k = 0; k < 3; k++)
+		  xij(k) = xi[ip*sxi+k];
+		
+		CalcElementDShapes (info, xij, dshapes);
+		
+		Mat<3> dxdxij;
+		dxdxij = 0.0;
+		for (int i = 0; i < coefs.Size(); i++)
+		  for (int j = 0; j < 3; j++)
+		    for (int k = 0; k < 3; k++)
+		      dxdxij(j,k) += dshapes(i,k) * coefs[i](j);
+		
+		
+		for (int j = 0; j < 3; j++)
+		  for (int k = 0; k < 3; k++)
+		    dxdxi[ip*sdxdxi+3*j+k] = dxdxij(j,k);
+	      }
+	  }
+      }
+  }
+
+
+
+
+
+
+
+
+
+};
+
+
diff --git a/contrib/Netgen/libsrc/meshing/curvedelems.hpp b/contrib/Netgen/libsrc/meshing/curvedelems.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..945f20c88627cc8f1d5f051bfc18e7d3a7496173
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/curvedelems.hpp
@@ -0,0 +1,223 @@
+#ifndef CURVEDELEMS
+#define CURVEDELEMS
+
+/**************************************************************************/
+/* File:   curvedelems.hpp                                                */
+/* Author: Robert Gaisbauer (first version)                               */
+/*         redesign by Joachim Schoeberl                                  */
+/* Date:   27. Sep. 02, Feb 2006                                          */
+/**************************************************************************/
+
+
+
+
+class Refinement;
+
+
+class CurvedElements
+{
+  const Mesh & mesh;
+
+  Array<int> edgeorder;
+  Array<int> faceorder;
+
+  Array<int> edgecoeffsindex;
+  Array<int> facecoeffsindex;
+
+  Array< Vec<3> > edgecoeffs;
+  Array< Vec<3> > facecoeffs;
+
+  Array< double > edgeweight;  // for rational 2nd order splines
+
+  int order;
+  bool rational;
+
+  bool ishighorder;
+
+public:
+  CurvedElements (const Mesh & amesh);
+  ~CurvedElements();
+
+  // bool IsHighOrder() const { return order > 1; }
+  bool IsHighOrder() const { return ishighorder; }
+
+  // void SetHighOrder (int aorder) { order=aorder; }
+  void SetIsHighOrder (bool ho) { ishighorder = ho; }
+  
+  void BuildCurvedElements(const Refinement * ref, int aorder, bool arational = false);
+
+  int GetOrder () { return order; }
+
+
+  bool IsSegmentCurved (SegmentIndex segnr) const;
+  bool IsSurfaceElementCurved (SurfaceElementIndex sei) const;
+  bool IsElementCurved (ElementIndex ei) const;
+
+
+  void CalcSegmentTransformation (double xi, SegmentIndex segnr,
+				  Point<3> & x)
+  { CalcSegmentTransformation (xi, segnr, &x, NULL); };
+
+  void CalcSegmentTransformation (double xi, SegmentIndex segnr,
+				  Vec<3> & dxdxi)
+  { CalcSegmentTransformation (xi, segnr, NULL, &dxdxi); };
+
+  void CalcSegmentTransformation (double xi, SegmentIndex segnr,
+				  Point<3> & x, Vec<3> & dxdxi)
+  { CalcSegmentTransformation (xi, segnr, &x, &dxdxi, NULL); };
+
+  void CalcSegmentTransformation (double xi, SegmentIndex segnr,
+				  Point<3> & x, Vec<3> & dxdxi, bool & curved)
+  { CalcSegmentTransformation (xi, segnr, &x, &dxdxi, &curved); };
+
+
+
+  void CalcSurfaceTransformation (const Point<2> & xi, SurfaceElementIndex elnr,
+				  Point<3> & x)
+  { CalcSurfaceTransformation (xi, elnr, &x, NULL); };
+
+  void CalcSurfaceTransformation (const Point<2> & xi, SurfaceElementIndex elnr,
+				  Mat<3,2> & dxdxi)
+  { CalcSurfaceTransformation (xi, elnr, NULL, &dxdxi); };
+
+  void CalcSurfaceTransformation (const Point<2> & xi, SurfaceElementIndex elnr,
+				  Point<3> & x, Mat<3,2> & dxdxi)
+  { CalcSurfaceTransformation (xi, elnr, &x, &dxdxi, NULL); };
+
+  void CalcSurfaceTransformation (const Point<2> & xi, SurfaceElementIndex elnr,
+				  Point<3> & x, Mat<3,2> & dxdxi, bool & curved)
+  { CalcSurfaceTransformation (xi, elnr, &x, &dxdxi, &curved); };
+
+
+
+
+
+  void CalcElementTransformation (const Point<3> & xi, ElementIndex elnr,
+				  Point<3> & x)
+  { CalcElementTransformation (xi, elnr, &x, NULL); };
+
+  void CalcElementTransformation (const Point<3> & xi, ElementIndex elnr,
+				  Mat<3,3> & dxdxi)
+  { CalcElementTransformation (xi, elnr, NULL, &dxdxi); };
+
+  void CalcElementTransformation (const Point<3> & xi, ElementIndex elnr,
+				  Point<3> & x, Mat<3,3> & dxdxi)
+  { CalcElementTransformation (xi, elnr, &x, &dxdxi /* , NULL */ ); };
+
+  void CalcElementTransformation (const Point<3> & xi, ElementIndex elnr,
+				  Point<3> & x, Mat<3,3> & dxdxi,
+                                  void * buffer, bool valid)
+  { CalcElementTransformation (xi, elnr, &x, &dxdxi, /* NULL, */ buffer, valid ); };
+
+  // void CalcElementTransformation (const Point<3> & xi, ElementIndex elnr,
+  // 				  Point<3> & x, Mat<3,3> & dxdxi) // , bool & curved)
+  //   { CalcElementTransformation (xi, elnr, &x, &dxdxi /* , &curved * ); }
+
+
+
+  void CalcMultiPointSegmentTransformation (Array<double> * xi, SegmentIndex segnr,
+					    Array<Point<3> > * x,
+					    Array<Vec<3> > * dxdxi);
+
+  template <int DIM_SPACE>
+  void CalcMultiPointSegmentTransformation (SegmentIndex elnr, int n,
+                                            const double * xi, size_t sxi,
+                                            double * x, size_t sx,
+                                            double * dxdxi, size_t sdxdxi);
+
+
+  void CalcMultiPointSurfaceTransformation (Array< Point<2> > * xi, SurfaceElementIndex elnr,
+					    Array< Point<3> > * x,
+					    Array< Mat<3,2> > * dxdxi);
+
+  template <int DIM_SPACE>
+  void CalcMultiPointSurfaceTransformation (SurfaceElementIndex elnr, int n,
+                                            const double * xi, size_t sxi,
+                                            double * x, size_t sx,
+                                            double * dxdxi, size_t sdxdxi);
+
+  void CalcMultiPointElementTransformation (Array< Point<3> > * xi, ElementIndex elnr,
+					    Array< Point<3> > * x,
+					    Array< Mat<3,3> > * dxdxi);
+
+  void CalcMultiPointElementTransformation (ElementIndex elnr, int n,
+                                            const double * xi, size_t sxi,
+                                            double * x, size_t sx,
+                                            double * dxdxi, size_t sdxdxi);
+
+
+
+
+private:
+  
+  void CalcSegmentTransformation (double xi, SegmentIndex segnr,
+				  Point<3> * x = NULL, Vec<3> * dxdxi = NULL, bool * curved = NULL);
+
+  void CalcSurfaceTransformation (Point<2> xi, SurfaceElementIndex elnr,
+				  Point<3> * x = NULL, Mat<3,2> * dxdxi = NULL, bool * curved = NULL);
+
+  void CalcElementTransformation (Point<3> xi, ElementIndex elnr,
+				  Point<3> * x = NULL, Mat<3,3> * dxdxi = NULL, // bool * curved = NULL,
+                                  void * buffer = NULL, bool valid = 0);
+
+
+
+
+
+
+  class SegmentInfo
+  {
+  public:
+    SegmentIndex elnr;
+    int order;
+    int nv;
+    int ndof;
+    int edgenr;
+  };
+
+  void CalcElementShapes (SegmentInfo &  elnr, double xi, Vector & shapes) const;
+  void GetCoefficients (SegmentInfo & elnr, Array<Vec<3> > & coefs) const;
+  void CalcElementDShapes (SegmentInfo & elnr, double xi, Vector & dshapes) const;
+
+
+  class ElementInfo
+  {
+  public:
+    ElementIndex elnr;
+    int order;
+    int nv;
+    int ndof;
+    int nedges;
+    int nfaces;
+    int edgenrs[12];
+    int facenrs[6];
+    Mat<3> hdxdxi;
+    Vec<3> hcoefs[10]; // enough for second order tets
+  };
+
+
+  void CalcElementShapes (ElementInfo & info, const Point<3> & xi, Vector & shapes) const;
+  void GetCoefficients (ElementInfo & info, Vec<3> * coefs) const;
+  void CalcElementDShapes (ElementInfo & info, const Point<3> & xi, MatrixFixWidth<3> & dshapes) const;
+
+  
+  class SurfaceElementInfo
+  {
+  public:
+    SurfaceElementIndex elnr;
+    int order;
+    int nv;
+    int ndof;
+    ArrayMem<int,4> edgenrs;
+    int facenr;
+  };
+
+  void CalcElementShapes (SurfaceElementInfo & elinfo, const Point<2> & xi, Vector & shapes) const;
+  template <int DIM_SPACE>
+  void GetCoefficients (SurfaceElementInfo & elinfo, Array<Vec<DIM_SPACE> > & coefs) const;
+  void CalcElementDShapes (SurfaceElementInfo & elinfo, const Point<2> & xi, MatrixFixWidth<2> & dshapes) const;
+};
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/delaunay.cpp b/contrib/Netgen/libsrc/meshing/delaunay.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..90dbb6215049a515abd54ba0e6c01c877d4a490c
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/delaunay.cpp
@@ -0,0 +1,1676 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+
+namespace netgen
+{
+
+
+  static const int deltetfaces[][3] = 
+    { { 1, 2, 3 },
+      { 2, 0, 3 },
+      { 0, 1, 3 },
+      { 1, 0, 2 } };
+
+
+
+
+
+
+  class DelaunayTet
+  {
+    PointIndex pnums[4];
+    int nb[4];
+
+  public:
+    DelaunayTet () { ; }
+
+    DelaunayTet (const DelaunayTet & el)
+    {
+      for (int i = 0; i < 4; i++)
+	pnums[i] = el[i];
+    }
+
+    DelaunayTet (const Element & el)
+    {
+      for (int i = 0; i < 4; i++)
+	pnums[i] = el[i];
+    }
+    
+    PointIndex & operator[] (int i) { return pnums[i]; }
+    PointIndex operator[] (int i) const { return pnums[i]; }
+
+    int & NB1(int i) { return nb[i-1]; }
+    int NB1(int i) const { return nb[i-1]; }
+
+    int & NB(int i) { return nb[i]; }
+    int NB(int i) const { return nb[i]; }
+
+
+    int FaceNr (INDEX_3 & face) const  // which face nr is it ?
+    {
+      for (int i = 0; i < 3; i++)
+	if (pnums[i] != face.I1() && 
+	    pnums[i] != face.I2() && 
+	    pnums[i] != face.I3())
+	  return i;
+      return 3;
+    }
+    
+    void GetFace1 (int i, INDEX_3 & face) const
+    {
+      face.I(1) = pnums[deltetfaces[i-1][0]];
+      face.I(2) = pnums[deltetfaces[i-1][1]];
+      face.I(3) = pnums[deltetfaces[i-1][2]];
+    }
+
+    void GetFace (int i, INDEX_3 & face) const
+    {
+      face.I(1) = pnums[deltetfaces[i][0]];
+      face.I(2) = pnums[deltetfaces[i][1]];
+      face.I(3) = pnums[deltetfaces[i][2]];
+    }
+      
+    INDEX_3 GetFace1 (int i) const
+    {
+      return INDEX_3 (pnums[deltetfaces[i-1][0]],
+		      pnums[deltetfaces[i-1][1]],
+		      pnums[deltetfaces[i-1][2]]);
+    }
+
+    INDEX_3 GetFace (int i) const
+    {
+      return INDEX_3 (pnums[deltetfaces[i][0]],
+		      pnums[deltetfaces[i][1]],
+		      pnums[deltetfaces[i][2]]);
+    }
+     
+    void GetFace1 (int i, Element2d & face) const
+    {
+      // face.SetType(TRIG);
+      face[0] = pnums[deltetfaces[i-1][0]];
+      face[1] = pnums[deltetfaces[i-1][1]];
+      face[2] = pnums[deltetfaces[i-1][2]];
+    }
+  };
+
+
+
+
+
+
+
+
+
+  /*
+    Table to maintain neighbour elements
+  */
+  class MeshNB
+  {
+    // face nodes -> one element
+    INDEX_3_CLOSED_HASHTABLE<int> faces;
+
+    // 
+    Array<DelaunayTet> & tets;
+
+  public:
+
+    // estimated number of points
+    MeshNB (Array<DelaunayTet> & atets, int np)
+      : faces(200), tets(atets)
+    { ; }
+
+    // add element with 4 nodes
+    void Add (int elnr);
+
+    // delete element with 4 nodes
+    void Delete (int elnr)
+    {
+      DelaunayTet & el = tets.Elem(elnr);
+      for (int i = 0; i < 4; i++)
+	faces.Set (el.GetFace(i).Sort(), el.NB(i));
+    }
+
+    // get neighbour of element elnr in direction fnr 
+    int GetNB (int elnr, int fnr)
+    { 
+      return tets.Get(elnr).NB1(fnr); 
+    }
+
+    //
+    void ResetFaceHT (int size)
+    {
+      faces.SetSize (size);
+    }
+  };
+
+
+
+  void MeshNB :: Add (int elnr)
+  {
+    DelaunayTet & el = tets.Elem(elnr);
+
+    for (int i = 0; i < 4; i++)
+      {
+	INDEX_3 i3 = INDEX_3::Sort (el.GetFace(i));
+
+	int posnr;
+	
+	if (!faces.PositionCreate (i3, posnr))
+	  {
+	    // face already in use
+	    int othertet = faces.GetData (posnr);
+
+	    el.NB(i) = othertet;
+	    if (othertet)
+	      {
+		int fnr = tets.Get(othertet).FaceNr (i3);
+		tets.Elem(othertet).NB(fnr) = elnr;
+	      }
+	  }
+	else
+	  {
+	    faces.SetData (posnr, elnr);	
+	    el.NB(i) = 0;
+	  }
+      }
+  }
+
+
+
+
+
+
+  /*
+    connected lists of cosphereical elements
+  */
+  class SphereList 
+  {
+    Array<int> links;
+  public:
+    SphereList () 
+    { ; }
+
+    void AddElement (int elnr)
+    {
+      if (elnr > links.Size())
+	links.Append (1);
+      links.Elem(elnr) = elnr;
+    }
+
+    void DeleteElement (int elnr)
+    {
+      links.Elem(elnr) = 0;
+    }    
+  
+    void ConnectElement (int eli, int toi)
+    {
+      links.Elem (eli) = links.Get (toi);
+      links.Elem (toi) = eli;
+    }
+      
+    void GetList (int eli, Array<int> & linked) const;
+  };
+
+
+  void SphereList :: GetList (int eli, Array<int> & linked) const
+  {
+    linked.SetSize (0);
+    int pi = eli;
+
+    do
+      {
+	if (pi <= 0 || pi > links.Size())
+	  {
+	    cerr << "link, error " << endl;
+	    cerr << "pi = " << pi << " linked.s = " << linked.Size() << endl;
+	    exit(1);
+	  }
+	if (linked.Size() > links.Size())
+	  {
+	    cerr << "links have loop" << endl;
+	    exit(1);
+	  }
+
+	linked.Append (pi);
+	pi = links.Get(pi);
+      }
+    while (pi != eli);
+  }
+
+
+
+
+
+  void AddDelaunayPoint (PointIndex newpi, const Point3d & newp, 
+			 Array<DelaunayTet> & tempels, 
+			 Mesh & mesh,
+			 Box3dTree & tettree, 
+			 MeshNB & meshnb,
+			 Array<Point<3> > & centers, Array<double> & radi2,
+			 Array<int> & connected, Array<int> & treesearch, 
+			 Array<int> & freelist, SphereList & list,
+			 IndexSet & insphere, IndexSet & closesphere)
+  {
+    /*
+      find any sphere, such that newp is contained in
+    */
+  
+    DelaunayTet el;
+    int cfelind = -1;
+
+    const Point<3> * pp[4];
+    Point<3> pc;
+    double r2;
+    Point3d tpmin, tpmax;
+
+    tettree.GetIntersecting (newp, newp, treesearch);
+    
+    double quot,minquot(1e20);
+
+    for (int j = 0; j < treesearch.Size(); j++)
+      {
+	int jjj = treesearch[j];
+	quot = Dist2 (centers.Get(jjj), newp) / radi2.Get(jjj);
+	
+	if((cfelind == -1 || quot < 0.99*minquot) && quot < 1)
+	  {
+	    minquot = quot;
+	    el = tempels.Get(jjj);
+	    cfelind = jjj;
+	    if(minquot < 0.917632)
+	      break;
+	  }
+      }
+
+
+    /*
+      int i, j, k, l;
+      if (!felind)
+      {
+      cerr << "not in any sphere, 1" << endl;
+      // old, non tree search
+
+      double mindist = 1e10;
+      for (j = 1; j <= tempels.Size(); j++)
+      {
+      if (tempels.Get(j).PNum(1))
+      {
+      double toofar = 
+      Dist2 (centers.Get(j), newp) - radi2.Get(j);
+      if (toofar < mindist || toofar < 1e-7) 
+      {
+      mindist = toofar;
+      cout << " dist2 = " << Dist2 (centers.Get(j), newp)
+      << " radi2 = " << radi2.Get(j) << endl;
+      }
+      if (toofar < 0)
+      {
+      el = tempels.Get(j);
+      felind = j;
+      cout << "sphere found !" << endl;
+      break; 
+      }
+      }
+      }
+      cout << "point is too far from sheres: " << mindist << endl;
+      }
+    */      
+
+    if (cfelind == -1)
+      {
+	PrintWarning ("Delaunay, point not in any sphere");
+	return;
+      }
+	
+
+    /*
+      insphere:     point is in sphere -> delete element
+      closesphere:  point is close to sphere -> considered for same center
+    */
+
+    // save overestimate
+    insphere.SetMaxIndex (2 * tempels.Size() + 5 * mesh.GetNP());
+    closesphere.SetMaxIndex (2 * tempels.Size() + 5 * mesh.GetNP());
+
+    insphere.Clear();
+    closesphere.Clear();
+
+
+    insphere.Add (cfelind);
+      
+    int changed = 1;
+    int nstarti = 1, starti;
+
+
+    while (changed)
+      {
+	changed = 0;
+	starti = nstarti;
+	nstarti = insphere.GetArray().Size()+1;
+
+
+	// if point in sphere, then it is also closesphere
+	for (int j = starti; j < nstarti; j++)
+	  {
+	    int helind = insphere.GetArray().Get(j);
+	    if (!closesphere.IsIn (helind))
+	      closesphere.Add (helind);
+	  }
+
+	// add connected spheres to insphere - list
+	for (int j = starti; j < nstarti; j++)
+	  {
+	    list.GetList (insphere.GetArray().Get(j), connected);
+	    for (int k = 0; k < connected.Size(); k++)
+	      {
+		int celind = connected[k];
+
+		if (tempels.Get(celind)[0] != -1 && 
+		    !insphere.IsIn (celind))
+		  {
+		    changed = 1;
+		    insphere.Add (celind);
+		  }
+	      }
+	  }
+	
+	// check neighbour-tets
+	for (int j = starti; j < nstarti; j++)
+	  for (int k = 1; k <= 4; k++)
+	    {
+	      int helind = insphere.GetArray().Get(j);
+	      int nbind = meshnb.GetNB (helind, k);
+
+	      if (nbind && !insphere.IsIn (nbind) )
+		{
+		  //changed
+		  //int prec = testout->precision();
+		  //testout->precision(12);
+		  //(*testout) << "val1 " << Dist2 (centers.Get(nbind), newp)
+		  //	     << " val2 " << radi2.Get(nbind) * (1+1e-8)
+		  //	     << " val3 " << radi2.Get(nbind)
+		  //	     << " val1 / val3 " << Dist2 (centers.Get(nbind), newp)/radi2.Get(nbind) << endl;
+		  //testout->precision(prec);
+		  if (Dist2 (centers.Get(nbind), newp) 
+		      < radi2.Get(nbind) * (1+1e-8) )
+		    closesphere.Add (nbind);
+		    
+		  if (Dist2 (centers.Get(nbind), newp) 
+		      < radi2.Get(nbind) * (1 + 1e-12))
+		    {
+		      // point is in sphere -> remove tet
+		      insphere.Add (nbind);
+		      changed = 1;
+		    }
+		  else
+		    {
+		      /*
+		      Element2d face;
+		      tempels.Get(helind).GetFace (k, face);
+
+		      const Point3d & p1 = mesh.Point (face.PNum(1));
+		      const Point3d & p2 = mesh.Point (face[1]);
+		      const Point3d & p3 = mesh.Point (face[2]);
+		      */
+
+		      INDEX_3 i3 = tempels.Get(helind).GetFace (k-1);
+
+		      const Point3d & p1 = mesh.Point ( PointIndex (i3.I1()));
+		      const Point3d & p2 = mesh.Point ( PointIndex (i3.I2()));
+		      const Point3d & p3 = mesh.Point ( PointIndex (i3.I3()));
+
+
+		      Vec3d v1(p1, p2);
+		      Vec3d v2(p1, p3);
+		      Vec3d n = Cross (v1, v2);
+		      n /= n.Length();
+
+		      if (n * Vec3d (p1, mesh.Point (tempels.Get(helind)[k-1])) > 0)
+			n *= -1;
+			
+		      double dist = n * Vec3d (p1, newp);
+
+
+		      if (dist > -1e-10)  // 1e-10
+			{
+			  insphere.Add (nbind);
+			  changed = 1;
+			}
+
+
+		    }
+		}
+	    }
+      } // while (changed)
+
+    //      (*testout) << "newels: " << endl;
+    Array<Element> newels;
+
+    Element2d face(TRIG);
+
+    for (int j = 1; j <= insphere.GetArray().Size(); j++)
+      for (int k = 1; k <= 4; k++)
+	{
+	  //	    int elind = insphere.GetArray().Get(j);
+	  int celind = insphere.GetArray().Get(j);
+	  int nbind = meshnb.GetNB (celind, k);
+
+	  if (!nbind || !insphere.IsIn (nbind))
+	    {
+	      tempels.Get (celind).GetFace1 (k, face);
+		
+	      Element newel(TET);
+	      for (int l = 0; l < 3; l++)
+                newel[l] = face[l];
+              newel[3] = newpi;
+
+	      newels.Append (newel);
+
+              Vec<3> v1 = mesh[face[1]] - mesh[face[0]];
+              Vec<3> v2 = mesh[face[2]] - mesh[face[0]];
+	      Vec<3> n = Cross (v1, v2);
+
+	      n.Normalize();
+	      if (n * Vec3d(mesh.Point (face[0]), 
+			    mesh.Point (tempels.Get(insphere.GetArray().Get(j))[k-1]))
+		  > 0)
+		n *= -1;
+
+              double hval = n *  ( newp - mesh[face[0]]);
+		
+	      if (hval > -1e-12)
+		{
+		  cerr << "vec to outer" << endl;
+		  (*testout) << "vec to outer, hval = " << hval << endl;
+		  (*testout) << "v1 x v2 = " << Cross (v1, v2) << endl;
+		  (*testout) << "facep: "
+			     << mesh.Point (face[0]) << " "
+			     << mesh.Point (face[1]) << " "
+			     << mesh.Point (face[2]) << endl;
+		}
+	    }
+	}
+
+    meshnb.ResetFaceHT (10*insphere.GetArray().Size()+1);
+
+    for (int j = 1; j <= insphere.GetArray().Size(); j++)
+      {
+	//	  int elind = 
+	int celind = insphere.GetArray().Get(j);
+
+	meshnb.Delete (celind); 
+	list.DeleteElement (celind);
+	  
+	for (int k = 0; k < 4; k++)
+	  tempels.Elem(celind)[k] = -1;
+
+	((ADTree6&)tettree.Tree()).DeleteElement (celind);
+	freelist.Append (celind);
+      }
+
+
+    int hasclose = 0;
+    for (int j = 1; j <= closesphere.GetArray().Size(); j++)
+      {
+	int ind = closesphere.GetArray().Get(j);
+	if (!insphere.IsIn(ind) &&
+	    fabs (Dist2 (centers.Get (ind), newp) - radi2.Get(ind)) < 1e-8 )
+	  hasclose = 1;
+      }
+
+    for (int j = 1; j <= newels.Size(); j++)
+      {
+	int nelind;
+
+	if (!freelist.Size())
+	  {
+	    tempels.Append (newels.Get(j));
+	    nelind = tempels.Size();
+	  }
+	else
+	  {
+	    nelind = freelist.Last();
+	    freelist.DeleteLast();
+
+	    tempels.Elem(nelind) = newels.Get(j);
+	  }
+
+	meshnb.Add (nelind);
+	list.AddElement (nelind);
+
+	for (int k = 0; k < 4; k++)
+	  pp[k] = &mesh.Point (newels.Get(j)[k]);
+
+	if (CalcSphereCenter (&pp[0], pc) )
+	  {
+	    PrintSysError ("Delaunay: New tet is flat");
+
+	    (*testout) << "new tet is flat" << endl;
+	    for (int k = 1; k <= 4; k++)
+	      (*testout) << newels.Get(j).PNum(k) << " ";
+	    (*testout) << endl;
+	    for (int k = 1; k <= 4; k++)
+	      (*testout) << *pp[k-1] << " ";
+	    (*testout) << endl;
+	  }
+
+	r2 = Dist2 (*pp[0], pc);
+	if (hasclose)
+	  for (int k = 1; k <= closesphere.GetArray().Size(); k++)
+	    {
+	      int csameind = closesphere.GetArray().Get(k); 
+	      if (!insphere.IsIn(csameind) &&
+		  fabs (r2 - radi2.Get(csameind)) < 1e-10 && 
+		  Dist (pc, centers.Get(csameind)) < 1e-10)
+		{
+		  pc = centers.Get(csameind);
+		  r2 = radi2.Get(csameind);
+		  list.ConnectElement (nelind, csameind);
+		  break;
+		}
+	    }
+      
+	if (centers.Size() < nelind)
+	  {
+	    centers.Append (pc);
+	    radi2.Append (r2);
+	  }
+	else
+	  {
+	    centers.Elem(nelind) = pc;
+	    radi2.Elem(nelind) = r2;
+	  }
+
+	closesphere.Add (nelind);
+	  
+	tpmax = tpmin = *pp[0];
+	for (int k = 1; k <= 3; k++)
+	  {
+	    tpmin.SetToMin (*pp[k]);
+	    tpmax.SetToMax (*pp[k]);
+	  }
+	tpmax = tpmax + 0.01 * (tpmax - tpmin);
+	tettree.Insert (tpmin, tpmax, nelind);
+      }
+  }
+
+
+
+
+
+
+  void Delaunay1 (Mesh & mesh, const MeshingParameters & mp, AdFront3 * adfront,
+		  Array<DelaunayTet> & tempels,
+		  int oldnp, DelaunayTet & startel, Point3d & pmin, Point3d & pmax)
+  {
+    int i, j, k;
+    const Point<3> * pp[4];
+
+    Array<Point<3> > centers;
+    Array<double> radi2;
+  
+    Point3d tpmin, tpmax;
+
+
+    // new: local box
+    mesh.GetBox (pmax, pmin);   // lower bound for pmax, upper for pmin
+    for (i = 1; i <= adfront->GetNF(); i++)
+      {
+	const MiniElement2d & face = adfront->GetFace(i);
+	for (j = 0; j < face.GetNP(); j++)
+	  {
+	    pmin.SetToMin  (mesh.Point (face[j]));
+	    pmax.SetToMax  (mesh.Point (face[j]));
+	  }
+      }
+  
+    for (i = 0; i < mesh.LockedPoints().Size(); i++)
+      {
+	pmin.SetToMin (mesh.Point (mesh.LockedPoints()[i]));
+	pmax.SetToMax (mesh.Point (mesh.LockedPoints()[i]));
+      }
+  
+
+
+    Vec3d vdiag(pmin, pmax);
+    // double r1 = vdiag.Length();
+    double r1 = sqrt (3.0) * max3(vdiag.X(), vdiag.Y(), vdiag.Z());
+    vdiag = Vec3d (r1, r1, r1);
+    //double r2;
+
+    Point3d pmin2 = pmin - 8 * vdiag;
+    Point3d pmax2 = pmax + 8 * vdiag;
+
+    Point3d cp1(pmin2), cp2(pmax2), cp3(pmax2), cp4(pmax2);
+    cp2.X() = pmin2.X();
+    cp3.Y() = pmin2.Y();
+    cp4.Z() = pmin2.Z();
+
+
+
+
+    int np = mesh.GetNP();
+
+    startel[0] = mesh.AddPoint (cp1);
+    startel[1] = mesh.AddPoint (cp2);
+    startel[2] = mesh.AddPoint (cp3);
+    startel[3] = mesh.AddPoint (cp4);
+
+    // flag points to use for Delaunay:
+    BitArrayChar<PointIndex::BASE> usep(np);
+    usep.Clear();
+    for (i = 1; i <= adfront->GetNF(); i++)
+      {
+	const MiniElement2d & face = adfront->GetFace(i);
+	for (j = 0; j < face.GetNP(); j++)
+	  usep.Set (face[j]);
+      }
+
+    for (i = oldnp + PointIndex::BASE; 
+	 i < np + PointIndex::BASE; i++)
+      usep.Set (i);
+
+    for (i = 0; i < mesh.LockedPoints().Size(); i++)
+      usep.Set (mesh.LockedPoints()[i]);
+  
+
+    Array<int> freelist;
+
+
+    int cntp = 0;
+
+    MeshNB meshnb (tempels, mesh.GetNP() + 5);
+    SphereList list;
+
+    pmin2 = pmin2 + 0.1 * (pmin2 - pmax2);
+    pmax2 = pmax2 + 0.1 * (pmax2 - pmin2);
+
+    Box3dTree tettree(pmin2, pmax2);
+
+
+    tempels.Append (startel);
+    meshnb.Add (1);
+    list.AddElement (1);
+    Array<int> connected, treesearch;
+
+
+    tpmin = tpmax = mesh.Point(startel[0]);
+    for (k = 1; k < 4; k++)
+      {
+	tpmin.SetToMin (mesh.Point (startel[k]));
+	tpmax.SetToMax (mesh.Point (startel[k]));
+      }
+    tpmax = tpmax + 0.01 * (tpmax - tpmin);
+    tettree.Insert (tpmin, tpmax, 1);
+
+
+    Point<3> pc;
+	  
+    for (k = 0; k < 4; k++)
+      {
+	pp[k] = &mesh.Point (startel[k]);
+      }
+  
+    CalcSphereCenter (&pp[0], pc);
+    
+    centers.Append (pc);
+    radi2.Append (Dist2 (*pp[0], pc));
+
+
+    IndexSet insphere(mesh.GetNP());
+    IndexSet closesphere(mesh.GetNP());
+
+
+
+    // "random" reordering of points  (speeds a factor 3 - 5 !!!)
+
+    Array<int> mixed(np);
+    int prims[] = { 11, 13, 17, 19, 23, 29, 31, 37 };
+    int prim;
+  
+    i = 0;
+    while (np % prims[i] == 0) i++;
+    prim = prims[i];
+
+    for (i = 1; i <= np; i++)
+      mixed.Elem(i) = (prim * i) % np + PointIndex::BASE;
+
+    for (i = 1; i <= np; i++)
+      {
+	if (i % 1000 == 0)
+	  {
+	    if (i % 10000 == 0)
+	      PrintDot ('+');
+	    else
+	      PrintDot ('.');
+	  }
+
+	multithread.percent = 100.0 * i / np;
+	if (multithread.terminate)
+	  break;
+
+	PointIndex newpi = mixed.Get(i);
+
+	if (!usep.Test(newpi)) 
+	  continue;
+
+	cntp++;
+
+	const Point3d & newp = mesh.Point(newpi);
+      
+	AddDelaunayPoint (newpi, newp, tempels, mesh,
+			  tettree, meshnb, centers, radi2, 
+			  connected, treesearch, freelist, list, insphere, closesphere);
+      }
+
+    for (i = tempels.Size(); i >= 1; i--)
+      if (tempels.Get(i)[0] <= 0)
+	tempels.DeleteElement (i);
+
+    PrintDot ('\n');
+
+    PrintMessage (3, "Points: ", cntp);
+    PrintMessage (3, "Elements: ", tempels.Size());
+    //   (*mycout) << cntp << " / " << tempels.Size() << " points/elements" << endl;
+
+    /*
+      cout << "tempels: ";
+      tempels.PrintMemInfo(cout);
+      cout << "Searchtree: ";
+      tettree.Tree().PrintMemInfo(cout);
+      cout << "MeshNB: ";
+      meshnb.PrintMemInfo(cout);
+    */
+  }
+
+
+
+
+
+
+  void Meshing3 :: Delaunay (Mesh & mesh, int domainnr, const MeshingParameters & mp)
+  {
+    int np, ne;
+
+    PrintMessage (1, "Delaunay meshing");
+    PrintMessage (3, "number of points: ", mesh.GetNP());
+    PushStatus ("Delaunay meshing");
+
+
+    Array<DelaunayTet> tempels;
+    Point3d pmin, pmax;
+
+    DelaunayTet startel;
+
+    int oldnp = mesh.GetNP();
+    if (mp.blockfill)
+      {
+	BlockFillLocalH (mesh, mp);
+	PrintMessage (3, "number of points: ", mesh.GetNP());
+      }
+
+    np = mesh.GetNP();
+
+    Delaunay1 (mesh, mp, adfront, tempels, oldnp, startel, pmin, pmax);
+
+    {
+      // improve delaunay - mesh by swapping !!!!
+
+      Mesh tempmesh;
+      for (PointIndex pi = PointIndex::BASE; pi < mesh.GetNP()+PointIndex::BASE; pi++)
+	tempmesh.AddPoint (mesh[pi]);
+      
+      for (int i = 1; i <= tempels.Size(); i++)
+	{   
+	  Element el(4);
+	  for (int j = 0; j < 4; j++)
+	    el[j] = tempels.Elem(i)[j];
+
+	  el.SetIndex (1);
+
+	  const Point3d & lp1 = mesh.Point (el[0]);
+	  const Point3d & lp2 = mesh.Point (el[1]);
+	  const Point3d & lp3 = mesh.Point (el[2]);
+	  const Point3d & lp4 = mesh.Point (el[3]);
+	  Vec3d v1(lp1, lp2);
+	  Vec3d v2(lp1, lp3);
+	  Vec3d v3(lp1, lp4);
+
+	  Vec3d n = Cross (v1, v2);
+	  double vol = n * v3;
+	  if (vol > 0) swap (el[2], el[3]);
+
+	  tempmesh.AddVolumeElement (el);
+	}
+
+
+      MeshQuality3d (tempmesh);
+
+      tempmesh.AddFaceDescriptor (FaceDescriptor (1, 1, 0, 0));
+      tempmesh.AddFaceDescriptor (FaceDescriptor (2, 1, 0, 0));
+
+
+    
+      for (int i = 1; i <= mesh.GetNOpenElements(); i++)
+	{
+	  Element2d sel = mesh.OpenElement(i);
+	  sel.SetIndex(1);
+	  tempmesh.AddSurfaceElement (sel);
+	  swap (sel[1], sel[2]);
+	  tempmesh.AddSurfaceElement (sel);
+	}
+
+
+      for (int i = 1; i <= 4; i++)
+	{
+	  Element2d self(TRIG);
+	  self.SetIndex (1);
+	  startel.GetFace1 (i, self);
+	  tempmesh.AddSurfaceElement (self);
+	}
+
+      
+      //  for (i = mesh.GetNP() - 3; i <= mesh.GetNP(); i++)
+      //    tempmesh.AddLockedPoint (i);
+      for (PointIndex pi = PointIndex::BASE; 
+	   pi < tempmesh.GetNP() + PointIndex::BASE; pi++)
+	tempmesh.AddLockedPoint (pi);
+      
+      //    tempmesh.PrintMemInfo(cout);
+      // tempmesh.Save ("tempmesh.vol");
+
+      for (int i = 1; i <= 2; i++)
+	{ 
+	  tempmesh.FindOpenElements ();
+
+	  PrintMessage (5, "Num open: ", tempmesh.GetNOpenElements());
+	  tempmesh.CalcSurfacesOfNode ();
+
+	  tempmesh.FreeOpenElementsEnvironment (1);
+
+	  MeshOptimize3d meshopt(mp);
+	  // tempmesh.CalcSurfacesOfNode();
+	  meshopt.SwapImprove(tempmesh, OPT_CONFORM);
+	}
+    
+      MeshQuality3d (tempmesh);
+    
+      tempels.SetSize(0);
+      for (int i = 1; i <= tempmesh.GetNE(); i++)
+	tempels.Append (tempmesh.VolumeElement(i));
+    }
+
+
+
+    // remove degenerated
+
+    BitArray badnode(mesh.GetNP());
+    badnode.Clear();
+    int ndeg = 0;
+    for (int i = 1; i <= tempels.Size(); i++)
+      {
+	Element el(4);
+	for (int j = 0; j < 4; j++)
+	  el[j] = tempels.Elem(i)[j];
+	//      Element & el = tempels.Elem(i);
+	const Point3d & lp1 = mesh.Point (el[0]);
+	const Point3d & lp2 = mesh.Point (el[1]);
+	const Point3d & lp3 = mesh.Point (el[2]);
+	const Point3d & lp4 = mesh.Point (el[3]);
+	Vec3d v1(lp1, lp2);
+	Vec3d v2(lp1, lp3);
+	Vec3d v3(lp1, lp4);
+	Vec3d n = Cross (v1, v2);
+	double vol = n * v3;
+
+	double h = v1.Length() + v2.Length() + v3.Length();
+	if (fabs (vol) < 1e-8 * (h * h * h) &&
+	    (el[0] <= np && el[1] <= np &&
+	     el[2] <= np && el[3] <= np) )   // old: 1e-12
+	  {
+	    badnode.Set(el[0]);
+	    badnode.Set(el[1]);
+	    badnode.Set(el[2]);
+	    badnode.Set(el[3]);
+	    ndeg++;
+	    (*testout) << "vol = " << vol << " h = " << h << endl;
+	  }
+
+	if (vol > 0)
+	  Swap (el[2], el[3]);
+      }
+
+    ne = tempels.Size();
+    for (int i = ne; i >= 1; i--)
+      {
+	const DelaunayTet & el = tempels.Get(i);
+	if (badnode.Test(el[0]) ||
+	    badnode.Test(el[1]) ||
+	    badnode.Test(el[2]) ||
+	    badnode.Test(el[3]) )
+	  tempels.DeleteElement(i);
+      }
+
+  
+    PrintMessage (3, ndeg, " degenerated elements removed");
+
+    // find surface triangles which are no face of any tet
+
+    INDEX_3_HASHTABLE<int> openeltab(mesh.GetNOpenElements()+3);
+    Array<int> openels;
+    for (int i = 1; i <= mesh.GetNOpenElements(); i++)
+      {
+	const Element2d & tri = mesh.OpenElement(i);
+	INDEX_3 i3(tri[0], tri[1], tri[2]);
+	i3.Sort();
+	openeltab.Set (i3, i);
+      }
+
+    for (int i = 1; i <= tempels.Size(); i++)
+      {
+	for (int j = 0; j < 4; j++)
+	  {
+	    INDEX_3 i3 = tempels.Get(i).GetFace (j);
+	    i3.Sort();
+	    if (openeltab.Used(i3))
+	      openeltab.Set (i3, 0);
+	  }
+      }
+  
+    // and store them in openels
+    for (int i = 1; i <= openeltab.GetNBags(); i++)
+      for (int j = 1; j <= openeltab.GetBagSize(i); j++)
+	{
+	  INDEX_3 i3;
+	  int fnr;
+	  openeltab.GetData (i, j, i3, fnr);
+	  if (fnr)
+	    openels.Append (fnr);
+	}
+
+
+
+
+
+    // find open triangle with close edge (from halfening of surface squares)
+  
+    INDEX_2_HASHTABLE<INDEX_2> twotrias(mesh.GetNOpenElements()+5); 
+    //  for (i = 1; i <= mesh.GetNOpenElements(); i++)
+    for (int ii = 1; ii <= openels.Size(); ii++)
+      {
+	int i = openels.Get(ii);
+	const Element2d & el = mesh.OpenElement(i);
+	for (int j = 1; j <= 3; j++)
+	  {
+	    INDEX_2 hi2 (el.PNumMod (j), el.PNumMod(j+1));
+	    hi2.Sort();
+	    if (twotrias.Used(hi2))
+	      {
+		INDEX_2 hi3;
+		hi3 = twotrias.Get (hi2);
+		hi3.I2() = el.PNumMod (j+2);
+		twotrias.Set (hi2, hi3);
+	      }
+	    else
+	      {
+		INDEX_2 hi3(el.PNumMod (j+2), 0);
+		twotrias.Set (hi2, hi3);
+	      }
+	  }
+      }
+
+    INDEX_2_HASHTABLE<int> tetedges(tempels.Size() + 5);
+    for (int i = 1; i <= tempels.Size(); i++)
+      {
+	const DelaunayTet & el = tempels.Get(i);
+	INDEX_2 i2;
+	for (int j = 1; j <= 6; j++)
+	  {
+	    switch (j)
+	      {
+	      case 1: i2.I1()=el[0]; i2.I2()=el[1]; break;
+	      case 2: i2.I1()=el[0]; i2.I2()=el[2]; break;
+	      case 3: i2.I1()=el[0]; i2.I2()=el[3]; break;
+	      case 4: i2.I1()=el[1]; i2.I2()=el[2]; break;
+	      case 5: i2.I1()=el[1]; i2.I2()=el[3]; break;
+	      case 6: i2.I1()=el[2]; i2.I2()=el[3]; break;
+		  default: i2.I1()=i2.I2()=0; break;
+	      }
+	    i2.Sort();
+	    tetedges.Set (i2, 1);
+	  }
+      }
+    //  cout << "tetedges:";
+    //  tetedges.PrintMemInfo (cout);
+
+
+    for (INDEX_2_HASHTABLE<INDEX_2>::Iterator it = twotrias.Begin();
+	 it != twotrias.End(); it++)
+      {
+	INDEX_2 hi2, hi3;
+	twotrias.GetData (it, hi2, hi3);
+	hi3.Sort();
+	if (tetedges.Used (hi3))
+	  {
+	    const Point3d & p1 = mesh.Point ( PointIndex (hi2.I1()));
+	    const Point3d & p2 = mesh.Point ( PointIndex (hi2.I2()));
+	    const Point3d & p3 = mesh.Point ( PointIndex (hi3.I1()));
+	    const Point3d & p4 = mesh.Point ( PointIndex (hi3.I2()));
+	    Vec3d v1(p1, p2);
+	    Vec3d v2(p1, p3);
+	    Vec3d v3(p1, p4);
+	    Vec3d n = Cross (v1, v2);
+	    double vol = n * v3;
+	    
+	    double h = v1.Length() + v2.Length() + v3.Length();
+	    if (fabs (vol) < 1e-4 * (h * h * h))   // old: 1e-12
+	      {
+		badnode.Set(hi3.I1());	
+		badnode.Set(hi3.I2());	
+	      }
+	  }
+      }
+
+    /*
+    for (i = 1; i <= twotrias.GetNBags(); i++)
+      for (j = 1; j <= twotrias.GetBagSize (i); j++)
+	{
+	  INDEX_2 hi2, hi3;
+	  twotrias.GetData (i, j, hi2, hi3);
+	  hi3.Sort();
+	  if (tetedges.Used (hi3))
+	    {
+	      const Point3d & p1 = mesh.Point (hi2.I1());
+	      const Point3d & p2 = mesh.Point (hi2.I2());
+	      const Point3d & p3 = mesh.Point (hi3.I1());
+	      const Point3d & p4 = mesh.Point (hi3.I2());
+	      Vec3d v1(p1, p2);
+	      Vec3d v2(p1, p3);
+	      Vec3d v3(p1, p4);
+	      Vec3d n = Cross (v1, v2);
+	      double vol = n * v3;
+	    
+	      double h = v1.Length() + v2.Length() + v3.Length();
+	      if (fabs (vol) < 1e-4 * (h * h * h))   // old: 1e-12
+		{
+		  badnode.Set(hi3.I1());	
+		  badnode.Set(hi3.I2());	
+		}
+	    }
+	}
+    */
+
+    ne = tempels.Size();
+    for (int i = ne; i >= 1; i--)
+      {
+	const DelaunayTet & el = tempels.Get(i);
+	if (badnode.Test(el[0]) ||
+	    badnode.Test(el[1]) ||
+	    badnode.Test(el[2]) ||
+	    badnode.Test(el[3]) )
+	  tempels.DeleteElement(i);
+      }
+
+
+
+
+    // find intersecting:
+    PrintMessage (3, "Remove intersecting");
+    if (openels.Size())
+      {
+	Box3dTree setree(pmin, pmax);
+
+	/*      
+		cout << "open elements in search tree: " << openels.Size() << endl;
+		cout << "pmin, pmax = " << pmin << " - " << pmax << endl;
+	*/
+
+	for (int i = 1; i <= openels.Size(); i++)
+	  {
+	    int fnr;
+	    fnr = openels.Get(i);
+	    if (fnr)
+	      {
+		const Element2d & tri = mesh.OpenElement(fnr);
+	      
+		Point3d ltpmin (mesh.Point(tri[0]));
+		Point3d ltpmax (ltpmin);
+	      
+		for (int k = 2; k <= 3; k++)
+		  {
+		    ltpmin.SetToMin (mesh.Point (tri.PNum(k)));
+		    ltpmax.SetToMax (mesh.Point (tri.PNum(k)));
+		  }
+		setree.Insert (ltpmin, ltpmax, fnr);
+	      }
+	  }
+      
+	Array<int> neartrias;
+	for (int i = 1; i <= tempels.Size(); i++)
+	  {
+	    const Point<3> *pp[4];
+	    int tetpi[4];
+	    DelaunayTet & el = tempels.Elem(i);
+	  
+	    int intersect = 0;
+	  
+	    for (int j = 0; j < 4; j++)
+	      {
+		pp[j] = &mesh.Point(el[j]);
+		tetpi[j] = el[j];
+	      }
+	  
+	    Point3d tetpmin(*pp[0]);
+	    Point3d tetpmax(tetpmin);
+	    for (int j = 1; j < 4; j++)
+	      {
+		tetpmin.SetToMin (*pp[j]);
+		tetpmax.SetToMax (*pp[j]);
+	      }
+	    tetpmin = tetpmin + 0.01 * (tetpmin - tetpmax);
+	    tetpmax = tetpmax + 0.01 * (tetpmax - tetpmin);
+	  
+	    setree.GetIntersecting (tetpmin, tetpmax, neartrias);
+	  
+	  
+	    //      for (j = 1; j <= mesh.GetNSE(); j++)
+	    //	{
+	    for (int jj = 1; jj <= neartrias.Size(); jj++)
+	      {
+		int j = neartrias.Get(jj);
+	      
+		const Element2d & tri = mesh.OpenElement(j);
+		const Point<3> *tripp[3];
+		int tripi[3];
+	      
+		for (int k = 1; k <= 3; k++)
+		  {
+		    tripp[k-1] = &mesh.Point (tri.PNum(k));
+		    tripi[k-1] = tri.PNum(k);
+		  }
+	      
+		if (IntersectTetTriangle (&pp[0], &tripp[0], tetpi, tripi))
+		  {
+		    /*
+		    int il1, il2;
+		    (*testout) << "intersect !" << endl;
+		    (*testout) << "triind: ";
+		    for (il1 = 0; il1 < 3; il1++)
+		      (*testout) << " " << tripi[il1];
+		    (*testout) << endl;
+		    (*testout) << "tetind: ";
+		    for (il2 = 0; il2 < 4; il2++)
+		      (*testout) << " " << tetpi[il2];
+		    (*testout) << endl;
+		  
+		    (*testout) << "trip: ";
+		    for (il1 = 0; il1 < 3; il1++)
+		      (*testout) << " " << *tripp[il1];
+		    (*testout) << endl;
+		    (*testout) << "tetp: ";
+		    for (il2 = 0; il2 < 4; il2++)
+		      (*testout) << " " << *pp[il2];
+		    (*testout) << endl;
+		    */
+		  
+		  
+		    intersect = 1;
+		    break;
+		  }
+	      }
+	  
+	  
+	    if (intersect)
+	      {
+		tempels.DeleteElement(i);
+		i--;
+	      }
+	  }
+      }
+  
+
+
+
+    PrintMessage (3, "Remove outer");
+
+    // find connected tets (with no face between, and no hole due
+    // to removed intersecting tets.
+    //  INDEX_3_HASHTABLE<INDEX_2> innerfaces(np);
+
+  
+    INDEX_3_HASHTABLE<int> boundaryfaces(mesh.GetNOpenElements()/3+1);
+    for (int i = 1; i <= mesh.GetNOpenElements(); i++)
+      {
+	const Element2d & tri = mesh.OpenElement(i);
+	INDEX_3 i3 (tri[0], tri[1], tri[2]);
+	i3.Sort();
+	boundaryfaces.PrepareSet (i3);
+      }
+    boundaryfaces.AllocateElements();
+    for (int i = 1; i <= mesh.GetNOpenElements(); i++)
+      {
+	const Element2d & tri = mesh.OpenElement(i);
+	INDEX_3 i3 (tri[0], tri[1], tri[2]);
+	i3.Sort();
+	boundaryfaces.Set (i3, 1);
+      }
+
+    for (int i = 0; i < tempels.Size(); i++)
+      for (int j = 0; j < 4; j++)
+	tempels[i].NB(j) = 0;
+  
+    TABLE<int,PointIndex::BASE> elsonpoint(mesh.GetNP());
+    for (int i = 0; i < tempels.Size(); i++)
+      {
+	const DelaunayTet & el = tempels[i];
+	INDEX_4 i4(el[0], el[1], el[2], el[3]);
+	i4.Sort();
+	elsonpoint.IncSizePrepare (i4.I1());
+	elsonpoint.IncSizePrepare (i4.I2());
+      }
+
+    elsonpoint.AllocateElementsOneBlock();
+
+    for (int i = 0; i < tempels.Size(); i++)
+      {
+	const DelaunayTet & el = tempels[i];
+	INDEX_4 i4(el[0], el[1], el[2], el[3]);
+	i4.Sort();
+	elsonpoint.Add (i4.I1(), i+1);
+	elsonpoint.Add (i4.I2(), i+1);
+      }
+
+    //  cout << "elsonpoint mem: ";
+    //  elsonpoint.PrintMemInfo(cout);
+
+    INDEX_3_CLOSED_HASHTABLE<INDEX_2> faceht(100);   
+  
+    Element2d hel(TRIG);
+    for (PointIndex pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+      {
+	faceht.SetSize (4 * elsonpoint[pi].Size());
+	for (int ii = 0; ii < elsonpoint[pi].Size(); ii++)
+	  {
+	    int i = elsonpoint[pi][ii];
+	    const DelaunayTet & el = tempels.Get(i);
+
+	    for (int j = 1; j <= 4; j++)
+	      {
+		el.GetFace1 (j, hel);
+		hel.Invert();
+		hel.NormalizeNumbering();
+	      
+		if (hel[0] == pi)
+		  {
+		    INDEX_3 i3(hel[0], hel[1], hel[2]);
+		  
+		    if (!boundaryfaces.Used (i3))
+		      {
+			if (faceht.Used (i3))
+			  {
+			    INDEX_2 i2 = faceht.Get(i3);
+			  
+			    tempels.Elem(i).NB1(j) = i2.I1();
+			    tempels.Elem(i2.I1()).NB1(i2.I2()) = i;
+			  }
+			else
+			  {
+			    hel.Invert();
+			    hel.NormalizeNumbering();
+			    INDEX_3 i3i(hel[0], hel[1], hel[2]);
+			    INDEX_2 i2(i, j);
+			    faceht.Set (i3i, i2);
+			  }
+		      }
+		  }
+	      }
+	  }
+      }
+  
+    /*
+      for (i = 1; i <= tempels.Size(); i++)
+      {
+      const DelaunayTet & el = tempels.Get(i);
+      for (j = 1; j <= 4; j++)
+      {
+      INDEX_3 i3;
+      Element2d face;
+      el.GetFace1 (j, face);
+      for (int kk = 1; kk <= 3; kk++)
+      i3.I(kk) = face.PNum(kk);
+
+      i3.Sort();
+      if (!boundaryfaces.Used (i3))
+      {
+      if (innerfaces.Used(i3))
+      {
+      INDEX_2 i2;
+      i2 = innerfaces.Get(i3);
+      i2.I2() = i;
+      innerfaces.Set (i3, i2);
+      }
+      else
+      {
+      INDEX_2 i2;
+      i2.I1() = i;
+      i2.I2() = 0;
+      innerfaces.Set (i3, i2);
+      }
+      }
+      }
+      }
+    */
+
+    /*
+      (*testout) << "nb elements:" << endl;
+      for (i = 1; i <= tempels.Size(); i++)
+      {
+      (*testout) << i << " ";
+      for (j = 1; j <= 4; j++)
+      (*testout) << tempels.Get(i).NB1(j) << " ";
+      (*testout) << endl;
+      }
+  
+      (*testout) << "pairs:" << endl;
+      for (i = 1; i <= innerfaces.GetNBags(); i++)
+      for (j = 1; j <= innerfaces.GetBagSize(i); j++)
+      {
+      INDEX_3 i3;
+      INDEX_2 i2;
+      innerfaces.GetData (i, j, i3, i2);
+      (*testout) << i2 << endl;
+      }
+    */
+
+
+
+
+
+
+
+    /*
+      cout << "innerfaces: ";
+      innerfaces.PrintMemInfo (cout);
+    */
+
+    //  cout << "boundaryfaces: ";
+    //  boundaryfaces.PrintMemInfo (cout);
+
+
+    PrintMessage (5, "tables filled");
+ 
+
+    ne = tempels.Size();
+    BitArray inner(ne), outer(ne);
+    inner.Clear();
+    outer.Clear();
+    Array<int> elstack;
+
+    /*
+      int starti = 0;
+      for (i = 1; i <= ne; i++)
+      {
+      const Element & el = tempels.Get(i);
+      for (j = 1; j <= 4; j++)
+      for (k = 1; k <= 4; k++)
+      if (el.PNum(j) == startel.PNum(k))
+      {
+      outer.Set(i);
+      starti = i;
+      }
+      }
+    */
+
+    while (1)
+      {
+	int inside;
+	bool done = 1;
+
+	int i;
+	for (i = 1; i <= ne; i++)
+	  if (!inner.Test(i) && !outer.Test(i))
+	    {
+	      done = 0;
+	      break;
+	    }
+
+	if (done) break;
+      
+	const DelaunayTet & el = tempels.Get(i);
+	const Point3d & p1 = mesh.Point (el[0]);
+	const Point3d & p2 = mesh.Point (el[1]);
+	const Point3d & p3 = mesh.Point (el[2]);
+	const Point3d & p4 = mesh.Point (el[3]);
+      
+	Point3d ci = Center (p1, p2, p3, p4);
+
+	inside = adfront->Inside (ci);
+
+	/*
+	  cout << "startel: " << i << endl;
+	  cout << "inside = " << inside << endl;
+	  cout << "ins2 = " << adfront->Inside (Center (ci, p1)) << endl;
+	  cout << "ins3 = " << adfront->Inside (Center (ci, p2)) << endl;
+	*/
+      
+	elstack.SetSize(0);
+	elstack.Append (i);
+  
+	while (elstack.Size())
+	  {
+	    int ei = elstack.Last();
+	    elstack.DeleteLast();
+	  
+	    if (!inner.Test(ei) && !outer.Test(ei))
+	      {
+		if (inside)
+		  inner.Set(ei);
+		else
+		  outer.Set(ei);
+
+
+		for (int j = 1; j <= 4; j++)
+		  {
+		    INDEX_3 i3 = tempels.Get(ei).GetFace1(j);
+		    /*
+		    Element2d face;
+		    tempels.Get(ei).GetFace(j, face);
+		    for (int kk = 1; kk <= 3; kk++)
+		      i3.I(kk) = face.PNum(kk);
+		    */
+		    i3.Sort();
+		  
+
+		    if (tempels.Get(ei).NB1(j))
+		      elstack.Append (tempels.Get(ei).NB1(j));
+
+		    /*
+		      if (innerfaces.Used(i3))
+		      {
+		      INDEX_2 i2 = innerfaces.Get(i3);
+		      int other = i2.I1() + i2.I2() - ei;
+
+		      if (other != tempels.Get(ei).NB1(j))
+		      cerr << "different1 !!" << endl;
+
+		      if (other)
+		      {
+		      elstack.Append (other);
+		      }
+		      }
+		      else
+		      if (tempels.Get(ei).NB1(j))
+		      cerr << "different2 !!" << endl;
+		    */
+
+		  }
+	      }
+	  }
+      }
+
+
+
+    // check outer elements
+    if (debugparam.slowchecks)
+      {
+	for (int i = 1; i <= ne; i++)
+	  {
+	    const DelaunayTet & el = tempels.Get(i);
+	    const Point3d & p1 = mesh.Point (el[0]);
+	    const Point3d & p2 = mesh.Point (el[1]);
+	    const Point3d & p3 = mesh.Point (el[2]);
+	    const Point3d & p4 = mesh.Point (el[3]);
+	  
+	    Point3d ci = Center (p1, p2, p3, p4);
+	  
+	    //       if (adfront->Inside (ci) != adfront->Inside (Center (ci, p1)))
+	    // 	cout << "ERROR: outer test unclear !!!" << endl;	
+	  
+	    if (inner.Test(i) != adfront->Inside (ci))
+	      {
+		/*
+		  cout << "ERROR: outer test wrong !!!" 
+		  << "inner = " << int(inner.Test(i))
+		  << "outer = " << int(outer.Test(i))
+		  << endl;
+	      
+		  cout << "Vol = " << Determinant(Vec3d(p1, p2),
+		  Vec3d(p1, p3),
+		  Vec3d(p1, p4)) << endl;
+	      
+		*/	      
+		for (int j = 1; j <= 4; j++)
+		  {
+		    Point3d hp;
+		    switch (j)
+		      {
+		      case 1: hp = Center (ci, p1); break;
+		      case 2: hp = Center (ci, p2); break;
+		      case 3: hp = Center (ci, p3); break;
+		      case 4: hp = Center (ci, p4); break;
+		      }
+		    //		  cout << "inside(" << hp << ") = " << adfront->Inside(hp) << endl;
+		  }
+	      
+	      }
+	  
+	    if (adfront->Inside(ci))
+	      outer.Clear(i);
+	    else
+	      outer.Set(i);
+	  }
+      }
+
+
+    /*
+
+    // find bug in innerfaces
+
+    tempmesh.DeleteVolumeElements();
+
+    for (i = 1; i <= innerfaces.GetNBags(); i++)
+    for (j = 1; j <= innerfaces.GetBagSize(i); j++)
+    {
+    INDEX_3 i3;
+    INDEX_2 i2;
+    innerfaces.GetData (i, j, i3, i2);
+    if (i2.I2())
+    {
+    if (outer.Test(i2.I1()) != outer.Test(i2.I2()))
+    {
+    tempmesh.AddVolumeElement (tempels.Get(i2.I1()));
+    tempmesh.AddVolumeElement (tempels.Get(i2.I2()));
+    cerr << "outer flag different for connected els" << endl;
+    }
+    }
+    }
+
+
+    cout << "Check intersectiong once more" << endl;
+
+    for (i = 1; i <= openels.Size(); i++)
+    {
+    tempmesh.SurfaceElement(2*openels.Get(i)).SetIndex(2);
+    tempmesh.SurfaceElement(2*openels.Get(i)-1).SetIndex(2);
+    }
+
+    //  for (i = 1; i <= tempmesh.GetNE(); i++)
+    //    for (j = 1; j <= tempmesh.GetNSE(); j++)
+    i = 6; j = 403;
+    if (i <= tempmesh.GetNE() && j <= tempmesh.GetNSE())
+    if (tempmesh.SurfaceElement(j).GetIndex()==2)
+    {
+    const Element & el = tempmesh.VolumeElement(i);
+    const Element2d & sel = tempmesh.SurfaceElement(j);
+
+    const Point3d *tripp[3];
+    const Point3d *pp[4];
+    int tetpi[4], tripi[3];
+
+    for (k = 1; k <= 4; k++)
+    {
+    pp[k-1] = &tempmesh.Point(el.PNum(k));
+    tetpi[k-1] = el.PNum(k);
+    }
+
+    for (k = 1; k <= 3; k++)
+    {
+    tripp[k-1] = &tempmesh.Point (sel.PNum(k));
+    tripi[k-1] = sel.PNum(k);
+    }
+
+    (*testout) << "Check Triangle " << j << ":";
+    for (k = 1; k <= 3; k++)
+    (*testout) << " " << sel.PNum(k);
+    for (k = 1; k <= 3; k++)
+    (*testout) << " " << tempmesh.Point(sel.PNum(k));
+    (*testout) << endl;
+
+    (*testout) << "Check Tet " << i << ":";
+    for (k = 1; k <= 4; k++)
+    (*testout) << " " << el.PNum(k);
+    for (k = 1; k <= 4; k++)
+    (*testout) << " " << tempmesh.Point(el.PNum(k));
+    (*testout) << endl;
+
+    if (IntersectTetTriangle (&pp[0], &tripp[0], tetpi, tripi))
+    {
+    cout << "Intesection detected !!" << endl;
+    }
+    }
+
+    tempmesh.Save ("temp.vol");
+
+    // end bug search
+    */
+
+
+    for (int i = ne; i >= 1; i--)
+      {
+	if (outer.Test(i))
+	  tempels.DeleteElement(i);
+      }
+
+
+    // mesh.points.SetSize(mesh.points.Size()-4);
+
+    for (int i = 0; i < tempels.Size(); i++)
+      {
+	Element el(4);
+	for (int j = 0; j < 4; j++)
+	  el[j] = tempels[i][j];
+	mesh.AddVolumeElement (el);
+      }
+
+    PrintMessage (5, "outer removed");
+
+    mesh.FindOpenElements(domainnr);
+
+    mesh.Compress();
+
+    PopStatus ();
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/delaunay2d.cpp b/contrib/Netgen/libsrc/meshing/delaunay2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b1119f190215368ac79acf6366380a649ebaf74
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/delaunay2d.cpp
@@ -0,0 +1,174 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+// not yet working ....
+
+namespace netgen
+{
+
+  void Meshing2 :: BlockFillLocalH (Mesh & mesh, const MeshingParameters & mp)
+  {
+    double filldist = mp.filldist;
+    
+    cout << "blockfill local h" << endl;
+    cout << "rel filldist = " << filldist << endl;
+    PrintMessage (3, "blockfill local h");
+
+    Array<Point<3> > npoints;
+  
+    // adfront -> CreateTrees();
+
+    Box<3> bbox ( Box<3>::EMPTY_BOX );
+    double maxh = 0;
+    
+    for (int i = 0; i < adfront->GetNFL(); i++)
+      {
+	const FrontLine & line = adfront->GetLine (i);
+
+	const Point<3> & p1 = adfront->GetPoint(line.L().I1());
+	const Point<3> & p2 = adfront->GetPoint(line.L().I2());
+	
+	double hi = Dist (p1, p2);
+	if (hi > maxh) maxh = hi;
+	
+	bbox.Add (p1);
+	bbox.Add (p2);
+      }
+
+    
+    cout << "bbox = " << bbox << endl;
+
+
+    // Point<3> mpc = bbox.Center();
+    bbox.Increase (bbox.Diam()/2);
+    Box<3> meshbox = bbox;
+    
+    LocalH loch2 (bbox, 1);
+    
+    if (mp.maxh < maxh) maxh = mp.maxh;
+    
+    bool changed;
+    do 
+      {
+	mesh.LocalHFunction().ClearFlags();
+	
+	for (int i = 0; i < adfront->GetNFL(); i++)
+	  {
+	    const FrontLine & line = adfront->GetLine(i);
+	    
+	    Box<3> bbox (adfront->GetPoint (line.L().I1()));
+	    bbox.Add (adfront->GetPoint (line.L().I2()));
+
+	    
+	    double filld = filldist * bbox.Diam();
+	    bbox.Increase (filld);
+	    
+	    mesh.LocalHFunction().CutBoundary (bbox); 
+	  }
+	
+
+	mesh.LocalHFunction().FindInnerBoxes (adfront, NULL);
+	
+	npoints.SetSize(0);
+	mesh.LocalHFunction().GetInnerPoints (npoints);
+
+	changed = false;
+	for (int i = 0; i < npoints.Size(); i++)
+	  {
+	    if (mesh.LocalHFunction().GetH(npoints[i]) > 1.5 * maxh)
+	      {
+		mesh.LocalHFunction().SetH (npoints[i], maxh);
+		changed = true;
+	      }
+	  }
+      }
+    while (changed);
+
+    if (debugparam.slowchecks)
+      (*testout) << "Blockfill with points: " << endl;
+    *testout << "loch = " << mesh.LocalHFunction() << endl;
+    
+    *testout << "npoints = " << endl << npoints << endl;
+
+    for (int i = 1; i <= npoints.Size(); i++)
+      {
+	if (meshbox.IsIn (npoints.Get(i)))
+	  {
+	    int gpnum = mesh.AddPoint (npoints.Get(i));
+	    adfront->AddPoint (npoints.Get(i), gpnum);
+	    
+	    if (debugparam.slowchecks)
+	      {
+		(*testout) << npoints.Get(i) << endl;
+
+		Point<2> p2d (npoints.Get(i)(0), npoints.Get(i)(1));
+		if (!adfront->Inside(p2d))
+		  {
+		    cout << "add outside point" << endl;
+		    (*testout) << "outside" << endl;
+		  }
+	      }
+	    
+	  }
+      }
+    
+  
+
+  // find outer points
+  
+    loch2.ClearFlags();
+
+    for (int i = 0; i < adfront->GetNFL(); i++)
+      {
+	const FrontLine & line = adfront->GetLine(i);
+	
+	Box<3> bbox (adfront->GetPoint (line.L().I1()));
+	bbox.Add (adfront->GetPoint (line.L().I2()));
+	
+	loch2.SetH (bbox.Center(), bbox.Diam());
+      }
+
+
+    for (int i = 0; i < adfront->GetNFL(); i++)
+      {
+	const FrontLine & line = adfront->GetLine(i);
+	
+	Box<3> bbox (adfront->GetPoint (line.L().I1()));
+	bbox.Add (adfront->GetPoint (line.L().I2()));
+
+	bbox.Increase (filldist * bbox.Diam());
+	loch2.CutBoundary (bbox);
+      }
+    
+    loch2.FindInnerBoxes (adfront, NULL);
+    
+    npoints.SetSize(0);
+    loch2.GetOuterPoints (npoints);
+    
+    for (int i = 1; i <= npoints.Size(); i++)
+      {
+	if (meshbox.IsIn (npoints.Get(i)))
+	  {
+	    int gpnum = mesh.AddPoint (npoints.Get(i));
+	    adfront->AddPoint (npoints.Get(i), gpnum);
+	  }
+      }  
+
+  }
+  
+  void Meshing2 :: Delaunay (Mesh & mesh, int domainnr, const MeshingParameters & mp)
+  {
+    cout << "2D Delaunay meshing (in progress)" << endl;
+
+    // int oldnp = mesh.GetNP();
+
+    cout << "np, old = " << mesh.GetNP() << endl;
+
+    BlockFillLocalH (mesh, mp);
+
+
+    cout << "np, now = " << mesh.GetNP() << endl;
+    
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/findip.hpp b/contrib/Netgen/libsrc/meshing/findip.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f5d8e4249f4926f492fc49b58d9eefc84eb9048f
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/findip.hpp
@@ -0,0 +1,192 @@
+// find inner point
+
+
+
+inline void Minimize (const Array<Vec3d> & a,
+		      const Array<double> & c,
+		      int * act, 
+		      Vec<3> & x, double & f,
+		      int * sol)
+{
+  int act1[4];
+  Mat<3> m, inv;
+  Vec<3> rs, xmax, center;
+
+  f = 1e99;
+
+  for (int j = 0; j < 5; j++)
+    {
+      for (int hk = 0, k = 0; hk < 4; hk++)
+	{
+	  if (hk == j) k++;
+	  act1[hk] = act[k];
+	  k++;
+	}
+
+      for (int k = 0; k < 3; k++)
+	{
+	  m(k, 0) = a[act1[0]].X() - a[act1[k+1]].X();
+	  m(k, 1) = a[act1[0]].Y() - a[act1[k+1]].Y();
+	  m(k, 2) = a[act1[0]].Z() - a[act1[k+1]].Z();
+	  rs(k) = c[act1[k+1]] - c[act1[0]];
+	}
+
+      /*
+      (*testout) << "act1 = "
+		 << act1[0] << " "
+		 << act1[1] << " "
+		 << act1[2] << " "
+		 << act1[3] << endl;
+      (*testout) << "Det = " << Det(m) << endl;
+      */
+
+      if (fabs (Det (m)) > 1e-10)
+	{
+	  CalcInverse (m, inv);
+	  xmax = inv * rs;
+	  
+	  double fmax = -1e10;
+	  for (int k = 0; k < 5; k++)
+	    {
+	      double hd = 
+		xmax(0) * a[act[k]].X() + xmax(1) * a[act[k]].Y() + xmax(2) * a[act[k]].Z() + c[act[k]];
+	      if (hd > fmax) fmax = hd;
+	    }
+
+	  if (fmax < f)
+	    {
+	      f = fmax;
+	      x = xmax;
+	      for (int k = 0; k < 4; k++)
+		sol[k] = act1[k];
+	    }
+	}
+    }
+}
+
+
+
+
+template <typename POINTArray, typename FACEArray>
+inline int FindInnerPoint (POINTArray & points,
+			   FACEArray & faces,
+			   Point3d & p)
+{
+  static int timer = NgProfiler::CreateTimer ("FindInnerPoint");
+  NgProfiler::RegionTimer reg (timer);
+
+  Array<Vec3d> a;
+  Array<double> c;
+  Mat<3> m, inv;
+  Vec<3> rs, x = 0.0, center;
+  double f;
+
+  int nf = faces.Size();
+
+  // minimize_x  max_i  a_i x + c_i
+
+  a.SetSize (nf+4);
+  c.SetSize (nf+4);
+
+  for (int i = 0; i < nf; i++)
+    {
+      Point3d p1 = points.Get(faces[i][0]);
+      a[i] = Cross (points.Get(faces[i][1]) - p1,
+		    points.Get(faces[i][2]) - p1);
+      a[i] /= a[i].Length();
+      c[i] = - (a[i].X() * p1.X() + a[i].Y() * p1.Y() + a[i].Z() * p1.Z());
+    }
+
+  /*
+  center = 0;
+  for (int i = 0; i < points.Size(); i++)
+    center += Vec<3> (points[i]);
+  center /= points.Size();
+  */
+
+  center = 0;
+  for (int i = 0; i < faces.Size(); i++)
+    for (int j = 0; j < 3; j++)
+      center += Vec<3> (points.Get(faces[i][j]));
+  center /= (3*faces.Size());
+
+
+  // (*testout) << "center = " << center << endl;
+
+  double hmax = 0;
+  for (int i = 0; i < nf; i++)
+    {
+      // const Element2d & el = faces[i];
+      // (*testout) << "el[" << i << "] = " << el << endl;
+      for (int j = 1; j <= 3; j++)
+	{
+	  double hi = Dist (points.Get(faces[i].PNumMod(j)),
+			    points.Get(faces[i].PNumMod(j+1)));
+	  if (hi > hmax) hmax = hi;
+	}
+    }
+  
+  // (*testout) << "hmax = " << hmax << endl;
+  
+  a[nf] = Vec<3> (1, 0, 0);
+  c[nf] = -center(0) - hmax;
+  a[nf+1] = Vec<3> (0, 1, 0);
+  c[nf+1] = -center(1) - hmax;
+  a[nf+2] = Vec<3> (0, 0, 1);
+  c[nf+2] = -center(2) - hmax;
+  a[nf+3] = Vec<3> (-1, -1, -1);
+  c[nf+3] = center(0)+center(1)+center(2)-3*hmax;
+
+  /*
+  (*testout) << "findip, a now = " << endl << a << endl;
+  (*testout) << "findip, c now = " << endl << c << endl;
+  */
+
+  int act[5] = { 0, nf, nf+1, nf+2, nf+3 };
+  int sol[4];
+
+  while (1)
+    {
+      /*
+      (*testout) << "try ";
+      for (int j = 0; j < 5; j++)
+	(*testout)  << act[j] << " ";
+      */
+
+      Minimize (a, c, act, x, f, sol);
+
+      /*
+      (*testout) << endl << "sol = ";
+      for (int j = 0; j < 4; j++)
+	(*testout)  << sol[j] << " ";
+
+      (*testout) << " fmin = " << f << endl;
+      */
+      for (int j = 0; j < 4; j++) act[j] = sol[j];
+      
+      bool found = 0;
+      double maxval = f;
+      for (int j = 0; j < nf; j++)
+	{
+	  double val = x(0) * a[j].X() + x(1) * a[j].Y() + x(2) * a[j].Z() + c[j];
+	  if (val > maxval + hmax * 1e-6)
+	    {
+	      found = 1;
+	      maxval = val;
+	      act[4] = j;
+	    }
+	}
+      
+      // (*testout) << "maxval = " << maxval << endl;
+      if (!found) break;
+    }
+  
+  // cout << "converged, f = " << f << endl;
+  
+  p = Point3d (x(0), x(1), x(2));
+  // (*testout) << "findip, f = " << f << ", hmax = " << hmax << endl;
+  return (f < -1e-5 * hmax);
+}
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/findip2.hpp b/contrib/Netgen/libsrc/meshing/findip2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9538553485d00f9b3c429289e90cae44e832fd8f
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/findip2.hpp
@@ -0,0 +1,95 @@
+// find inner point
+
+template <typename POINTArray, typename FACEArray>
+inline int FindInnerPoint2 (POINTArray & points,
+			    FACEArray & faces,
+			    Point3d & p)
+{
+  static int timer = NgProfiler::CreateTimer ("FindInnerPoint2");
+  NgProfiler::RegionTimer reg (timer);
+
+  Array<Vec3d> a;
+  Array<double> c;
+  Mat<3> m, inv;
+  Vec<3> rs, x, pmin;
+
+  int nf = faces.Size();
+
+  a.SetSize (nf);
+  c.SetSize (nf);
+
+  for (int i = 0; i < nf; i++)
+    {
+      Point3d p1 = points.Get(faces[i][0]);
+      a[i] = Cross (points.Get(faces[i][1]) - p1,
+		    points.Get(faces[i][2]) - p1);
+      a[i] /= a[i].Length();
+      c[i] = - (a[i].X() * p1.X() + a[i].Y() * p1.Y() + a[i].Z() * p1.Z());
+    }
+
+
+  x = 0;
+  
+  
+  double hmax = 0;
+  for (int i = 0; i < nf; i++)
+    {
+      const Element2d & el = faces[i];
+      for (int j = 1; j <= 3; j++)
+	{
+	  double hi = Dist (points.Get(el.PNumMod(j)),
+			    points.Get(el.PNumMod(j+1)));
+	  if (hi > hmax) hmax = hi;
+	}
+    }
+
+  double fmin = 0;
+
+  for (int i1 = 1; i1 <= nf; i1++)
+    for (int i2 = i1+1; i2 <= nf; i2++)
+      for (int i3 = i2+1; i3 <= nf; i3++)
+        for (int i4 = i3+1; i4 <= nf; i4++)
+          {
+	    m(0, 0) = a.Get(i1).X() - a.Get(i2).X();
+	    m(0, 1) = a.Get(i1).Y() - a.Get(i2).Y();
+	    m(0, 2) = a.Get(i1).Z() - a.Get(i2).Z();
+	    rs(0) = c.Get(i2) - c.Get(i1);
+
+	    m(1, 0) = a.Get(i1).X() - a.Get(i3).X();
+	    m(1, 1) = a.Get(i1).Y() - a.Get(i3).Y();
+	    m(1, 2) = a.Get(i1).Z() - a.Get(i3).Z();
+	    rs(1) = c.Get(i3) - c.Get(i1);
+
+	    m(2, 0) = a.Get(i1).X() - a.Get(i4).X();
+	    m(2, 1) = a.Get(i1).Y() - a.Get(i4).Y();
+	    m(2, 2) = a.Get(i1).Z() - a.Get(i4).Z();
+	    rs(2) = c.Get(i4) - c.Get(i1);
+
+
+	    if (fabs (Det (m)) > 1e-10)
+	      {
+		CalcInverse (m, inv);
+		x = inv * rs;
+
+		double f = -1e10;
+		for (int i = 0; i < nf; i++)
+		  {
+		    double hd = 
+		      x(0) * a[i].X() + x(1) * a[i].Y() + x(2) * a[i].Z() + c[i];
+		    if (hd > f) f = hd;
+		    if (hd > fmin) break;
+		  }
+
+		if (f < fmin)
+		  {
+		    fmin = f;
+		    pmin = x;
+		  }
+	      }
+          }
+
+  p = Point3d (pmin(0), pmin(1), pmin(2));
+  (*testout) << "fmin = " << fmin << endl;
+  return (fmin < -1e-3 * hmax);
+}
+
diff --git a/contrib/Netgen/libsrc/meshing/geomsearch.cpp b/contrib/Netgen/libsrc/meshing/geomsearch.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc217a4809401e1d46d775facde3b973c2c484ad
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/geomsearch.cpp
@@ -0,0 +1,263 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+  GeomSearch3d :: GeomSearch3d() 
+  {
+    size.i1 = 0; size.i2 = 0; size.i3 = 0; 
+  };
+
+  GeomSearch3d :: ~GeomSearch3d()
+  {
+    //delete old Hashtable:
+    if (size.i1 != 0)
+      {
+	for (int i = 0; i < size.i1*size.i2*size.i3; i++)
+	  delete hashtable[i];
+      } 
+  }
+
+  void GeomSearch3d :: Init (Array <FrontPoint3,PointIndex::BASE> *pointsi, Array <FrontFace> *facesi)
+  {
+    points = pointsi;
+    faces = facesi;
+    size.i1 = 0; size.i2 = 0; size.i3 = 0; 
+    reset = 1;
+    hashcount = 1;
+  }
+
+  void GeomSearch3d :: ElemMaxExt(Point3d& minp, Point3d& maxp, const MiniElement2d& elem)
+  {
+    maxp.X()=(*points)[elem.PNum(1)].P()(0);
+    maxp.Y()=(*points)[elem.PNum(1)].P()(1);
+    maxp.Z()=(*points)[elem.PNum(1)].P()(2);
+    minp.X()=(*points)[elem.PNum(1)].P()(0);
+    minp.Y()=(*points)[elem.PNum(1)].P()(1);
+    minp.Z()=(*points)[elem.PNum(1)].P()(2);
+  
+    for (int i=2; i <= 3; i++)
+      {
+	maxp.X()=max2((*points)[elem.PNum(i)].P()(0),maxp.X());
+	maxp.Y()=max2((*points)[elem.PNum(i)].P()(1),maxp.Y());
+	maxp.Z()=max2((*points)[elem.PNum(i)].P()(2),maxp.Z());
+	minp.X()=min2((*points)[elem.PNum(i)].P()(0),minp.X());
+	minp.Y()=min2((*points)[elem.PNum(i)].P()(1),minp.Y());
+	minp.Z()=min2((*points)[elem.PNum(i)].P()(2),minp.Z());
+      }
+  }
+
+  void GeomSearch3d :: MinCoords(const Point3d& p1, Point3d& p2)
+  {
+    p2.X()=min2(p1.X(),p2.X());
+    p2.Y()=min2(p1.Y(),p2.Y());
+    p2.Z()=min2(p1.Z(),p2.Z());
+  }
+
+  void GeomSearch3d :: MaxCoords(const Point3d& p1, Point3d& p2)
+  {
+    p2.X()=max2(p1.X(),p2.X());
+    p2.Y()=max2(p1.Y(),p2.Y());
+    p2.Z()=max2(p1.Z(),p2.Z());
+  }
+
+  void GeomSearch3d :: Create()
+  {
+    INDEX i,j,k;
+    if (reset)
+      {
+	const double hashelemsizefactor = 4;
+	reset = 0;
+	/*
+	  minext=Point3d(MAXDOUBLE, MAXDOUBLE, MAXDOUBLE);
+	  maxext=Point3d(MINDOUBLE, MINDOUBLE, MINDOUBLE);
+	*/
+	ElemMaxExt(minext, maxext, faces->Get(1).Face());
+	Point3d maxp, minp;
+	Vec3d midext(0,0,0);
+      
+	//get max Extension of Frontfaces
+	for (i = 1; i <= faces->Size(); i++)
+	  {
+	    ElemMaxExt(minp, maxp, faces->Get(i).Face());
+	    MinCoords(minp, minext);
+	    MaxCoords(maxp, maxext);
+	    midext+=maxp-minp;
+	  }
+
+
+	maxextreal = maxext;
+	maxext = maxext + 1e-4 * (maxext - minext);
+
+	midext*=1./faces->Size();
+	Vec3d boxext = maxext - minext;
+      
+	//delete old Hashtable:
+	if (size.i1 != 0)
+	  {
+	    for (i = 1; i <= size.i1*size.i2*size.i3; i++)
+	      {
+		delete hashtable.Get(i);
+	      }
+	  } 
+      
+	size.i1 = int (boxext.X()/midext.X()/hashelemsizefactor+1);
+	size.i2 = int (boxext.Y()/midext.Y()/hashelemsizefactor+1);
+	size.i3 = int (boxext.Z()/midext.Z()/hashelemsizefactor+1);
+	// PrintMessage (5, "hashsizes = ", size.i1, ", ", size.i2, ", ", size.i3);
+      
+	elemsize.X()=boxext.X()/size.i1;
+	elemsize.Y()=boxext.Y()/size.i2;
+	elemsize.Z()=boxext.Z()/size.i3;
+
+	//create Hasharrays:
+	hashtable.SetSize(size.i1*size.i2*size.i3);
+	for (i = 1; i <= size.i1; i++)
+	  {
+	    for (j = 1; j <= size.i2; j++)
+	      {
+		for (k = 1; k <= size.i3; k++)
+		  {
+		    INDEX ind=i+(j-1)*size.i1+(k-1)*size.i2*size.i1;
+		    hashtable.Elem(ind) = new Array <int> ();
+		  }
+	      }
+	  }
+      }
+    else
+      {
+	//Clear all Hash-Arrays
+	for (i = 1; i <= size.i1; i++)
+	  {
+	    for (j = 1; j <= size.i2; j++)
+	      {
+		for (k = 1; k <= size.i3; k++)
+		  {
+		    INDEX ind=i+(j-1)*size.i1+(k-1)*size.i2*size.i1;
+		    hashtable.Elem(ind)->SetSize(0);
+		  }
+	      }
+	  }	  
+      }
+  
+    //Faces in Hashtable einfuegen:
+    for (i = 1; i <= faces->Size(); i++)
+      {
+	AddElem(faces->Get(i).Face(),i);
+      }
+  
+  }
+
+  void GeomSearch3d :: AddElem(const MiniElement2d& elem, INDEX elemnum)
+  {
+    Point3d minp, maxp;
+    ElemMaxExt(minp, maxp, elem);
+    int sx = int ((minp.X()-minext.X())/elemsize.X()+1.);
+    int ex = int ((maxp.X()-minext.X())/elemsize.X()+1.);
+    int sy = int ((minp.Y()-minext.Y())/elemsize.Y()+1.);
+    int ey = int ((maxp.Y()-minext.Y())/elemsize.Y()+1.);
+    int sz = int ((minp.Z()-minext.Z())/elemsize.Z()+1.);
+    int ez = int ((maxp.Z()-minext.Z())/elemsize.Z()+1.);
+  
+    for (int ix = sx; ix <= ex; ix++)
+      for (int iy = sy; iy <= ey; iy++)
+        for (int iz = sz; iz <= ez; iz++)
+          {
+            INDEX ind=ix+(iy-1)*size.i1+(iz-1)*size.i2*size.i1;
+            if (ind < 1 || ind > size.i1 * size.i2 * size.i3)
+              {
+                cerr << "Illegal hash-position";
+                cerr << "Position: " << ix << "," << iy << "," << iz << endl;
+		    throw NgException ("Illegal position in Geomsearch");
+              }
+            hashtable.Elem(ind)->Append(elemnum);		      
+          }
+  }
+
+  void GeomSearch3d :: GetLocals(Array<MiniElement2d> & locfaces,  Array<INDEX> & findex,
+				 INDEX fstind, const Point3d& p0, double xh)
+  {
+    hashcount++;
+  
+    Point3d minp, maxp, midp; 
+
+    minp=p0-Vec3d(xh,xh,xh); //lay cube over sphere
+    maxp=p0+Vec3d(xh,xh,xh);
+
+    MaxCoords(minext,minp); //cube may not be out of hash-region
+    MinCoords(maxextreal,maxp);
+
+
+    int cluster = faces->Get(fstind).Cluster();
+  
+    int sx = int((minp.X()-minext.X())/elemsize.X()+1.);
+    int ex = int((maxp.X()-minext.X())/elemsize.X()+1.);
+    int sy = int((minp.Y()-minext.Y())/elemsize.Y()+1.);
+    int ey = int((maxp.Y()-minext.Y())/elemsize.Y()+1.);
+    int sz = int((minp.Z()-minext.Z())/elemsize.Z()+1.);
+    int ez = int((maxp.Z()-minext.Z())/elemsize.Z()+1.);
+    int ix,iy,iz,i,k;
+
+    int cnt1 = 0;  // test, how efficient hashtable is
+    int cnt2 = 0;
+    int cnt3 = 0;
+  
+    for (ix = sx; ix <= ex; ix++)
+      {
+	for (iy = sy; iy <= ey; iy++)
+	  {
+	    for (iz = sz; iz <= ez; iz++)
+	      {
+		INDEX ind=ix+(iy-1)*size.i1+(iz-1)*size.i2*size.i1;
+	      
+		//go through all elements in one hash area
+		const Array <int> & area = *hashtable.Elem(ind);
+		for (k = 1; k <= area.Size(); k++)
+		  {
+		    cnt2++;
+		    i = area.Get(k);
+		    if (faces->Get(i).Cluster() == cluster && 
+			faces->Get(i).Valid() &&
+			faces->Get(i).HashValue() != hashcount && 
+			i != fstind)
+		      {
+			cnt1++;
+			const MiniElement2d & face = faces->Get(i).Face();
+		      
+			const Point3d & p1 = (*points)[face.PNum(1)].P();
+			const Point3d & p2 = (*points)[face.PNum(2)].P();
+			const Point3d & p3 = (*points)[face.PNum(3)].P();
+		      
+			midp = Center (p1, p2, p3);
+		      
+			// if (Dist2 (midp, p0) <= xh*xh)  
+                        if((Dist2 (p1, p0) <= xh*xh) ||
+                           (Dist2 (p2, p0) <= xh*xh) ||
+                           (Dist2 (p3, p0) <= xh*xh) ||
+                           (Dist2 (midp, p0) <= xh*xh) )  // by Jochen Wild
+			  {
+			    cnt3++;
+			    locfaces.Append(faces->Get(i).Face());
+			    findex.Append(i);
+			    faces->Elem(i).SetHashValue(hashcount);
+			  }
+		      }
+		  }
+	      }
+	  }
+      }
+    /*
+      if (faces->Size() != 0 && hashcount % 200 == 0)
+      {
+      (*mycout) << "n.o.f= " << faces->Size();
+      (*mycout) << ", n.o.lf= " << locfaces.Size();
+      (*mycout) << ", hashf= " << (double)cnt2/(double)faces->Size();
+      (*mycout) << " (" << (double)cnt1/(double)faces->Size();
+      (*mycout) << ", " << (double)cnt3/(double)faces->Size() << ")" << endl;
+      }
+    */
+
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/geomsearch.hpp b/contrib/Netgen/libsrc/meshing/geomsearch.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..052073de590bb2f9feb03af51bfdded2f4c77ecd
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/geomsearch.hpp
@@ -0,0 +1,117 @@
+#ifndef FILE_GEOMSEARCH
+#define FILE_GEOMSEARCH
+
+/**************************************************************************/
+/* File:   geomsearch.hh                                                  */
+/* Author: Johannes Gerstmayr                                             */
+/* Date:   19. Nov. 97                                                    */
+/**************************************************************************/
+
+class FrontPoint3;
+class FrontFace;
+class MiniElement2d;
+
+  /// class for quick access of 3D-elements; class cannot delete elements, but only append
+class GeomSearch3d
+{
+
+public:
+  ///
+  GeomSearch3d();
+  ///
+  virtual ~GeomSearch3d();
+
+  ///
+  void Init (Array <FrontPoint3,PointIndex::BASE> *pointsi, Array <FrontFace> *facesi);
+
+  ///get elements max extension
+  void ElemMaxExt(Point3d& minp, Point3d& maxp, const MiniElement2d& elem);
+  
+  ///get minimum coordinates of two points ->p2
+  void MinCoords(const Point3d& p1, Point3d& p2);
+
+  ///get minimum coordinates of two points ->p2
+  void MaxCoords(const Point3d& p1, Point3d& p2);
+
+  ///create a hashtable from an existing array of triangles
+  ///sizei = number of pieces in one direction
+  void Create();
+
+  ///add new element to Hashtable
+  void AddElem(const MiniElement2d& elem, INDEX elemnum);
+
+  ///GetLocal faces in sphere with radius xh and middlepoint p
+  void GetLocals(Array<MiniElement2d> & locfaces,  Array<INDEX> & findex,
+		 INDEX fstind, const Point3d& p0, double xh);
+
+private:
+  
+  Array <FrontFace> *faces; // Pointers to Arrays in Adfront
+  Array <FrontPoint3,PointIndex::BASE> *points;
+
+  Array <Array <int>*> hashtable;
+
+  Point3d minext; //extension of Hashdomain
+  Point3d maxext;
+  Point3d maxextreal;
+  Vec3d elemsize;  //size of one Hash-Element
+
+  threeint size; // size of Hashtable in each direction
+  int reset;
+  int hashcount;
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/global.cpp b/contrib/Netgen/libsrc/meshing/global.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b0f0108a7c3c5379c36e57e5cccbd6f8cb0f4fb2
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/global.cpp
@@ -0,0 +1,59 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+  // stringstream emptystr;
+  // ostream * testout = &emptystr;
+  // testout -> clear(ios::failbit);
+
+  // ostream * testout = &cout;
+  ostream * testout = new ostream(0);
+
+  // NetgenOutStream * testout = new NetgenOutStream;
+
+  ostream * mycout = &cout;
+  ostream * myerr = &cerr;
+
+
+  //  Flags parameters;
+
+
+  int silentflag = 0;
+  int testmode = 0;
+
+  volatile multithreadt multithread;
+
+  string ngdir = ".";
+
+  Array<int> tets_in_qualclass;
+
+
+  multithreadt :: multithreadt()
+  {
+    pause =0;
+    testmode = 0;
+    redraw = 0;
+    drawing = 0;
+    terminate = 0;
+    running = 0;
+    percent = 0;
+    task = "";
+  }
+
+  DebugParameters debugparam;
+  bool verbose = 0;
+
+  int timestamp = 0;
+  int GetTimeStamp() 
+  { 
+    return timestamp; 
+  }
+
+  int NextTimeStamp()
+  {
+    timestamp++;
+    return timestamp;
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/global.hpp b/contrib/Netgen/libsrc/meshing/global.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..43a204930f9ac3a9de0048c2d94d3ade44bf269e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/global.hpp
@@ -0,0 +1,54 @@
+#ifndef FILE_GLOBAL
+#define FILE_GLOBAL
+
+
+/**************************************************************************/
+/* File:   global.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+/*
+  global functions and variables
+*/
+
+namespace netgen
+{
+
+  ///
+  DLL_HEADER extern double GetTime ();
+  extern void ResetTime ();
+
+  ///
+  extern int testmode;
+
+  /// calling parameters
+  // extern Flags parameters;
+
+  // extern DLL_HEADER MeshingParameters mparam;
+
+  extern Array<int> tets_in_qualclass;
+
+  class multithreadt
+  {
+  public:
+    int pause;
+    int testmode;
+    int redraw;
+    int drawing;
+    int terminate;
+    int running;
+    double percent;
+    const char * task;
+    bool demorunning;
+    multithreadt();
+  };
+
+  extern volatile multithreadt multithread;
+
+  extern string ngdir;
+  extern DebugParameters debugparam;
+  extern bool verbose;  
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/hpref_hex.hpp b/contrib/Netgen/libsrc/meshing/hpref_hex.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..11e3f86e4949da4beccb772f2c2d4beeff066e8d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_hex.hpp
@@ -0,0 +1,236 @@
+// SZ 
+
+// HP_HEX  ... no refinement
+int refhex_splitedges[][3] =
+  {
+      { 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE refhex_newelstypes[] =
+  {
+    HP_HEX,
+    HP_NONE,
+  };
+int refhex_newels[][8] =
+  {
+    { 1, 2, 3, 4, 5, 6, 7, 8 }
+  };
+HPRef_Struct refhex =
+  {
+    HP_HEX,
+    refhex_splitedges, 
+    0, 0,
+    refhex_newelstypes, 
+    refhex_newels
+  };
+
+// HP_HEX_1F  ... face (1 - 4 - 3 -2) singular 
+int refhex_1f_0e_0v_splitedges[][3] =
+  {
+    { 1, 5, 9 },
+    { 2, 6, 10 },
+    { 3, 7, 11 },
+    { 4, 8, 12 }, 
+    { 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE refhex_1f_0e_0v_newelstypes[] =
+  {
+    HP_HEX,
+    HP_HEX_1F_0E_0V,
+    HP_NONE,
+  };
+int  refhex_1f_0e_0v_newels[][8] =
+  {
+    { 9, 10, 11, 12, 5, 6, 7, 8 }, 
+    { 1, 2, 3, 4, 9, 10, 11, 12}  
+ }; 
+HPRef_Struct refhex_1f_0e_0v =
+  {
+    HP_HEX,
+    refhex_1f_0e_0v_splitedges, 
+    0, 0,
+    refhex_1f_0e_0v_newelstypes, 
+    refhex_1f_0e_0v_newels
+  };
+
+
+
+// HP_HEX_1FA_1FB  ... face (1 - 4 - 3 -2) and face (1-2-6-5) singular 
+int refhex_1fa_1fb_0e_0v_splitedges[][3] =
+  {
+    { 1, 5, 9 },
+    { 2, 6, 10 },
+    { 3, 7, 11 },
+    { 4, 8, 12 },
+    { 1, 4, 13 },
+    { 2, 3, 14 },  
+    { 6, 7, 15 }, 
+    { 5, 8, 16 }, 
+    { 0, 0, 0 }
+  };
+
+int refhex_1fa_1fb_0e_0v_splitfaces[][4] =
+  {
+    { 2, 3, 6, 17 },
+    { 1, 4, 5, 18 },
+    { 0, 0, 0, 0 },
+  };
+HPREF_ELEMENT_TYPE refhex_1fa_1fb_0e_0v_newelstypes[] =
+  {
+    HP_HEX,
+    HP_HEX_1F_0E_0V,
+    HP_HEX_1F_0E_0V, 
+    HP_HEX_1FA_1FB_0E_0V, 
+    HP_NONE,
+  };
+int  refhex_1fa_1fb_0e_0v_newels[][8] =
+  {
+    {18, 17, 11, 12, 16, 15, 7, 8}, 
+    {13, 14, 3, 4, 18, 17, 11, 12},
+    { 5, 6, 10, 9, 16, 15, 17, 18}, 
+    { 1, 2, 14, 13, 9, 10, 17, 18} 
+  }; 
+HPRef_Struct refhex_1fa_1fb_0e_0v =
+  {
+    HP_HEX,
+    refhex_1fa_1fb_0e_0v_splitedges, 
+    refhex_1fa_1fb_0e_0v_splitfaces, 0,
+    refhex_1fa_1fb_0e_0v_newelstypes, 
+    refhex_1fa_1fb_0e_0v_newels
+  };
+
+
+
+// Refine Dummies 
+  // HP_HEX_0E_1V
+  int refhex_0e_1v_splitedges[][3] =
+    {
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refhex_0e_1v_newelstypes[] =
+    {
+      HP_TET_0E_1V,
+      HP_TET,
+      HP_TET,
+      HP_TET,
+      HP_TET,
+      HP_TET,
+      HP_NONE,
+    };
+  int refhex_0e_1v_newels[][8] =
+    {
+      { 1, 5, 2, 4 },
+      { 7, 3, 6, 8 },
+      { 2, 8, 5, 6 },
+      { 2, 8, 6, 3 },
+      { 2, 8, 3, 4 },
+      { 2, 8, 4, 5 },
+    };
+  HPRef_Struct refhex_0e_1v =
+    {
+      HP_HEX,
+      refhex_0e_1v_splitedges, 
+      0, 0,
+      refhex_0e_1v_newelstypes, 
+      refhex_0e_1v_newels
+    };
+
+
+
+// Refine Dummies 
+  // HP_HEX_1E_1V
+  int refhex_1e_1v_splitedges[][3] =
+    {
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refhex_1e_1v_newelstypes[] =
+    {
+      HP_TET_1E_1VA,
+      HP_TET,
+      HP_TET_0E_1V,
+      HP_TET_0E_1V,
+      HP_TET_0E_1V,
+      HP_TET_0E_1V,
+      HP_NONE,
+    };
+  int refhex_1e_1v_newels[][8] =
+    {
+      // { 1, 5, 2, 4 }, 
+      { 1, 2, 4, 5 },
+      { 7, 3, 6, 8 },
+      { 2, 8, 5, 6 },
+      { 2, 8, 6, 3 },
+      { 2, 8, 3, 4 },
+      { 2, 8, 4, 5 },
+    };
+  HPRef_Struct refhex_1e_1v =
+    {
+      HP_HEX,
+      refhex_1e_1v_splitedges, 
+      0, 0,
+      refhex_1e_1v_newelstypes, 
+      refhex_1e_1v_newels
+    };
+
+
+// Refine Dummies 
+  // HP_HEX_3E_0V
+  int refhex_3e_0v_splitedges[][3] =
+    {
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refhex_3e_0v_newelstypes[] =
+    {
+      HP_TET_1E_1VA,
+      HP_TET_1E_1VA,
+      HP_TET_1E_1VA,
+      HP_TET_0E_1V,
+      HP_TET,
+      HP_NONE,
+    };
+  int refhex_3e_0v_newels[][8] =
+    {
+      { 1, 2, 3, 6 },
+      { 1, 4, 8, 3 },
+      { 1, 5, 6, 8 },
+      { 1, 6, 3, 8 },
+      { 3, 8, 6, 7 },
+    };
+  HPRef_Struct refhex_3e_0v =
+    {
+      HP_HEX,
+      refhex_3e_0v_splitedges, 
+      0, 0,
+      refhex_3e_0v_newelstypes, 
+      refhex_3e_0v_newels
+    };
+
+
+
+// Refine Dummies 
+  // HP_HEX_1E_0V 
+  int refhex_1e_0v_splitedges[][3] =
+    {
+      { 0, 0, 0 }
+    };
+
+  HPREF_ELEMENT_TYPE refhex_1e_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE,         // HP_PRISM_SINGEDGE_H1,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refhex_1e_0v_newels[][8] =
+    {
+      { 1, 4, 5, 2, 3, 6 },
+      { 5, 4, 8, 6, 3, 7 },
+    };
+  HPRef_Struct refhex_1e_0v =
+    {
+      HP_HEX,
+      refhex_1e_0v_splitedges, 
+      0, 0,
+      refhex_1e_0v_newelstypes, 
+      refhex_1e_0v_newels
+    };
+
+
diff --git a/contrib/Netgen/libsrc/meshing/hpref_prism.hpp b/contrib/Netgen/libsrc/meshing/hpref_prism.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3cceb44a5726d52fa25f5c0af623fe375bd096a0
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_prism.hpp
@@ -0,0 +1,3405 @@
+
+  // HP_PRISM  ... no refinement
+  int refprism_splitedges[][3] =
+    {
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refprism_newels[][8] =
+    {
+      { 1, 2, 3, 4, 5, 6 }
+    };
+  HPRef_Struct refprism =
+    {
+      HP_PRISM,
+      refprism_splitedges, 
+      0, 0,
+      refprism_newelstypes, 
+      refprism_newels
+    };
+
+
+
+  // HP_PRISM_SINGEDGE  ... vertical edge 1-4 is singular
+  int refprism_singedge_splitedges[][3] =
+    {
+      { 1, 2, 7 },
+      { 1, 3, 8 },
+      { 4, 5, 9 },
+      { 4, 6, 10 },
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_singedge_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE,
+      HP_HEX,
+      HP_NONE,
+    };
+  int refprism_singedge_newels[][8] =
+    {
+      { 1, 7, 8, 4, 9, 10 },
+      { 3, 8, 7, 2, 6, 10, 9, 5 }
+    };
+  HPRef_Struct refprism_singedge =
+    {
+      HP_PRISM,
+      refprism_singedge_splitedges, 
+      0, 0,
+      refprism_singedge_newelstypes, 
+      refprism_singedge_newels
+    };
+
+
+
+
+
+
+  // HP_PRISM_SINGEDGE_V12  vertical edges 1-4 and 2-5 are singular 
+  int refprism_singedge_v12_splitedges[][3] =
+    {
+      { 1, 2, 7 },
+      { 1, 3, 8 },
+      { 2, 1, 9 },
+      { 2, 3, 10 },
+      { 4, 5, 11 },
+      { 4, 6, 12 },
+      { 5, 4, 13 },
+      { 5, 6, 14},
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_singedge_v12_newelstypes[] =
+    {
+      HP_HEX,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refprism_singedge_v12_newels[][8] =
+    {
+      { 7, 9, 10, 8, 11, 13, 14, 12 },
+      { 1, 7, 8, 4, 11, 12 },
+      { 2, 10, 9, 5, 14, 13 },
+      { 3, 8, 10, 6, 12, 14 },
+    };
+  HPRef_Struct refprism_singedge_v12 =
+    {
+      HP_PRISM,
+      refprism_singedge_v12_splitedges, 
+      0, 0,
+      refprism_singedge_v12_newelstypes, 
+      refprism_singedge_v12_newels
+    };
+
+
+
+
+
+
+  // HP_PRISM_SINGEDGE_H12
+  int refprism_singedge_h12_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 1, 8 },
+      { 2, 3, 9 },
+      { 3, 1, 10 },
+
+      { 4, 6, 12 },
+      { 5, 4, 13 },
+      { 5, 6, 14 },
+      { 6, 4, 15 },
+
+      { 0, 0, 0 }
+    };
+
+  int refprism_singedge_h12_splitfaces[][4] =
+    {
+      { 2, 1, 3, 11 },
+      { 5, 4, 6, 16 },
+      { 0, 0, 0, 0 },
+    };
+
+  HPREF_ELEMENT_TYPE refprism_singedge_h12_newelstypes[] =
+    {
+      HP_HEX,
+      HP_HEX,
+      HP_PRISM,
+      HP_PRISM,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refprism_singedge_h12_newels[][8] =
+    {
+      { 1, 8, 11, 7, 4, 13, 16, 12 },
+      { 9, 3, 10, 11, 14, 6, 15, 16 },
+      { 7, 11, 10, 12, 16, 15 },
+      { 2, 9, 11, 5, 14, 16 },
+      { 8, 2, 11, 13, 5, 16 }
+    };
+  HPRef_Struct refprism_singedge_h12 =
+    {
+      HP_PRISM,
+      refprism_singedge_h12_splitedges, 
+      refprism_singedge_h12_splitfaces, 
+      0,
+      refprism_singedge_h12_newelstypes, 
+      refprism_singedge_h12_newels
+    };
+
+
+
+
+
+
+  // HP_PRISM_SINGEDGE_H1
+  int refprism_singedge_h1_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 3, 8 },
+      { 4, 6, 9 },
+      { 5, 6, 10 },
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_singedge_h1_newelstypes[] =
+    {
+      HP_HEX,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refprism_singedge_h1_newels[][8] =
+    {
+      { 1, 2, 8, 7, 4, 5, 10, 9 },
+      { 3, 7, 8, 6, 9, 10 }
+    };
+  HPRef_Struct refprism_singedge_h1 =
+    {
+      HP_PRISM,
+      refprism_singedge_h1_splitedges, 
+      0, 0,
+      refprism_singedge_h1_newelstypes, 
+      refprism_singedge_h1_newels
+    };
+
+
+
+//  HP_PRISM_1FA_0E_0V
+  int refprism_1fa_0e_0v_splitedges[][3] =
+    {
+      { 1, 4, 16 },
+      { 2, 5, 17 },
+      { 3, 6, 18 },
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_1fa_0e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_PRISM_1FA_0E_0V,
+      HP_NONE,
+    };
+  int refprism_1fa_0e_0v_newels[][8] =
+    {
+      { 16, 17, 18, 4, 5, 6 },      
+      { 1, 2, 3, 16, 17, 18 }
+    };
+  HPRef_Struct refprism_1fa_0e_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_0e_0v_splitedges, 
+      0, 0,
+      refprism_1fa_0e_0v_newelstypes, 
+      refprism_1fa_0e_0v_newels
+    };
+
+//  HP_PRISM_1FA_1E_0V
+  int refprism_1fa_1e_0v_splitedges[][3] =
+    {
+      { 1, 4, 16 },
+      { 2, 5, 17 },
+      { 3, 6, 18 },
+      { 1, 2, 7},
+      { 1, 3, 12},
+      { 4, 6, 45},
+      { 4, 5, 40},
+      { 0, 0, 0 }
+    };
+  int refprism_1fa_1e_0v_splitfaces[][4] =
+    {
+      {1,2,4,19},
+      {1,3,4,24},
+      {0,0,0,0}
+    }; 
+
+  HPREF_ELEMENT_TYPE refprism_1fa_1e_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE,
+      HP_HEX,
+      HP_PRISM_1FA_1E_0V,
+      HP_HEX_1F_0E_0V,
+      HP_NONE,
+    };
+  int refprism_1fa_1e_0v_newels[][8] =
+    {
+      { 16, 19, 24, 4, 40, 45 }, 
+      { 24, 19,  17, 18, 45 , 40, 5, 6 }, 
+      { 1, 7 , 12 , 16, 19, 24 }, 
+      { 7, 2, 3, 12,  19, 17, 18, 24 }
+    };
+  HPRef_Struct refprism_1fa_1e_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1e_0v_splitedges, 
+      refprism_1fa_1e_0v_splitfaces, 
+      0,
+      refprism_1fa_1e_0v_newelstypes, 
+      refprism_1fa_1e_0v_newels
+    };
+
+//  HP_PRISM_2FA_1E_0V
+  int refprism_2fa_1e_0v_splitedges[][3] =
+    {
+      { 1, 4, 16 },
+      { 2, 5, 17 },
+      { 3, 6, 18 },
+      { 1, 2, 7}, 
+      { 1, 3, 12},
+      { 4, 6, 45},
+      { 4, 5, 40},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      { 0, 0, 0 }
+    };
+  int refprism_2fa_1e_0v_splitfaces[][4] =
+    {
+      {1,2,4,19},
+      {1,3,4,24},
+      {4,1,5,31},
+      {4,1,6,36},
+      {0,0,0,0}
+    }; 
+
+  HPREF_ELEMENT_TYPE refprism_2fa_1e_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE,
+      HP_HEX,
+      HP_PRISM_1FA_1E_0V,
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_1E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_NONE,
+    };
+  int refprism_2fa_1e_0v_newels[][8] =
+    {
+      { 16, 19, 24, 28, 31, 36 }, 
+      { 24, 19,  17, 18, 36, 31, 29, 30 }, 
+      { 1, 7 , 12 , 16, 19, 24 }, 
+      { 12, 7, 2, 3, 24, 19, 17, 18 },
+      { 4, 45, 40, 28, 36, 31 }, 
+      { 40, 45, 6, 5, 31, 36, 30, 29,}
+    };
+  HPRef_Struct refprism_2fa_1e_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_1e_0v_splitedges, 
+      refprism_2fa_1e_0v_splitfaces,
+      0,
+      refprism_2fa_1e_0v_newelstypes, 
+      refprism_2fa_1e_0v_newels
+    };
+
+//  HP_PRISM_1FB_0E_0V   ... quad face 1-2-4-5
+  int refprism_1fb_0e_0v_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 3, 8 },
+      { 4, 6, 9 },
+      { 5, 6, 10 },
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_1fb_0e_0v_newelstypes[] =
+    {
+      HP_HEX_1F_0E_0V,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refprism_1fb_0e_0v_newels[][8] =
+    {
+      { 1, 4, 5, 2, 7, 9, 10, 8  },
+      { 7, 8, 3, 9, 10, 6 }
+    };
+  HPRef_Struct refprism_1fb_0e_0v =
+    {
+      HP_PRISM,
+      refprism_1fb_0e_0v_splitedges, 
+
+      0, 0,
+      refprism_1fb_0e_0v_newelstypes, 
+      refprism_1fb_0e_0v_newels
+    };
+
+
+//  HP_PRISM_1FB_1EA_0V   ... quad face 1-2-4-5
+  int refprism_1fb_1ea_0v_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 3, 8 },
+      { 4, 6, 9 },
+      { 5, 6, 10 },
+      { 1, 2, 11 },
+      { 4, 5, 12 },
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_1fb_1ea_0v_newelstypes[] =
+    {
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refprism_1fb_1ea_0v_newels[][8] =
+    {
+      { 11, 12, 5, 2, 7, 9, 10, 8  },
+      { 1, 11, 7, 4, 12, 9 },
+      { 7, 8, 3, 9, 10, 6 }
+    };
+  HPRef_Struct refprism_1fb_1ea_0v =
+    {
+      HP_PRISM,
+      refprism_1fb_1ea_0v_splitedges, 
+      0, 0,
+      refprism_1fb_1ea_0v_newelstypes, 
+      refprism_1fb_1ea_0v_newels
+    };
+
+//  HP_PRISM_1FB_1EC_0V   ... quad face 1-2-4-5 with singular edge 3-6 
+  int refprism_1fb_1ec_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {3,2,10},
+      {3,1,11},
+      {5,6,42},
+      {4,6,45},
+      {6,5,43},
+      {6,4,44},
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_1fb_1ec_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE, 
+      HP_HEX, 
+      HP_HEX_1F_0E_0V,
+      HP_NONE,
+    };
+  int refprism_1fb_1ec_0v_newels[][8] =
+    {
+      { 3, 11, 10, 6, 44, 43},
+      { 12, 9, 10, 11, 45, 42, 43, 44}, 
+      { 4, 5, 2, 1, 45, 42, 9, 12 } 
+    };
+  HPRef_Struct refprism_1fb_1ec_0v =
+    {
+      HP_PRISM,
+      refprism_1fb_1ec_0v_splitedges, 
+      0, 0,
+      refprism_1fb_1ec_0v_newelstypes, 
+      refprism_1fb_1ec_0v_newels
+    };
+
+//  HP_PRISM_1FA_1FB_1EC_0V   ... bot-trig face, quad face 1-2-4-5 with singular edge 3-6 
+  int refprism_1fa_1fb_1ec_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {3,2,10},
+      {3,1,11},
+      {5,6,42},
+      {4,6,45},
+      {6,5,43},
+      {6,4,44},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      { 0, 0, 0 }
+    };
+
+ int refprism_1fa_1fb_1ec_0v_splitfaces[][4] =
+   {
+     {2,3,5,21},
+     {3,2,6,22},
+     {3,1,6,23},
+     {1,3,4,24},
+     {0,0,0,0}
+   }; 
+  HPREF_ELEMENT_TYPE refprism_1fa_1fb_1ec_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE, 
+      HP_HEX, 
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_1E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_NONE,
+    };
+  int refprism_1fa_1fb_1ec_0v_newels[][8] =
+    {
+      { 18, 23, 22, 6, 44, 43},
+      { 24, 21, 22, 23, 45, 42, 43, 44}, 
+      { 4, 5, 17, 16, 45, 42, 21, 24}, 
+      { 3, 11, 10, 18, 23, 22}, 
+      { 12, 9, 10, 11, 24, 21, 22, 23},
+      { 1, 2, 9, 12, 16, 17, 21, 24}
+    };
+  HPRef_Struct refprism_1fa_1fb_1ec_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1fb_1ec_0v_splitedges,
+      refprism_1fa_1fb_1ec_0v_splitfaces, 0,
+      refprism_1fa_1fb_1ec_0v_newelstypes, 
+      refprism_1fa_1fb_1ec_0v_newels
+    };
+
+
+//  HP_PRISM_1FA_1FB_2EB_0V  
+  int refprism_1fa_1fb_2eb_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {3,2,10},
+      {3,1,11},
+      {5,6,42},
+      {4,6,45},
+      {6,5,43},
+      {6,4,44},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      { 4, 5, 40},
+      { 4, 6, 45},
+      { 1, 2, 7},
+      { 0, 0, 0 }
+    };
+
+ int refprism_1fa_1fb_2eb_0v_splitfaces[][4] =
+   {
+     {2,3,5,21},
+     {3,2,6,22},
+     {3,1,6,23},
+     {1,3,4,24},
+     {1,2,4,19},
+     {0,0,0,0}
+   }; 
+  HPREF_ELEMENT_TYPE refprism_1fa_1fb_2eb_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE, 
+      HP_HEX, 
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_1E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_NONE,
+    };
+  int refprism_1fa_1fb_2eb_0v_newels[][8] =
+    {
+      { 18, 23, 22, 6, 44, 43},
+      { 24, 21, 22, 23, 45, 42, 43, 44}, 
+      { 40, 5, 17, 19, 45, 42, 21, 24}, 
+      { 3, 11, 10, 18, 23, 22}, 
+      { 12, 9, 10, 11, 24, 21, 22, 23},
+      { 7, 2, 9, 12, 19, 17, 21, 24},
+      {16,19,24,4,40,45},
+      {1,7,12,16,19,24}
+    };
+  HPRef_Struct refprism_1fa_1fb_2eb_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1fb_2eb_0v_splitedges, 
+      refprism_1fa_1fb_2eb_0v_splitfaces, 0,
+      refprism_1fa_1fb_2eb_0v_newelstypes, 
+      refprism_1fa_1fb_2eb_0v_newels
+    };
+
+ //  HP_PRISM_1FA_1FB_2EC_0V 
+  int refprism_1fa_1fb_2ec_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {3,2,10},
+      {3,1,11},
+      {5,6,42},
+      {4,6,45},
+      {6,5,43},
+      {6,4,44},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,4,41},
+      {2,1,8},
+      { 0, 0, 0 }
+    };
+
+ int refprism_1fa_1fb_2ec_0v_splitfaces[][4] =
+   {
+     {2,3,5,21},
+     {3,2,6,22},
+     {3,1,6,23},
+     {1,3,4,24},
+     {2,1,5,20},
+     {0,0,0,0}
+   }; 
+  HPREF_ELEMENT_TYPE refprism_1fa_1fb_2ec_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE, 
+      HP_HEX, 
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_1E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_NONE,
+    };
+  int refprism_1fa_1fb_2ec_0v_newels[][8] =
+    {
+      { 18, 23, 22, 6, 44, 43},
+      { 24, 21, 22, 23, 45, 42, 43, 44}, 
+      { 4, 41, 20, 16, 45, 42, 21, 24}, 
+      { 3, 11, 10, 18, 23, 22}, 
+      { 12, 9, 10, 11, 24, 21, 22, 23},
+      { 1, 8, 9, 12, 16, 20, 21, 24}, 
+      {8,2,9,20,17,21}, 
+      {5,41,42,17,20,21}
+    };
+  HPRef_Struct refprism_1fa_1fb_2ec_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1fb_2ec_0v_splitedges, 
+      refprism_1fa_1fb_2ec_0v_splitfaces,
+      0,
+      refprism_1fa_1fb_2ec_0v_newelstypes, 
+      refprism_1fa_1fb_2ec_0v_newels
+    };
+
+
+
+
+
+
+
+//  HP_PRISM_2FA_1FB_1EC_0V   ... trig faces, quad face 1-2-4-5 with singular edge 3-6 
+  int refprism_2fa_1fb_1ec_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {3,2,10},
+      {3,1,11},
+      {5,6,42},
+      {4,6,45},
+      {6,5,43},
+      {6,4,44},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      { 0, 0, 0 }
+    };
+
+ int refprism_2fa_1fb_1ec_0v_splitfaces[][4] =
+   {
+     {2,3,5,21},
+     {3,2,6,22},
+     {3,1,6,23},
+     {1,3,4,24},
+     {5,2,6,33},
+     {6,5,3,34},
+     {6,4,3,35},
+     {4,1,6,36},
+     {0,0,0,0}
+   }; 
+  HPREF_ELEMENT_TYPE refprism_2fa_1fb_1ec_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE, 
+      HP_HEX, 
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_1E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FA_1E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_NONE,
+    };
+  int refprism_2fa_1fb_1ec_0v_newels[][8] =
+    {
+      { 18, 23, 22, 30, 35, 34},
+      { 24, 21, 22, 23, 36, 33, 34, 35}, 
+      { 28, 29, 17, 16, 36, 33, 21, 24}, 
+      { 3, 11, 10, 18, 23, 22}, 
+      { 12, 9, 10, 11, 24, 21, 22, 23},
+      { 1, 2, 9, 12, 16, 17, 21, 24},
+      { 6, 43, 44, 30, 34, 35},
+      { 44, 43, 42, 45, 35, 34, 33, 36}, 
+      { 5, 4, 45, 42, 29, 28, 36, 33 }, 
+    };
+  HPRef_Struct refprism_2fa_1fb_1ec_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_1fb_1ec_0v_splitedges, 
+      refprism_2fa_1fb_1ec_0v_splitfaces,
+      0,
+      refprism_2fa_1fb_1ec_0v_newelstypes, 
+      refprism_2fa_1fb_1ec_0v_newels
+    };
+
+//  HP_PRISM_2FA_1FB_2EB_0V  
+  int refprism_2fa_1fb_2eb_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {3,2,10},
+      {3,1,11},
+      {5,6,42},
+      {4,6,45},
+      {6,5,43},
+      {6,4,44},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      {4,5,40},
+      {1,2,7},
+      { 0, 0, 0 }
+    };
+
+ int refprism_2fa_1fb_2eb_0v_splitfaces[][4] =
+   {
+     {2,3,5,21},
+     {3,2,6,22},
+     {3,1,6,23},
+     {1,3,4,24},
+     {5,6,2,33},
+     {6,5,3,34},
+     {6,4,3,35},
+     {4,1,6,36},
+     {4,1,5,31},
+     {1,2,4,19},
+     {0,0,0,0}
+   }; 
+  HPREF_ELEMENT_TYPE refprism_2fa_1fb_2eb_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE, 
+      HP_HEX, 
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_1E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FA_1E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_NONE,
+    };
+  int refprism_2fa_1fb_2eb_0v_newels[][8] =
+    {
+      { 18, 23, 22, 30, 35, 34},
+      { 24, 21, 22, 23, 36, 33, 34, 35}, 
+      { 31, 29, 17, 19, 36, 33, 21, 24}, 
+      { 3, 11, 10, 18, 23, 22}, 
+      { 12, 9, 10, 11, 24, 21, 22, 23},
+      { 7, 2, 9, 12, 19, 17, 21, 24},
+      { 6, 43, 44, 30, 34, 35},
+      { 44, 43, 42, 45, 35, 34, 33, 36}, 
+      { 5, 40, 45, 42, 29, 31, 36, 33 }, 
+      { 1, 7, 12, 16, 19, 24 }, 
+      { 16, 19, 24, 28, 31, 36 }, 
+      { 40, 4, 45, 31, 28, 36 }, 
+    };
+  HPRef_Struct refprism_2fa_1fb_2eb_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_1fb_2eb_0v_splitedges, 
+      refprism_2fa_1fb_2eb_0v_splitfaces, 0,
+      refprism_2fa_1fb_2eb_0v_newelstypes, 
+      refprism_2fa_1fb_2eb_0v_newels
+    };
+
+//  HP_PRISM_1FB_2EA_0V   ... quad face 1-2-4-5 with singular edges 1-4, 2-5
+  int refprism_1fb_2ea_0v_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 3, 8 },
+      { 1, 2, 9 }, 
+      { 2, 1, 10 }, 
+      { 4, 6, 11 }, 
+      { 5, 6, 12 }, 
+      { 4, 5, 13 }, 
+      { 5, 4, 14 }, 
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_1fb_2ea_0v_newelstypes[] =
+    {
+      HP_PRISM, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V, 
+      HP_NONE,
+    };
+  int refprism_1fb_2ea_0v_newels[][8] =
+    {
+      { 7, 8, 3, 11, 12, 6 }, 
+      { 1, 9, 7, 4, 13, 11 }, 
+      { 13, 14, 10, 9, 11, 12, 8, 7 }, 
+      { 5, 14, 12, 2, 10, 8 }, 
+    };
+  HPRef_Struct refprism_1fb_2ea_0v =
+    {
+      HP_PRISM,
+      refprism_1fb_2ea_0v_splitedges, 
+      0, 0,
+      refprism_1fb_2ea_0v_newelstypes, 
+      refprism_1fb_2ea_0v_newels
+    };
+
+//  HP_PRISM_1FB_2EB_0V   ... quad face 1-2-4-5 with singular edges 1-4, 3-6 
+  int refprism_1fb_2eb_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 4, 5, 40},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0 }
+    };
+HPREF_ELEMENT_TYPE refprism_1fb_2eb_0v_newelstypes[] =
+    {
+      HP_PRISM_SINGEDGE,
+      HP_HEX, 
+      HP_PRISM_1FB_1EA_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_NONE,
+    };
+  int refprism_1fb_2eb_0v_newels[][8] =
+    {
+      { 3, 11, 10, 6, 44, 43 },  
+      { 12, 9, 10, 11, 45, 42, 43, 44}, 
+      { 1, 7, 12, 4, 40, 45}, 
+      { 40, 5, 2, 7, 45, 42, 9, 12}
+    };
+  HPRef_Struct refprism_1fb_2eb_0v =
+    {
+      HP_PRISM,
+      refprism_1fb_2eb_0v_splitedges, 
+      0, 0,
+      refprism_1fb_2eb_0v_newelstypes, 
+      refprism_1fb_2eb_0v_newels
+    };
+
+//  HP_PRISM_1FB_3E_0V   ... quad face 1-2-4-5 with singular edges 1-4, 3-6
+  int refprism_1fb_3e_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_1fb_3e_0v_newelstypes[] =
+    { 
+      HP_PRISM_SINGEDGE,
+      HP_HEX, 
+      HP_PRISM_1FB_1EA_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V, 
+      HP_NONE,
+    };
+  int refprism_1fb_3e_0v_newels[][8] =
+    {
+      { 3, 11, 10, 6, 44, 43 }, 
+      { 12, 9, 10, 11, 45, 42, 43, 44}, 
+      { 1, 7, 12, 4, 40, 45 }, 
+      { 40, 41, 8, 7, 45, 42, 9, 12}, 
+      { 5, 41, 42, 2, 8, 9}, 
+    };
+  HPRef_Struct refprism_1fb_3e_0v =
+    {
+      HP_PRISM,
+      refprism_1fb_3e_0v_splitedges, 
+      0, 0,
+      refprism_1fb_3e_0v_newelstypes, 
+      refprism_1fb_3e_0v_newels
+    };
+
+
+
+//  HP_PRISM_2FB    ... quad face 1-2-4-5 and quad face 1-4-6-3
+  int refprism_2fb_0e_0v_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 3, 8 },
+      { 1, 2, 9 }, 
+      { 3, 2, 10 }, 
+      { 4, 6, 11 },
+      { 5, 6, 12 },
+      { 4, 5, 13 },
+      { 6, 5, 14 },
+      { 0, 0, 0 }
+    };
+ int refprism_2fb_0e_0v_splitfaces[][4] =
+    {
+      { 1, 2, 3, 15 },
+      { 4, 5, 6, 16 },
+      { 0, 0, 0, 0 },
+    };
+  HPREF_ELEMENT_TYPE refprism_2fb_0e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_NONE,
+    };
+  int refprism_2fb_0e_0v_newels[][8] =
+    {
+      { 15, 8, 10, 16, 12, 14 }, 
+      { 13, 5, 2, 9, 16, 12, 8, 15}, 
+      { 11, 7, 3, 6, 16, 15, 10, 14 }, 
+      { 1, 9, 15, 4, 13, 16 }, 
+      { 4, 11, 16, 1,7, 15 }
+    };
+  HPRef_Struct refprism_2fb_0e_0v =
+    {
+      HP_PRISM,
+      refprism_2fb_0e_0v_splitedges, 
+      refprism_2fb_0e_0v_splitfaces,
+      0,
+      refprism_2fb_0e_0v_newelstypes, 
+      refprism_2fb_0e_0v_newels
+    };
+
+//  HP_PRISM_2FB    ... quad face 1-2-4-5 and quad face 1-4-6-3 and sing edge 3-6
+  int refprism_2fb_1ec_0v_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 3, 8 },
+      { 1, 2, 9 }, 
+      { 3, 2, 10 }, 
+      { 4, 6, 11 },
+      { 5, 6, 12 },
+      { 4, 5, 13 },
+      { 6, 5, 14 },
+      { 3, 1, 17},
+      { 6, 4, 18}, 
+      { 0, 0, 0 }
+    };
+ int refprism_2fb_1ec_0v_splitfaces[][4] =
+    {
+      { 1, 2, 3, 15 },
+      { 4, 5, 6, 16 },
+      { 0, 0, 0, 0 },
+    };
+  HPREF_ELEMENT_TYPE refprism_2fb_1ec_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_NONE,
+    };
+  int refprism_2fb_1ec_0v_newels[][8] =
+    {
+      { 15, 8, 10, 16, 12, 14 }, 
+      { 13, 5, 2, 9, 16, 12, 8, 15}, 
+      { 11, 7, 17, 18, 16, 15, 10, 14 }, 
+      { 1, 9, 15, 4, 13, 16 }, 
+      { 4, 11, 16, 1,7, 15 }, 
+      { 3, 17, 10, 6, 18, 14 } 
+    };
+  HPRef_Struct refprism_2fb_1ec_0v =
+    {
+      HP_PRISM,
+      refprism_2fb_1ec_0v_splitedges, 
+      refprism_2fb_1ec_0v_splitfaces,
+      0,
+      refprism_2fb_1ec_0v_newelstypes, 
+      refprism_2fb_1ec_0v_newels
+    };
+
+
+
+//  HP_PRISM_2FB    ... quad face 1-2-4-5 and quad face 1-4-6-3 and 3 sing edges
+  int refprism_2fb_3e_0v_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 3, 8 },
+      { 1, 2, 9 }, 
+      { 3, 2, 10 }, 
+      { 4, 6, 11 },
+      { 5, 6, 12 },
+      { 4, 5, 13 },
+      { 6, 5, 14 },
+      { 3, 1, 17},
+      { 6, 4, 18}, 
+      { 2, 1, 19},
+      { 5, 4, 20}, 
+      { 0, 0, 0 }
+    };
+ int refprism_2fb_3e_0v_splitfaces[][4] =
+    {
+      { 1, 2, 3, 15 },
+      { 4, 5, 6, 16 },
+      { 0, 0, 0, 0 },
+    };
+  HPREF_ELEMENT_TYPE refprism_2fb_3e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_NONE,
+    };
+  int refprism_2fb_3e_0v_newels[][8] =
+    {
+      { 15, 8, 10, 16, 12, 14 }, 
+      { 13, 20, 19, 9, 16, 12, 8, 15}, 
+      { 11, 7, 17, 18, 16, 15, 10, 14 }, 
+      { 1, 9, 15, 4, 13, 16 }, 
+      { 4, 11, 16, 1,7, 15 }, 
+      { 3, 17, 10, 6, 18, 14 }, 
+      { 5, 20, 12, 2, 19, 8 }
+    };
+  HPRef_Struct refprism_2fb_3e_0v =
+    {
+      HP_PRISM,
+      refprism_2fb_3e_0v_splitedges, 
+      refprism_2fb_3e_0v_splitfaces, 0,
+      refprism_2fb_3e_0v_newelstypes, 
+      refprism_2fb_3e_0v_newels
+    };
+
+
+
+//  HP_PRISM_1FA_1FB_0E_0V   ... quad face 1-2-4-5 and trig face 1-2-3
+  int refprism_1fa_1fb_0e_0v_splitedges[][3] = 
+    {
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {2,3,9},
+      {1,3,12},
+      {5,6,42},
+      {4,6,45},
+      {0,0,0}
+    };
+  int refprism_1fa_1fb_0e_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      { 0, 0, 0, 0 }
+    };
+
+HPREF_ELEMENT_TYPE refprism_1fa_1fb_0e_0v_newelstypes[] =
+    {
+      HP_PRISM, 
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_NONE,
+    };
+  int refprism_1fa_1fb_0e_0v_newels[][8] =
+    {
+      { 24, 21, 18, 45, 42, 6 }, 
+      { 4, 5, 17, 16, 45, 42, 21, 24 },
+      { 12, 9, 3, 24, 21, 18 }, 
+      { 1, 2, 9, 12, 16, 17, 21, 24 } 
+    };
+  HPRef_Struct refprism_1fa_1fb_0e_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1fb_0e_0v_splitedges, 
+
+      refprism_1fa_1fb_0e_0v_splitfaces, 0,
+      refprism_1fa_1fb_0e_0v_newelstypes, 
+      refprism_1fa_1fb_0e_0v_newels
+    };
+
+/*
+//  HP_PRISM_1FA_1FB_1EC_0V   ... quad face 1-2-4-5 and trig face 1-2-3
+int refprism_1fa_1fb_1ec_0v_splitedges[][3] =
+    {
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {2,3,9},
+      {1,3,12},
+      {5,6,42},
+      {4,6,45},
+      {6,5,43},
+      {6,4,44},
+      {3,2,10},
+      {3,1,11},
+      {0,0,0}
+    };
+  int refprism_1fa_1fb_1ec_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      { 0, 0, 0, 0 }
+    };
+
+  HPREF_ELEMENT_TYPE refprism_1fa_1fb_1ec_0v_newelstypes[] =
+    {
+      HP_PRISM, 
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_1FA_1E_0V, 
+      HP_PRISM_
+      HP_NONE,
+    };
+  int refprism_1fa_1fb_0e_0v_newels[][8] =
+    {
+      { 24, 21, 18, 45, 42, 6 }, 
+      { 4, 5, 17, 16, 45, 42, 21, 24 },
+      { 12, 9, 3, 24, 21, 18 }, 
+      { 1, 2, 9, 12, 16, 17, 21, 24 } 
+    };
+  HPRef_Struct refprism_1fa_1fb_0e_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1fb_1ec_0v_splitedges, 
+
+      refprism_1fa_1fb_1ec_0v_splitfaces, 0,
+      refprism_1fa_1fb_1ec_0v_newelstypes, 
+      refprism_1fa_1fb_1ec_0v_newels
+    };
+
+
+*/
+
+
+
+
+//  HP_PRISM_2FA_1FB_0E_0V   ... quad face 1-2-4-5 and trig face 1-2-3 
+  int refprism_2fa_1fb_0e_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,6,42},
+      {4,6,45},
+      {4,1,28},
+      {5,2,29},
+      {6,3,30},
+      {0,0,0}
+      
+    };
+  int refprism_2fa_1fb_0e_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      {5,6,2,33},
+      {4,1,6,36},
+      {0,0,0,0}
+    };
+
+  HPREF_ELEMENT_TYPE refprism_2fa_1fb_0e_0v_newelstypes[] =
+    {  
+      HP_HEX_1F_0E_0V,
+      HP_PRISM,
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_NONE,
+    };
+  int refprism_2fa_1fb_0e_0v_newels[][8] =
+    {
+      {28,29,17,16,36,33,21,24}, 
+      {24,21,18, 36, 33, 30}, 
+      {12,9,3,24,21,18},
+      {1,2,9,12,16,17,21,24}, 
+      {6,42,45,30,33,36},
+      {4,5,29,28,45,42,33,36}
+    };
+  HPRef_Struct refprism_2fa_1fb_0e_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_1fb_0e_0v_splitedges, 
+
+      refprism_2fa_1fb_0e_0v_splitfaces, 0,
+      refprism_2fa_1fb_0e_0v_newelstypes, 
+      refprism_2fa_1fb_0e_0v_newels
+    };
+
+
+//  HP_PRISM_1FA_1FB_1EA_0V   ... quad face 1-2-4-5 and trig face 1-2-3 
+  int refprism_1fa_1fb_1ea_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,6,42},
+      {4,6,45},
+      {4,5,40},
+      {1,2,7},
+      {0,0,0}, 
+    };
+  int refprism_1fa_1fb_1ea_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      {1,2,4,19},
+      {0,0,0,0}, 
+    };
+
+  HPREF_ELEMENT_TYPE refprism_1fa_1fb_1ea_0v_newelstypes[] =
+    {
+      HP_HEX_1F_0E_0V,
+      HP_PRISM,
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_NONE
+    };
+  int refprism_1fa_1fb_1ea_0v_newels[][8] =
+    {
+      {40,5,17,19,45,42,21,24}, 
+      {24,21,18,45,42,6},
+      {12,9,3,24,21,18},
+      {7,2,9,12,19,17,21,24}, 
+      {16,19,24,4,40,45},
+      {1,7,12,16,19,24}
+      
+    };
+  HPRef_Struct refprism_1fa_1fb_1ea_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1fb_1ea_0v_splitedges, 
+      refprism_1fa_1fb_1ea_0v_splitfaces, 0,
+      refprism_1fa_1fb_1ea_0v_newelstypes, 
+      refprism_1fa_1fb_1ea_0v_newels
+    };
+
+//  HP_PRISM_2FA_1FB_1EA_0V   
+  int refprism_2fa_1fb_1ea_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,6,42},
+      {4,6,45},
+      {4,1,28},
+      {5,2,29},
+      {6,3,30},
+      {4,5,40},
+      {1,2,7},
+      {0,0,0}, 
+    };
+  int refprism_2fa_1fb_1ea_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      {1,2,4,19},
+      {4,1,6,36},
+      {4,1,5,31},
+      {5,6,2,33},
+      {0,0,0,0}, 
+    };
+
+  HPREF_ELEMENT_TYPE refprism_2fa_1fb_1ea_0v_newelstypes[] =
+    {
+      HP_PRISM, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V, 
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_NONE
+    };
+  int refprism_2fa_1fb_1ea_0v_newels[][8] =
+    {
+      { 18, 24, 21, 30, 36, 33}, 
+      { 31, 29, 17, 19, 36, 33, 21, 24}, 
+      { 16,19, 24, 28, 31, 36 }, 
+      { 3, 12, 9, 18, 24, 21 }, 
+      { 7, 2, 9, 12, 19, 17, 21, 24},  
+      { 1, 7, 12, 16, 19, 24 }, 
+      { 6, 42, 45, 30, 33, 36 }, 
+      { 40, 5, 29, 31, 45, 42, 33, 36 },
+      { 40, 4, 45, 31, 28, 36} 
+    };
+  HPRef_Struct refprism_2fa_1fb_1ea_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_1fb_1ea_0v_splitedges, 
+      refprism_2fa_1fb_1ea_0v_splitfaces, 0,
+      refprism_2fa_1fb_1ea_0v_newelstypes, 
+      refprism_2fa_1fb_1ea_0v_newels
+    };
+
+
+//  HP_PRISM_2FA_1FB_2EA_0V   
+  int refprism_2fa_1fb_2ea_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,6,42},
+      {4,6,45},
+      {4,1,28},
+      {5,2,29},
+      {6,3,30},
+      {4,5,40},
+      {1,2,7},
+      { 5, 4, 41},
+      { 2, 1, 8},
+      {0,0,0}, 
+    };
+  int refprism_2fa_1fb_2ea_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      {1,2,4,19},
+      {4,1,6,36},
+      {4,1,5,31},
+      {5,6,2,33},
+      {5,4,2,32},
+      {2,1,5,20},
+      {0,0,0,0}, 
+    };
+
+  HPREF_ELEMENT_TYPE refprism_2fa_1fb_2ea_0v_newelstypes[] =
+    {
+      HP_PRISM, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V, 
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_NONE
+    };
+  int refprism_2fa_1fb_2ea_0v_newels[][8] =
+    {
+      { 18, 24, 21, 30, 36, 33}, 
+      { 31, 32, 20, 19, 36, 33, 21, 24}, 
+      { 16,19, 24, 28, 31, 36 }, 
+      { 3, 12, 9, 18, 24, 21 }, 
+      {7,8,9,12,19,20,21,24},
+      { 1, 7, 12, 16, 19, 24 }, 
+      { 6, 42, 45, 30, 33, 36 }, 
+      { 40, 41, 32, 31, 45, 42, 33, 36}, 
+      { 40, 4, 45, 31, 28, 36}, 
+      { 8, 2, 9, 20, 17, 21 },  
+      { 29, 32, 33, 17, 20, 21 }, 
+      { 5, 41, 42, 29, 32, 33 }, 
+    };
+  HPRef_Struct refprism_2fa_1fb_2ea_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_1fb_2ea_0v_splitedges, 
+      refprism_2fa_1fb_2ea_0v_splitfaces, 0,
+      refprism_2fa_1fb_2ea_0v_newelstypes, 
+      refprism_2fa_1fb_2ea_0v_newels
+    };
+
+//  HP_PRISM_2FA_1FB_3E_0V   
+  int refprism_2fa_1fb_3e_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      {0,0,0}, 
+    };
+  int refprism_2fa_1fb_3e_0v_splitfaces[][4] = 
+    {
+      {1,2,4,19},
+      {2,1,5,20},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,1,5,31},
+      {5,4,2,32},
+      {5,6,2,33},
+      {6,5,3,34},
+      {6,4,3,35},
+      {4,1,6,36},
+      {0,0,0,0}, 
+    };
+
+  HPREF_ELEMENT_TYPE refprism_2fa_1fb_3e_0v_newelstypes[] =
+    {
+      HP_HEX,
+      HP_PRISM_SINGEDGE,
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V, 
+      HP_PRISM_1FB_1EA_0V, 
+
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1FB_1EB_0V,
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_HEX_1FA_1FB_0E_0V,
+      
+      HP_NONE
+    };
+  int refprism_2fa_1fb_3e_0v_newels[][8] =
+    {
+      {24, 21, 22, 23, 36, 33, 34, 35},
+      {18, 23, 22, 30, 35, 34}, 
+      { 31, 32, 20, 19, 36, 33, 21, 24}, 
+      { 16,19, 24, 28, 31, 36 }, 
+      { 29, 32, 33, 17, 20, 21},
+      
+      
+      { 12, 9,10,11, 24, 21, 22, 23 }, 
+      { 3, 11, 10, 18,23,22}, 
+      { 1, 7, 12 , 16, 19, 24}, 
+      { 8,2,9, 20, 17,21}, 
+      { 7,8,9,12,19, 20, 21, 24}, 
+      
+      { 44, 43, 42, 45, 35, 34, 33, 36}, 
+      { 6, 43, 44, 30, 34, 35}, 
+      { 40, 4, 45, 31,28, 36}, 
+      { 5, 41,42, 29, 32, 33},  
+      { 40, 41, 32, 31, 45, 42, 33, 36}, 
+    };
+  HPRef_Struct refprism_2fa_1fb_3e_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_1fb_3e_0v_splitedges, 
+
+      refprism_2fa_1fb_3e_0v_splitfaces, 0,
+      refprism_2fa_1fb_3e_0v_newelstypes, 
+      refprism_2fa_1fb_3e_0v_newels
+    };
+
+
+
+
+//  HP_PRISM_1FA_1FB_1EB_0V   ... quad face 1-2-4-5 and trig face 1-2-3 
+  int refprism_1fa_1fb_1eb_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,6,42},
+      {4,6,45},
+      {5,4,41},
+      {2,1,8},
+      {0,0,0}, 
+    };
+  int refprism_1fa_1fb_1eb_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      {2,1,5,20},
+      {0,0,0,0}, 
+    };
+
+  HPREF_ELEMENT_TYPE refprism_1fa_1fb_1eb_0v_newelstypes[] =
+    {
+      HP_HEX_1F_0E_0V,
+      HP_PRISM,
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V ,
+      HP_NONE
+    };
+  int refprism_1fa_1fb_1eb_0v_newels[][8] =
+    {
+      {4,41,20,16,45,42,21,24}, 
+      {24,21,18,45,42,6},
+      {12,9,3,24,21,18},
+      {1,8,9,12,16,20,21,24},
+      {5,41,42,17,20,21},
+      {8,2,9,20,17,21}
+    };
+  HPRef_Struct refprism_1fa_1fb_1eb_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1fb_1eb_0v_splitedges, 
+
+       refprism_1fa_1fb_1eb_0v_splitfaces, 0,
+      refprism_1fa_1fb_1eb_0v_newelstypes, 
+      refprism_1fa_1fb_1eb_0v_newels
+    };
+
+
+//  HP_PRISM_1FA_1FB_2EA_0V   ... quad face 1-2-4-5 and trig face 1-2-3 
+  int refprism_1fa_1fb_2ea_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,6,42},
+      {4,6,45},
+      {5,4,41},
+      {2,1,8},
+      {4,5,40},
+      {1,2,7},
+      {0,0,0},
+
+    };
+  int refprism_1fa_1fb_2ea_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      {2,1,5,20},
+      {1,2,4,19},
+      {0,0,0,0},
+    };
+
+  HPREF_ELEMENT_TYPE refprism_1fa_1fb_2ea_0v_newelstypes[] =
+    {
+      HP_HEX_1F_0E_0V,
+      HP_PRISM,
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V ,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_NONE
+    };
+  int refprism_1fa_1fb_2ea_0v_newels[][8] =
+    {
+      {40,41,20,19,45,42,21,24}, 
+      {24,21,18,45,42,6},
+      {12,9,3,24,21,18},
+      {7,8,9,12,19,20,21,24},
+      {5,41,42,17,20,21},
+      {8,2,9,20,17,21},
+      {16,19,24,4,40,45},
+      {1,7,12,16,19,24}
+    };
+  HPRef_Struct refprism_1fa_1fb_2ea_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1fb_2ea_0v_splitedges, 
+
+      refprism_1fa_1fb_2ea_0v_splitfaces, 0,
+      refprism_1fa_1fb_2ea_0v_newelstypes, 
+      refprism_1fa_1fb_2ea_0v_newels
+    };
+
+
+//  HP_PRISM_1FA_1FB_3E_0V   
+  int refprism_1fa_1fb_3e_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,6,42},
+      {4,6,45},
+      {5,4,41},
+      {2,1,8},
+      {4,5,40},
+      {1,2,7},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      {0,0,0},
+
+    };
+  int refprism_1fa_1fb_3e_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      {2,1,5,20},
+      {1,2,4,19},
+      {3,2,6,22},
+      {3,1,6,23},
+      {0,0,0,0},
+    };
+
+  HPREF_ELEMENT_TYPE refprism_1fa_1fb_3e_0v_newelstypes[] =
+    {
+      HP_HEX_1F_0E_0V,
+      HP_HEX,
+      HP_PRISM_SINGEDGE,
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V ,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_NONE
+    };
+  int refprism_1fa_1fb_3e_0v_newels[][8] =
+    {
+      {40,41,20,19,45,42,21,24}, 
+      {24, 21, 22, 23, 45, 42, 43, 44},
+      {18, 23, 22, 6, 44, 43}, 
+      {12, 9, 10, 11, 24, 21, 22, 23}, 
+      {3, 11, 10, 18, 23, 22}, 
+      {7,8,9,12,19,20,21,24},
+      {5,41,42,17,20,21},
+      {8,2,9,20,17,21},
+      {16,19,24,4,40,45},
+      {1,7,12,16,19,24}
+    };
+  HPRef_Struct refprism_1fa_1fb_3e_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_1fb_3e_0v_splitedges, 
+
+      refprism_1fa_1fb_3e_0v_splitfaces, 0,
+      refprism_1fa_1fb_3e_0v_newelstypes, 
+      refprism_1fa_1fb_3e_0v_newels
+    };
+
+
+
+
+
+
+
+
+//  HP_PRISM_2FA_0E_0V  singular trig faces
+  int refprism_2fa_0e_0v_splitedges[][3] =
+    {
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {4,1,28},
+      {5,2,29},
+      {6,3,30},
+      {0,0,0}
+    };
+  
+HPREF_ELEMENT_TYPE refprism_2fa_0e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_NONE
+    };
+  int refprism_2fa_0e_0v_newels[][8] =
+    {
+      {16,17,18,28,29,30},
+      {1,2,3,16,17,18},
+      {4,6,5,28,30,29}, 
+    };
+
+HPRef_Struct refprism_2fa_0e_0v = 
+
+    {
+      HP_PRISM,
+      refprism_2fa_0e_0v_splitedges, 
+      0, 0,
+      refprism_2fa_0e_0v_newelstypes, 
+      refprism_2fa_0e_0v_newels
+    };
+
+
+
+
+
+//  HP_PRISM_1FA_2FB    ... quad face 1-2-4-5 and quad face 1-4-6-3
+int refprism_1fa_2fb_0e_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 5, 40},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 4, 6, 45},
+      { 0, 0, 0 }
+    };
+int refprism_1fa_2fb_0e_0v_splitfaces[][4] =
+    {
+      {1,2,3,13},
+      {1,2,4,19},
+      {2,3,5,21},
+      {3,2,6,22},
+      {1,3,4,24},
+      {4,5,6,46},
+      { 0, 0, 0, 0 }
+    };
+int refprism_1fa_2fb_0e_0v_splitelement[][5] = 
+  {
+    {1,2,3,4,25}, 
+    {0,0,0,0,0} 
+  };
+  
+HPREF_ELEMENT_TYPE refprism_1fa_2fb_0e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V,
+      HP_NONE,
+    };
+  int refprism_1fa_2fb_0e_0v_newels[][8] =
+    {
+      { 25, 21, 22, 46, 42, 43 }, 
+      { 40, 5, 17, 19, 46, 42, 21, 25 }, 
+      { 24, 18, 6, 45, 25, 22, 43, 46}, 
+      { 16, 19, 25, 4, 40, 46 }, 
+      { 4, 45, 46, 16, 24, 25 }, 
+      { 13, 9, 10, 25, 21, 22 }, 
+      { 7, 2, 9, 13, 19, 17, 21, 25 }, 
+      { 3, 12, 13, 10, 18, 24, 25, 22 }, 
+      { 1, 7, 13, 16, 19, 25 }, 
+      { 12, 1, 13, 24, 16, 25 }
+      
+    };
+  HPRef_Struct refprism_1fa_2fb_0e_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_2fb_0e_0v_splitedges, 
+      refprism_1fa_2fb_0e_0v_splitfaces, 
+      refprism_1fa_2fb_0e_0v_splitelement, 
+      refprism_1fa_2fb_0e_0v_newelstypes, 
+      refprism_1fa_2fb_0e_0v_newels
+    };
+
+//  HP_PRISM_1FA_2FB_1EC    ... quad face 1-2-4-5 and quad face 1-4-6-3
+int refprism_1fa_2fb_1ec_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 5, 40},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0 }
+    };
+int refprism_1fa_2fb_1ec_0v_splitfaces[][4] =
+    {
+      {1,2,3,13},
+      {1,2,4,19},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,5,6,46},
+      { 0, 0, 0, 0 }
+    };
+int refprism_1fa_2fb_1ec_0v_splitelement[][5] = 
+  {
+    {1,2,3,4,25}, 
+    {0,0,0,0,0} 
+  };
+  
+HPREF_ELEMENT_TYPE refprism_1fa_2fb_1ec_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V, 
+      
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V,
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      
+      HP_NONE,
+    };
+  int refprism_1fa_2fb_1ec_0v_newels[][8] =
+    {
+      { 25, 21, 22, 46, 42, 43 }, 
+      { 40, 5, 17, 19, 46, 42, 21, 25 }, 
+      { 24, 23, 44, 45, 25, 22, 43, 46}, 
+      { 16, 19, 25, 4, 40, 46 }, 
+      { 4, 45, 46, 16, 24, 25 }, 
+      { 18, 23, 22, 6, 44, 43}, 
+
+
+      { 13, 9, 10, 25, 21, 22 }, 
+      { 7, 2, 9, 13, 19, 17, 21, 25 }, 
+      { 11, 12, 13, 10, 23, 24, 25, 22 }, 
+      { 1, 7, 13, 16, 19, 25 }, 
+      { 12, 1, 13, 24, 16, 25 }, 
+      { 3, 11, 10, 18, 23, 22},
+      
+    };
+  HPRef_Struct refprism_1fa_2fb_1ec_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_2fb_1ec_0v_splitedges, 
+      refprism_1fa_2fb_1ec_0v_splitfaces, 
+      refprism_1fa_2fb_1ec_0v_splitelement, 
+      refprism_1fa_2fb_1ec_0v_newelstypes, 
+      refprism_1fa_2fb_1ec_0v_newels
+    };
+
+
+//  HP_PRISM_1FA_2FB_3E    ... quad face 1-2-4-5 and quad face 1-4-6-3
+int refprism_1fa_2fb_3e_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0 }
+    };
+int refprism_1fa_2fb_3e_0v_splitfaces[][4] =
+    {
+      {1,2,3,13},
+      {1,2,4,19},
+      {2,1,5,20},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,5,6,46},
+      { 0, 0, 0, 0 }
+    };
+int refprism_1fa_2fb_3e_0v_splitelement[][5] = 
+  {
+    {1,2,3,4,25}, 
+    {0,0,0,0,0} 
+  };
+  
+HPREF_ELEMENT_TYPE refprism_1fa_2fb_3e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      
+      
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V,
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V,
+      
+      HP_NONE,
+    };
+  int refprism_1fa_2fb_3e_0v_newels[][8] =
+    {
+      { 25, 21, 22, 46, 42, 43 }, 
+      { 40, 41, 20, 19, 46, 42, 21, 25 }, 
+      { 24, 23, 44, 45, 25, 22, 43, 46}, 
+      { 16, 19, 25, 4, 40, 46 }, 
+      { 4, 45, 46, 16, 24, 25 }, 
+      { 18, 23, 22, 6, 44, 43}, 
+      { 5, 41, 42, 17, 20, 21}, 
+      
+
+      { 13, 9, 10, 25, 21, 22 }, 
+      { 7, 8, 9, 13, 19, 20, 21, 25 }, 
+      { 11, 12, 13, 10, 23, 24, 25, 22 }, 
+      { 1, 7, 13, 16, 19, 25 }, 
+      
+      { 12, 1, 13, 24, 16, 25 }, 
+      { 3, 11, 10, 18, 23, 22},
+      { 8, 2, 9, 20, 17, 21}, 
+      
+    };
+  HPRef_Struct refprism_1fa_2fb_3e_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_2fb_3e_0v_splitedges, 
+      refprism_1fa_2fb_3e_0v_splitfaces, 
+      refprism_1fa_2fb_3e_0v_splitelement, 
+      refprism_1fa_2fb_3e_0v_newelstypes, 
+      refprism_1fa_2fb_3e_0v_newels
+    };
+
+
+
+
+
+
+
+
+
+//  HP_PRISM_1FA_2FB_1eb    ... quad face 1-2-4-5 and quad face 1-4-6-3
+int refprism_1fa_2fb_1eb_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 4, 6, 45},
+      { 0, 0, 0 }
+    };
+int refprism_1fa_2fb_1eb_0v_splitfaces[][4] =
+    {
+      {1,2,3,13},
+      {1,2,4,19},
+      {2,1,5,20},
+      {2,3,5,21},
+      {3,2,6,22},
+      {1,3,4,24},
+      {4,5,6,46},
+      { 0, 0, 0, 0 }
+    };
+int refprism_1fa_2fb_1eb_0v_splitelement[][5] = 
+  {
+    {1,2,3,4,25}, 
+    {0,0,0,0,0} 
+  };
+
+
+HPREF_ELEMENT_TYPE refprism_1fa_2fb_1eb_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V,
+      
+      HP_NONE,
+    };
+
+  int refprism_1fa_2fb_1eb_0v_newels[][8] =
+    {
+      { 25, 21, 22, 46, 42, 43 }, 
+      { 40, 41, 20, 19, 46, 42, 21, 25 }, 
+      { 24, 18, 6, 45, 25, 22, 43, 46}, 
+      { 16, 19, 25, 4, 40, 46 },
+      { 4, 45, 46, 16, 24, 25 }, 
+      { 5, 41, 42, 17, 20, 21 },
+
+
+      { 13, 9, 10, 25, 21, 22 }, 
+      { 7, 8, 9, 13, 19, 20, 21, 25 }, 
+      { 3, 12, 13, 10, 18, 24, 25, 22 }, 
+      { 1, 7, 13, 16, 19, 25 },  
+      { 12, 1, 13, 24, 16, 25 }, 
+      { 8, 2, 9, 20, 17, 21}, 
+      
+    };
+  HPRef_Struct refprism_1fa_2fb_1eb_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_2fb_1eb_0v_splitedges, 
+      refprism_1fa_2fb_1eb_0v_splitfaces, 
+      refprism_1fa_2fb_1eb_0v_splitelement, 
+      refprism_1fa_2fb_1eb_0v_newelstypes, 
+      refprism_1fa_2fb_1eb_0v_newels
+    };
+
+
+
+
+
+
+//  HP_PRISM_2FA_2FB 
+int refprism_2fa_2fb_0e_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 5, 40},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 4, 6, 45},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      { 0, 0, 0 }
+    };
+int refprism_2fa_2fb_0e_0v_splitfaces[][4] =
+    {
+      {1,2,3,13},
+      {1,2,4,19},
+      {2,3,5,21},
+      {3,2,6,22},
+      {1,3,4,24},
+      {4,5,6,46},  
+      {4,1,5,31},
+      {5,6,2,33},
+      {6,5,3,34},
+      {4,1,6,36},
+      { 0, 0, 0, 0 }
+    };
+int refprism_2fa_2fb_0e_0v_splitelement[][5] = 
+  {
+    {1,2,3,4,25}, 
+    {4,1,6,5,37},
+    {0,0,0,0,0} 
+  };
+  
+HPREF_ELEMENT_TYPE refprism_2fa_2fb_0e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V,
+      
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V,
+      HP_PRISM_1FA_1FB_1EA_0V,
+      
+      HP_NONE,
+    };
+  int refprism_2fa_2fb_0e_0v_newels[][8] =
+    {
+      { 25, 21, 22, 37, 33, 34}, 
+      { 31, 29, 17, 19, 37, 33, 21, 25}, 
+      { 36, 24, 18, 30, 37, 25, 22, 34}, 
+      { 16, 19, 25, 28, 31, 37}, 
+      { 28, 36, 37, 16, 24, 25},
+      
+      { 13, 9, 10, 25, 21, 22 }, 
+      { 7, 2, 9, 13, 19, 17, 21, 25 }, 
+      { 3, 12, 13, 10, 18, 24, 25, 22 }, 
+      { 1, 7, 13, 16, 19, 25 }, 
+      { 12, 1, 13, 24, 16, 25 }, 
+
+      {  46, 43, 42 ,37, 34, 33},
+      { 40, 5, 29, 31, 46, 42, 33, 37 }, 
+      { 6, 45, 36, 30, 43, 46, 37, 34 }, 
+      { 40, 4, 46, 31, 28, 37 }, 
+      { 4, 45, 46, 28, 36, 37},  
+      
+    };
+  HPRef_Struct refprism_2fa_2fb_0e_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_2fb_0e_0v_splitedges, 
+      refprism_2fa_2fb_0e_0v_splitfaces, 
+      refprism_2fa_2fb_0e_0v_splitelement, 
+      refprism_2fa_2fb_0e_0v_newelstypes, 
+      refprism_2fa_2fb_0e_0v_newels
+    };
+
+
+//  HP_PRISM_2FA_2FB_1EC 
+int refprism_2fa_2fb_1ec_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      { 4, 5, 40},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0 }
+    };
+int refprism_2fa_2fb_1ec_0v_splitfaces[][4] =
+    {
+      {1,2,3,13},
+      {1,2,4,19},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,5,6,46},  
+      {4,1,5,31},
+      {5,6,2,33},
+      {6,5,3,34},
+      {6,4,3,35},
+      {4,1,6,36},
+      { 0, 0, 0, 0 }
+    };
+int refprism_2fa_2fb_1ec_0v_splitelement[][5] = 
+  {
+    {1,2,3,4,25}, 
+    {4,1,6,5,37},
+    {0,0,0,0,0} 
+  };
+  
+HPREF_ELEMENT_TYPE refprism_2fa_2fb_1ec_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V,
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V,
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      
+      HP_NONE,
+    };
+  int refprism_2fa_2fb_1ec_0v_newels[][8] =
+    {
+      { 25, 21, 22, 37, 33, 34}, 
+      { 31, 29, 17, 19, 37, 33, 21, 25}, 
+      { 36, 24, 23, 35, 37, 25, 22, 34}, 
+      { 16, 19, 25, 28, 31, 37}, 
+      { 28, 36, 37, 16, 24, 25},
+      { 18, 23, 22, 30, 35, 34}, 
+            
+      { 13, 9, 10, 25, 21, 22 }, 
+      { 7, 2, 9, 13, 19, 17, 21, 25 }, 
+      { 11, 12, 13, 10, 23, 24, 25, 22 }, 
+      { 1, 7, 13, 16, 19, 25 }, 
+      { 12, 1, 13, 24, 16, 25 }, 
+      { 3, 11, 10, 18, 23, 22 }, 
+
+      { 46, 43, 42 ,37, 34, 33},
+      { 40, 5, 29, 31, 46, 42, 33, 37 }, 
+      { 44, 45, 36, 35, 43, 46, 37, 34 }, 
+      { 40, 4, 46, 31, 28, 37 }, 
+      { 4, 45, 46, 28, 36, 37},  
+      { 44, 6, 43, 35, 30, 34}, 
+      
+    };
+  HPRef_Struct refprism_2fa_2fb_1ec_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_2fb_1ec_0v_splitedges, 
+      refprism_2fa_2fb_1ec_0v_splitfaces, 
+      refprism_2fa_2fb_1ec_0v_splitelement, 
+      refprism_2fa_2fb_1ec_0v_newelstypes, 
+      refprism_2fa_2fb_1ec_0v_newels
+    };
+
+
+
+//  HP_PRISM_2FA_2FB_3E 
+int refprism_2fa_2fb_3e_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0 }
+    };
+int refprism_2fa_2fb_3e_0v_splitfaces[][4] =
+    {
+      {1,2,3,13},
+      {1,2,4,19},
+      {2,1,5,20},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,5,6,46},  
+      {4,1,5,31},
+      {5,4,2,32},
+      {5,6,2,33},
+      {6,5,3,34},
+      {6,4,3,35},
+      {4,1,6,36},
+      { 0, 0, 0, 0 }
+    };
+int refprism_2fa_2fb_3e_0v_splitelement[][5] = 
+  {
+    {1,2,3,4,25}, 
+    {4,1,6,5,37},
+    {0,0,0,0,0} 
+  };
+  
+HPREF_ELEMENT_TYPE refprism_2fa_2fb_3e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V, 
+      
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V,
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V,
+      HP_PRISM_1FA_1FB_1EA_0V,
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      
+      HP_NONE,
+    };
+  int refprism_2fa_2fb_3e_0v_newels[][8] =
+    {
+      { 25, 21, 22, 37, 33, 34}, 
+      { 31, 32, 20, 19, 37, 33, 21, 25}, 
+      { 36, 24, 23, 35, 37, 25, 22, 34}, 
+      { 16, 19, 25, 28, 31, 37}, 
+      { 28, 36, 37, 16, 24, 25},
+      { 18, 23, 22, 30, 35, 34}, 
+      { 29, 32, 33, 17, 20, 21}, 
+            
+      { 13, 9, 10, 25, 21, 22 }, 
+      { 7, 8, 9, 13, 19, 20, 21, 25 }, 
+      { 11, 12, 13, 10, 23, 24, 25, 22 }, 
+      { 1, 7, 13, 16, 19, 25 }, 
+      { 12, 1, 13, 24, 16, 25 }, 
+      { 3, 11, 10, 18, 23, 22 }, 
+      { 8, 2, 9, 20, 17, 21 }, 
+
+      { 46, 43, 42 ,37, 34, 33},
+      { 40, 41, 32, 31, 46, 42, 33, 37 }, 
+      { 44, 45, 36, 35, 43, 46, 37, 34 }, 
+      { 40, 4, 46, 31, 28, 37 }, 
+      { 4, 45, 46, 28, 36, 37},  
+      { 44, 6, 43, 35, 30, 34},
+      { 5, 41, 42, 29, 32, 33}, 
+      
+    };
+  HPRef_Struct refprism_2fa_2fb_3e_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_2fb_3e_0v_splitedges, 
+      refprism_2fa_2fb_3e_0v_splitfaces, 
+      refprism_2fa_2fb_3e_0v_splitelement, 
+      refprism_2fa_2fb_3e_0v_newelstypes, 
+      refprism_2fa_2fb_3e_0v_newels
+    };
+
+
+
+
+//  HP_PRISM_1FA_2E_0V  
+  int refprism_1fa_2e_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,6,42},
+      {4,6,45},
+      {5,4,41},
+      {2,1,8},
+      {4,5,40},
+      {1,2,7},
+      {0,0,0},
+
+    };
+  int refprism_1fa_2e_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      {2,1,5,20},
+      {1,2,4,19},
+      {0,0,0,0},
+    };
+
+  HPREF_ELEMENT_TYPE refprism_1fa_2e_0v_newelstypes[] =
+    {
+      HP_HEX,
+      HP_PRISM,
+      HP_PRISM_1FA_0E_0V, 
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_SINGEDGE, 
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_1FA_1E_0V,
+      HP_NONE
+    };
+  int refprism_1fa_2e_0v_newels[][8] =
+    {
+      {40,41,20,19,45,42,21,24}, 
+      {24,21,18,45,42,6},
+      {12,9,3,24,21,18},
+      {9, 12, 7, 8, 21, 24, 19, 20}, 
+      { 17, 21, 20, 5, 42, 41},
+      {2, 9, 8, 17, 21, 20},
+      {16,19,24,4,40,45},
+      {1,7,12,16,19,24}
+    };
+  HPRef_Struct refprism_1fa_2e_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_2e_0v_splitedges, 
+
+      refprism_1fa_2e_0v_splitfaces, 0,
+      refprism_1fa_2e_0v_newelstypes, 
+      refprism_1fa_2e_0v_newels
+    };
+    
+//  HP_PRISM_2FA_2E_0V   
+  int refprism_2fa_2e_0v_splitedges[][3] =
+    {
+      {2,3,9},
+      {1,3,12},
+      {1,4,16}, 
+      {2,5,17},
+      {3,6,18},
+      {5,6,42},
+      {4,6,45},
+      {4,1,28},
+      {5,2,29},
+      {6,3,30},
+      {4,5,40},
+      {1,2,7},
+      { 5, 4, 41},
+      { 2, 1, 8},
+      {0,0,0}, 
+    };
+  int refprism_2fa_2e_0v_splitfaces[][4] = 
+    {
+      {2,3,5,21},
+      {1,3,4,24},
+      {1,2,4,19},
+      {4,1,6,36},
+      {4,1,5,31},
+      {5,6,2,33},
+      {5,4,2,32},
+      {2,1,5,20},
+      {0,0,0,0}, 
+    };
+
+  HPREF_ELEMENT_TYPE refprism_2fa_2e_0v_newelstypes[] =
+    {
+      HP_PRISM, 
+      HP_HEX, 
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE, 
+      
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FA_1E_0V, 
+      HP_PRISM_1FA_1E_0V, 
+
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FA_1E_0V, 
+      HP_PRISM_1FA_1E_0V, 
+      HP_NONE,
+      
+    };
+  int refprism_2fa_2e_0v_newels[][8] =
+    {
+      { 24, 21, 18, 36, 33, 30}, 
+      { 19, 20, 21, 24, 31, 32, 33, 36}, 
+      { 16, 19, 24, 28, 31, 36}, 
+      { 17, 21, 20, 29, 33, 32}, 
+      
+      { 12, 9, 3, 24, 21, 18}, 
+      { 7, 8, 9, 12, 19, 20, 21, 24}, 
+      { 1, 7, 12, 16, 19, 24},
+      { 2, 9, 8, 17, 21, 20}, 
+      
+      { 45, 6, 42, 36, 30, 33}, 
+      { 40, 45, 42, 41, 31, 36, 33, 32}, 
+      { 4, 45, 40, 28, 36, 31 }, 
+      { 5, 41, 42, 29, 32, 33 },
+    };
+  HPRef_Struct refprism_2fa_2e_0v =
+    {
+      HP_PRISM,
+      refprism_2fa_2e_0v_splitedges, 
+      refprism_2fa_2e_0v_splitfaces, 0,
+      refprism_2fa_2e_0v_newelstypes, 
+      refprism_2fa_2e_0v_newels
+    };
+
+
+ 
+//  HP_PRISM_3E_0V   
+  int refprism_3e_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0}, 
+    };
+  int refprism_3e_0v_splitfaces[][4] = 
+    {
+      {1,2,3,13},
+      {2,3,1,14},
+      {3,1,2,15},
+      {4,5,6,46},
+      {5,4,6,47},
+      {6,4,5,48},
+      {0,0,0,0}, 
+    };
+
+  HPREF_ELEMENT_TYPE refprism_3e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX,
+      HP_HEX,
+      HP_HEX,
+      HP_PRISM,
+      HP_PRISM,
+      HP_PRISM,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE,
+      HP_NONE
+    };
+  int refprism_3e_0v_newels[][8] =
+    {
+      { 13, 14, 15, 46, 47, 48}, 
+      { 7, 8, 14, 13, 40, 41, 47, 46}, 
+      { 15, 14, 9, 10, 48, 47, 42, 43}, 
+      { 12, 13, 15, 11, 45, 46, 48, 44}, 
+      { 14, 8, 9, 47, 41, 42 }, 
+      { 11, 15, 10, 44, 48, 43 }, 
+      { 7, 13, 12, 40, 46, 45}, 
+      { 1, 7, 12, 4, 40, 45}, 
+      { 2, 9, 8, 5, 42, 41 }, 
+      { 3, 11, 10, 6, 44, 43 }
+    };
+  HPRef_Struct refprism_3e_0v =
+    {
+      HP_PRISM,
+      refprism_3e_0v_splitedges, 
+      refprism_3e_0v_splitfaces, 0,
+      refprism_3e_0v_newelstypes, 
+      refprism_3e_0v_newels
+    };
+
+
+//  HP_PRISM_3E_0V   
+int refprism_1fa_3e_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      
+      { 0, 0, 0}, 
+    };
+int refprism_1fa_3e_0v_splitfaces[][4] = 
+    {
+      {1,2,3,13},
+      {2,3,1,14},
+      {3,1,2,15},
+      {1,2,4,19},
+      {2,1,5,20},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,5,6,46},
+      {5,4,6,47},
+      {6,4,5,48}, 
+      {0,0,0,0}, 
+    };
+
+int refprism_1fa_3e_0v_splitelements[][5] = 
+  {
+      {1,2,3,4,25},
+      {2,1,3,5,26},
+      {3,1,2,6,27}, 
+      {0,0,0,0,0},
+  };
+
+  HPREF_ELEMENT_TYPE refprism_1fa_3e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX,
+      HP_HEX,
+      HP_HEX,
+      HP_PRISM,
+      HP_PRISM,
+      HP_PRISM,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE,
+
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_NONE
+    };
+int refprism_1fa_3e_0v_newels[][8] =
+    {
+      { 25, 26, 27, 46, 47, 48}, 
+      { 19, 20, 26, 25, 40, 41, 47, 46},  
+      { 27, 26, 21, 22, 48, 47, 42, 43}, 
+      { 23, 24, 25, 27, 44, 45, 46, 48}, 
+      { 19, 25, 24, 40, 46, 45}, 
+      { 26, 20, 21, 47, 41, 42},
+      { 23, 27, 22, 44, 48, 43}, 
+      { 16, 19, 24, 4, 40, 45}, 
+      { 17, 21, 20, 5, 42, 41}, 
+      { 18, 23, 22, 6, 44, 43}, 
+
+      { 13, 14, 15, 25, 26, 27}, 
+      { 7, 8, 14, 13, 19, 20, 26, 25},
+      { 15, 14, 9, 10, 27, 26, 21, 22}, 
+      { 12, 13, 15, 11, 24, 25, 27, 23}, 
+      { 14, 8, 9, 26, 20, 21}, 
+      { 11, 15, 10, 23, 27, 22}, 
+      { 7, 13 , 12, 19, 25, 24}, 
+      { 2, 9, 8, 17, 21, 20}, 
+      { 3, 11, 10, 18, 23, 22}, 
+      { 1, 7, 12, 16, 19, 24}, 
+    };
+  HPRef_Struct refprism_1fa_3e_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_3e_0v_splitedges, 
+      refprism_1fa_3e_0v_splitfaces, 
+      refprism_1fa_3e_0v_splitelements, 
+      refprism_1fa_3e_0v_newelstypes, 
+      refprism_1fa_3e_0v_newels
+    };
+
+
+
+//  HP_PRISM_2FA_3E_0V   
+int refprism_2fa_3e_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0}, 
+    };
+int refprism_2fa_3e_0v_splitfaces[][4] = 
+    {
+      {1,2,3,13},
+      {2,3,1,14},
+      {3,1,2,15},
+      {1,2,4,19},
+      {2,1,5,20},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,1,5,31},
+      {5,4,2,32},
+      {5,6,2,33},
+      {6,5,3,34},
+      {6,4,3,35},
+      {4,1,6,36},
+      {4,5,6,46},
+      {5,4,6,47},
+      {6,4,5,48}, 
+      {0,0,0,0}, 
+    };
+
+int refprism_2fa_3e_0v_splitelements[][5] = 
+  {
+      {1,2,3,4,25},
+      {2,1,3,5,26},
+      {3,1,2,6,27}, 
+      {4,1,6,5,37},
+      {5,2,4,6,38},
+      {6,4,5,3,39}, 
+      {0,0,0,0,0},
+  };
+
+  HPREF_ELEMENT_TYPE refprism_2fa_3e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX,
+      HP_HEX,
+      HP_HEX,
+      HP_PRISM,
+      HP_PRISM,
+      HP_PRISM,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE,
+
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+
+      HP_NONE
+    };
+
+  int refprism_2fa_3e_0v_newels[][8] =
+    {
+      { 25, 26, 27, 37, 38, 39}, 
+      { 19, 20, 26, 25, 31, 32, 38, 37},  
+      { 27, 26, 21, 22, 39, 38, 33, 34}, 
+      { 23, 24, 25, 27, 35, 36, 37, 39}, 
+      { 19, 25, 24, 31, 37, 36}, 
+      { 26, 20, 21, 38, 32, 33},
+      { 23, 27, 22, 35, 39, 34}, 
+      { 16, 19, 24, 28, 31, 36}, 
+      { 17, 21, 20, 29, 33, 32}, 
+      { 18, 23, 22, 30, 35, 34}, 
+
+      { 13, 14, 15, 25, 26, 27}, 
+      { 7, 8, 14, 13, 19, 20, 26, 25},
+      { 15, 14, 9, 10, 27, 26, 21, 22}, 
+      { 12, 13, 15, 11, 24, 25, 27, 23}, 
+      { 14, 8, 9, 26, 20, 21}, 
+      { 11, 15, 10, 23, 27, 22}, 
+      { 7, 13 , 12, 19, 25, 24}, 
+      { 2, 9, 8, 17, 21, 20}, 
+      { 3, 11, 10, 18, 23, 22}, 
+      { 1, 7, 12, 16, 19, 24}, 
+
+      { 48, 47, 46, 39, 38, 37 }, 
+      { 48, 43, 42, 47, 39, 34, 33, 38}, 
+      { 45, 44, 48, 46, 36, 35, 39, 37},
+      { 46, 47, 41, 40, 37, 38, 32, 31}, 
+      { 47, 42, 41, 38, 33, 32}, 
+      { 45, 46, 40, 36, 37, 31}, 
+      { 44, 43, 48, 35, 34, 39},
+      { 6, 43, 44, 30, 34, 35}, 
+      { 5, 41, 42, 29, 32, 33}, 
+      { 4, 45, 40, 28, 36, 31},
+    };
+
+HPRef_Struct refprism_2fa_3e_0v =
+  {
+    HP_PRISM,
+    refprism_2fa_3e_0v_splitedges, 
+    refprism_2fa_3e_0v_splitfaces, 
+    refprism_2fa_3e_0v_splitelements, 
+    refprism_2fa_3e_0v_newelstypes, 
+    refprism_2fa_3e_0v_newels
+  };
+
+
+
+//  HP_PRISM_3FB_0V   
+  int refprism_3fb_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0}, 
+    };
+  int refprism_3fb_0v_splitfaces[][4] = 
+    {
+      {1,2,3,13},
+      {2,3,1,14},
+      {3,1,2,15},
+      {4,5,6,46},
+      {5,4,6,47},
+      {6,4,5,48},
+      {0,0,0,0}, 
+    };
+
+  HPREF_ELEMENT_TYPE refprism_3fb_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V,
+      HP_HEX_1F_0E_0V,
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_NONE
+    };
+  int refprism_3fb_0v_newels[][8] =
+    {
+      { 13, 14, 15, 46, 47, 48}, 
+      { 8, 7, 40, 41, 14,13, 46, 47 }, 
+      { 10, 9, 42, 43, 15, 14, 47, 48 }, 
+      { 44, 45, 12, 11, 48, 46, 13, 15}, 
+      { 1, 7, 13, 4, 40, 46 }, 
+      { 4, 45, 46, 1, 12, 13}, 
+      { 2, 9, 14, 5, 42, 47 }, 
+      { 5, 41, 47, 2, 8, 14 }, 
+      { 3, 11, 15, 6, 44, 48}, 
+      { 6, 43, 48, 3, 10, 15},
+
+    };
+  HPRef_Struct refprism_3fb_0v =
+    {
+      HP_PRISM,
+      refprism_3fb_0v_splitedges, 
+      refprism_3fb_0v_splitfaces, 0,
+      refprism_3fb_0v_newelstypes, 
+      refprism_3fb_0v_newels
+    };
+
+
+//  HP_PRISM_3FB_0V   
+int refprism_1fa_3fb_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0}, 
+    };
+int refprism_1fa_3fb_0v_splitfaces[][4] = 
+    {
+      {1,2,3,13},
+      {2,3,1,14},
+      {3,1,2,15},
+      {1,2,4,19},
+      {2,1,5,20},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,5,6,46},
+      {5,4,6,47},
+      {6,4,5,48}, 
+      {0,0,0,0}, 
+    };
+
+int refprism_1fa_3fb_0v_splitelements[][5] = 
+  {
+      {1,2,3,4,25},
+      {2,1,3,5,26},
+      {3,1,2,6,27}, 
+      {0,0,0,0,0},
+  };
+
+  HPREF_ELEMENT_TYPE refprism_1fa_3fb_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX_1F_0E_0V,
+      HP_HEX_1F_0E_0V,
+      HP_HEX_1F_0E_0V,
+      
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      
+      HP_NONE
+    };
+  int refprism_1fa_3fb_0v_newels[][8] =
+    {
+      { 25, 26, 27, 46, 47, 48}, 
+      { 19, 40, 41, 20, 25, 46, 47, 26}, 
+      { 22, 21, 42, 43, 27, 26, 47, 48}, 
+      { 24, 23, 44, 45, 25, 27, 48, 46},
+      
+      { 16, 19, 25, 4, 40, 46 }, 
+      { 4, 45, 46, 16, 24, 25 }, 
+      { 17, 21, 26, 5, 42, 47 }, 
+      { 5, 41, 47, 17, 20, 26}, 
+      { 18, 23, 27, 6, 44, 48}, 
+      { 6, 43, 48, 18, 22, 27},
+
+      { 13, 14, 15, 25, 26, 27}, 
+      { 7, 8, 14, 13, 19, 20, 26, 25}, 
+      { 9, 10, 15, 14, 21, 22, 27, 26},
+      { 11, 12, 13, 15, 23, 24, 25, 27},
+
+      { 2, 9, 14, 17, 21, 26}, 
+      { 8, 2, 14, 20, 17, 26}, 
+      { 1, 7, 13, 16, 19, 25}, 
+      { 12, 1, 13, 24, 16, 25 }, 
+      { 3, 11, 15, 18, 23, 27 },
+      { 10, 3, 15, 22, 18, 27}, 
+      
+      };
+  HPRef_Struct refprism_1fa_3fb_0v =
+    {
+      HP_PRISM,
+      refprism_1fa_3fb_0v_splitedges, 
+      refprism_1fa_3fb_0v_splitfaces, 
+      refprism_1fa_3fb_0v_splitelements, 
+      refprism_1fa_3fb_0v_newelstypes, 
+      refprism_1fa_3fb_0v_newels
+    };
+     
+
+
+//  HP_PRISM_2FA_3E_0V   
+int refprism_2fa_3fb_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0}, 
+    };
+int refprism_2fa_3fb_0v_splitfaces[][4] = 
+    {
+      {1,2,3,13},
+      {2,3,1,14},
+      {3,1,2,15},
+      {1,2,4,19},
+      {2,1,5,20},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,1,5,31},
+      {5,4,2,32},
+      {5,6,2,33},
+      {6,5,3,34},
+      {6,4,3,35},
+      {4,1,6,36},
+      {4,5,6,46},
+      {5,4,6,47},
+      {6,4,5,48}, 
+      {0,0,0,0}, 
+    };
+
+int refprism_2fa_3fb_0v_splitelements[][5] = 
+  {
+      {1,2,3,4,25},
+      {2,1,3,5,26},
+      {3,1,2,6,27}, 
+      {4,1,6,5,37},
+      {5,2,4,6,38},
+      {6,4,5,3,39}, 
+      {0,0,0,0,0},
+  };
+
+  HPREF_ELEMENT_TYPE refprism_2fa_3fb_0v_newelstypes[] =
+    {
+
+      HP_PRISM,
+      HP_HEX_1F_0E_0V,
+      HP_HEX_1F_0E_0V,
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V, 
+      HP_HEX_1FA_1FB_0E_0V,
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+      HP_PRISM_1FA_1FB_1EA_0V, 
+      HP_PRISM_1FA_1FB_1EB_0V, 
+
+      HP_NONE
+    };
+  int refprism_2fa_3fb_0v_newels[][8] =
+    {
+      { 25, 26, 27, 37, 38, 39}, 
+      { 19, 31, 32, 20, 25, 37, 38, 26}, 
+      { 33, 34, 22, 21, 38, 39, 27, 26}, 
+      { 35, 36, 24, 23, 39, 37, 25, 27}, 
+
+      { 16, 19, 25, 28, 31, 37}, 
+      { 28, 36, 37, 16, 24, 25 }, 
+      { 17, 21, 26, 29, 33, 38 }, 
+      { 29, 32, 38, 17, 20, 26}, 
+      { 18, 23, 27, 30, 35, 39}, 
+      { 30, 34, 39, 18, 22, 27},
+
+ 
+      { 13, 14, 15, 25, 26, 27}, 
+      { 7, 8, 14, 13, 19, 20, 26, 25}, 
+      { 9, 10, 15, 14, 21, 22, 27, 26},
+      { 11, 12, 13, 15, 23, 24, 25, 27},
+
+      { 2, 9, 14, 17, 21, 26}, 
+      { 8, 2, 14, 20, 17, 26}, 
+      { 1, 7, 13, 16, 19, 25}, 
+      { 12, 1, 13, 24, 16, 25 }, 
+      { 3, 11, 15, 18, 23, 27 },
+      { 10, 3, 15, 22, 18, 27}, 
+
+      
+      { 48, 47, 46, 39, 38, 37 }, 
+      { 44, 45, 36, 35, 48, 46, 37, 39}, 
+      { 40, 41, 32, 31, 46, 47, 38, 37}, 
+      { 42, 43, 34, 33, 47, 48, 39, 38}, 
+      
+      { 6, 43, 48, 30, 34, 39}, 
+      { 44, 6, 48, 35, 30, 39}, 
+      { 4, 45, 46, 28, 36, 37}, 
+      { 40, 4, 46, 31, 28, 37}, 
+      { 5, 41, 47, 29, 32, 38}, 
+      { 42, 5, 47, 33, 29, 38},
+    };
+
+HPRef_Struct refprism_2fa_3fb_0v =
+  {
+    HP_PRISM,
+    refprism_2fa_3fb_0v_splitedges, 
+    refprism_2fa_3fb_0v_splitfaces, 
+    refprism_2fa_3fb_0v_splitelements, 
+    refprism_2fa_3fb_0v_newelstypes, 
+    refprism_2fa_3fb_0v_newels
+  };
+
+
+/* 
+
+
+//  HP_PRISM_3E_4EH
+int refprism_3e_4eh_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0},
+
+    };
+int refprism_3e_4eh_splitfaces[][4] = 
+    {
+      {3,1,2,15},
+      {6,4,5,48}, 
+      {0,0,0,0}, 
+    };
+
+HPREF_ELEMENT_TYPE refprism_2fa_3fb_0v_newelstypes[] =
+  {
+    HP_PRISM, 
+    HP_HEX_2EH_0V,
+    HP_HEX_2EH_0V,
+    HP_TET_2E,
+    HP_TET_2E,
+    HP_PRISM_1E_2EH_0V, 
+    HP_PRISM_1E_2EH_0V, 
+    HP_NONE
+    };
+  int refprism_2fa_3fb_0v_newels[][8] =
+    {
+      {15, 7, 8, 48, 40, 41 }, 
+      
+    };
+
+HPRef_Struct refprism_2fa_3fb_0v =
+  {
+    HP_PRISM,
+    refprism_2fa_3fb_0v_splitedges, 
+    refprism_2fa_3fb_0v_splitfaces, 
+    refprism_2fa_3fb_0v_splitelements, 
+    refprism_2fa_3fb_0v_newelstypes, 
+    refprism_2fa_3fb_0v_newels
+  };
+*/ 
+
+/*
+//  HP_PRISM_2FA_3E_0V   
+int refprism_3e_4_0v_splitedges[][3] =
+    {
+      { 1, 2, 7},
+      { 2, 1, 8},
+      { 2, 3, 9},
+      { 3, 2, 10},
+      { 3, 1, 11},
+      { 1, 3, 12},
+      { 1, 4, 16}, 
+      { 2, 5, 17},
+      { 3, 6, 18},
+      { 4, 1, 28},
+      { 5, 2, 29},
+      { 6, 3, 30},
+      { 4, 5, 40},
+      { 5, 4, 41},
+      { 5, 6, 42},
+      { 6, 5, 43},
+      { 6, 4, 44},
+      { 4, 6, 45},
+      { 0, 0, 0}, 
+    };
+int refprism_2fa_3e_0v_splitfaces[][4] = 
+    {
+      {1,2,3,13},
+      {2,3,1,14},
+      {3,1,2,15},
+      {1,2,4,19},
+      {2,1,5,20},
+      {2,3,5,21},
+      {3,2,6,22},
+      {3,1,6,23},
+      {1,3,4,24},
+      {4,1,5,31},
+      {5,4,2,32},
+      {5,6,2,33},
+      {6,5,3,34},
+      {6,4,3,35},
+      {4,1,6,36},
+      {4,5,6,46},
+      {5,4,6,47},
+      {6,4,5,48}, 
+      {0,0,0,0}, 
+    };
+
+int refprism_2fa_3e_0v_splitelements[][5] = 
+  {
+      {1,2,3,4,25},
+      {2,1,3,5,26},
+      {3,1,2,6,27}, 
+      {4,1,6,5,37},
+      {5,2,4,6,38},
+      {6,4,5,3,39}, 
+      {0,0,0,0,0},
+  };
+
+  HPREF_ELEMENT_TYPE refprism_2fa_3e_0v_newelstypes[] =
+    {
+      HP_PRISM,
+      HP_HEX,
+      HP_HEX,
+      HP_HEX,
+      HP_PRISM,
+      HP_PRISM,
+      HP_PRISM,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE,
+      HP_PRISM_SINGEDGE,
+
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+
+      HP_PRISM_1FA_0E_0V,
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_HEX_1F_0E_0V, 
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+      HP_PRISM_1FA_1E_0V,
+
+      HP_NONE
+    };
+
+  int refprism_2fa_3e_0v_newels[][8] =
+    {
+      { 25, 26, 27, 37, 38, 39}, 
+      { 19, 20, 26, 25, 31, 32, 38, 37},  
+      { 27, 26, 21, 22, 39, 38, 33, 34}, 
+      { 23, 24, 25, 27, 35, 36, 37, 39}, 
+      { 19, 25, 24, 31, 37, 36}, 
+      { 26, 20, 21, 38, 32, 33},
+      { 23, 27, 22, 35, 39, 34}, 
+      { 16, 19, 24, 28, 31, 36}, 
+      { 17, 21, 20, 29, 33, 32}, 
+      { 18, 23, 22, 30, 35, 34}, 
+
+      { 13, 14, 15, 25, 26, 27}, 
+      { 7, 8, 14, 13, 19, 20, 26, 25},
+      { 15, 14, 9, 10, 27, 26, 21, 22}, 
+      { 12, 13, 15, 11, 24, 25, 27, 23}, 
+      { 14, 8, 9, 26, 20, 21}, 
+      { 11, 15, 10, 23, 27, 22}, 
+      { 7, 13 , 12, 19, 25, 24}, 
+      { 2, 9, 8, 17, 21, 20}, 
+      { 3, 11, 10, 18, 23, 22}, 
+      { 1, 7, 12, 16, 19, 24}, 
+
+      { 48, 47, 46, 39, 38, 37 }, 
+      { 48, 43, 42, 47, 39, 34, 33, 38}, 
+      { 45, 44, 48, 46, 36, 35, 39, 37},
+      { 46, 47, 41, 40, 37, 38, 32, 31}, 
+      { 47, 42, 41, 38, 33, 32}, 
+      { 45, 46, 40, 36, 37, 31}, 
+      { 44, 43, 48, 35, 34, 39},
+      { 6, 43, 44, 30, 34, 35}, 
+      { 5, 41, 42, 29, 32, 33}, 
+      { 4, 45, 40, 28, 36, 31},
+    };
+
+HPRef_Struct refprism_2fa_3e_0v =
+  {
+    HP_PRISM,
+    refprism_2fa_3e_0v_splitedges, 
+    refprism_2fa_3e_0v_splitfaces, 
+    refprism_2fa_3e_0v_splitelements, 
+    refprism_2fa_3e_0v_newelstypes, 
+    refprism_2fa_3e_0v_newels
+  };
+
+*/
+/*
+
+//  HP_PRISM_1FB_1EB_0V   ... quad face 1-2-4-5
+  int refprism_1fb_1eb_0v_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 3, 8 },
+      { 4, 6, 9 },
+      { 5, 6, 10 },
+      { 2, 1, 11 },
+      { 5, 4, 12 },
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_1fb_1eb_0v_newelstypes[] =
+    {
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FB_1EB_0V,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refprism_1fb_1eb_0v_newels[][8] =
+    {
+      { 1, 4, 12, 11, 7, 9, 10, 8  },
+      { 11, 2, 8, 12, 5, 10 },
+      { 7, 8, 3, 9, 10, 6 }
+    };
+  HPRef_Struct refprism_1fb_1eb_0v =
+    {
+      HP_PRISM,
+      refprism_1fb_1eb_0v_splitedges, 
+      0, 0,
+      refprism_1fb_1eb_0v_newelstypes, 
+      refprism_1fb_1eb_0v_newels
+    };
+
+
+
+
+
+
+
+
+
+
+  // HP_PRISM_2F_0E_0V
+  int refprism_2f_0e_0v_splitedges[][3] =
+    {
+      { 1, 3, 7 },
+      { 2, 1, 8 },
+      { 2, 3, 9 },
+      { 3, 1, 10 },
+
+      { 4, 6, 12 },
+      { 5, 4, 13 },
+      { 5, 6, 14 },
+      { 6, 4, 15 },
+
+      { 0, 0, 0 }
+    };
+
+  int refprism_2f_0e_0v_splitfaces[][4] =
+    {
+      { 2, 1, 3, 11 },
+      { 5, 4, 6, 16 },
+      { 0, 0, 0, 0 },
+    };
+
+  HPREF_ELEMENT_TYPE refprism_2f_0e_0v_newelstypes[] =
+    {
+      HP_HEX_1F_0E_0V,
+      HP_HEX_1F_0E_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM_1FB_1EA_0V,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refprism_2f_0e_0v_newels[][8] =
+    {
+      //{ 1, 8, 11, 7, 4, 13, 16, 12 },
+      // { 9, 3, 10, 11, 14, 6, 15, 16 },
+      { 1, 4, 13, 8, 7, 12, 16, 11 },
+      { 9, 14, 6, 3, 11, 16, 15, 10 },
+      { 2, 9, 11, 5, 14, 16 },
+      // { 8, 2, 11, 13, 5, 16 },
+      { 5, 13, 16, 2, 8, 11 },
+      { 7, 11, 10, 12, 16, 15 }
+    };
+  HPRef_Struct refprism_2f_0e_0v =
+    {
+      HP_PRISM,
+      refprism_2f_0e_0v_splitedges, 
+      refprism_2f_0e_0v_splitfaces, 
+      0,
+      refprism_2f_0e_0v_newelstypes, 
+      refprism_2f_0e_0v_newels
+    };
+
+*/
diff --git a/contrib/Netgen/libsrc/meshing/hpref_pyramid.hpp b/contrib/Netgen/libsrc/meshing/hpref_pyramid.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..521daf5081e84bf11891b835a8af358cd2af5c84
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_pyramid.hpp
@@ -0,0 +1,118 @@
+
+  // HP_PYRAMID
+  int refpyramid_splitedges[][3] =
+    {
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refpyramid_newelstypes[] =
+    {
+      HP_PYRAMID,
+      HP_NONE,
+    };
+  int refpyramid_newels[][8] =
+    {
+      { 1, 2, 3, 4, 5 }
+    };
+  HPRef_Struct refpyramid =
+    {
+      HP_PYRAMID,
+      refpyramid_splitedges, 
+      0, 0,
+      refpyramid_newelstypes, 
+      refpyramid_newels
+    };
+
+
+// singular point 1      
+  // HP_PYRAMID_0E_1V
+  int refpyramid_0e_1v_splitedges[][3] =
+    {
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refpyramid_0e_1v_newelstypes[] =
+    {
+      HP_TET_0E_1V,
+      HP_TET,
+      HP_NONE,
+    };
+  int refpyramid_0e_1v_newels[][8] =
+    {
+      { 1, 2, 4, 5 },
+      { 2, 3, 4, 5 },
+    };
+  HPRef_Struct refpyramid_0e_1v =
+    {
+      HP_PYRAMID,
+      refpyramid_0e_1v_splitedges, 
+      0, 0,
+      refpyramid_0e_1v_newelstypes, 
+      refpyramid_0e_1v_newels
+    };
+
+
+// singular edges 1-2 1-4 singular point 1 
+  // HP_PYRAMID_EDGES
+  int refpyramid_edges_splitedges[][3] =
+    {
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refpyramid_edges_newelstypes[] =
+    {
+      HP_TET_1E_1VA,
+      HP_TET_1E_1VA,
+      HP_NONE,
+    };
+  int refpyramid_edges_newels[][8] =
+    {
+      { 1, 2, 3, 5 },
+      { 1, 4, 5, 3 },
+    };
+  HPRef_Struct refpyramid_edges =
+    {
+      HP_PYRAMID,
+      refpyramid_edges_splitedges, 
+      0, 0,
+      refpyramid_edges_newelstypes, 
+      refpyramid_edges_newels
+    };
+
+
+
+// singular face 1-2-5 singular point 5
+  // HP_PYRAMID_1FB_0E_1VA
+  int refpyramid_1fb_0e_1va_splitedges[][3] =
+    {
+      { 1, 4, 6 },
+      { 2, 3, 7 },
+      { 5, 1, 8 },
+      { 5, 2, 9 },
+      { 5, 3, 10 },
+      { 5, 4, 11 },
+      { 0, 0, 0 },
+    };
+
+  HPREF_ELEMENT_TYPE refpyramid_1fb_0e_1va_newelstypes[] =
+    {
+      HP_HEX_1F_0E_0V,
+      HP_PYRAMID_1FB_0E_1VA,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refpyramid_1fb_0e_1va_newels[][8] =
+    {
+      { 1, 8, 9, 2, 6, 11, 10, 7 },
+      { 8, 9, 10, 11, 5 },
+      { 3, 7, 10, 4, 6, 11 }
+    };
+  HPRef_Struct refpyramid_1fb_0e_1va =
+    {
+      HP_PYRAMID,
+      refpyramid_1fb_0e_1va_splitedges, 
+      0, 0,
+      refpyramid_1fb_0e_1va_newelstypes, 
+      refpyramid_1fb_0e_1va_newels
+    };
+
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/hpref_quad.hpp b/contrib/Netgen/libsrc/meshing/hpref_quad.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a23156d1c66e7bc1efeb637a60de8756e77946f
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_quad.hpp
@@ -0,0 +1,2082 @@
+// HP_QUAD
+int refquad_splitedges[][3] =
+{
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_newelstypes[] =
+{
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_newels[][8] =
+{
+  { 1, 2, 3, 4 },
+};
+HPRef_Struct refquad =
+{
+  HP_QUAD,
+  refquad_splitedges, 
+  0, 0,
+  refquad_newelstypes, 
+  refquad_newels
+};
+
+
+
+
+
+
+
+// HP_QUAD_SINGCORNER
+int refquad_singcorner_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_singcorner_newelstypes[] =
+{
+  HP_TRIG_SINGCORNER,
+  HP_QUAD,
+  HP_TRIG,
+  HP_NONE,
+};
+int refquad_singcorner_newels[][8] =
+{
+  { 1, 5, 6 },
+  { 2, 4, 6, 5 },
+  { 2, 3, 4 },
+};
+HPRef_Struct refquad_singcorner =
+{
+  HP_QUAD,
+  refquad_singcorner_splitedges, 
+  0, 0,
+  refquad_singcorner_newelstypes, 
+  refquad_singcorner_newels
+};
+
+
+
+
+
+// HP_DUMMY_QUAD_SINGCORNER
+int refdummyquad_singcorner_splitedges[][3] =
+{
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refdummyquad_singcorner_newelstypes[] =
+{
+  HP_TRIG_SINGCORNER,
+  HP_TRIG,
+  HP_NONE,
+};
+int refdummyquad_singcorner_newels[][8] =
+{
+  { 1, 2, 4 },
+  { 4, 2, 3 },
+};
+HPRef_Struct refdummyquad_singcorner =
+{
+  HP_QUAD,
+  refdummyquad_singcorner_splitedges, 
+  0, 0,
+  refdummyquad_singcorner_newelstypes, 
+  refdummyquad_singcorner_newels
+};
+
+
+
+
+
+
+
+// HP_QUAD_SINGEDGE
+int refquad_singedge_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_singedge_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_singedge_newels[][8] =
+{
+  { 1, 2, 6, 5 },
+  { 5, 6, 3, 4 },
+};
+HPRef_Struct refquad_singedge =
+{
+  HP_QUAD,
+  refquad_singedge_splitedges, 
+  0, 0,
+  refquad_singedge_newelstypes, 
+  refquad_singedge_newels
+};
+
+
+
+
+
+
+// HP_QUAD_0E_2VA
+int refquad_0e_2va_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_0e_2va_newelstypes[] =
+{
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_0e_2va_newels[][8] =
+{
+  { 1, 5, 6 },
+  { 2, 8, 7 },
+  { 5, 7, 8, 6 },
+  { 6, 8, 3, 4 },
+};
+HPRef_Struct refquad_0e_2va =
+{
+  HP_QUAD,
+  refquad_0e_2va_splitedges, 
+  0, 0,
+  refquad_0e_2va_newelstypes, 
+  refquad_0e_2va_newels
+};
+
+
+
+// HP_QUAD_0E_2VB
+int refquad_0e_2vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 3, 4, 7 },
+  { 3, 2, 8 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_0e_2vb_newelstypes[] =
+{
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_0e_2vb_newels[][8] =
+{
+  { 1, 5, 6 },
+  { 3, 7, 8 },
+  { 5, 2, 4, 6 },
+  { 2, 8, 7, 4 },
+};
+HPRef_Struct refquad_0e_2vb =
+{
+  HP_QUAD,
+  refquad_0e_2vb_splitedges, 
+  0, 0,
+  refquad_0e_2vb_newelstypes, 
+  refquad_0e_2vb_newels
+};
+
+
+
+
+// HP_QUAD_0E_3V
+int refquad_0e_3v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 3, 2, 9 },
+  { 3, 4, 10 },
+  { 0, 0, 0 }
+};
+
+int refquad_0e_3v_splitfaces[][4] =
+{
+  { 2, 3, 1, 14 },
+  { 0, 0, 0, 0 },
+};
+
+HPREF_ELEMENT_TYPE refquad_0e_3v_newelstypes[] =
+{
+  HP_TRIG_SINGCORNER,
+  HP_DUMMY_QUAD_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD,
+  HP_QUAD,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_0e_3v_newels[][8] =
+{
+  { 1, 5, 6 },
+  { 2, 8, 14, 7 },
+  { 3, 10, 9 },
+  { 5, 7, 14, 6 },
+  { 8, 9, 10, 14 },
+  { 6, 14, 10, 4 },
+};
+HPRef_Struct refquad_0e_3v =
+{
+  HP_QUAD,
+  refquad_0e_3v_splitedges, 
+  refquad_0e_3v_splitfaces, 
+  0,
+  refquad_0e_3v_newelstypes, 
+  refquad_0e_3v_newels
+};
+
+
+
+
+// HP_QUAD_0E_4V
+int refquad_0e_4v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 3, 2, 9 },
+  { 3, 4, 10 },
+  { 4, 1, 11 },
+  { 4, 3, 12 },
+  { 0, 0, 0 }
+};
+
+int refquad_0e_4v_splitfaces[][4] =
+{
+  { 1, 2, 4, 13 },
+  { 2, 3, 1, 14 },
+  { 3, 4, 2, 15 },
+  { 4, 1, 3, 16 },
+  { 0, 0, 0, 0 },
+};
+
+HPREF_ELEMENT_TYPE refquad_0e_4v_newelstypes[] =
+{
+  HP_DUMMY_QUAD_SINGCORNER,
+  HP_DUMMY_QUAD_SINGCORNER,
+  HP_DUMMY_QUAD_SINGCORNER,
+  HP_DUMMY_QUAD_SINGCORNER,
+
+  HP_QUAD,
+  HP_QUAD,
+  HP_QUAD,
+  HP_QUAD,
+
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_0e_4v_newels[][8] =
+{
+  { 1, 5, 13, 6 },
+  { 2, 8, 14, 7 },
+  { 3, 10, 15, 9 },
+  { 4, 11, 16, 12 },
+  { 5, 7, 14, 13 },
+  { 8, 9, 15, 14 },
+  { 10, 12, 16, 15 },
+  { 11, 6, 13, 16 },
+  { 13, 14, 15, 16 }
+};
+HPRef_Struct refquad_0e_4v =
+{
+  HP_QUAD,
+  refquad_0e_4v_splitedges, 
+  refquad_0e_4v_splitfaces, 
+  0,
+  refquad_0e_4v_newelstypes, 
+  refquad_0e_4v_newels
+};
+
+
+
+
+
+
+
+
+// HP_QUAD_1E_1VA
+int refquad_1e_1va_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_1va_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_NONE,
+};
+int refquad_1e_1va_newels[][8] =
+{
+  { 7, 2, 6, 5 },
+  { 5, 6, 3, 4 },
+  { 1, 7, 5 },
+};
+HPRef_Struct refquad_1e_1va =
+{
+  HP_QUAD,
+  refquad_1e_1va_splitedges, 
+  0, 0,
+  refquad_1e_1va_newelstypes, 
+  refquad_1e_1va_newels
+};
+
+
+
+
+// HP_QUAD_1E_1VB
+int refquad_1e_1vb_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 2, 1, 7 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_1vb_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_NONE,
+};
+int refquad_1e_1vb_newels[][8] =
+{
+  { 1, 7, 6, 5 },
+  { 5, 6, 3, 4 },
+  { 7, 2, 6 },
+};
+HPRef_Struct refquad_1e_1vb =
+{
+  HP_QUAD,
+  refquad_1e_1vb_splitedges, 
+  0, 0,
+  refquad_1e_1vb_newelstypes, 
+  refquad_1e_1vb_newels
+};
+
+
+
+// HP_QUAD_1E_1VC
+int refquad_1e_1vc_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 3, 4, 8 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_1vc_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int refquad_1e_1vc_newels[][8] =
+{
+  { 1, 2, 6, 5 },
+  { 5, 6, 4 },
+  { 4, 6, 7, 8 },
+  { 3, 8, 7 }
+};
+HPRef_Struct refquad_1e_1vc =
+{
+  HP_QUAD,
+  refquad_1e_1vc_splitedges, 
+  0, 0,
+  refquad_1e_1vc_newelstypes, 
+  refquad_1e_1vc_newels
+};
+
+
+
+// HP_QUAD_1E_1VD
+int refquad_1e_1vd_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 4, 1, 7 },
+  { 4, 3, 8 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_1vd_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int refquad_1e_1vd_newels[][8] =
+{
+  { 1, 2, 6, 5 },
+  { 5, 6, 3 },
+  { 5, 3, 8, 7 },
+  { 4, 7, 8 }
+};
+HPRef_Struct refquad_1e_1vd =
+{
+  HP_QUAD,
+  refquad_1e_1vd_splitedges, 
+  0, 0,
+  refquad_1e_1vd_newelstypes, 
+  refquad_1e_1vd_newels
+};
+
+
+
+
+
+
+
+// HP_QUAD_1E_2VA
+int refquad_1e_2va_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 2, 1, 8 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_2va_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_NONE,
+};
+int refquad_1e_2va_newels[][8] =
+{
+  { 7, 8, 6, 5 },
+  { 5, 6, 3, 4 },
+  { 1, 7, 5 },
+  { 8, 2, 6 }
+};
+HPRef_Struct refquad_1e_2va =
+{
+  HP_QUAD,
+  refquad_1e_2va_splitedges, 
+  0, 0,
+  refquad_1e_2va_newelstypes, 
+  refquad_1e_2va_newels
+};
+
+
+
+
+// HP_QUAD_1E_2VB
+int refquad_1e_2vb_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 3, 2, 8 },
+  { 3, 4, 9 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_2vb_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int refquad_1e_2vb_newels[][8] =
+{
+  { 7, 2, 6, 5 },
+  { 1, 7, 5 },
+  { 5, 6, 4 },
+  { 4, 6, 8, 9 },
+  { 3, 9, 8 }
+};
+HPRef_Struct refquad_1e_2vb =
+{
+  HP_QUAD,
+  refquad_1e_2vb_splitedges, 
+  0, 0,
+  refquad_1e_2vb_newelstypes, 
+  refquad_1e_2vb_newels
+};
+
+
+
+
+// HP_QUAD_1E_2VC
+int refquad_1e_2vc_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 4, 1, 8 },
+  { 4, 3, 9 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_2vc_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int refquad_1e_2vc_newels[][8] =
+{
+  { 7, 2, 6, 5 },
+  { 1, 7, 5 },
+  { 5, 6, 3 },
+  { 5, 3, 9, 8 },
+  { 4, 8, 9 }
+};
+HPRef_Struct refquad_1e_2vc =
+{
+  HP_QUAD,
+  refquad_1e_2vc_splitedges, 
+  0, 0,
+  refquad_1e_2vc_newelstypes, 
+  refquad_1e_2vc_newels
+};
+
+
+
+
+// HP_QUAD_1E_2VD
+int refquad_1e_2vd_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 2, 1, 7 },
+  { 3, 2, 8 },
+  { 3, 4, 9 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_2vd_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int refquad_1e_2vd_newels[][8] =
+{
+  { 1, 7, 6, 5 },
+  { 7, 2, 6 },
+  { 5, 6, 4 },
+  { 4, 6, 8, 9 },
+  { 3, 9, 8 }
+};
+HPRef_Struct refquad_1e_2vd =
+{
+  HP_QUAD,
+  refquad_1e_2vd_splitedges, 
+  0, 0,
+  refquad_1e_2vd_newelstypes, 
+  refquad_1e_2vd_newels
+};
+
+
+
+
+
+// HP_QUAD_1E_2VE
+int refquad_1e_2ve_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 2, 1, 7 },
+  { 4, 1, 8 },
+  { 4, 3, 9 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_2ve_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int refquad_1e_2ve_newels[][8] =
+{
+  { 1, 7, 6, 5 },
+  { 7, 2, 6 },
+  { 5, 6, 3 },
+  { 5, 3, 9, 8 },
+  { 4, 8, 9 }
+};
+HPRef_Struct refquad_1e_2ve =
+{
+  HP_QUAD,
+  refquad_1e_2ve_splitedges, 
+  0, 0,
+  refquad_1e_2ve_newelstypes, 
+  refquad_1e_2ve_newels
+};
+
+
+
+
+
+
+// HP_QUAD_1E_2VF
+int refquad_1e_2vf_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 4, 1, 7 },
+  { 4, 3, 8 },
+  { 3, 2, 9 },
+  { 3, 4, 10 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_2vf_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int refquad_1e_2vf_newels[][8] =
+{
+  { 1, 2, 6, 5 },
+  { 5, 6, 9, 7 },
+  { 7, 9, 10, 8 },
+  { 4, 7, 8 },
+  { 3, 10, 9 },
+};
+HPRef_Struct refquad_1e_2vf =
+{
+  HP_QUAD,
+  refquad_1e_2vf_splitedges, 
+  0, 0,
+  refquad_1e_2vf_newelstypes, 
+  refquad_1e_2vf_newels
+};
+
+
+
+
+
+// HP_QUAD_1E_3VA
+int refquad_1e_3va_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 2, 1, 8 },
+  { 3, 2, 9 },
+  { 3, 4, 10 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_3va_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG,
+  HP_NONE,
+};
+int refquad_1e_3va_newels[][8] =
+{
+  { 1, 7, 5 },
+  { 8, 2, 6 },
+  { 3, 10, 9 },
+  { 7, 8, 6, 5 },
+  { 4, 6, 9, 10 },
+  { 5, 6, 4 }
+};
+HPRef_Struct refquad_1e_3va =
+{
+  HP_QUAD,
+  refquad_1e_3va_splitedges, 
+  0, 0,
+  refquad_1e_3va_newelstypes, 
+  refquad_1e_3va_newels
+};
+
+
+
+
+
+// HP_QUAD_1E_3VB
+int refquad_1e_3vb_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 2, 1, 8 },
+  { 4, 1, 9 },
+  { 4, 3, 10 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_3vb_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG,
+  HP_NONE,
+};
+int refquad_1e_3vb_newels[][8] =
+{
+  { 1, 7, 5 },
+  { 8, 2, 6 },
+  { 4, 9, 10 },
+  { 7, 8, 6, 5 },
+  { 5, 3, 10, 9 },
+  { 5, 6, 3 }
+};
+HPRef_Struct refquad_1e_3vb =
+{
+  HP_QUAD,
+  refquad_1e_3vb_splitedges, 
+  0, 0,
+  refquad_1e_3vb_newelstypes, 
+  refquad_1e_3vb_newels
+};
+
+
+
+
+
+// HP_QUAD_1E_3VC
+int refquad_1e_3vc_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 3, 2, 8 },
+  { 3, 4, 9 },
+  { 4, 3, 10 },
+  { 4, 1, 11 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_3vc_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_1e_3vc_newels[][8] =
+{
+  { 1, 7, 5 },
+  { 3, 9, 8 },
+  { 4, 11, 10 },
+  { 7, 2, 6, 5 },
+  { 5, 6, 8, 11 },
+  { 11, 8, 9, 10 }
+};
+HPRef_Struct refquad_1e_3vc =
+{
+  HP_QUAD,
+  refquad_1e_3vc_splitedges, 
+  0, 0,
+  refquad_1e_3vc_newelstypes, 
+  refquad_1e_3vc_newels
+};
+
+
+
+
+// HP_QUAD_1E_3VD
+int refquad_1e_3vd_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 2, 1, 7 },
+  { 3, 2, 8 },
+  { 3, 4, 9 },
+  { 4, 3, 10 },
+  { 4, 1, 11 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_3vd_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_1e_3vd_newels[][8] =
+{
+  { 7, 2, 6 },
+  { 3, 9, 8 },
+  { 4, 11, 10 },
+  { 1, 7, 6, 5 },
+  { 5, 6, 8, 11 },
+  { 11, 8, 9, 10 }
+};
+HPRef_Struct refquad_1e_3vd =
+{
+  HP_QUAD,
+  refquad_1e_3vd_splitedges, 
+  0, 0,
+  refquad_1e_3vd_newelstypes, 
+  refquad_1e_3vd_newels
+};
+
+
+
+
+
+
+// HP_QUAD_1E_4V
+int refquad_1e_4v_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 2, 1, 8 },
+  { 4, 1, 9 },
+  { 3, 2, 10 },
+  { 4, 3, 11 },
+  { 3, 4, 12 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_1e_4v_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_1e_4v_newels[][8] =
+{
+  { 1, 7, 5 },
+  { 8, 2, 6 },
+  { 3, 12, 10 },
+  { 4, 9, 11 },
+  { 7, 8, 6, 5 },
+  { 5, 6, 10, 9 },
+  { 9, 10, 12, 11 }
+};
+HPRef_Struct refquad_1e_4v =
+{
+  HP_QUAD,
+  refquad_1e_4v_splitedges, 
+  0, 0,
+  refquad_1e_4v_newelstypes, 
+  refquad_1e_4v_newels
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+// HP_QUAD_2E
+int refquad_2e_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 4, 3, 8 },
+  { 0, 0, 0 }
+};
+int refquad_2e_splitfaces[][4] =
+{
+  { 1, 2, 4, 9 },
+  { 0, 0, 0, 0 },
+};
+
+
+/* 
+   HPREF_ELEMENT_TYPE refquad_2e_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2e_newels[][8] =
+{
+  { 1, 5, 9 },
+  { 6, 1, 9 },
+  { 5, 2, 7, 9 },
+  { 4, 6, 9, 8 },
+  { 9, 7, 3, 8 },
+};
+*/ 
+
+// SZ refine to 4 quads 
+HPREF_ELEMENT_TYPE refquad_2e_newelstypes[] =
+{
+  HP_QUAD_2E,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2e_newels[][8] =
+{
+  { 1, 5, 9, 6 },
+  { 5, 2, 7, 9 },
+  { 4, 6, 9, 8 },
+  { 9, 7, 3, 8 },
+};
+
+HPRef_Struct refquad_2e =
+{
+  HP_QUAD,
+  refquad_2e_splitedges, 
+  refquad_2e_splitfaces, 
+  0,
+  refquad_2e_newelstypes, 
+  refquad_2e_newels
+};
+
+
+// HP_QUAD_2E_1VA
+int refquad_2e_1va_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 4, 3, 8 },
+  { 2, 1, 10 },
+  { 0, 0, 0 }
+};
+int refquad_2e_1va_splitfaces[][4] =
+{
+  { 1, 2, 4, 9 },
+  { 0, 0, 0, 0 },
+};
+
+/* 
+HPREF_ELEMENT_TYPE refquad_2e_1va_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_NONE,
+};
+int refquad_2e_1va_newels[][8] =
+{
+  { 1, 5, 9 },
+  { 6, 1, 9 },
+  { 5, 10, 7, 9 },
+  { 4, 6, 9, 8 },
+  { 9, 7, 3, 8 },
+  { 10, 2, 7 },
+};
+*/ 
+// SZ Quad_2e refinement 
+HPREF_ELEMENT_TYPE refquad_2e_1va_newelstypes[] =
+{
+  HP_QUAD_2E,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_NONE,
+};
+int refquad_2e_1va_newels[][8] =
+{
+  { 1, 5, 9, 6 },
+  { 5, 10, 7, 9 },
+  { 4, 6, 9, 8 },
+  { 9, 7, 3, 8 },
+  { 10, 2, 7 },
+};
+
+HPRef_Struct refquad_2e_1va =
+{
+  HP_QUAD,
+  refquad_2e_1va_splitedges, 
+  refquad_2e_1va_splitfaces, 
+  0,
+  refquad_2e_1va_newelstypes, 
+  refquad_2e_1va_newels
+};
+
+
+
+// HP_QUAD_2E_1VB
+int refquad_2e_1vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 4, 3, 8 },
+  { 3, 2, 10 },
+  { 3, 4, 11 },
+  { 0, 0, 0 }
+};
+int refquad_2e_1vb_splitfaces[][4] =
+{
+  { 1, 2, 4, 9 },
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2e_1vb_newelstypes[] =
+{
+  // HP_TRIG_SINGEDGECORNER1,
+  // HP_TRIG_SINGEDGECORNER2,
+  // SZ QUAD_2E 
+  HP_QUAD_2E,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int refquad_2e_1vb_newels[][8] =
+{
+  //{ 1, 5, 9 },
+  //{ 6, 1, 9 },
+  { 1, 5, 9, 6 },
+  { 5, 2, 7, 9 },
+  { 4, 6, 9, 8 },
+  { 7, 8, 9 },
+  { 8, 7, 10, 11 },
+  { 3, 11, 10 }
+};
+HPRef_Struct refquad_2e_1vb =
+{
+  HP_QUAD,
+  refquad_2e_1vb_splitedges, 
+  refquad_2e_1vb_splitfaces, 
+  0,
+  refquad_2e_1vb_newelstypes, 
+  refquad_2e_1vb_newels
+}
+;
+
+// HP_QUAD_2E_1VC
+int refquad_2e_1vc_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 4, 1, 8 },
+  { 4, 3, 9 },
+  { 0, 0, 0 }
+};
+int refquad_2e_1vc_splitfaces[][4] =
+{
+  { 1, 2, 4, 10 },
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2e_1vc_newelstypes[] =
+{
+  //  HP_TRIG_SINGEDGECORNER1,
+  // HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_2E, 
+  HP_TRIG_SINGEDGECORNER1,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2e_1vc_newels[][8] =
+{
+  //{ 1, 5, 10 },
+  //{ 6, 1, 10 },
+  { 1, 5, 10, 6}, 
+  { 4, 8, 9 },
+  { 5, 2, 7, 10 },
+  { 8, 6, 10, 9 },
+  { 10, 7, 3, 9 },
+};
+HPRef_Struct refquad_2e_1vc =
+{
+  HP_QUAD,
+  refquad_2e_1vc_splitedges, 
+  refquad_2e_1vc_splitfaces, 
+  0,
+  refquad_2e_1vc_newelstypes, 
+  refquad_2e_1vc_newels
+};
+
+// HP_QUAD_2E_2VA
+int refquad_2e_2va_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 4, 3, 8 },
+  { 3, 2, 10 },
+  { 3, 4, 11 },
+  { 2, 1, 12 },
+  { 0, 0, 0 }
+};
+int refquad_2e_2va_splitfaces[][4] =
+{
+  { 1, 2, 4, 9 },
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2e_2va_newelstypes[] =
+{
+  //HP_TRIG_SINGEDGECORNER1,
+  //HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_2E,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_NONE,
+};
+int refquad_2e_2va_newels[][8] =
+{
+  // { 1, 5, 9 },
+  // { 6, 1, 9 },
+  { 1, 5, 9, 6 }, 
+  { 5, 12, 7, 9 },
+  { 4, 6, 9, 8 },
+  { 7, 8, 9 },
+  { 8, 7, 10, 11 },
+  { 3, 11, 10 },
+  { 12, 2, 7 }
+};
+HPRef_Struct refquad_2e_2va =
+{
+  HP_QUAD,
+  refquad_2e_2va_splitedges, 
+  refquad_2e_2va_splitfaces, 
+  0,
+  refquad_2e_2va_newelstypes, 
+  refquad_2e_2va_newels
+};
+
+
+
+
+
+
+// HP_QUAD_2E_2VB
+int refquad_2e_2vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 4, 1, 9 },
+  { 4, 3, 10 },
+  { 0, 0, 0 }
+};
+int refquad_2e_2vb_splitfaces[][4] =
+{
+  { 1, 2, 4, 11 },
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2e_2vb_newelstypes[] =
+{
+  // HP_TRIG_SINGEDGECORNER1,
+  // HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_2E, 
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2e_2vb_newels[][8] =
+{
+  //{ 1, 5, 11 },
+  //{ 6, 1, 11 },
+  { 1, 5, 11, 6 }, 
+  { 4, 9, 10 },
+  { 7, 2, 8 },
+  { 5, 7, 8, 11 },
+  { 9, 6, 11, 10 },
+  { 3, 10, 11, 8 },
+};
+HPRef_Struct refquad_2e_2vb =
+{
+  HP_QUAD,
+  refquad_2e_2vb_splitedges, 
+  refquad_2e_2vb_splitfaces, 
+  0,
+  refquad_2e_2vb_newelstypes, 
+  refquad_2e_2vb_newels
+};
+
+// HP_QUAD_2E_2VC
+int refquad_2e_2vc_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 4, 3, 8 },
+  { 3, 2, 10 },
+  { 3, 4, 11 },
+  { 4, 1, 12 },
+  { 0, 0, 0 }
+};
+int refquad_2e_2vc_splitfaces[][4] =
+{
+  { 1, 2, 4, 9 },
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2e_2vc_newelstypes[] =
+{
+  // HP_TRIG_SINGEDGECORNER1,
+  // HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_2E, 
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGEDGECORNER1, //SZ (vorher: SINGEDGECORNER2) 
+  HP_NONE,
+};
+int refquad_2e_2vc_newels[][8] =
+{
+  { 1, 5, 9 },
+  { 6, 1, 9 },
+  { 5, 2, 7, 9 },
+  { 12, 6, 9, 8 },
+  { 7, 8, 9 },
+  { 8, 7, 10, 11 },
+  { 3, 11, 10 },
+  { 4, 12, 8 }
+};
+HPRef_Struct refquad_2e_2vc =
+{
+  HP_QUAD,
+  refquad_2e_2vc_splitedges, 
+  refquad_2e_2vc_splitfaces, 
+  0,
+  refquad_2e_2vc_newelstypes, 
+  refquad_2e_2vc_newels
+};
+
+// HP_QUAD_2E_3V  
+int refquad_2e_3v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 4, 3, 8 },
+  { 3, 2, 10 },
+  { 3, 4, 11 },
+  { 2, 1, 12 },
+  { 4, 1, 13 },
+  { 0, 0, 0 }
+};
+int refquad_2e_3v_splitfaces[][4] =
+{
+  { 1, 2, 4, 9 },
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2e_3v_newelstypes[] =
+{
+  // HP_TRIG_SINGEDGECORNER1,
+  // HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_2E, 
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_NONE,
+};
+int refquad_2e_3v_newels[][8] =
+{
+  //{ 1, 5, 9 },
+  //{ 6, 1, 9 },
+  { 1, 5, 9, 6 }, 
+  { 5, 12, 7, 9 },
+  { 13, 6, 9, 8 },
+  { 7, 8, 9 },
+  { 8, 7, 10, 11 },
+  { 3, 11, 10 },
+  { 12, 2, 7 },
+  { 4, 13, 8 }
+};
+HPRef_Struct refquad_2e_3v =
+{
+  HP_QUAD,
+  refquad_2e_3v_splitedges, 
+  refquad_2e_3v_splitfaces, 
+  0,
+  refquad_2e_3v_newelstypes, 
+  refquad_2e_3v_newels
+};
+
+// HP_QUAD_2EB_0V
+int refquad_2eb_0v_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 4, 1, 8 },
+  { 0, 0, 0 }
+};
+int refquad_2eb_0v_splitfaces[][4] =
+{
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2eb_0v_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2eb_0v_newels[][8] =
+{
+  { 1, 2, 6, 5 },
+  { 3, 4, 8, 7 },
+  { 5, 6, 7, 8 }
+};
+HPRef_Struct refquad_2eb_0v =
+{
+  HP_QUAD,
+  refquad_2eb_0v_splitedges, 
+  refquad_2eb_0v_splitfaces, 
+  0,
+  refquad_2eb_0v_newelstypes, 
+  refquad_2eb_0v_newels
+};
+
+
+// HP_QUAD_2EB_1VA
+int refquad_2eb_1va_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 4, 1, 8 },
+  { 1, 2, 9 },
+  { 0, 0, 0 }
+};
+int refquad_2eb_1va_splitfaces[][4] =
+{
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2eb_1va_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2eb_1va_newels[][8] =
+{
+  { 9, 2, 6, 5 },
+  { 3, 4, 8, 7 },
+  { 1, 9, 5 },
+  { 5, 6, 7, 8 }
+};
+HPRef_Struct refquad_2eb_1va =
+{
+  HP_QUAD,
+  refquad_2eb_1va_splitedges, 
+  refquad_2eb_1va_splitfaces, 
+  0,
+  refquad_2eb_1va_newelstypes, 
+  refquad_2eb_1va_newels
+};
+
+// HP_QUAD_2EB_1VB
+int refquad_2eb_1vb_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 4, 1, 8 },
+  { 2, 1, 9 },
+  { 0, 0, 0 }
+};
+int refquad_2eb_1vb_splitfaces[][4] =
+{
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2eb_1vb_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2eb_1vb_newels[][8] =
+{
+  { 1, 9, 6, 5 },
+  { 3, 4, 8, 7 },
+  { 9, 2, 6 },
+  { 5, 6, 7, 8 }
+};
+HPRef_Struct refquad_2eb_1vb =
+{
+  HP_QUAD,
+  refquad_2eb_1vb_splitedges, 
+  refquad_2eb_1vb_splitfaces, 
+  0,
+  refquad_2eb_1vb_newelstypes, 
+  refquad_2eb_1vb_newels
+};
+
+// HP_QUAD_2EB_2VA
+int refquad_2eb_2va_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 4, 1, 8 },
+  { 1, 2, 9 },
+  { 2, 1, 10 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_2eb_2va_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2eb_2va_newels[][8] =
+{
+  { 9, 10, 6, 5 },
+  { 3, 4, 8, 7 },
+  { 1, 9, 5 },
+  { 10, 2, 6 },
+  { 5, 6, 7, 8 }
+};
+HPRef_Struct refquad_2eb_2va =
+{
+  HP_QUAD,
+  refquad_2eb_2va_splitedges, 
+  0, 0,
+  refquad_2eb_2va_newelstypes, 
+  refquad_2eb_2va_newels
+};
+
+
+
+// HP_QUAD_2EB_2VB
+int refquad_2eb_2vb_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 4, 1, 8 },
+  { 1, 2, 9 },
+  { 3, 4, 10 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_2eb_2vb_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2eb_2vb_newels[][8] =
+{
+  { 9, 2, 6, 5 },
+  { 10, 4, 8, 7 },
+  { 1, 9, 5 },
+  { 3, 10, 7 },
+  { 5, 6, 7, 8 }
+};
+HPRef_Struct refquad_2eb_2vb =
+{
+  HP_QUAD,
+  refquad_2eb_2vb_splitedges, 
+  0, 0,
+  refquad_2eb_2vb_newelstypes, 
+  refquad_2eb_2vb_newels
+};
+
+
+
+// HP_QUAD_2EB_2VC
+int refquad_2eb_2vc_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 4, 1, 8 },
+  { 1, 2, 9 },
+  { 4, 3, 10 },
+  { 0, 0, 0 }
+};
+int refquad_2eb_2vc_splitfaces[][4] =
+{
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2eb_2vc_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2eb_2vc_newels[][8] =
+{
+  { 9, 2, 6, 5 },
+  { 3, 10, 8, 7 },
+  { 1, 9, 5 },
+  { 10, 4, 8 },
+  { 5, 6, 7, 8 }
+};
+HPRef_Struct refquad_2eb_2vc =
+{
+  HP_QUAD,
+  refquad_2eb_2vc_splitedges, 
+  refquad_2eb_2vc_splitfaces, 
+  0,
+  refquad_2eb_2vc_newelstypes, 
+  refquad_2eb_2vc_newels
+};
+
+
+// HP_QUAD_2EB_2VD
+int refquad_2eb_2vd_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 4, 1, 8 },
+  { 2, 1, 9 },
+  { 4, 3, 10 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_2eb_2vd_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2eb_2vd_newels[][8] =
+{
+  { 1, 9, 6, 5 },
+  { 3, 10, 8, 7 },
+  { 9, 2, 6 },
+  { 10, 4, 8 },
+  { 5, 6, 7, 8 }
+};
+HPRef_Struct refquad_2eb_2vd =
+{
+  HP_QUAD,
+  refquad_2eb_2vd_splitedges, 
+  0, 0,
+  refquad_2eb_2vd_newelstypes, 
+  refquad_2eb_2vd_newels
+};
+
+
+// HP_QUAD_2EB_3VA
+int refquad_2eb_3va_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 2, 1, 8 },
+  { 3, 2, 9 },
+  { 4, 1, 10 },
+  { 3, 4, 11 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_2eb_3va_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2eb_3va_newels[][8] =
+{
+  { 1, 7, 5 },
+  { 8, 2, 6 },
+  { 3, 11, 9},
+  { 7, 8, 6, 5 },
+  { 11, 4, 10, 9 },
+  { 5, 6, 9, 10 }
+};
+HPRef_Struct refquad_2eb_3va =
+{
+  HP_QUAD,
+  refquad_2eb_3va_splitedges, 
+  0, 0,
+  refquad_2eb_3va_newelstypes, 
+  refquad_2eb_3va_newels
+};
+
+
+// HP_QUAD_2EB_3VB
+int refquad_2eb_3vb_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 1, 2, 7 },
+  { 2, 1, 8 },
+  { 3, 2, 9 },
+  { 4, 1, 10 },
+  { 4, 3, 11 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE refquad_2eb_3vb_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2eb_3vb_newels[][8] =
+{
+  { 1, 7, 5 },
+  { 8, 2, 6 },
+  { 11, 4, 10 },
+  { 7, 8, 6, 5 },
+  { 3, 11, 10, 9 },
+  { 5, 6, 9, 10 }
+};
+HPRef_Struct refquad_2eb_3vb =
+{
+  HP_QUAD,
+  refquad_2eb_3vb_splitedges, 
+  0, 0,
+  refquad_2eb_3vb_newelstypes, 
+  refquad_2eb_3vb_newels
+};
+
+
+// HP_QUAD_2EB_4V
+int refquad_2eb_4v_splitedges[][3] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 4, 1, 8 },
+  { 1, 2, 9 },
+  { 2, 1, 10 },
+  { 3, 4, 11 },
+  { 4, 3, 12 },
+  { 0, 0, 0 }
+};
+int refquad_2eb_4v_splitfaces[][4] =
+{
+  { 0, 0, 0, 0 },
+};
+HPREF_ELEMENT_TYPE refquad_2eb_4v_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_NONE,
+};
+int refquad_2eb_4v_newels[][8] =
+{
+  { 9, 10, 6, 5 },
+  { 11, 12, 8, 7 },
+  { 5, 6, 7, 8 },
+  { 1, 9, 5 },
+  { 10, 2, 6 },
+  { 3, 11, 7 },
+  { 12, 4, 8 },
+};
+HPRef_Struct refquad_2eb_4v =
+{
+  HP_QUAD,
+  refquad_2eb_4v_splitedges, 
+  refquad_2eb_4v_splitfaces, 
+  0,
+  refquad_2eb_4v_newelstypes, 
+  refquad_2eb_4v_newels
+};
+
+
+
+// HP_QUAD_3E
+int refquad_3e_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 3, 4, 10 },
+  { 4, 3, 12 },
+  { 0, 0, 0 }
+};
+
+int refquad_3e_splitfaces[][4] =
+{
+  { 1, 2, 4, 13 },
+  { 2, 3, 1, 14 },
+  { 0, 0, 0, 0 },
+};
+
+HPREF_ELEMENT_TYPE refquad_3e_newelstypes[] =
+{
+  HP_QUAD_2E,
+  HP_QUAD_2E,
+//   HP_TRIG_SINGEDGECORNER1,
+//   HP_TRIG_SINGEDGECORNER2,
+//   HP_TRIG_SINGEDGECORNER2,
+//   HP_TRIG_SINGEDGECORNER1,
+
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_3e_newels[][8] =
+{
+//   { 1, 5, 13 },
+//   { 6, 1, 13 },
+//   { 7, 2, 14 },
+//   { 2, 8, 14 },
+  { 1, 5, 13, 6 },
+  { 2, 8, 14, 7 },
+  { 5, 7, 14, 13 },
+  { 8, 3, 10, 14 },
+  { 4, 6, 13, 12 },
+  { 13, 14, 10, 12 }
+};
+HPRef_Struct refquad_3e =
+{
+  HP_QUAD,
+  refquad_3e_splitedges, 
+  refquad_3e_splitfaces, 
+  0,
+  refquad_3e_newelstypes, 
+  refquad_3e_newels
+};
+
+
+
+
+
+
+
+// HP_QUAD_3E_3VA
+int refquad_3e_3va_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 3, 4, 10 },
+  { 3, 2, 11 },
+  { 4, 3, 12 },
+  { 0, 0, 0 }
+};
+
+int refquad_3e_3va_splitfaces[][4] =
+{
+  { 1, 2, 4, 13 },
+  { 2, 3, 1, 14 },
+  { 0, 0, 0, 0 },
+};
+
+HPREF_ELEMENT_TYPE refquad_3e_3va_newelstypes[] =
+{
+  HP_QUAD_2E,
+  HP_QUAD_2E,
+
+//   HP_TRIG_SINGEDGECORNER1,
+//   HP_TRIG_SINGEDGECORNER2,
+//   HP_TRIG_SINGEDGECORNER2,
+//   HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_3e_3va_newels[][8] =
+{
+//   { 1, 5, 13 },
+//   { 6, 1, 13 },
+//   { 7, 2, 14 },
+//   { 2, 8, 14 },
+  { 1, 5, 13, 6 },
+  { 2, 8, 14, 7 },
+  { 11, 3, 10 },
+  { 5, 7, 14, 13 },
+  { 8, 11, 10, 14 },
+  { 4, 6, 13, 12 },
+  { 13, 14, 10, 12 }
+};
+HPRef_Struct refquad_3e_3va =
+{
+  HP_QUAD,
+  refquad_3e_3va_splitedges, 
+  refquad_3e_3va_splitfaces, 
+  0,
+  refquad_3e_3va_newelstypes, 
+  refquad_3e_3va_newels
+};
+
+// HP_QUAD_3E_3VB
+int refquad_3e_3vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 3, 4, 10 },
+  { 4, 1, 11 },
+  { 4, 3, 12 },
+  { 0, 0, 0 }
+};
+
+int refquad_3e_3vb_splitfaces[][4] =
+{
+  { 1, 2, 4, 13 },
+  { 2, 3, 1, 14 },
+  { 0, 0, 0, 0 },
+};
+
+HPREF_ELEMENT_TYPE refquad_3e_3vb_newelstypes[] =
+{
+  HP_QUAD_2E,
+  HP_QUAD_2E,
+
+//   HP_TRIG_SINGEDGECORNER1,
+//   HP_TRIG_SINGEDGECORNER2,
+//   HP_TRIG_SINGEDGECORNER2,
+//   HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER1,
+
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_3e_3vb_newels[][8] =
+{
+//   { 1, 5, 13 },
+//   { 6, 1, 13 },
+//   { 7, 2, 14 },
+//   { 2, 8, 14 },
+  { 1, 5, 13, 6 },
+  { 2, 8, 14, 7 },
+  { 4, 11, 12 },
+  { 5, 7, 14, 13 },
+  { 8, 3, 10, 14 },
+  { 11, 6, 13, 12 },
+  { 13, 14, 10, 12 }
+};
+HPRef_Struct refquad_3e_3vb =
+{
+  HP_QUAD,
+  refquad_3e_3vb_splitedges, 
+  refquad_3e_3vb_splitfaces, 
+  0,
+  refquad_3e_3vb_newelstypes, 
+  refquad_3e_3vb_newels
+};
+
+
+
+
+
+
+
+
+
+// HP_QUAD_3E_4V
+int refquad_3e_4v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 3, 4, 10 },
+  { 3, 2, 11 },
+  { 4, 3, 12 },
+  { 4, 1, 15 },
+  { 0, 0, 0 }
+};
+
+int refquad_3e_4v_splitfaces[][4] =
+{
+  { 1, 2, 4, 13 },
+  { 2, 3, 1, 14 },
+  { 0, 0, 0, 0 },
+};
+
+HPREF_ELEMENT_TYPE refquad_3e_4v_newelstypes[] =
+{
+  HP_QUAD_2E,
+  HP_QUAD_2E,
+
+//   HP_TRIG_SINGEDGECORNER1,
+//   HP_TRIG_SINGEDGECORNER2,
+//   HP_TRIG_SINGEDGECORNER2,
+//   HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGEDGECORNER1,
+
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_3e_4v_newels[][8] =
+{
+//   { 1, 5, 13 },
+//   { 6, 1, 13 },
+//   { 7, 2, 14 },
+//   { 2, 8, 14 },
+  { 1, 5, 13, 6 },
+  { 2, 8, 14, 7 },
+  { 11, 3, 10 },
+  { 4, 15, 12 },
+  { 5, 7, 14, 13 },
+  { 8, 11, 10, 14 },
+  { 15, 6, 13, 12 },
+  { 13, 14, 10, 12 }
+};
+HPRef_Struct refquad_3e_4v =
+{
+  HP_QUAD,
+  refquad_3e_4v_splitedges, 
+  refquad_3e_4v_splitfaces, 
+  0,
+  refquad_3e_4v_newelstypes, 
+  refquad_3e_4v_newels
+};
+
+
+
+
+
+
+
+
+
+// HP_QUAD_4E
+int refquad_4e_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 3, 2, 9 },
+  { 3, 4, 10 },
+  { 4, 1, 11 },
+  { 4, 3, 12 },
+  { 0, 0, 0 }
+};
+
+int refquad_4e_splitfaces[][4] =
+{
+  { 1, 2, 4, 13 },
+  { 2, 3, 1, 14 },
+  { 3, 4, 2, 15 },
+  { 4, 1, 3, 16 },
+  { 0, 0, 0, 0 },
+};
+
+HPREF_ELEMENT_TYPE refquad_4e_newelstypes[] =
+{
+  HP_QUAD_2E,
+  HP_QUAD_2E,
+  HP_QUAD_2E,
+  HP_QUAD_2E,
+
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_4e_newels[][8] =
+{
+  { 1, 5, 13, 6 },
+  { 2, 8, 14, 7 },
+  { 3, 10, 15, 9 },
+  { 4, 11, 16, 12 },
+  { 5, 7, 14, 13 },
+  { 8, 9, 15, 14 },
+  { 10, 12, 16, 15 },
+  { 11, 6, 13, 16 },
+  { 13, 14, 15, 16 }
+};
+HPRef_Struct refquad_4e =
+{
+  HP_QUAD,
+  refquad_4e_splitedges, 
+  refquad_4e_splitfaces, 
+  0,
+  refquad_4e_newelstypes, 
+  refquad_4e_newels
+};
diff --git a/contrib/Netgen/libsrc/meshing/hpref_segm.hpp b/contrib/Netgen/libsrc/meshing/hpref_segm.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..254458232106e82712d96084678bc4b95fc33b31
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_segm.hpp
@@ -0,0 +1,122 @@
+  // HP_SEGM
+  int refsegm_splitedges[][3] =
+    {
+      { 0, 0, 0 }
+    };
+  
+  HPREF_ELEMENT_TYPE refsegm_newelstypes[] =
+    {
+      HP_SEGM,
+      HP_NONE,
+    };
+  int refsegm_newels[][8] =
+    {
+      { 1, 2 },
+    };
+  HPRef_Struct refsegm =
+    {
+      HP_SEGM, 
+      refsegm_splitedges, 
+      0, 0, 
+      refsegm_newelstypes, 
+      refsegm_newels
+    };
+
+  // HP_SEGM_SINGCORNERL = 2,
+  int refsegm_scl_splitedges[][3] =
+    {
+      { 1, 2, 3 }, 
+      { 0, 0, 0 }
+    };
+  
+  HPREF_ELEMENT_TYPE refsegm_scl_newelstypes[] = 
+    {
+      HP_SEGM_SINGCORNERL,
+      HP_SEGM,
+      HP_NONE,
+    };
+  
+  int refsegm_scl_newels[][8] =
+    {
+      { 1, 3 },
+      { 3, 2 },
+      { 0, 0 },
+    };
+  HPRef_Struct refsegm_scl =
+    {
+      HP_SEGM,
+      refsegm_scl_splitedges,
+      0, 0,
+      refsegm_scl_newelstypes,
+      refsegm_scl_newels
+    };
+
+
+
+  // HP_SEGM_SINGCORNERR
+  int refsegm_scr_splitedges[][3] =
+    {
+      { 2, 1, 3 },
+      { 0, 0, 0 }
+    };
+
+  HPREF_ELEMENT_TYPE refsegm_scr_newelstypes[] =
+    {
+      HP_SEGM,
+      HP_SEGM_SINGCORNERR,
+      HP_NONE,
+    };
+  int refsegm_scr_newels[][8] =
+    {
+      { 1, 3 },
+      { 3, 2 },
+      { 0, 0 },
+    };
+  HPRef_Struct refsegm_scr =
+    {
+      HP_SEGM,
+      refsegm_scr_splitedges,
+      0, 0,
+      refsegm_scr_newelstypes,
+      refsegm_scr_newels
+    };
+
+
+
+
+
+
+  // HP_SEGM_SINGCORNERS = 3,
+  int refsegm_sc2_splitedges[][3] =
+    {
+      { 1, 2, 3 },
+      { 2, 1, 4 },
+      { 0, 0, 0 }
+    };
+
+  HPREF_ELEMENT_TYPE refsegm_sc2_newelstypes[] =
+    {
+      HP_SEGM_SINGCORNERL,
+      HP_SEGM_SINGCORNERR,
+      HP_SEGM,
+      HP_NONE,
+    };
+  int refsegm_sc2_newels[][8] =
+    {
+      { 1, 3 },
+      { 4, 2 },
+      { 3, 4 },
+      { 0, 0 },
+    };
+  HPRef_Struct refsegm_sc2 =
+    {  
+      HP_SEGM,
+      refsegm_sc2_splitedges,
+      0, 0, 
+      refsegm_sc2_newelstypes,
+      refsegm_sc2_newels
+    };
+
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/hpref_tet.hpp b/contrib/Netgen/libsrc/meshing/hpref_tet.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..df0e2af890aec6de2c30a1821912e8ed79ca0a6b
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_tet.hpp
@@ -0,0 +1,3128 @@
+
+ 
+
+// HP_TET
+int reftet_splitedges[][3] =
+{
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_newelstypes[] =
+{
+  HP_TET,
+  HP_NONE,
+};
+int reftet_newels[][8] =
+{
+  { 1, 2, 3, 4 },
+};
+HPRef_Struct reftet =
+{
+  HP_TET,
+  reftet_splitedges, 
+  0, 0,
+  reftet_newelstypes, 
+  reftet_newels
+};
+
+
+
+/* *********** Tet - Refinement - 0 edges *************** */
+
+// HP_TET_0E_1V
+int reftet_0e_1v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_0e_1v_newelstypes[] =
+{
+  HP_TET_0E_1V,
+  HP_PRISM,
+  HP_NONE,
+};
+int reftet_0e_1v_newels[][8] =
+{
+  { 1, 5, 6, 7 },
+  { 5, 6, 7, 2, 3, 4 }
+};
+HPRef_Struct reftet_0e_1v =
+{
+  HP_TET,
+  reftet_0e_1v_splitedges, 
+  0, 0,
+  reftet_0e_1v_newelstypes, 
+  reftet_0e_1v_newels
+};
+
+
+
+// HP_TET_0E_2V
+int reftet_0e_2v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_0e_2v_newelstypes[] =
+{
+  HP_TET_0E_1V,
+  HP_TET_0E_1V,
+  HP_PRISM,
+  HP_PRISM,
+  HP_NONE,
+};
+int reftet_0e_2v_newels[][8] =
+{
+  { 1, 5, 6, 7 },
+  { 2, 10, 9, 8 },
+  { 5, 6, 7, 8, 9, 10 },
+  { 4, 10, 7, 3, 9, 6 },
+};
+HPRef_Struct reftet_0e_2v =
+{
+  HP_TET,
+  reftet_0e_2v_splitedges, 
+  0, 0,
+  reftet_0e_2v_newelstypes, 
+  reftet_0e_2v_newels
+};
+
+
+
+
+
+// HP_TET_0E_3V
+int reftet_0e_3v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 0, 0, 0 }
+};
+int reftet_0e_3v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 14 },
+    { 2, 3, 1, 15 },
+    { 3, 1, 2, 16 },
+    { 0, 0, 0, 0 },
+  };
+HPREF_ELEMENT_TYPE reftet_0e_3v_newelstypes[] =
+{
+  HP_PYRAMID_0E_1V,
+  HP_PYRAMID_0E_1V,
+  HP_PYRAMID_0E_1V,
+  HP_PRISM,
+  HP_PRISM,
+  HP_PRISM,
+  HP_PRISM,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_0e_3v_newels[][8] =
+{
+  { 1, 5, 14, 6, 7 },
+  { 2, 9, 15, 8, 10 },
+  { 3, 11, 16, 12, 13 },
+  { 5, 14, 7, 8, 15, 10 },
+  { 9, 15, 10, 12, 16, 13 },
+  { 6, 7, 14, 11, 13, 16 },
+  { 14, 15, 16, 7, 10, 13 },
+  { 7, 10, 13, 4 }
+};
+HPRef_Struct reftet_0e_3v =
+{
+  HP_TET,
+  reftet_0e_3v_splitedges, 
+  reftet_0e_3v_splitfaces, 
+  0,
+  reftet_0e_3v_newelstypes, 
+  reftet_0e_3v_newels
+};
+
+
+
+
+
+// HP_TET_0E_4V
+int reftet_0e_4v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_0e_4v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 1, 2, 4, 18 },
+    { 1, 3, 4, 19 },
+
+    { 2, 1, 3, 20 },
+    { 2, 1, 4, 21 },
+    { 2, 3, 4, 22 },
+
+    { 3, 1, 2, 23 },
+    { 3, 1, 4, 24 },
+    { 3, 2, 4, 25 },
+
+    { 4, 1, 2, 26 },
+    { 4, 1, 3, 27 },
+    { 4, 2, 3, 28 },
+    { 0, 0, 0, 0 },
+  };
+int reftet_0e_4v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 29 },
+    { 2, 3, 4, 1, 30 },
+    { 3, 4, 1, 2, 31 },
+    { 4, 1, 2, 3, 32 },
+    { 0 },
+  };
+HPREF_ELEMENT_TYPE reftet_0e_4v_newelstypes[] =
+{
+  HP_HEX_0E_1V,
+  HP_HEX_0E_1V,
+  HP_HEX_0E_1V,
+  HP_HEX_0E_1V,
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM,
+  HP_PRISM,
+  HP_PRISM,
+  HP_PRISM,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_0e_4v_newels[][8] =
+{
+  { 1, 5, 17, 6, 7, 18, 29, 19 },
+  { 2, 9, 20, 8, 10, 22, 30, 21 },
+  { 3, 11, 23, 12, 13, 24, 31, 25 },
+  { 4, 15, 26, 14, 16, 28, 32, 27 },
+  { 5, 17, 18, 8, 20, 21 },
+  { 18, 17, 29, 21, 20, 30 },
+  { 6, 19, 17,  11, 24, 23 },
+  { 17, 19, 29,  23, 24, 31 },
+  { 7, 18, 19, 14, 26, 27 },
+  { 19, 18, 29, 27, 26, 32 },
+  { 9, 20, 22, 12, 23, 25 },
+  { 22, 20, 30, 25, 23, 31 },
+  { 10, 22, 21, 15, 28, 26 },
+  { 21, 22, 30, 26, 28, 32 },
+  { 13, 24, 25, 16, 27, 28 },
+  { 25, 24, 31, 28, 27, 32 },
+  { 17, 20, 23, 29, 30, 31 },
+  { 18, 26, 21, 29, 32, 30 },
+  { 19, 24, 27, 29, 31, 32 },
+  { 22, 28, 25, 30, 32, 31 },
+  { 29, 30, 31, 32 },
+};
+HPRef_Struct reftet_0e_4v =
+{
+  HP_TET,
+  reftet_0e_4v_splitedges, 
+  reftet_0e_4v_splitfaces, 
+  reftet_0e_4v_splitelements, 
+  reftet_0e_4v_newelstypes, 
+  reftet_0e_4v_newels
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* *********** Tet - Refinement - 1 edge *************** */
+
+
+
+// HP_TET_1E_0V
+int reftet_1e_0v_splitedges[][3] =
+{
+  { 1, 3, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 2, 4, 8 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_1e_0v_newelstypes[] =
+{
+  HP_PRISM_SINGEDGE,
+  HP_PRISM,
+  HP_NONE,
+};
+int reftet_1e_0v_newels[][8] =
+{
+  { 1, 5, 6, 2, 7, 8 },
+  { 7, 3, 5, 8, 4, 6 }
+};
+HPRef_Struct reftet_1e_0v =
+{
+  HP_TET,
+  reftet_1e_0v_splitedges, 
+  0, 0,
+  reftet_1e_0v_newelstypes, 
+  reftet_1e_0v_newels
+};
+
+
+
+
+
+// HP_TET_1E_1VA
+int reftet_1e_1va_splitedges[][3] =
+{
+  { 1, 3, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 2, 4, 8 },
+  { 1, 2, 9 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_1e_1va_newelstypes[] =
+{
+  HP_TET_1E_1VA,
+  HP_PRISM_SINGEDGE,
+  HP_PRISM,
+  HP_NONE,
+};
+int reftet_1e_1va_newels[][8] =
+{
+  { 1, 9, 5, 6 },
+  { 9, 5, 6, 2, 7, 8 },
+  { 7, 3, 5, 8, 4, 6 }
+};
+HPRef_Struct reftet_1e_1va =
+{
+  HP_TET,
+  reftet_1e_1va_splitedges, 
+  0, 0,
+  reftet_1e_1va_newelstypes, 
+  reftet_1e_1va_newels
+};
+
+
+
+
+
+
+// HP_TET_1E_1VB
+int reftet_1e_1vb_splitedges[][3] =
+{
+  { 1, 3, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 2, 4, 8 },
+  { 4, 1, 9 },
+  { 4, 2, 10 },
+  { 4, 3, 11 },
+  { 0, 0, 0 }
+};
+int reftet_1e_1vb_splitelements[][5] =
+{
+  { 4, 1, 2, 3, 12 },
+  { 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_1e_1vb_newelstypes[] =
+{
+  HP_PRISM_SINGEDGE,
+  HP_TET_0E_1V,
+  HP_PYRAMID,
+  HP_TET,
+  HP_PYRAMID, 
+  HP_TET,
+  HP_PYRAMID,
+  HP_TET,
+  HP_PYRAMID,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_1e_1vb_newels[][8] =
+{
+  { 1, 5, 6, 2, 7, 8 },
+  { 4, 11, 10, 9 },
+  { 7, 8, 10, 11, 12 },
+  { 3, 7, 11, 12 },
+  { 5, 11, 9, 6, 12 },
+  { 5, 3, 11, 12 },
+  { 6, 9, 10, 8, 12 },
+  { 5, 7, 3, 12 },
+  { 5, 6, 8, 7, 12 },
+  { 9, 11, 10, 12 }
+};
+HPRef_Struct reftet_1e_1vb =
+{
+  HP_TET,
+  reftet_1e_1vb_splitedges, 
+  0,
+  reftet_1e_1vb_splitelements, 
+  reftet_1e_1vb_newelstypes, 
+  reftet_1e_1vb_newels
+};
+
+
+
+
+
+
+
+
+// HP_TET_1E_2VA
+int reftet_1e_2va_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_1e_2va_newelstypes[] =
+{
+  HP_TET_1E_1VA,
+  HP_TET_1E_1VA,
+  HP_PRISM_SINGEDGE,
+  HP_PRISM,
+  HP_NONE,
+};
+int reftet_1e_2va_newels[][8] =
+{
+  { 1, 5, 6, 7 },
+  { 2, 8, 10, 9 },
+  { 5, 6, 7, 8, 9, 10 },
+  { 4, 10, 7, 3, 9, 6 },
+};
+HPRef_Struct reftet_1e_2va =
+{
+  HP_TET,
+  reftet_1e_2va_splitedges, 
+  0, 0,
+  reftet_1e_2va_newelstypes, 
+  reftet_1e_2va_newels
+};
+
+
+
+
+
+
+
+// HP_TET_1E_2VB
+int reftet_1e_2vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 3, 8 },
+  { 2, 4, 9 },
+  { 3, 1, 10 },
+  { 3, 2, 11 },
+  { 3, 4, 12 },
+  { 0, 0, 0 }
+};
+int reftet_1e_2vb_splitelements[][5] =
+{
+  { 3, 4, 1, 2, 13 },
+  { 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_1e_2vb_newelstypes[] =
+{
+  HP_TET_1E_1VA,
+  HP_PRISM_SINGEDGE,
+  HP_TET_0E_1V,
+  HP_PYRAMID,
+  HP_TET,
+  HP_PYRAMID, 
+  HP_TET,
+  HP_PYRAMID,
+  HP_TET,
+  HP_PYRAMID,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_1e_2vb_newels[][8] =
+{
+  { 1, 5, 6, 7 },
+  { 5, 6, 7, 2, 8, 9 },
+  { 3, 10, 11, 12 },
+
+  { 8, 9, 12, 11, 13 },
+  { 4, 12, 9, 13 },
+  { 6, 10, 12, 7, 13 },
+  { 4, 7, 12, 13 },
+  { 6, 8, 11, 10, 13 },
+  { 4, 9, 7, 13 },
+  { 6, 7, 9, 8, 13 },
+  { 10, 11, 12, 13 },
+};
+HPRef_Struct reftet_1e_2vb =
+{
+  HP_TET,
+  reftet_1e_2vb_splitedges, 
+  0,
+  reftet_1e_2vb_splitelements, 
+  reftet_1e_2vb_newelstypes, 
+  reftet_1e_2vb_newels
+};
+
+
+
+
+
+
+// HP_TET_1E_2VC
+int reftet_1e_2vc_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 3, 8 },
+  { 2, 4, 9 },
+  { 4, 1, 10 },
+  { 4, 2, 11 },
+  { 4, 3, 12 },
+  { 0, 0, 0 }
+};
+int reftet_1e_2vc_splitelements[][5] =
+{
+  { 4, 1, 2, 3, 13 },
+  { 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_1e_2vc_newelstypes[] =
+{
+  HP_TET_1E_1VA,
+  HP_PRISM_SINGEDGE,
+  HP_TET_0E_1V,
+  HP_PYRAMID,
+  HP_TET,
+  HP_PYRAMID, 
+  HP_TET,
+  HP_PYRAMID,
+  HP_TET,
+  HP_PYRAMID,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_1e_2vc_newels[][8] =
+{
+  { 1, 5, 6, 7 },
+  { 5, 6, 7, 2, 8, 9 },
+  { 4, 11, 10, 12 },
+  { 8, 9, 11, 12, 13 },
+  { 3, 8, 12, 13 },
+  { 7, 6, 12, 10, 13 },
+  { 3, 12, 6, 13 },
+  { 9, 7, 10, 11, 13 },
+  { 3, 6, 8, 13 },
+  { 6, 7, 9, 8, 13 },
+  { 10, 12, 11, 13 }
+};
+HPRef_Struct reftet_1e_2vc =
+{
+  HP_TET,
+  reftet_1e_2vc_splitedges, 
+  0,
+  reftet_1e_2vc_splitelements, 
+  reftet_1e_2vc_newelstypes, 
+  reftet_1e_2vc_newels
+};
+
+
+
+
+
+
+
+
+/*
+
+// HP_TET_1E_2VD
+int reftet_1e_2vd_splitedges[][3] =
+{
+  { 1, 3, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 2, 4, 8 },
+  { 3, 1, 9 },
+  { 3, 2, 10 },
+  { 3, 4, 11 },
+  { 4, 1, 12 },
+  { 4, 2, 13 },
+  { 4, 3, 14 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_1e_2vd_newelstypes[] =
+{
+  HP_PRISM_SINGEDGE,
+  HP_TET_0E_1V,
+  HP_TET_0E_1V,
+  HP_PRISM,
+  HP_HEX,
+  HP_NONE,
+};
+int reftet_1e_2vd_newels[][8] =
+{
+  { 1, 5, 6, 2, 7, 8 },
+  { 4, 13, 12, 14 },
+  { 3, 10, 11, 9 },
+  { 14, 13, 12, 11, 10, 9 },
+  { 6, 12, 13, 8, 5, 9, 10, 7 },
+};
+HPRef_Struct reftet_1e_2vd =
+{
+  HP_TET,
+  reftet_1e_2vd_splitedges, 
+  0, 0,
+  reftet_1e_2vd_newelstypes, 
+  reftet_1e_2vd_newels
+};
+
+*/
+
+
+
+
+//  HP_TET_1E_2VD,  // 1 v on edge
+int reftet_1e_2vd_splitedges[][3] =
+{
+  // { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  // { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_1e_2vd_splitfaces[][4] =
+  {
+    { 1, 3, 4, 19 },
+    { 2, 3, 4, 22 },
+    { 3, 1, 4, 24 },
+    { 3, 2, 4, 25 },
+    { 4, 1, 3, 27 },
+    { 4, 2, 3, 28 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_1e_2vd_newelstypes[] =
+  {
+    HP_PRISM_SINGEDGE,
+    HP_TET_0E_1V,
+    HP_TET_0E_1V,
+    HP_PRISM,
+    HP_HEX,
+    HP_PYRAMID,
+    HP_HEX,
+    HP_PYRAMID,
+    HP_PRISM,
+    HP_PRISM,
+    HP_NONE,
+  };
+int reftet_1e_2vd_newels[][8] =
+{
+  { 1, 6, 7, 2, 9, 10 },
+  { 3, 11, 12, 13 },
+  { 4, 16, 15, 14 },
+  { 7, 6, 19, 10, 9, 22 },
+  { 7, 19, 27, 14, 10, 22, 28, 15 },
+  { 14, 15, 28, 27, 16 },
+  { 9, 6, 19, 22, 12, 11, 24, 25 },
+  { 12, 11, 24, 25, 13 },
+  { 19, 24, 27, 22, 25, 28 },
+  { 16, 28, 27, 13, 25, 24 }
+};
+HPRef_Struct reftet_1e_2vd =
+{
+  HP_TET,
+  reftet_1e_2vd_splitedges, 
+  reftet_1e_2vd_splitfaces, 
+  0,
+  reftet_1e_2vd_newelstypes, 
+  reftet_1e_2vd_newels
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// HP_TET_1E_3VA
+int reftet_1e_3va_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 0, 0, 0 }
+};
+int reftet_1e_3va_splitelements[][5] =
+{
+  { 1, 2, 3, 4, 14 },
+  { 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_1e_3va_newelstypes[] =
+{
+  HP_PRISM_SINGEDGE,
+  HP_TET_1E_1VA,
+  HP_TET_1E_1VA,
+  HP_TET_0E_1V,
+
+  HP_PYRAMID,
+  HP_TET,
+  HP_PYRAMID, 
+  HP_TET,
+  HP_PYRAMID,
+  HP_TET,
+  HP_PYRAMID,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_1e_3va_newels[][8] =
+{
+  { 5, 6, 7, 8, 9, 10 },
+  { 1, 5, 6, 7 },
+  { 2, 8, 10, 9 },
+  { 3, 11, 12, 13 },
+
+  { 6, 7, 10, 9, 14 },
+  { 4, 10, 7, 14 },
+  { 9, 10, 13, 12, 14 },
+  { 4, 13, 10, 14 },
+  { 6, 11, 13, 7, 14 },
+  { 4, 7, 13, 14 },
+  { 6, 11, 12, 9, 14 },
+  { 11, 13, 12, 14 },
+};
+
+HPRef_Struct reftet_1e_3va =
+{
+  HP_TET,
+  reftet_1e_3va_splitedges, 
+  0,
+  reftet_1e_3va_splitelements, 
+  reftet_1e_3va_newelstypes, 
+  reftet_1e_3va_newels
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//  HP_TET_1E_3VB,  // 1 v on edge
+int reftet_1e_3vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  // { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_1e_3vb_splitfaces[][4] =
+  {
+    { 1, 3, 4, 19 },
+    { 2, 3, 4, 22 },
+    { 3, 1, 4, 24 },
+    { 3, 2, 4, 25 },
+    { 4, 1, 3, 27 },
+    { 4, 2, 3, 28 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_1e_3vb_newelstypes[] =
+  {
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_TET_0E_1V,
+    HP_TET_0E_1V,
+    HP_PRISM,
+    HP_HEX,
+    HP_PYRAMID,
+    HP_HEX,
+    HP_PYRAMID,
+    HP_PRISM,
+    HP_PRISM,
+    HP_NONE,
+  };
+int reftet_1e_3vb_newels[][8] =
+{
+  { 1, 5, 6, 7 },
+  { 5, 6, 7, 2, 9, 10 },
+  { 3, 11, 12, 13 },
+  { 4, 16, 15, 14 },
+  { 7, 6, 19, 10, 9, 22 },
+  { 7, 19, 27, 14, 10, 22, 28, 15 },
+  { 14, 15, 28, 27, 16 },
+  { 9, 6, 19, 22, 12, 11, 24, 25 },
+  { 12, 11, 24, 25, 13 },
+  { 19, 24, 27, 22, 25, 28 },
+  { 16, 28, 27, 13, 25, 24 }
+};
+HPRef_Struct reftet_1e_3vb =
+{
+  HP_TET,
+  reftet_1e_3vb_splitedges, 
+  reftet_1e_3vb_splitfaces, 
+  0,
+  reftet_1e_3vb_newelstypes, 
+  reftet_1e_3vb_newels
+};
+
+
+
+
+
+
+/*
+// HP_TET_1E_4V
+int reftet_1e_4v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_1e_4v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 1, 2, 4, 18 },
+    { 1, 3, 4, 19 },
+
+    { 2, 1, 3, 20 },
+    { 2, 1, 4, 21 },
+    { 2, 3, 4, 22 },
+
+    { 3, 1, 2, 23 },
+    { 3, 1, 4, 24 },
+    { 3, 2, 4, 25 },
+
+    { 4, 1, 2, 26 },
+    { 4, 1, 3, 27 },
+    { 4, 2, 3, 28 },
+    { 0, 0, 0, 0 },
+  };
+int reftet_1e_4v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 29 },
+    { 2, 3, 4, 1, 30 },
+    { 3, 4, 1, 2, 31 },
+    { 4, 1, 2, 3, 32 },
+    { 0 },
+  };
+HPREF_ELEMENT_TYPE reftet_1e_4v_newelstypes[] =
+{
+  HP_HEX_1E_1V,
+  HP_HEX_1E_1V,
+  HP_HEX_0E_1V,
+  HP_HEX_0E_1V,
+  HP_PRISM_SINGEDGE, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM, HP_PRISM, 
+  HP_PRISM,
+  HP_PRISM,
+  HP_PRISM,
+  HP_PRISM,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_1e_4v_newels[][8] =
+{
+  { 1, 5, 17, 6, 7, 18, 29, 19 },
+  //  { 2, 9, 20, 8, 10, 22, 30, 21 },
+  { 2, 8, 21, 10, 9, 20, 30, 22 },
+  { 3, 11, 23, 12, 13, 24, 31, 25 },
+  { 4, 15, 26, 14, 16, 28, 32, 27 },
+  { 5, 17, 18, 8, 20, 21 },
+  { 18, 17, 29, 21, 20, 30 },
+  { 6, 19, 17,  11, 24, 23 },
+  { 17, 19, 29,  23, 24, 31 },
+  { 7, 18, 19, 14, 26, 27 },
+  { 19, 18, 29, 27, 26, 32 },
+  { 9, 20, 22, 12, 23, 25 },
+  { 22, 20, 30, 25, 23, 31 },
+  { 10, 22, 21, 15, 28, 26 },
+  { 21, 22, 30, 26, 28, 32 },
+  { 13, 24, 25, 16, 27, 28 },
+  { 25, 24, 31, 28, 27, 32 },
+  { 17, 20, 23, 29, 30, 31 },
+  { 18, 26, 21, 29, 32, 30 },
+  { 19, 24, 27, 29, 31, 32 },
+  { 22, 28, 25, 30, 32, 31 },
+
+  { 29, 30, 31, 32 },
+};
+HPRef_Struct reftet_1e_4v =
+{
+  HP_TET,
+  reftet_1e_4v_splitedges, 
+  reftet_1e_4v_splitfaces, 
+  reftet_1e_4v_splitelements, 
+  reftet_1e_4v_newelstypes, 
+  reftet_1e_4v_newels
+};
+*/
+
+
+
+
+// HP_TET_1E_4V
+int reftet_1e_4v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_1e_4v_splitfaces[][4] =
+  {
+    { 1, 3, 4, 17 },
+    { 2, 3, 4, 18 },
+
+    { 3, 1, 4, 19 },
+    { 3, 2, 4, 20 },
+
+    { 4, 1, 3, 21 },
+    { 4, 2, 3, 22 },
+    { 0, 0, 0, 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_1e_4v_newelstypes[] =
+{
+  HP_TET_1E_1VA,
+  HP_TET_1E_1VA,
+  //  HP_TET_1E_1VA,
+  //  HP_TET_1E_1VA,
+  HP_PRISM_SINGEDGE,
+  HP_PRISM,
+  HP_HEX, 
+  HP_HEX, 
+  HP_PRISM,
+  HP_PRISM,
+
+  HP_PYRAMID,
+  HP_TET_0E_1V,
+
+  HP_PYRAMID,
+  HP_TET_0E_1V,
+
+  HP_NONE,
+};
+
+int reftet_1e_4v_newels[][8] =
+{
+  { 1, 5, 6, 7 },
+  { 2, 8, 10, 9 },
+
+  { 5, 6, 7, 8, 9, 10 },
+  { 7, 6, 17, 10, 9, 18 },
+
+  { 7, 10, 18, 17, 14, 15, 22, 21 },
+  { 9, 6, 17, 18, 12, 11, 19, 20 },
+
+  { 17, 19, 21, 18, 20, 22 },
+  { 16, 22, 21, 13, 20, 19 },
+
+  { 14, 15, 22, 21, 16 },
+  { 4, 14, 16, 15 },
+  { 12, 11, 19, 20, 13 },
+  { 3, 11, 12, 13 },
+
+
+
+  { 1, 5, 17, 6, 7, 18, 29, 19 },
+  //  { 2, 9, 20, 8, 10, 22, 30, 21 },
+  { 2, 8, 21, 10, 9, 20, 30, 22 },
+  { 3, 11, 23, 12, 13, 24, 31, 25 },
+  { 4, 15, 26, 14, 16, 28, 32, 27 },
+  { 5, 17, 18, 8, 20, 21 },
+  { 18, 17, 29, 21, 20, 30 },
+  { 6, 19, 17,  11, 24, 23 },
+  { 17, 19, 29,  23, 24, 31 },
+  { 7, 18, 19, 14, 26, 27 },
+  { 19, 18, 29, 27, 26, 32 },
+  { 9, 20, 22, 12, 23, 25 },
+  { 22, 20, 30, 25, 23, 31 },
+  { 10, 22, 21, 15, 28, 26 },
+  { 21, 22, 30, 26, 28, 32 },
+  { 13, 24, 25, 16, 27, 28 },
+  { 25, 24, 31, 28, 27, 32 },
+  { 17, 20, 23, 29, 30, 31 },
+  { 18, 26, 21, 29, 32, 30 },
+  { 19, 24, 27, 29, 31, 32 },
+  { 22, 28, 25, 30, 32, 31 },
+
+  { 29, 30, 31, 32 },
+};
+HPRef_Struct reftet_1e_4v =
+{
+  HP_TET,
+  reftet_1e_4v_splitedges, 
+  reftet_1e_4v_splitfaces, 
+  0, 
+  reftet_1e_4v_newelstypes, 
+  reftet_1e_4v_newels
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+//  HP_TET_2EA_0V,  // 2 edges connected
+int reftet_2ea_0v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 0, 0, 0 }
+};
+int reftet_2ea_0v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_2ea_0v_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    HP_TET,
+    HP_NONE,
+  };
+int reftet_2ea_0v_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  { 5, 17, 7, 2, 9, 10 },
+  { 6, 7, 17, 3, 13, 12 },
+  { 17, 9, 12, 7, 10, 13 },
+  { 7, 10, 13, 4 },
+};
+HPRef_Struct reftet_2ea_0v =
+{
+  HP_TET,
+  reftet_2ea_0v_splitedges, 
+  reftet_2ea_0v_splitfaces, 
+  0,
+  reftet_2ea_0v_newelstypes, 
+  reftet_2ea_0v_newels
+};
+
+
+
+
+
+
+//  HP_TET_2EA_1VA,  // 2 edges connected
+int reftet_2ea_1va_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 0, 0, 0 }
+};
+int reftet_2ea_1va_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_2ea_1va_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_PRISM_SINGEDGE,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    HP_TET,
+    HP_NONE,
+  };
+int reftet_2ea_1va_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  { 5, 17, 7, 8, 9, 10 },
+  { 2, 8, 10, 9 },
+  { 6, 7, 17, 3, 13, 12 },
+  { 17, 9, 12, 7, 10, 13 },
+  { 7, 10, 13, 4 },
+};
+HPRef_Struct reftet_2ea_1va =
+{
+  HP_TET,
+  reftet_2ea_1va_splitedges, 
+  reftet_2ea_1va_splitfaces, 
+  0,
+  reftet_2ea_1va_newelstypes, 
+  reftet_2ea_1va_newels
+};
+
+
+
+
+
+
+
+
+//  HP_TET_2EA_1VB, 
+int reftet_2ea_1vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 0, 0, 0 }
+};
+int reftet_2ea_1vb_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_2ea_1vb_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    HP_TET,
+    HP_NONE,
+  };
+int reftet_2ea_1vb_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  { 3, 11, 12, 13 },
+  { 5, 17, 7, 2, 9, 10 },
+  { 6, 7, 17, 11, 13, 12 },
+  { 17, 9, 12, 7, 10, 13 },
+  { 7, 10, 13, 4 },
+};
+HPRef_Struct reftet_2ea_1vb =
+{
+  HP_TET,
+  reftet_2ea_1vb_splitedges, 
+  reftet_2ea_1vb_splitfaces, 
+  0,
+  reftet_2ea_1vb_newelstypes, 
+  reftet_2ea_1vb_newels
+};
+
+
+
+
+
+
+//  HP_TET_2EA_1VC,  // 2 edges connected
+int reftet_2ea_1vc_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  //  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_2ea_1vc_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 2, 3, 4, 18 },
+    { 3, 4, 2, 19 },
+    { 4, 2, 3, 20 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_2ea_1vc_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 21 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_2ea_1vc_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    //    HP_TET_1E_1VA,
+    HP_TET_0E_1V,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+
+    HP_TET, HP_TET, HP_TET, HP_TET, 
+    HP_PYRAMID, HP_PYRAMID, HP_PYRAMID, 
+    HP_PYRAMID, HP_PYRAMID, HP_TET,
+    HP_PYRAMID, HP_PYRAMID, HP_TET,
+    //     HP_PRISM,
+    //    HP_PRISM,
+    HP_NONE,
+  };
+int reftet_2ea_1vc_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  // { 3, 11, 12, 13 },
+  { 4, 15, 14, 16 }, 
+  { 5, 17, 7, 2, 9, 10 },
+  { 6, 7, 17, 3, 13, 12 },
+ 
+  { 9, 10, 18, 21 },
+  { 13, 12, 19, 21 },
+  { 15, 16, 20, 21 },
+  { 18, 20, 19, 21 },
+  { 10, 15, 20, 18, 21 },
+  { 13, 19, 20, 16, 21 },
+  { 9, 18, 19, 12, 21 },
+  
+  { 7, 13, 16, 14, 21 },
+  { 7, 14, 15, 10, 21 },
+  { 9, 12, 17, 21 },
+  { 7, 10, 9, 17, 21 },
+  { 7, 17, 12, 13, 21 },
+  { 14, 16, 15, 21 },
+  //  { 17, 9, 12, 7, 10, 13 },
+  //  { 7, 10, 13, 14, 15, 16 },
+};
+HPRef_Struct reftet_2ea_1vc =
+{
+  HP_TET,
+  reftet_2ea_1vc_splitedges, 
+  reftet_2ea_1vc_splitfaces, 
+  reftet_2ea_1vc_splitelements, 
+  reftet_2ea_1vc_newelstypes, 
+  reftet_2ea_1vc_newels
+};
+
+
+
+
+
+
+
+
+
+
+
+ 
+//  HP_TET_2EA_2VA, 
+int reftet_2ea_2va_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 0, 0, 0 }
+};
+int reftet_2ea_2va_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_2ea_2va_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    HP_TET,
+    HP_NONE,
+  };
+int reftet_2ea_2va_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  { 3, 11, 12, 13 },
+  { 2, 8, 10, 9 },
+  { 5, 17, 7, 8, 9, 10 },
+  { 6, 7, 17, 11, 13, 12 },
+  { 17, 9, 12, 7, 10, 13 },
+  { 7, 10, 13, 4 },
+};
+HPRef_Struct reftet_2ea_2va =
+{
+  HP_TET,
+  reftet_2ea_2va_splitedges, 
+  reftet_2ea_2va_splitfaces, 
+  0,
+  reftet_2ea_2va_newelstypes, 
+  reftet_2ea_2va_newels
+};
+
+
+
+
+
+
+
+
+
+
+
+//  HP_TET_2EA_2VB,  // 2 edges connected
+int reftet_2ea_2vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  //  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_2ea_2vb_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 2, 3, 4, 18 },
+    { 3, 4, 2, 19 },
+    { 4, 2, 3, 20 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_2ea_2vb_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 21 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_2ea_2vb_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_TET_1E_1VA,
+    //  HP_TET_1E_1VA,
+    HP_TET_0E_1V,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+
+    HP_TET, HP_TET, HP_TET, HP_TET, 
+    HP_PYRAMID, HP_PYRAMID, HP_PYRAMID, 
+    HP_PYRAMID, HP_PYRAMID, HP_TET,
+    HP_PYRAMID, HP_PYRAMID, HP_TET,
+    //     HP_PRISM,
+    //    HP_PRISM,
+    HP_NONE,
+  };
+int reftet_2ea_2vb_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  { 2, 8, 10, 9 },
+  //  { 3, 11, 12, 13 },
+  { 4, 15, 14, 16 }, 
+  { 5, 17, 7, 8, 9, 10 },
+  { 6, 7, 17, 3, 13, 12 },
+ 
+  { 9, 10, 18, 21 },
+  { 13, 12, 19, 21 },
+  { 15, 16, 20, 21 },
+  { 18, 20, 19, 21 },
+  { 10, 15, 20, 18, 21 },
+  { 13, 19, 20, 16, 21 },
+  { 9, 18, 19, 12, 21 },
+  
+  { 7, 13, 16, 14, 21 },
+  { 7, 14, 15, 10, 21 },
+  { 9, 12, 17, 21 },
+  { 7, 10, 9, 17, 21 },
+  { 7, 17, 12, 13, 21 },
+  { 14, 16, 15, 21 },
+  //  { 17, 9, 12, 7, 10, 13 },
+  //  { 7, 10, 13, 14, 15, 16 },
+};
+HPRef_Struct reftet_2ea_2vb =
+{
+  HP_TET,
+  reftet_2ea_2vb_splitedges, 
+  reftet_2ea_2vb_splitfaces, 
+  reftet_2ea_2vb_splitelements, 
+  reftet_2ea_2vb_newelstypes, 
+  reftet_2ea_2vb_newels
+};
+
+
+
+
+
+
+
+ 
+
+
+//  HP_TET_2EA_2VC,  // 2 edges connected
+int reftet_2ea_2vc_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  //  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_2ea_2vc_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 2, 3, 4, 18 },
+    { 3, 4, 2, 19 },
+    { 4, 2, 3, 20 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_2ea_2vc_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 21 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_2ea_2vc_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    HP_TET_0E_1V,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+
+    HP_TET, HP_TET, HP_TET, HP_TET, 
+    HP_PYRAMID, HP_PYRAMID, HP_PYRAMID, 
+    HP_PYRAMID, HP_PYRAMID, HP_TET,
+    HP_PYRAMID, HP_PYRAMID, HP_TET,
+    //     HP_PRISM,
+    //    HP_PRISM,
+    HP_NONE,
+  };
+int reftet_2ea_2vc_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  //  { 2, 8, 10, 9 },
+  { 3, 11, 12, 13 },
+  { 4, 15, 14, 16 }, 
+  { 5, 17, 7, 2, 9, 10 },
+  { 6, 7, 17, 11, 13, 12 },
+ 
+  { 9, 10, 18, 21 },
+  { 13, 12, 19, 21 },
+  { 15, 16, 20, 21 },
+  { 18, 20, 19, 21 },
+  { 10, 15, 20, 18, 21 },
+  { 13, 19, 20, 16, 21 },
+  { 9, 18, 19, 12, 21 },
+  
+  { 7, 13, 16, 14, 21 },
+  { 7, 14, 15, 10, 21 },
+  { 9, 12, 17, 21 },
+  { 7, 10, 9, 17, 21 },
+  { 7, 17, 12, 13, 21 },
+  { 14, 16, 15, 21 },
+  //  { 17, 9, 12, 7, 10, 13 },
+  //  { 7, 10, 13, 14, 15, 16 },
+};
+HPRef_Struct reftet_2ea_2vc =
+{
+  HP_TET,
+  reftet_2ea_2vc_splitedges, 
+  reftet_2ea_2vc_splitfaces, 
+  reftet_2ea_2vc_splitelements, 
+  reftet_2ea_2vc_newelstypes, 
+  reftet_2ea_2vc_newels
+};
+
+
+
+
+
+
+
+
+//  HP_TET_2EA_3V,  // 2 edges connected
+int reftet_2ea_3v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_2ea_3v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 2, 3, 4, 18 },
+    { 3, 4, 2, 19 },
+    { 4, 2, 3, 20 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_2ea_3v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 21 },
+    { 0, 0, 0, 0 }
+  };
+HPREF_ELEMENT_TYPE reftet_2ea_3v_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    HP_TET_0E_1V,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+
+    HP_TET, HP_TET, HP_TET, HP_TET, 
+    HP_PYRAMID, HP_PYRAMID, HP_PYRAMID, 
+    HP_PYRAMID, HP_PYRAMID, HP_TET,
+    HP_PYRAMID, HP_PYRAMID, HP_TET,
+    //     HP_PRISM,
+    //    HP_PRISM,
+    HP_NONE,
+  };
+int reftet_2ea_3v_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  { 2, 8, 10, 9 },
+  { 3, 11, 12, 13 },
+  { 4, 15, 14, 16 }, 
+  { 5, 17, 7, 8, 9, 10 },
+  { 6, 7, 17, 11, 13, 12 },
+ 
+  { 9, 10, 18, 21 },
+  { 13, 12, 19, 21 },
+  { 15, 16, 20, 21 },
+  { 18, 20, 19, 21 },
+  { 10, 15, 20, 18, 21 },
+  { 13, 19, 20, 16, 21 },
+  { 9, 18, 19, 12, 21 },
+  
+  { 7, 13, 16, 14, 21 },
+  { 7, 14, 15, 10, 21 },
+  { 9, 12, 17, 21 },
+  { 7, 10, 9, 17, 21 },
+  { 7, 17, 12, 13, 21 },
+  { 14, 16, 15, 21 },
+  //  { 17, 9, 12, 7, 10, 13 },
+  //  { 7, 10, 13, 14, 15, 16 },
+};
+HPRef_Struct reftet_2ea_3v =
+{
+  HP_TET,
+  reftet_2ea_3v_splitedges, 
+  reftet_2ea_3v_splitfaces, 
+  reftet_2ea_3v_splitelements, 
+  reftet_2ea_3v_newelstypes, 
+  reftet_2ea_3v_newels
+};
+
+
+
+
+
+
+
+//  HP_TET_2EB_0V,  // 2 opposite edges
+int reftet_2eb_0v_splitedges[][3] =
+{
+  { 1, 3, 5 },
+  { 1, 4, 6 },
+  { 2, 3, 7 },
+  { 2, 4, 8 },
+  { 3, 1, 9 },
+  { 3, 2, 10 },
+  { 4, 1, 11 },
+  { 4, 2, 12 },
+  { 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_2eb_0v_newelstypes[] =
+  {
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_HEX,
+    HP_NONE,
+  };
+int reftet_2eb_0v_newels[][8] =
+{
+  { 1, 5, 6, 2, 7, 8 },
+  { 3, 9, 10, 4, 11, 12 },
+  { 6, 11, 12, 8, 5, 9, 10, 7 },
+};
+HPRef_Struct reftet_2eb_0v =
+{
+  HP_TET,
+  reftet_2eb_0v_splitedges, 
+  0, 0,
+  reftet_2eb_0v_newelstypes, 
+  reftet_2eb_0v_newels
+};
+
+
+//  HP_TET_2EB_1V,    // V1
+
+
+//  HP_TET_2EB_1V,  // 2 opposite edges, V1
+int reftet_2eb_1v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_2eb_1v_newelstypes[] =
+  {
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    HP_HEX,
+    HP_NONE,
+  };
+int reftet_2eb_1v_newels[][8] =
+{
+  { 5, 6, 7, 2, 9, 10 },
+  { 4, 15, 14, 3, 12, 11 },
+  { 1, 5, 6, 7 },
+  //  { 2, 8, 10, 9 },
+  //  { 3, 13, 11, 12 },
+  //  { 4, 16, 15, 14 },
+  { 7, 14, 15, 10, 6, 11, 12, 9 }
+};
+HPRef_Struct reftet_2eb_1v =
+{
+  HP_TET,
+  reftet_2eb_1v_splitedges, 
+  0, 0,
+  reftet_2eb_1v_newelstypes, 
+  reftet_2eb_1v_newels
+};
+
+
+
+//  HP_TET_2EB_2VA,  // 2 opposite edges, V1,2
+int reftet_2eb_2va_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_2eb_2va_newelstypes[] =
+  {
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    HP_HEX,
+    HP_NONE,
+  };
+int reftet_2eb_2va_newels[][8] =
+{
+  { 5, 6, 7, 8, 9, 10 },
+  { 4, 15, 14, 3, 12, 11 },
+  { 1, 5, 6, 7 },
+  { 2, 8, 10, 9 },
+  //  { 3, 13, 11, 12 },
+  //  { 4, 16, 15, 14 },
+  { 7, 14, 15, 10, 6, 11, 12, 9 }
+};
+HPRef_Struct reftet_2eb_2va =
+{
+  HP_TET,
+  reftet_2eb_2va_splitedges, 
+  0, 0,
+  reftet_2eb_2va_newelstypes, 
+  reftet_2eb_2va_newels
+};
+
+
+//  HP_TET_2EB_2VB,   // V1,3
+int reftet_2eb_2vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_2eb_2vb_newelstypes[] =
+  {
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_TET_1E_1VA,
+    // HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    // HP_TET_1E_1VA,
+    HP_HEX,
+    HP_NONE,
+  };
+int reftet_2eb_2vb_newels[][8] =
+{
+  { 5, 6, 7, 2, 9, 10 },
+  { 4, 15, 14, 13, 12, 11 },
+  { 1, 5, 6, 7 },
+  // { 2, 8, 10, 9 },
+  { 3, 13, 11, 12 },
+  // { 4, 16, 15, 14 },
+  { 7, 14, 15, 10, 6, 11, 12, 9 }
+};
+HPRef_Struct reftet_2eb_2vb =
+{
+  HP_TET,
+  reftet_2eb_2vb_splitedges, 
+  0, 0,
+  reftet_2eb_2vb_newelstypes, 
+  reftet_2eb_2vb_newels
+};
+
+
+
+
+//  HP_TET_2EB_2VC,   // V1,4
+int reftet_2eb_2vc_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_2eb_2vc_newelstypes[] =
+  {
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_TET_1E_1VA,
+    // HP_TET_1E_1VA,
+    // HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    HP_HEX,
+    HP_NONE,
+  };
+int reftet_2eb_2vc_newels[][8] =
+{
+  { 5, 6, 7, 2, 9, 10 },
+  { 16, 15, 14, 3, 12, 11 },
+  { 1, 5, 6, 7 },
+  // { 2, 8, 10, 9 },
+  // { 3, 13, 11, 12 },
+  { 4, 16, 15, 14 },
+  { 7, 14, 15, 10, 6, 11, 12, 9 }
+};
+HPRef_Struct reftet_2eb_2vc =
+{
+  HP_TET,
+  reftet_2eb_2vc_splitedges, 
+  0, 0,
+  reftet_2eb_2vc_newelstypes, 
+  reftet_2eb_2vc_newels
+};
+
+
+
+
+
+
+//  HP_TET_2EB_3V,    // V1,2,3
+int reftet_2eb_3v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_2eb_3v_newelstypes[] =
+  {
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    // HP_TET_1E_1VA,
+    HP_HEX,
+    HP_NONE,
+  };
+int reftet_2eb_3v_newels[][8] =
+{
+  { 5, 6, 7, 8, 9, 10 },
+  { 4, 15, 14, 13, 12, 11 },
+  { 1, 5, 6, 7 },
+  { 2, 8, 10, 9 },
+  { 3, 13, 11, 12 },
+  // { 4, 16, 15, 14 },
+  { 7, 14, 15, 10, 6, 11, 12, 9 }
+};
+HPRef_Struct reftet_2eb_3v =
+{
+  HP_TET,
+  reftet_2eb_3v_splitedges, 
+  0, 0,
+  reftet_2eb_3v_newelstypes, 
+  reftet_2eb_3v_newels
+};
+
+
+
+
+
+
+//  HP_TET_2EB_4V,  // 2 opposite edges
+int reftet_2eb_4v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftet_2eb_4v_newelstypes[] =
+  {
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    HP_HEX,
+    HP_NONE,
+  };
+int reftet_2eb_4v_newels[][8] =
+{
+  { 5, 6, 7, 8, 9, 10 },
+  { 16, 15, 14, 13, 12, 11 },
+  { 1, 5, 6, 7 },
+  { 2, 8, 10, 9 },
+  { 3, 13, 11, 12 },
+  { 4, 16, 15, 14 },
+  { 7, 14, 15, 10, 6, 11, 12, 9 }
+};
+HPRef_Struct reftet_2eb_4v =
+{
+  HP_TET,
+  reftet_2eb_4v_splitedges, 
+  0, 0,
+  reftet_2eb_4v_newelstypes, 
+  reftet_2eb_4v_newels
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//  HP_TET_3EA_0V,  
+int reftet_3ea_0v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 3, 8 },
+  { 2, 4, 9 },
+  { 3, 2, 10 },
+  { 3, 4, 11 },
+  { 4, 2, 12 },
+  { 4, 3, 13 },
+  { 0, 0, 0 }
+};
+int reftet_3ea_0v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 14 },
+    { 1, 2, 4, 15 },
+    { 1, 3, 4, 16 },
+    { 2, 3, 4, 17 },
+    { 3, 4, 2, 18 },
+    { 4, 2, 3, 19 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3ea_0v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3ea_0v_newelstypes[] =
+  {
+    HP_HEX_3E_0V,
+    HP_HEX_1E_0V,
+    HP_HEX_1E_0V,
+    HP_HEX_1E_0V,
+    HP_PRISM,
+    HP_PRISM,
+    HP_PRISM,
+    HP_TET,
+    HP_NONE,
+  };
+int reftet_3ea_0v_newels[][8] =
+{
+  { 1, 5, 14, 6, 7, 15, 20, 16 },
+  { 5, 2, 8, 14, 15, 9, 17, 20 },
+  { 3, 6, 14, 10, 11, 16, 20, 18 },
+  { 7, 4, 12, 15, 16, 13, 19, 20 },
+  { 11, 13, 16, 18, 19, 20 },
+  { 15, 12, 9, 20, 19, 17 },
+  { 8, 10, 14, 17, 18, 20 },
+  { 20, 17, 18, 19 },
+};
+HPRef_Struct reftet_3ea_0v =
+{
+  HP_TET,
+  reftet_3ea_0v_splitedges, 
+  reftet_3ea_0v_splitfaces, 
+  reftet_3ea_0v_splitelements, 
+  reftet_3ea_0v_newelstypes, 
+  reftet_3ea_0v_newels
+};
+
+
+
+
+
+
+
+
+
+
+//  HP_TET_3EA_1V,  
+int reftet_3ea_1v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 3, 8 },
+  { 2, 4, 9 },
+  { 3, 2, 10 },
+  { 3, 4, 11 },
+  { 4, 2, 12 },
+  { 4, 3, 13 },
+  { 2, 1, 21 },
+  { 3, 1, 22 },
+  { 4, 1, 23 },
+  { 0, 0, 0 }
+};
+int reftet_3ea_1v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 14 },
+    { 1, 2, 4, 15 },
+    { 1, 3, 4, 16 },
+    { 2, 3, 4, 17 },
+    { 3, 4, 2, 18 },
+    { 4, 2, 3, 19 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3ea_1v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3ea_1v_newelstypes[] =
+  {
+    HP_HEX_3E_0V,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    //    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    //    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+
+    HP_PRISM,
+    HP_PRISM,
+    HP_PRISM,
+    HP_TET,
+    HP_NONE,
+  };
+int reftet_3ea_1v_newels[][8] =
+{
+  { 1, 5, 14, 6, 7, 15, 20, 16 },
+
+  { 2, 21, 9, 8 },
+  { 5, 14, 15, 21, 8, 9 },
+  { 15, 14, 20, 9, 8, 17 },
+  //  { 3, 22, 10, 11 },
+  //  { 6, 16, 14, 22, 11, 10 },
+  { 6, 16, 14, 3, 11, 10 },
+  { 14, 16, 20, 10, 11, 18 },
+  //  { 4, 23, 13, 12 },
+  //  { 7, 15, 16, 23, 12, 13 },
+  { 7, 15, 16, 4, 12, 13 },
+  { 16, 15, 20, 13, 12, 19 },
+
+  { 11, 13, 16, 18, 19, 20 },
+  { 15, 12, 9, 20, 19, 17 },
+  { 8, 10, 14, 17, 18, 20 },
+  { 20, 17, 18, 19 },
+};
+HPRef_Struct reftet_3ea_1v =
+{
+  HP_TET,
+  reftet_3ea_1v_splitedges, 
+  reftet_3ea_1v_splitfaces, 
+  reftet_3ea_1v_splitelements, 
+  reftet_3ea_1v_newelstypes, 
+  reftet_3ea_1v_newels
+};
+
+
+
+
+
+
+
+
+
+
+//  HP_TET_3EA_2V,  
+int reftet_3ea_2v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 3, 8 },
+  { 2, 4, 9 },
+  { 3, 2, 10 },
+  { 3, 4, 11 },
+  { 4, 2, 12 },
+  { 4, 3, 13 },
+  { 2, 1, 21 },
+  { 3, 1, 22 },
+  { 4, 1, 23 },
+  { 0, 0, 0 }
+};
+int reftet_3ea_2v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 14 },
+    { 1, 2, 4, 15 },
+    { 1, 3, 4, 16 },
+    { 2, 3, 4, 17 },
+    { 3, 4, 2, 18 },
+    { 4, 2, 3, 19 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3ea_2v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3ea_2v_newelstypes[] =
+  {
+    HP_HEX_3E_0V,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    //    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+
+    HP_PRISM,
+    HP_PRISM,
+    HP_PRISM,
+    HP_TET,
+    HP_NONE,
+  };
+int reftet_3ea_2v_newels[][8] =
+{
+  { 1, 5, 14, 6, 7, 15, 20, 16 },
+
+  { 2, 21, 9, 8 },
+  { 5, 14, 15, 21, 8, 9 },
+  { 15, 14, 20, 9, 8, 17 },
+  { 3, 22, 10, 11 },
+  { 6, 16, 14, 22, 11, 10 },
+  { 14, 16, 20, 10, 11, 18 },
+  //  { 4, 23, 13, 12 },
+  { 7, 15, 16, 4, 12, 13 },
+  { 16, 15, 20, 13, 12, 19 },
+
+  { 11, 13, 16, 18, 19, 20 },
+  { 15, 12, 9, 20, 19, 17 },
+  { 8, 10, 14, 17, 18, 20 },
+  { 20, 17, 18, 19 },
+};
+HPRef_Struct reftet_3ea_2v =
+{
+  HP_TET,
+  reftet_3ea_2v_splitedges, 
+  reftet_3ea_2v_splitfaces, 
+  reftet_3ea_2v_splitelements, 
+  reftet_3ea_2v_newelstypes, 
+  reftet_3ea_2v_newels
+};
+
+
+
+
+
+
+
+
+//  HP_TET_3EA_3V,  
+int reftet_3ea_3v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 3, 8 },
+  { 2, 4, 9 },
+  { 3, 2, 10 },
+  { 3, 4, 11 },
+  { 4, 2, 12 },
+  { 4, 3, 13 },
+  { 2, 1, 21 },
+  { 3, 1, 22 },
+  { 4, 1, 23 },
+  { 0, 0, 0 }
+};
+int reftet_3ea_3v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 14 },
+    { 1, 2, 4, 15 },
+    { 1, 3, 4, 16 },
+    { 2, 3, 4, 17 },
+    { 3, 4, 2, 18 },
+    { 4, 2, 3, 19 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3ea_3v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3ea_3v_newelstypes[] =
+  {
+    HP_HEX_3E_0V,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM,
+
+    HP_PRISM,
+    HP_PRISM,
+    HP_PRISM,
+    HP_TET,
+    HP_NONE,
+  };
+int reftet_3ea_3v_newels[][8] =
+{
+  { 1, 5, 14, 6, 7, 15, 20, 16 },
+
+  { 2, 21, 9, 8 },
+  { 5, 14, 15, 21, 8, 9 },
+  { 15, 14, 20, 9, 8, 17 },
+  { 3, 22, 10, 11 },
+  { 6, 16, 14, 22, 11, 10 },
+  { 14, 16, 20, 10, 11, 18 },
+  { 4, 23, 13, 12 },
+  { 7, 15, 16, 23, 12, 13 },
+  { 16, 15, 20, 13, 12, 19 },
+
+  { 11, 13, 16, 18, 19, 20 },
+  { 15, 12, 9, 20, 19, 17 },
+  { 8, 10, 14, 17, 18, 20 },
+  { 20, 17, 18, 19 },
+};
+HPRef_Struct reftet_3ea_3v =
+{
+  HP_TET,
+  reftet_3ea_3v_splitedges, 
+  reftet_3ea_3v_splitfaces, 
+  reftet_3ea_3v_splitelements, 
+  reftet_3ea_3v_newelstypes, 
+  reftet_3ea_3v_newels
+};
+
+
+
+
+
+
+
+//  HP_TET_3EV_0V,  
+int reftet_3eb_0v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  //  { 3, 2, 12 },
+  { 3, 4, 13 },
+  //  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_3eb_0v_splitfaces[][4] =
+  {
+    { 1, 2, 4, 17 },
+    { 2, 1, 3, 18 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3eb_0v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3eb_0v_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_PYRAMID_EDGES,
+    //    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_TET,
+    HP_TET,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_NONE,
+  };
+int reftet_3eb_0v_newels[][8] =
+{
+  { 1, 7, 17, 5, 6 },
+  { 2, 9, 18, 8, 10 },
+  //  { 3, 12, 13, 11 },
+  //  { 4, 14, 16, 15 },
+  { 5, 6, 17, 8, 18, 10 },
+  { 7, 17, 6, 4, 15, 16 },
+  { 9, 18, 10, 3, 11, 13 },
+  
+  { 10, 15, 16, 13, 20 },
+  { 6, 11, 13, 16, 20 },
+  { 10, 17, 15, 20 },
+  { 6, 18, 11, 20 },
+  { 6, 17, 10, 18, 20 },
+  { 6, 16, 15, 17, 20 },
+  { 18, 10, 13, 11, 20 },
+};
+HPRef_Struct reftet_3eb_0v =
+{
+  HP_TET,
+  reftet_3eb_0v_splitedges, 
+  reftet_3eb_0v_splitfaces, 
+  reftet_3eb_0v_splitelements, 
+  reftet_3eb_0v_newelstypes, 
+  reftet_3eb_0v_newels
+};
+
+
+
+
+
+
+
+
+
+//  HP_TET_3EV_1V,  
+int reftet_3eb_1v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  //  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_3eb_1v_splitfaces[][4] =
+  {
+    { 1, 2, 4, 17 },
+    { 2, 1, 3, 18 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3eb_1v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3eb_1v_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_PYRAMID_EDGES,
+    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_TET,
+    HP_TET,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_NONE,
+  };
+int reftet_3eb_1v_newels[][8] =
+{
+  { 1, 7, 17, 5, 6 },
+  { 2, 9, 18, 8, 10 },
+  { 3, 12, 13, 11 },
+  //  { 4, 14, 16, 15 },
+  { 5, 6, 17, 8, 18, 10 },
+  { 7, 17, 6, 4, 15, 16 },
+  { 9, 18, 10, 12, 11, 13 },
+  
+  { 10, 15, 16, 13, 20 },
+  { 6, 11, 13, 16, 20 },
+  { 10, 17, 15, 20 },
+  { 6, 18, 11, 20 },
+  { 6, 17, 10, 18, 20 },
+  { 6, 16, 15, 17, 20 },
+  { 18, 10, 13, 11, 20 },
+};
+HPRef_Struct reftet_3eb_1v =
+{
+  HP_TET,
+  reftet_3eb_1v_splitedges, 
+  reftet_3eb_1v_splitfaces, 
+  reftet_3eb_1v_splitelements, 
+  reftet_3eb_1v_newelstypes, 
+  reftet_3eb_1v_newels
+};
+
+
+
+
+
+
+
+
+//  HP_TET_3EV_2V,  
+int reftet_3eb_2v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_3eb_2v_splitfaces[][4] =
+  {
+    { 1, 2, 4, 17 },
+    { 2, 1, 3, 18 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3eb_2v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3eb_2v_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_PYRAMID_EDGES,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_TET,
+    HP_TET,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_NONE,
+  };
+int reftet_3eb_2v_newels[][8] =
+{
+  { 1, 7, 17, 5, 6 },
+  { 2, 9, 18, 8, 10 },
+  { 3, 12, 13, 11 },
+  { 4, 14, 16, 15 },
+  { 5, 6, 17, 8, 18, 10 },
+  { 7, 17, 6, 14, 15, 16 },
+  { 9, 18, 10, 12, 11, 13 },
+  
+  { 10, 15, 16, 13, 20 },
+  { 6, 11, 13, 16, 20 },
+  { 10, 17, 15, 20 },
+  { 6, 18, 11, 20 },
+  { 6, 17, 10, 18, 20 },
+  { 6, 16, 15, 17, 20 },
+  { 18, 10, 13, 11, 20 },
+};
+HPRef_Struct reftet_3eb_2v =
+{
+  HP_TET,
+  reftet_3eb_2v_splitedges, 
+  reftet_3eb_2v_splitfaces, 
+  reftet_3eb_2v_splitelements, 
+  reftet_3eb_2v_newelstypes, 
+  reftet_3eb_2v_newels
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+//  HP_TET_3EC_0V,  
+int reftet_3ec_0v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  //  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  //  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_3ec_0v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 2, 1, 4, 18 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3ec_0v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3ec_0v_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_PYRAMID_EDGES,
+    //    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_TET,
+    HP_TET,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_NONE,
+  };
+int reftet_3ec_0v_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  { 2, 8, 18, 10, 9 },
+  //  { 3, 11, 12, 13 },
+  //  { 4, 15, 14, 16 },
+  { 5, 17, 7, 8, 9, 18 },
+  { 6, 7, 17, 3, 13, 12 },
+  { 10, 9, 18, 4, 16, 14 },
+  
+  { 9, 16, 13, 12, 20 },
+  { 7, 13, 16, 14, 20 },
+  { 7, 14, 18, 20 },
+  { 9, 12, 17, 20 },
+  { 17, 7, 18, 9, 20 },
+  { 7, 17, 12, 13, 20 },
+  { 9, 18, 14, 16, 20 },
+};
+HPRef_Struct reftet_3ec_0v =
+{
+  HP_TET,
+  reftet_3ec_0v_splitedges, 
+  reftet_3ec_0v_splitfaces, 
+  reftet_3ec_0v_splitelements, 
+  reftet_3ec_0v_newelstypes, 
+  reftet_3ec_0v_newels
+};
+
+
+
+
+
+
+ 
+
+
+//  HP_TET_3EC_1V,  
+int reftet_3ec_1v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  // { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_3ec_1v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 2, 1, 4, 18 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3ec_1v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3ec_1v_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_PYRAMID_EDGES,
+    HP_TET_1E_1VA,
+    //    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_TET,
+    HP_TET,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_NONE,
+  };
+int reftet_3ec_1v_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  { 2, 8, 18, 10, 9 },
+  { 3, 11, 12, 13 },
+  //  { 4, 15, 14, 16 },
+  { 5, 17, 7, 8, 9, 18 },
+  { 6, 7, 17, 11, 13, 12 },
+  { 10, 9, 18, 4, 16, 14 },
+  
+  { 9, 16, 13, 12, 20 },
+  { 7, 13, 16, 14, 20 },
+  { 7, 14, 18, 20 },
+  { 9, 12, 17, 20 },
+  { 17, 7, 18, 9, 20 },
+  { 7, 17, 12, 13, 20 },
+  { 9, 18, 14, 16, 20 },
+};
+HPRef_Struct reftet_3ec_1v =
+{
+  HP_TET,
+  reftet_3ec_1v_splitedges, 
+  reftet_3ec_1v_splitfaces, 
+  reftet_3ec_1v_splitelements, 
+  reftet_3ec_1v_newelstypes, 
+  reftet_3ec_1v_newels
+};
+
+
+
+
+
+
+
+
+//  HP_TET_3EC_2V,  
+int reftet_3ec_2v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 2, 3, 9 },
+  { 2, 4, 10 },
+  { 3, 1, 11 },
+  { 3, 2, 12 },
+  { 3, 4, 13 },
+  { 4, 1, 14 },
+  { 4, 2, 15 },
+  { 4, 3, 16 },
+  { 0, 0, 0 }
+};
+int reftet_3ec_2v_splitfaces[][4] =
+  {
+    { 1, 2, 3, 17 },
+    { 2, 1, 4, 18 },
+    { 0, 0, 0, 0 }
+  };
+int reftet_3ec_2v_splitelements[][5] =
+  {
+    { 1, 2, 3, 4, 20 },
+    { 0 },
+  };
+
+HPREF_ELEMENT_TYPE reftet_3ec_2v_newelstypes[] =
+  {
+    HP_PYRAMID_EDGES,
+    HP_PYRAMID_EDGES,
+    HP_TET_1E_1VA,
+    HP_TET_1E_1VA,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    HP_PRISM_SINGEDGE,
+    
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_TET,
+    HP_TET,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_PYRAMID,
+    HP_NONE,
+  };
+int reftet_3ec_2v_newels[][8] =
+{
+  { 1, 5, 17, 6, 7 },
+  { 2, 8, 18, 10, 9 },
+  { 3, 11, 12, 13 },
+  { 4, 15, 14, 16 },
+  { 5, 17, 7, 8, 9, 18 },
+  { 6, 7, 17, 11, 13, 12 },
+  { 10, 9, 18, 15, 16, 14 },
+  
+  { 9, 16, 13, 12, 20 },
+  { 7, 13, 16, 14, 20 },
+  { 7, 14, 18, 20 },
+  { 9, 12, 17, 20 },
+  { 17, 7, 18, 9, 20 },
+  { 7, 17, 12, 13, 20 },
+  { 9, 18, 14, 16, 20 },
+};
+HPRef_Struct reftet_3ec_2v =
+{
+  HP_TET,
+  reftet_3ec_2v_splitedges, 
+  reftet_3ec_2v_splitfaces, 
+  reftet_3ec_2v_splitelements, 
+  reftet_3ec_2v_newelstypes, 
+  reftet_3ec_2v_newels
+};
+
+
+
+
+
+
+
+
+
+
+/* ************************ 1 singular face ******************** */
+
+
+// HP_TET_1F_0E_0V
+int reftet_1f_0e_0v_splitedges[][3] =
+{
+  { 2, 1, 5 },
+  { 3, 1, 6 },
+  { 4, 1, 7 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_1f_0e_0v_newelstypes[] =
+{
+  HP_PRISM_1FA_0E_0V,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_1f_0e_0v_newels[][8] =
+{
+  { 3, 2, 4, 6, 5, 7 },
+  { 5, 7, 6, 1 }
+};
+HPRef_Struct reftet_1f_0e_0v =
+{
+  HP_TET,
+  reftet_1f_0e_0v_splitedges, 
+  0, 0,
+  reftet_1f_0e_0v_newelstypes, 
+  reftet_1f_0e_0v_newels
+};
+
+
+
+
+
+// HP_TET_1F_0E_1VA    ... singular vertex in face
+int reftet_1f_0e_1va_splitedges[][3] =
+{
+  { 2, 1, 5 },
+  { 2, 3, 6 },
+  { 2, 4, 7 },
+  { 3, 1, 8 },
+  { 4, 1, 9 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_1f_0e_1va_newelstypes[] =
+{
+  HP_HEX_1F_0E_0V,
+  HP_TET_1F_0E_1VA,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_1f_0e_1va_newels[][8] =
+{
+  { 3, 6, 7, 4, 8, 5, 5, 9 },
+  { 5, 2, 6, 7 },
+  { 5, 9, 8, 1 },
+};
+HPRef_Struct reftet_1f_0e_1va =
+{
+  HP_TET,
+  reftet_1f_0e_1va_splitedges, 
+  0, 0,
+  reftet_1f_0e_1va_newelstypes, 
+  reftet_1f_0e_1va_newels
+};
+
+
+
+
+
+// HP_TET_1F_0E_1VB    ... singular vertex not in face
+int reftet_1f_0e_1vb_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 1, 3, 6 },
+  { 1, 4, 7 },
+  { 2, 1, 8 },
+  { 3, 1, 9 },
+  { 4, 1, 10 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftet_1f_0e_1vb_newelstypes[] =
+{
+  HP_PRISM_1FA_0E_0V,
+  HP_PRISM,
+  HP_TET_0E_1V,
+  HP_NONE,
+};
+int reftet_1f_0e_1vb_newels[][8] =
+{
+  { 2, 4, 3, 8, 10, 9 },
+  { 8, 10, 9, 5, 7, 6 }, 
+  { 1, 5, 6, 7 },
+};
+HPRef_Struct reftet_1f_0e_1vb =
+{
+  HP_TET,
+  reftet_1f_0e_1vb_splitedges, 
+  0, 0,
+  reftet_1f_0e_1vb_newelstypes, 
+  reftet_1f_0e_1vb_newels
+};
+
+
+
+
+
+
+
+
+// HP_TET_1F_1EA_0V  ... sing edge is 1..2
+int reftet_1f_1ea_0v_splitedges[][3] =
+{
+  { 1, 3, 5 },
+  { 1, 4, 6 },
+  { 2, 1, 7 },
+  { 2, 3, 8 },
+  { 2, 4, 9 },
+  { 3, 1, 10 },
+  { 4, 1, 11 },
+  { 0, 0, 0 }
+};
+
+int reftet_1f_1ea_0v_splitfaces[][4] =
+  {
+    { 2, 1, 3, 12 },
+    { 2, 1, 4, 13 },
+    { 0, 0, 0, 0 }
+  };
+
+
+HPREF_ELEMENT_TYPE reftet_1f_1ea_0v_newelstypes[] =
+{
+  HP_HEX_1F_0E_0V,
+  //  HP_PRISM,
+  HP_PYRAMID_1FB_0E_1VA,
+  HP_TET_1E_1VA,
+  HP_PRISM_SINGEDGE,
+  HP_PRISM,
+  HP_NONE,
+};
+int reftet_1f_1ea_0v_newels[][8] =
+{
+  { 3, 8, 9, 4, 10, 12, 13, 11 },
+  // { 2, 9, 8, 7, 13, 12 },
+  { 8, 9, 13, 12, 2 },
+  { 2, 7, 13, 12 },
+  { 7, 13, 12, 1, 6, 5 },
+  { 6, 11, 13, 5, 10, 12 }
+};
+HPRef_Struct reftet_1f_1ea_0v =
+{
+  HP_TET,
+  reftet_1f_1ea_0v_splitedges, 
+  reftet_1f_1ea_0v_splitfaces, 
+  0, 
+  reftet_1f_1ea_0v_newelstypes, 
+  reftet_1f_1ea_0v_newels
+};
+
+
+
+
+
+
+
+
+// HP_TET_1F_1EB_0V     singular edge in face, edge is 2-3
+int reftet_1f_1eb_0v_splitedges[][3] =
+{
+  { 2, 1, 5 },
+  { 2, 4, 6 },
+  { 3, 1, 7 },
+  { 3, 4, 8 },
+  { 4, 1, 9 },
+  { 0, 0, 0 }
+};
+
+
+HPREF_ELEMENT_TYPE reftet_1f_1eb_0v_newelstypes[] =
+{
+  HP_PRISM_1FB_1EA_0V,
+  HP_PRISM_1FA_0E_0V,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_1f_1eb_0v_newels[][8] =
+{
+  // { 2, 5, 6, 3, 7, 8 },
+  { 3, 8, 7, 2, 6, 5 },
+  { 6, 4, 8, 5, 9, 7 },
+  { 5, 9, 7, 1}
+};
+HPRef_Struct reftet_1f_1eb_0v =
+{
+  HP_TET,
+  reftet_1f_1eb_0v_splitedges, 
+  0, 0, 
+  reftet_1f_1eb_0v_newelstypes, 
+  reftet_1f_1eb_0v_newels
+};
+
+
+
+
+
+
+
+
+
+
+/* ************************ 2 singular faces ******************** */
+
+
+// HP_TET_2F_0E_0V
+int reftet_2f_0e_0v_splitedges[][3] =
+{
+  { 1, 2, 5 },
+  { 2, 1, 6 },
+  { 3, 1, 7 },
+  { 3, 2, 8 },
+  { 4, 1, 9 },
+  { 4, 2, 10 },
+  { 0, 0, 0 }
+};
+
+int reftet_2f_0e_0v_splitfaces[][4] =
+  {
+    { 3, 1, 2, 11 },
+    { 4, 1, 2, 12 },
+    { 0, 0, 0, 0 }
+  };
+
+
+HPREF_ELEMENT_TYPE reftet_2f_0e_0v_newelstypes[] =
+{
+  HP_PRISM_1FA_0E_0V,
+  HP_PRISM_1FA_0E_0V,
+  HP_PRISM_1FB_1EA_0V,
+  HP_PRISM_1FB_1EA_0V,
+  HP_TET,
+  HP_NONE,
+};
+int reftet_2f_0e_0v_newels[][8] =
+{
+  { 2, 10, 8, 6, 12, 11 },
+  { 1, 7, 9, 5, 11, 12 },
+  //   { 3, 11, 8, 4, 12, 10 },
+  { 4, 10, 12, 3, 8, 11 }, 
+  { 3, 7, 11, 4, 9, 12 },
+  { 5, 6, 11, 12 }
+};
+HPRef_Struct reftet_2f_0e_0v =
+{
+  HP_TET,
+  reftet_2f_0e_0v_splitedges, 
+  reftet_2f_0e_0v_splitfaces, 
+  0, 
+  reftet_2f_0e_0v_newelstypes, 
+  reftet_2f_0e_0v_newels
+};
+
diff --git a/contrib/Netgen/libsrc/meshing/hpref_trig.hpp b/contrib/Netgen/libsrc/meshing/hpref_trig.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3676ad0f33f5fdd5c1f0d9d428ec02f723155aba
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_trig.hpp
@@ -0,0 +1,776 @@
+
+
+// HP_TRIG
+int reftrig_splitedges[][3] =
+{
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_newelstypes[] =
+{
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_newels[][8] =
+{
+  { 1, 2, 3 },
+};
+HPRef_Struct reftrig =
+{
+  HP_TRIG, 
+  reftrig_splitedges, 
+  0, 0, 
+  reftrig_newelstypes, 
+  reftrig_newels
+};
+
+
+
+// HP_TRIG_SINGCORNER
+int reftrig_singcorner_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singcorner_newelstypes[] =
+{
+  HP_TRIG_SINGCORNER,
+  HP_QUAD,
+  HP_NONE,
+};
+int reftrig_singcorner_newels[][8] =
+{
+  { 1, 4, 5 },
+  { 2, 3, 5, 4 },
+};
+HPRef_Struct reftrig_singcorner =
+{
+  HP_TRIG,
+  reftrig_singcorner_splitedges, 
+  0, 0,
+  reftrig_singcorner_newelstypes, 
+  reftrig_singcorner_newels
+};
+
+
+/*
+// HP_TRIG_SINGCORNER, trigs only
+int reftrig_singcorner_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singcorner_newelstypes[] =
+{
+  HP_TRIG_SINGCORNER,
+  HP_TRIG,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singcorner_newels[][8] =
+{
+  { 1, 4, 5 },
+  { 4, 2, 5 },
+  { 5, 2, 3 },
+};
+HPRef_Struct reftrig_singcorner =
+{
+  HP_TRIG,
+  reftrig_singcorner_splitedges, 
+  0, 0,
+  reftrig_singcorner_newelstypes, 
+  reftrig_singcorner_newels
+};
+*/
+
+
+
+
+
+// HP_TRIG_SINGCORNER12
+int reftrig_singcorner12_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 1, 6 },
+  { 2, 3, 7 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singcorner12_newelstypes[] =
+{
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singcorner12_newels[][8] =
+{
+  { 1, 4, 5 },
+  { 2, 7, 6 },
+  { 4, 6, 7, 5 },
+  { 5, 7, 3 },
+};
+HPRef_Struct reftrig_singcorner12 =
+{
+  HP_TRIG,
+  reftrig_singcorner12_splitedges, 
+  0, 0,
+  reftrig_singcorner12_newelstypes, 
+  reftrig_singcorner12_newels
+};
+
+
+
+
+// HP_TRIG_SINGCORNER123_2D
+int reftrig_singcorner123_2D_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 1, 6 },
+  { 2, 3, 7 },
+  { 3, 1, 8 },
+  { 3, 2, 9 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singcorner123_2D_newelstypes[] =
+{
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER,
+  HP_QUAD,
+  HP_QUAD,
+  HP_NONE,
+};
+int reftrig_singcorner123_2D_newels[][8] =
+{
+  { 1, 4, 5 },
+  { 2, 7, 6 },
+  { 3, 8, 9 },
+  { 4, 6, 8, 5 },
+  { 6, 7, 9, 8 },
+};
+HPRef_Struct reftrig_singcorner123_2D =
+{
+  HP_TRIG,
+  reftrig_singcorner123_2D_splitedges, 
+  0, 0,
+  reftrig_singcorner123_2D_newelstypes, 
+  reftrig_singcorner123_2D_newels
+};
+
+
+
+
+
+
+// HP_TRIG_SINGCORNER123
+int reftrig_singcorner123_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 1, 6 },
+  { 2, 3, 7 },
+  { 3, 1, 8 },
+  { 3, 2, 9 },
+  { 0, 0, 0 }
+};
+
+int reftrig_singcorner123_splitfaces[][4] =
+{
+  { 1, 2, 3, 10 },
+  { 2, 3, 1, 11 },
+  { 3, 1, 2, 12 },
+  { 0, 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singcorner123_newelstypes[] =
+{
+  HP_DUMMY_QUAD_SINGCORNER,
+  HP_DUMMY_QUAD_SINGCORNER,
+  HP_DUMMY_QUAD_SINGCORNER,
+  //  HP_TRIG_SINGCORNER,
+  //  HP_TRIG,
+  //  HP_TRIG_SINGCORNER,
+  //  HP_TRIG,
+  //  HP_TRIG_SINGCORNER,
+  //  HP_TRIG,
+  HP_QUAD,
+  HP_QUAD,
+  HP_QUAD,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singcorner123_newels[][8] =
+{
+  { 1, 4, 10, 5 },
+  { 2, 7, 11, 6 },
+  { 3, 8, 12, 9 },
+  //  { 1, 4, 5 },
+  //  { 5, 4, 10 },
+  //  { 2, 7, 6 },
+  //  { 6, 7, 11 },
+  //  { 3, 8, 9 },
+  //  { 8, 12, 9 },
+  { 4, 6, 11, 10 },
+  { 7, 9, 12, 11 },
+  { 8, 5, 10, 12 },
+  { 10, 11, 12 },
+};
+HPRef_Struct reftrig_singcorner123 =
+{
+  HP_TRIG,
+  reftrig_singcorner123_splitedges, 
+  reftrig_singcorner123_splitfaces, 
+  0, 
+  reftrig_singcorner123_newelstypes, 
+  reftrig_singcorner123_newels
+};
+
+// HP_TRIG_SINGEDGE
+int reftrig_singedge_splitedges[][3] =
+{
+  { 2, 3, 4 },
+  { 1, 3, 5 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singedge_newelstypes[] =
+{
+  HP_TRIG,
+  HP_QUAD_SINGEDGE,
+  HP_NONE,
+};
+int reftrig_singedge_newels[][8] =
+{
+  { 4, 3, 5 },
+  { 1, 2, 4, 5 },
+};
+HPRef_Struct reftrig_singedge =
+{
+  HP_TRIG,
+  reftrig_singedge_splitedges, 
+  0, 0,
+  reftrig_singedge_newelstypes, 
+  reftrig_singedge_newels
+};
+
+
+
+
+
+
+// HP_TRIG_SINGEDGECORNER1
+int reftrig_singedgecorner1_splitedges[][3] =
+{
+  { 1, 2, 6 },
+  { 1, 3, 5 },
+  { 2, 3, 4 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singedgecorner1_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singedgecorner1_newels[][8] =
+{
+  { 1, 6, 5 },
+  { 6, 2, 4, 5 },
+  { 5, 4, 3 },
+};
+HPRef_Struct reftrig_singedgecorner1 =
+{
+  HP_TRIG,
+  reftrig_singedgecorner1_splitedges, 
+  0, 0, 
+  reftrig_singedgecorner1_newelstypes, 
+  reftrig_singedgecorner1_newels
+};
+
+
+
+
+
+
+
+
+// HP_TRIG_SINGEDGECORNER2
+int reftrig_singedgecorner2_splitedges[][3] =
+{
+  { 2, 1, 6 },
+  { 1, 3, 5 },
+  { 2, 3, 4 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singedgecorner2_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singedgecorner2_newels[][8] =
+{
+  { 6, 2, 4},
+  { 1, 6, 4, 5 },
+  { 5, 4, 3 },
+};
+HPRef_Struct reftrig_singedgecorner2 =
+{
+  HP_TRIG,
+  reftrig_singedgecorner2_splitedges, 
+  0, 0,
+  reftrig_singedgecorner2_newelstypes, 
+  reftrig_singedgecorner2_newels
+};
+
+
+
+
+// HP_TRIG_SINGEDGECORNER12
+int reftrig_singedgecorner12_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 1, 6 },
+  { 2, 3, 7 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singedgecorner12_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singedgecorner12_newels[][8] =
+{
+  { 1, 4, 5 },
+  { 6, 2, 7 },
+  { 4, 6, 7, 5 },
+  { 5, 7, 3 },
+};
+HPRef_Struct reftrig_singedgecorner12 =
+{
+  HP_TRIG,
+  reftrig_singedgecorner12_splitedges, 
+  0, 0,
+  reftrig_singedgecorner12_newelstypes, 
+  reftrig_singedgecorner12_newels
+};
+
+
+
+
+
+
+
+// HP_TRIG_SINGEDGECORNER3
+int reftrig_singedgecorner3_splitedges[][3] =
+{
+  { 1, 3, 4 },
+  { 3, 1, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singedgecorner3_newelstypes[] =
+{
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int reftrig_singedgecorner3_newels[][8] =
+{
+  { 1, 2, 6, 4 },
+  { 4, 6, 7, 5 },
+  { 3, 5, 7 },
+};
+HPRef_Struct reftrig_singedgecorner3 =
+{
+  HP_TRIG,
+  reftrig_singedgecorner3_splitedges, 
+  0, 0,
+  reftrig_singedgecorner3_newelstypes, 
+  reftrig_singedgecorner3_newels
+};
+
+
+
+
+// HP_TRIG_SINGEDGECORNER13
+int reftrig_singedgecorner13_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 3, 6 },
+  { 3, 1, 7 },
+  { 3, 2, 8 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singedgecorner13_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int reftrig_singedgecorner13_newels[][8] =
+{
+  { 1, 4, 5 },
+  { 4, 2, 6, 5 },
+  { 5, 6, 8, 7 },
+  { 3, 7, 8 },
+};
+HPRef_Struct reftrig_singedgecorner13 =
+{
+  HP_TRIG,
+  reftrig_singedgecorner13_splitedges, 
+  0, 0,
+  reftrig_singedgecorner13_newelstypes, 
+  reftrig_singedgecorner13_newels
+};
+
+
+
+
+
+// HP_TRIG_SINGEDGECORNER23
+int reftrig_singedgecorner23_splitedges[][3] =
+{
+  { 1, 3, 4 },
+  { 2, 1, 5 },
+  { 2, 3, 6 },
+  { 3, 1, 7 },
+  { 3, 2, 8 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singedgecorner23_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int reftrig_singedgecorner23_newels[][8] =
+{
+  { 5, 2, 6 },
+  { 1, 5, 6, 4 },
+  { 4, 6, 8, 7 },
+  { 3, 7, 8 },
+};
+HPRef_Struct reftrig_singedgecorner23 =
+{
+  HP_TRIG,
+  reftrig_singedgecorner23_splitedges, 
+  0, 0,
+  reftrig_singedgecorner23_newelstypes, 
+  reftrig_singedgecorner23_newels
+};
+
+
+
+// HP_TRIG_SINGEDGECORNER123
+int reftrig_singedgecorner123_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 1, 6 },
+  { 2, 3, 7 },
+  { 3, 1, 8 },
+  { 3, 2, 9 },
+  { 0, 0, 0 }
+};
+HPREF_ELEMENT_TYPE reftrig_singedgecorner123_newelstypes[] =
+{
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_NONE,
+};
+int reftrig_singedgecorner123_newels[][8] =
+{
+  { 1, 4, 5 },
+  { 6, 2, 7 },
+  { 4, 6, 7, 5 },
+  { 5, 7, 9, 8 },
+  { 3, 8, 9 },
+};
+HPRef_Struct reftrig_singedgecorner123 =
+{
+  HP_TRIG,
+  reftrig_singedgecorner123_splitedges, 
+  0, 0,
+  reftrig_singedgecorner123_newelstypes, 
+  reftrig_singedgecorner123_newels
+};
+
+// HP_TRIG_SINGEDGES
+int reftrig_singedges_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 3, 6 },
+  { 3, 2, 7 },
+  { 0, 0, 0 }
+};
+int reftrig_singedges_splitfaces[][4] =
+{
+  { 1, 2, 3, 8 },
+  { 0, 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftrig_singedges_newelstypes[] =
+{
+  //  HP_QUAD_2E,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singedges_newels[][8] =
+{
+  // { 1, 4, 8, 5 },
+  { 1, 4, 8 },
+  { 5, 1, 8 },
+  { 4, 2, 6, 8 },
+  { 3, 5, 8, 7 },
+  { 6, 7, 8 },
+};
+HPRef_Struct reftrig_singedges =
+{
+  HP_TRIG,
+  reftrig_singedges_splitedges, 
+  reftrig_singedges_splitfaces, 
+  0,
+  reftrig_singedges_newelstypes, 
+  reftrig_singedges_newels
+};
+
+
+
+
+
+
+
+
+// HP_TRIG_SINGEDGES2
+int reftrig_singedges2_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 1, 6 },
+  { 2, 3, 7 },
+  { 3, 2, 8 },
+  { 0, 0, 0 }
+};
+int reftrig_singedges2_splitfaces[][4] =
+{
+  { 1, 2, 3, 9 },
+  { 0, 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftrig_singedges2_newelstypes[] =
+{
+  //  HP_QUAD_2E,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singedges2_newels[][8] =
+{
+  //  { 1, 4, 9, 5 },
+  { 1, 4, 9 },
+  { 5, 1, 9 },
+  { 4, 6, 7, 9 },
+  { 3, 5, 9, 8 },
+  { 6, 2, 7 },
+  { 7, 8, 9 },
+};
+HPRef_Struct reftrig_singedges2 =
+{
+  HP_TRIG,
+  reftrig_singedges2_splitedges, 
+  reftrig_singedges2_splitfaces, 
+  0,
+  reftrig_singedges2_newelstypes, 
+  reftrig_singedges2_newels
+};
+
+
+
+
+// HP_TRIG_SINGEDGES3
+int reftrig_singedges3_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 3, 6 },
+  { 3, 1, 7 },
+  { 3, 2, 8 },
+  { 0, 0, 0 }
+};
+int reftrig_singedges3_splitfaces[][4] =
+{
+  { 1, 2, 3, 9 },
+  { 0, 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftrig_singedges3_newelstypes[] =
+{
+  //  HP_QUAD_2E,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singedges3_newels[][8] =
+{
+  //  { 1, 4, 9, 5 },
+  { 1, 4, 9 },
+  { 5, 1, 9 },
+  { 4, 2, 6, 9 },
+  { 7, 5, 9, 8 },
+  { 3, 7, 8 },
+  { 6, 8, 9 },
+};
+HPRef_Struct reftrig_singedges3 =
+{
+  HP_TRIG,
+  reftrig_singedges3_splitedges, 
+  reftrig_singedges3_splitfaces, 
+  0,
+  reftrig_singedges3_newelstypes, 
+  reftrig_singedges3_newels
+};
+
+
+
+
+
+
+// HP_TRIG_SINGEDGES23
+int reftrig_singedges23_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 1, 3, 5 },
+  { 2, 1, 6 },
+  { 2, 3, 7 },
+  { 3, 1, 8 },
+  { 3, 2, 9 },
+  { 0, 0, 0 }
+};
+int reftrig_singedges23_splitfaces[][4] =
+{
+  { 1, 2, 3, 10 },
+  { 0, 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftrig_singedges23_newelstypes[] =
+{
+  //  HP_QUAD_2E,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG_SINGEDGECORNER2,
+
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG_SINGEDGECORNER2,
+  HP_TRIG_SINGEDGECORNER1,
+  HP_TRIG,
+  HP_NONE,
+};
+int reftrig_singedges23_newels[][8] =
+{
+  //  { 1, 4, 10, 5 },
+  { 1 , 4, 10 },
+  { 5, 1, 10 },
+  { 4, 6, 7, 10 },
+  { 8, 5, 10, 9 },
+  { 6, 2, 7 },
+  { 3, 8, 9 },
+  { 7, 9, 10 },
+};
+HPRef_Struct reftrig_singedges23 =
+{
+  HP_TRIG,
+  reftrig_singedges23_splitedges, 
+  reftrig_singedges23_splitfaces, 
+  0,
+  reftrig_singedges23_newelstypes, 
+  reftrig_singedges23_newels
+};
+
+
+// HP_TRIG_3SINGEDGES
+int reftrig_3singedges_splitedges[][3] =
+{
+  { 1, 2, 4 },
+  { 2, 1, 5 }, 
+  { 2, 3, 6 }, 
+  { 3, 2, 7 }, 
+  { 3, 1, 8 }, 
+  { 1, 3, 9 }, 
+  { 0, 0, 0 }
+};
+int reftrig_3singedges_splitfaces[][4] =
+{
+  { 1, 2, 3, 10 },
+  { 2, 3, 1, 11 },
+  { 3, 1, 2, 12 }, 
+  { 0, 0, 0, 0 }
+};
+
+HPREF_ELEMENT_TYPE reftrig_3singedges_newelstypes[] =
+{
+  HP_TRIG, 
+  HP_QUAD_SINGEDGE, 
+  HP_QUAD_SINGEDGE, 
+  HP_QUAD_SINGEDGE, 
+  HP_TRIG_SINGEDGECORNER1, 
+  HP_TRIG_SINGEDGECORNER2, 
+  HP_TRIG_SINGEDGECORNER1, 
+  HP_TRIG_SINGEDGECORNER2, 
+  HP_TRIG_SINGEDGECORNER1, 
+  HP_TRIG_SINGEDGECORNER2, 
+  HP_NONE, 
+};
+int reftrig_3singedges_newels[][8] =
+{
+  { 10, 11, 12 }, 
+  { 4, 5, 11, 10 }, 
+  { 6, 7, 12, 11 },
+  { 8, 9, 10, 12 }, 
+  { 1, 4, 10 }, 
+  { 9, 1, 10 }, 
+  { 2, 6, 11 }, 
+  { 5, 2, 11 }, 
+  { 3, 8, 12 }, 
+  { 7, 3, 12 }, 
+};
+HPRef_Struct reftrig_3singedges =
+{
+  HP_TRIG,
+  reftrig_3singedges_splitedges, 
+  reftrig_3singedges_splitfaces, 
+  0,
+  reftrig_3singedges_newelstypes, 
+  reftrig_3singedges_newels
+};
diff --git a/contrib/Netgen/libsrc/meshing/hprefinement.cpp b/contrib/Netgen/libsrc/meshing/hprefinement.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9c8155ad5590d6566d1bb64b0e1f5642b733eee6
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hprefinement.cpp
@@ -0,0 +1,1969 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+#include "hprefinement.hpp" 
+
+namespace netgen
+{
+
+#include "hpref_segm.hpp"
+#include "hpref_trig.hpp"  
+#include "hpref_quad.hpp"
+#include "hpref_tet.hpp"  
+#include "hpref_prism.hpp"
+#include "hpref_hex.hpp" 
+#include "hpref_pyramid.hpp" 
+#include "classifyhpel.hpp"  
+
+
+  void HPRefElement :: Reset(void)
+  {
+    np = 8; 
+    for (int i = 0; i < 8; i++)
+      {
+	pnums[i] = -1;
+	param[i][0] = param[i][1] = param[i][2] = 0;
+        domin=-1; domout=-1; // he:
+      }
+  } 
+
+  HPRefElement :: HPRefElement () 
+  {
+    Reset();
+  }
+
+  HPRefElement :: HPRefElement(Element & el)
+  { 
+    //Reset();
+    np = el.GetNV(); 
+    for (int i=0; i<np ; i++) 
+      pnums[i] = el[i]; 
+    
+    index = el.GetIndex(); 
+    const Point3d * points = 
+      MeshTopology :: GetVertices (el.GetType());
+    for(int i=0;i<np;i++)
+      for(int l=0;l<3;l++) 
+	param[i][l] = points[i].X(l+1); 
+    type = HP_NONE; 
+    domin=-1; domout=-1; // he: needed for segments
+  }
+
+  
+  HPRefElement :: HPRefElement(Element2d & el)
+  { 
+    //Reset();
+    np = el.GetNV(); 
+    
+    for (int i=0; i<np ; i++) 
+      pnums[i] = el[i]; 
+    
+    index = el.GetIndex(); 
+    const Point3d * points = 
+      MeshTopology :: GetVertices (el.GetType());
+    for(int i=0;i<np;i++)
+      for(int l=0;l<3;l++) 
+	param[i][l] = points[i].X(l+1); 
+    type = HP_NONE; 
+    domin=-1; domout=-1; // he: needed for segments
+  }
+
+  HPRefElement :: HPRefElement(Segment & el)
+  { 
+    //Reset();
+    np = 2; 
+    for (int i=0; i<np ; i++) 
+      pnums[i] = el[i];
+    const Point3d * points = 
+      MeshTopology :: GetVertices (SEGMENT); 
+    for(int i=0;i<np;i++)
+      for(int l=0;l<3;l++) 
+        param[i][l] = points[i].X(l+1); 
+
+    /*
+    for (int i=0; i<np; i++)
+    {
+      param[i][0] = i;   
+      param[i][1] = -1; param[i][2] = -1;
+    }
+    */
+
+    singedge_left = el.singedge_left; 
+    singedge_right = el.singedge_right; 
+    type = HP_NONE; 
+    // he: needed for orientation!
+    domin = el.domin;
+    domout = el.domout;
+  }
+  
+  HPRefElement :: HPRefElement(HPRefElement & el)
+  {
+    //Reset();
+    np = el.np; 
+    for (int i=0; i<np ; i++) 
+      {
+	pnums[i] = el[i];
+	for(int l=0; l<3; l++) param[i][l] = el.param[i][l]; 
+      }
+    index = el.index; 
+    levelx = el.levelx; 
+    levely = el.levely; 
+    levelz = el.levelz; 
+    type = el.type; 
+    coarse_elnr = el.coarse_elnr; 
+    singedge_left = el.singedge_left; 
+    singedge_right = el.singedge_right; 
+    domin = el.domin; // he: needed for segments
+    domout=el.domout;
+          
+  }
+
+  void HPRefElement :: SetType (HPREF_ELEMENT_TYPE t) 
+  {
+    type = t; 
+    switch(type)
+      {
+      case HP_SEGM: np=2; break; 
+      case HP_TRIG: np=3; break; 
+      case HP_QUAD: np=4; break; 
+      case HP_TET: np=4; break; 
+      case HP_PRISM: np=6; break; 
+      case HP_PYRAMID: np=5; break; 
+      case HP_HEX: np=8; break;      
+
+      default:
+        cerr << "HPRefElement: illegal type " << type << endl;
+        throw NgException ("HPRefElement::SetType: illegal type");
+      } 
+
+    for(int k = 0; k < 8;k++)
+      {
+	pnums[k]=0;
+	for(int l = 0; l < 3; l++) 
+          param[k][l]=0.;
+      }
+  }
+  
+
+  HPRef_Struct * Get_HPRef_Struct (HPREF_ELEMENT_TYPE type)
+  {
+    HPRef_Struct * hps = NULL;
+
+    switch (type)
+      {
+      case HP_SEGM:
+	hps = &refsegm; break;
+      case HP_SEGM_SINGCORNERL:
+	hps = &refsegm_scl; break;
+      case HP_SEGM_SINGCORNERR:
+	hps = &refsegm_scr; break;
+      case HP_SEGM_SINGCORNERS:
+	hps = &refsegm_sc2; break;
+	
+      case HP_TRIG:
+	hps = &reftrig; break;
+      case HP_TRIG_SINGCORNER:
+	hps = &reftrig_singcorner; break;
+      case HP_TRIG_SINGCORNER12:
+	hps = &reftrig_singcorner12; break; 
+      case HP_TRIG_SINGCORNER123:
+	hps = &reftrig_singcorner123; break;
+      case HP_TRIG_SINGCORNER123_2D:
+	hps = &reftrig_singcorner123_2D; break;
+      case HP_TRIG_SINGEDGE:
+	hps = &reftrig_singedge; break;
+      case HP_TRIG_SINGEDGECORNER1:
+	hps = &reftrig_singedgecorner1; break;
+      case HP_TRIG_SINGEDGECORNER2:
+	hps = &reftrig_singedgecorner2; break;
+      case HP_TRIG_SINGEDGECORNER12:
+	hps = &reftrig_singedgecorner12; break;
+      case HP_TRIG_SINGEDGECORNER3:
+	hps = &reftrig_singedgecorner3; break;
+      case HP_TRIG_SINGEDGECORNER13:
+	hps = &reftrig_singedgecorner13; break;
+      case HP_TRIG_SINGEDGECORNER23:
+	hps = &reftrig_singedgecorner23; break;
+      case HP_TRIG_SINGEDGECORNER123:
+	hps = &reftrig_singedgecorner123; break;
+      case HP_TRIG_SINGEDGES:
+	hps = &reftrig_singedges; break; 
+      case HP_TRIG_SINGEDGES2: 
+	hps = &reftrig_singedges2; break;
+      case HP_TRIG_SINGEDGES3:
+	hps = &reftrig_singedges3; break;
+      case HP_TRIG_SINGEDGES23:  
+	hps = &reftrig_singedges23; break;
+      case HP_TRIG_3SINGEDGES:
+	hps = &reftrig_3singedges; break;
+ 
+ 
+      case HP_QUAD:
+	hps = &refquad; break;
+      case HP_DUMMY_QUAD_SINGCORNER:
+	hps = &refdummyquad_singcorner; break;
+      case HP_QUAD_SINGCORNER:
+	hps = &refquad_singcorner; break;
+      case HP_QUAD_SINGEDGE:
+	hps = &refquad_singedge; break;
+
+      case HP_QUAD_0E_2VA:
+	hps = &refquad_0e_2va; break;
+      case HP_QUAD_0E_2VB:
+	hps = &refquad_0e_2vb; break;
+
+      case HP_QUAD_0E_3V:
+	hps = &refquad_0e_3v; break;
+      case HP_QUAD_0E_4V:
+	hps = &refquad_0e_4v; break;
+
+      case HP_QUAD_1E_1VA:
+	hps = &refquad_1e_1va; break;
+      case HP_QUAD_1E_1VB:
+	hps = &refquad_1e_1vb; break;
+      case HP_QUAD_1E_1VC:
+	hps = &refquad_1e_1vc; break;
+      case HP_QUAD_1E_1VD:
+	hps = &refquad_1e_1vd; break;
+
+      case HP_QUAD_1E_2VA:
+	hps = &refquad_1e_2va; break;
+      case HP_QUAD_1E_2VB:
+	hps = &refquad_1e_2vb; break;
+      case HP_QUAD_1E_2VC:
+	hps = &refquad_1e_2vc; break;
+      case HP_QUAD_1E_2VD:
+	hps = &refquad_1e_2vd; break;
+      case HP_QUAD_1E_2VE:
+	hps = &refquad_1e_2ve; break;
+      case HP_QUAD_1E_2VF:
+	hps = &refquad_1e_2vf; break;
+
+      case HP_QUAD_1E_3VA:
+	hps = &refquad_1e_3va; break;
+      case HP_QUAD_1E_3VB:
+	hps = &refquad_1e_3vb; break;
+      case HP_QUAD_1E_3VC:
+	hps = &refquad_1e_3vc; break;
+      case HP_QUAD_1E_3VD:
+	hps = &refquad_1e_3vd; break;
+      case HP_QUAD_1E_4V:
+	hps = &refquad_1e_4v; break;
+
+
+      case HP_QUAD_2E:
+	hps = &refquad_2e; break;
+      case HP_QUAD_2E_1VA:
+	hps = &refquad_2e_1va; break;
+      case HP_QUAD_2E_1VB:
+	hps = &refquad_2e_1vb; break;
+      case HP_QUAD_2E_1VC:
+	hps = &refquad_2e_1vc; break;
+      case HP_QUAD_2E_2VA:
+	hps = &refquad_2e_2va; break;
+      case HP_QUAD_2E_2VB:
+	hps = &refquad_2e_2vb; break;
+      case HP_QUAD_2E_2VC:
+	hps = &refquad_2e_2vc; break;
+      case HP_QUAD_2E_3V:
+	hps = &refquad_2e_3v; break;
+
+      case HP_QUAD_2EB_0V:
+	hps = &refquad_2eb_0v; break;
+
+      case HP_QUAD_2EB_1VA:
+	hps = &refquad_2eb_1va; break;
+      case HP_QUAD_2EB_1VB:
+	hps = &refquad_2eb_1vb; break;
+
+
+      case HP_QUAD_2EB_2VA:
+	hps = &refquad_2eb_2va; break;
+      case HP_QUAD_2EB_2VB:
+	hps = &refquad_2eb_2vb; break;
+      case HP_QUAD_2EB_2VC:
+	hps = &refquad_2eb_2vc; break;
+      case HP_QUAD_2EB_2VD:
+	hps = &refquad_2eb_2vd; break;
+
+      case HP_QUAD_2EB_3VA:
+	hps = &refquad_2eb_3va; break;
+      case HP_QUAD_2EB_3VB:
+	hps = &refquad_2eb_3vb; break;
+
+      case HP_QUAD_2EB_4V:
+	hps = &refquad_2eb_4v; break;
+
+      case HP_QUAD_3E:
+	hps = &refquad_3e; break;
+      case HP_QUAD_3E_3VA:
+	hps = &refquad_3e_3va; break;
+      case HP_QUAD_3E_3VB:
+	hps = &refquad_3e_3vb; break;
+      case HP_QUAD_3E_4V:
+	hps = &refquad_3e_4v; break;
+
+
+      case HP_QUAD_4E:
+	hps = &refquad_4e; break;
+
+
+      case HP_TET:
+	hps = &reftet; break;
+      case HP_TET_0E_1V:
+	hps = &reftet_0e_1v; break;
+      case HP_TET_0E_2V:
+	hps = &reftet_0e_2v; break;
+      case HP_TET_0E_3V:
+	hps = &reftet_0e_3v; break;
+      case HP_TET_0E_4V:
+	hps = &reftet_0e_4v; break;
+
+      case HP_TET_1E_0V:      
+	hps = &reftet_1e_0v; break;
+      case HP_TET_1E_1VA:
+	hps = &reftet_1e_1va; break;
+      case HP_TET_1E_1VB:
+	hps = &reftet_1e_1vb; break;
+
+      case HP_TET_1E_2VA:
+	hps = &reftet_1e_2va; break;
+      case HP_TET_1E_2VB:
+	hps = &reftet_1e_2vb; break;
+      case HP_TET_1E_2VC:
+	hps = &reftet_1e_2vc; break;
+      case HP_TET_1E_2VD:
+	hps = &reftet_1e_2vd; break;
+
+      case HP_TET_1E_3VA:
+	hps = &reftet_1e_3va; break;
+      case HP_TET_1E_3VB:
+	hps = &reftet_1e_3vb; break;
+      case HP_TET_1E_4V:
+	hps = &reftet_1e_4v; break;
+
+      case HP_TET_2EA_0V:
+	hps = &reftet_2ea_0v; break;
+      case HP_TET_2EA_1VB:
+	hps = &reftet_2ea_1vb; break;
+      case HP_TET_2EA_1VC:
+	hps = &reftet_2ea_1vc; break;
+      case HP_TET_2EA_1VA:
+	hps = &reftet_2ea_1va; break;
+      case HP_TET_2EA_2VA:
+	hps = &reftet_2ea_2va; break;
+      case HP_TET_2EA_2VB:
+	hps = &reftet_2ea_2vb; break;
+      case HP_TET_2EA_2VC:
+	hps = &reftet_2ea_2vc; break;
+      case HP_TET_2EA_3V:
+	hps = &reftet_2ea_3v; break;
+
+      case HP_TET_2EB_0V:
+	hps = &reftet_2eb_0v; break;
+      case HP_TET_2EB_1V:
+	hps = &reftet_2eb_1v; break;
+      case HP_TET_2EB_2VA:
+	hps = &reftet_2eb_2va; break;
+      case HP_TET_2EB_2VB:
+	hps = &reftet_2eb_2vb; break;
+      case HP_TET_2EB_2VC:
+	hps = &reftet_2eb_2vc; break;
+      case HP_TET_2EB_3V:
+	hps = &reftet_2eb_3v; break;
+      case HP_TET_2EB_4V:
+	hps = &reftet_2eb_4v; break;
+
+
+      case HP_TET_3EA_0V:
+	hps = &reftet_3ea_0v; break;
+      case HP_TET_3EA_1V:
+	hps = &reftet_3ea_1v; break;
+      case HP_TET_3EA_2V:
+	hps = &reftet_3ea_2v; break;
+      case HP_TET_3EA_3V:
+	hps = &reftet_3ea_3v; break;
+
+      case HP_TET_3EB_0V:
+	hps = &reftet_3eb_0v; break;
+      case HP_TET_3EB_1V:
+	hps = &reftet_3eb_1v; break;
+      case HP_TET_3EB_2V:
+	hps = &reftet_3eb_2v; break;
+      case HP_TET_3EC_0V:
+	hps = &reftet_3ec_0v; break;
+      case HP_TET_3EC_1V:
+	hps = &reftet_3ec_1v; break;
+      case HP_TET_3EC_2V:
+	hps = &reftet_3ec_2v; break;
+
+
+      case HP_TET_1F_0E_0V:
+	hps = &reftet_1f_0e_0v; break;
+      case HP_TET_1F_0E_1VA:
+	hps = &reftet_1f_0e_1va; break;
+      case HP_TET_1F_0E_1VB:
+	hps = &reftet_1f_0e_1vb; break;
+      case HP_TET_1F_1EA_0V:
+	hps = &reftet_1f_1ea_0v; break;
+      case HP_TET_1F_1EB_0V:
+	hps = &reftet_1f_1eb_0v; break;
+      case HP_TET_2F_0E_0V:
+	hps = &reftet_2f_0e_0v; break;
+
+
+      case HP_PRISM:
+	hps = &refprism; break;
+      case HP_PRISM_SINGEDGE:
+	hps = &refprism_singedge; break;
+	//      case HP_PRISM_SINGEDGE_H1:
+	//	hps = &refprism_singedge_h1; break;
+	// case HP_PRISM_SINGEDGE_H12:
+	//	hps = &refprism_singedge_h12; break;
+      case HP_PRISM_SINGEDGE_V12:
+	hps = &refprism_singedge_v12; break;
+	
+
+      case HP_PRISM_1FA_0E_0V:
+	hps = &refprism_1fa_0e_0v; break;
+      case HP_PRISM_2FA_0E_0V:
+	hps = &refprism_2fa_0e_0v; break;
+      case HP_PRISM_1FB_0E_0V:
+	hps = &refprism_1fb_0e_0v; break;
+      case HP_PRISM_1FB_1EA_0V: 
+	hps = &refprism_1fb_1ea_0v; break;
+ 
+      case HP_PRISM_1FA_1E_0V:
+	hps = &refprism_1fa_1e_0v; break;
+      case HP_PRISM_2FA_1E_0V:
+	hps = &refprism_2fa_1e_0v; break; 
+      case HP_PRISM_1FA_1FB_0E_0V: 
+	hps = &refprism_1fa_1fb_0e_0v; break;
+      case HP_PRISM_2FA_1FB_0E_0V: 
+	hps = &refprism_2fa_1fb_0e_0v; break; 
+      case HP_PRISM_1FA_1FB_1EA_0V: 
+	hps = &refprism_1fa_1fb_1ea_0v; break; 
+      case HP_PRISM_1FA_1FB_1EB_0V: 
+	hps = &refprism_1fa_1fb_1eb_0v; break; 
+      case HP_PRISM_2FA_1FB_1EA_0V:  
+	hps = &refprism_2fa_1fb_1ea_0v; break; 
+      case HP_PRISM_1FB_1EC_0V: 
+	hps = &refprism_1fb_1ec_0v; break; 
+      case HP_PRISM_1FA_1FB_1EC_0V: 
+	hps = &refprism_1fa_1fb_1ec_0v; break; 
+      case HP_PRISM_2FA_1FB_1EC_0V: 
+	hps = &refprism_2fa_1fb_1ec_0v; break;  
+      case HP_PRISM_1FB_2EA_0V: 
+	hps = &refprism_1fb_2ea_0v; break; 
+      case HP_PRISM_1FA_1FB_2EA_0V:   
+	hps = &refprism_1fa_1fb_2ea_0v; break; 
+      case HP_PRISM_2FA_1FB_2EA_0V:  
+	hps = &refprism_2fa_1fb_2ea_0v; break;   
+      case HP_PRISM_1FB_2EB_0V: 
+	hps = &refprism_1fb_2eb_0v; break; 
+      case HP_PRISM_1FA_1FB_2EB_0V:   
+	hps = &refprism_1fa_1fb_2eb_0v; break;  
+      case HP_PRISM_1FA_1FB_2EC_0V: 
+	hps = &refprism_1fa_1fb_2ec_0v; break; 
+      case HP_PRISM_2FA_1FB_2EB_0V: 
+	hps = &refprism_2fa_1fb_2eb_0v; break;  
+      case HP_PRISM_1FB_3E_0V: 
+	hps = &refprism_1fb_3e_0v; break; 
+      case HP_PRISM_1FA_1FB_3E_0V:  
+	hps = &refprism_1fa_1fb_3e_0v; break;  
+      case HP_PRISM_2FA_1FB_3E_0V:  
+        hps = &refprism_2fa_1fb_3e_0v; break; 
+      case HP_PRISM_2FB_0E_0V: 
+	hps = &refprism_2fb_0e_0v; break;  
+      case HP_PRISM_1FA_2FB_0E_0V: 
+	hps = &refprism_1fa_2fb_0e_0v; break; 
+      case HP_PRISM_2FA_2FB_0E_0V:    
+        hps = &refprism_2fa_2fb_0e_0v; break;  
+      case HP_PRISM_2FB_1EC_0V:  
+	hps = &refprism_2fb_1ec_0v; break;  
+      case HP_PRISM_1FA_2FB_1EC_0V: 
+        hps = &refprism_1fa_2fb_1ec_0v; break; 
+      case HP_PRISM_2FA_2FB_1EC_0V: 
+	hps = &refprism_2fa_2fb_1ec_0v; break; 
+      case HP_PRISM_1FA_2FB_1EB_0V: 
+	hps = &refprism_1fa_2fb_1eb_0v; break; 
+      case HP_PRISM_2FB_3E_0V:    
+	hps = &refprism_2fb_3e_0v; break;
+      case HP_PRISM_1FA_2FB_3E_0V: 
+	hps = &refprism_1fa_2fb_3e_0v; break;
+      case HP_PRISM_2FA_2FB_3E_0V:  
+	hps = &refprism_2fa_2fb_3e_0v; break;
+      case HP_PRISM_1FA_2E_0V:   
+	hps = &refprism_1fa_2e_0v; break; 
+      case HP_PRISM_2FA_2E_0V:  
+	hps = &refprism_2fa_2e_0v; break;   
+      case HP_PRISM_3E_0V:  
+	hps = &refprism_3e_0v; break;
+      case HP_PRISM_1FA_3E_0V:   
+	hps = &refprism_1fa_3e_0v; break; 
+      case HP_PRISM_2FA_3E_0V:  
+	hps = &refprism_2fa_3e_0v; break;   
+      case HP_PRISM_3FB_0V:  
+	hps = &refprism_3fb_0v; break;
+      case HP_PRISM_1FA_3FB_0V:   
+	hps = &refprism_1fa_3fb_0v; break; 
+      case HP_PRISM_2FA_3FB_0V:  
+	hps = &refprism_2fa_3fb_0v; break;   
+	//  case HP_PRISM_3E_4EH:
+	//  hps = &refprism_3e_4eh; break;   
+	
+	
+	/*case HP_PRISM_1FB_1EB_0V:
+	hps = &refprism_1fb_1eb_0v; break;
+      case HP_PRISM_2F_0E_0V:
+	hps = &refprism_2f_0e_0v; break;
+	*/
+	
+	
+      case HP_PYRAMID:
+	hps = &refpyramid; break;
+      case HP_PYRAMID_0E_1V:
+	hps = &refpyramid_0e_1v; break;
+      case HP_PYRAMID_EDGES:
+	hps = &refpyramid_edges; break;
+      case HP_PYRAMID_1FB_0E_1VA:
+	hps = &refpyramid_1fb_0e_1va; break;
+
+	
+      case HP_HEX:
+	hps = &refhex; break;
+      case HP_HEX_0E_1V:
+	hps = &refhex_0e_1v; break;
+      case HP_HEX_1E_1V:
+	hps = &refhex_1e_1v; break;
+      case HP_HEX_1E_0V:
+	hps = &refhex_1e_0v; break;
+      case HP_HEX_3E_0V:
+	hps = &refhex_3e_0v; break;
+
+      case HP_HEX_1F_0E_0V:
+	hps = &refhex_1f_0e_0v; break;
+      case HP_HEX_1FA_1FB_0E_0V: 
+	hps = &refhex_1fa_1fb_0e_0v; break; 
+      }
+
+    /*
+    if (type != HP_TET_1E_4V && type != HP_TET_1E_2VD)
+      {
+	if (hps->geom == HP_TET)
+	  hps = &reftet;
+	if (hps->geom == HP_TRIG)
+	  hps = &reftrig;
+      }
+    */
+
+    if (!hps)
+      {
+	cout << "Attention hps : hp-refinement not implemented for case " << type << endl;
+	PrintSysError ("hp-refinement not implemented for case ", type);
+      }
+
+    return hps;
+  }
+
+  bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoiclt_dom, 
+		       BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int> & face_edges, 
+			INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint, int & levels, int & act_ref); 
+
+  bool ClassifyHPElements (Mesh & mesh, Array<HPRefElement> & elements, int & act_ref, int & levels);
+  
+  
+  void  InitHPElements(Mesh & mesh, Array<HPRefElement> & elements) 
+  { 
+    for(ElementIndex i = 0; i < mesh.GetNE(); i++) 
+      {
+	HPRefElement hpel(mesh[i]); 
+	hpel.coarse_elnr = i; 
+	
+	switch (mesh[i].GetType()) 
+	  { 
+	  case PRISM:   hpel.type = HP_PRISM;   break; 
+	  case HEX:     hpel.type = HP_HEX;     break; 
+	  case TET:     hpel.type = HP_TET;     break; 
+	  case PYRAMID: hpel.type = HP_PYRAMID; break; 
+
+          default:
+            cerr << "HPRefElement: illegal elementtype (1) " << mesh[i].GetType() << endl;
+            throw NgException ("HPRefElement: illegal elementtype (1)");
+	  } 
+	elements.Append(hpel); 
+      }
+	    
+    for(SurfaceElementIndex i = 0; i < mesh.GetNSE(); i++)
+      {
+	HPRefElement hpel(mesh[i]);
+	hpel.coarse_elnr = i; 
+	switch(mesh[i].GetType())
+	  { 
+	  case TRIG: hpel.type = HP_TRIG; break; 
+	  case QUAD: hpel.type = HP_QUAD; break; 
+
+          default:
+            cerr << "HPRefElement: illegal elementtype (1b) " << mesh[i].GetType() << endl;
+            throw NgException ("HPRefElement: illegal elementtype (1b)");
+	  } 
+	elements.Append(hpel);
+      } 
+        
+    for(SegmentIndex i = 0; i < mesh.GetNSeg(); i++) 
+      { 
+	Segment & seg = mesh[i];
+	HPRefElement hpel(mesh[i]);
+	hpel.coarse_elnr = i; 
+	hpel.type = HP_SEGM; 
+	hpel.index = seg.edgenr + 10000*seg.si; 
+	if(seg.edgenr >= 10000)
+	  {
+	    throw NgException("assumption that seg.edgenr < 10000 is wrong");
+	  }
+	elements.Append(hpel); 
+      }
+  }
+
+ 
+ 
+  /* *******************************  DoRefinement *************************************** */
+  void DoRefinement (Mesh & mesh, Array<HPRefElement> & elements,
+		     Refinement * ref, double fac1) 
+  {
+    elements.SetAllocSize (5 * elements.Size());
+    INDEX_2_HASHTABLE<int> newpts(elements.Size()+1);
+    INDEX_3_HASHTABLE<int> newfacepts(elements.Size()+1);
+
+    // prepare new points  
+    
+    fac1 = max(0.001,min(0.33,fac1));
+    cout << " in HP-REFINEMENT with fac1 " << fac1 << endl; 
+    *testout << " in HP-REFINEMENT with fac1 " << fac1 <<  endl; 
+   
+
+    int oldelsize = elements.Size();
+       
+    for (int i = 0; i < oldelsize; i++)
+      {
+	HPRefElement & el = elements[i]; 
+	HPRef_Struct * hprs = Get_HPRef_Struct (el.type);
+		
+	if (!hprs) 
+	  {
+	    cout << "Refinementstruct not defined for element " << el.type << endl;
+	    continue;
+	  }
+
+	int j = 0;
+	while (hprs->splitedges[j][0])
+	  {
+	    INDEX_2 i2(el.pnums[hprs->splitedges[j][0]-1],
+		       el.pnums[hprs->splitedges[j][1]-1]);
+	    if (!newpts.Used (i2))
+	      {
+		Point<3> np; 
+		for( int l=0;l<3;l++)
+		  np(l) = (1-fac1)*mesh.Point(i2.I1())(l) 
+		    + fac1 * mesh.Point(i2.I2())(l); 
+	
+		int npi = mesh.AddPoint (np);
+		newpts.Set (i2, npi);
+	      }
+	    j++;
+	  }
+	
+	j = 0;
+	if (hprs->splitfaces)
+	  while (hprs->splitfaces[j][0])
+	    {
+	      INDEX_3 i3(el.pnums[hprs->splitfaces[j][0]-1],
+			 el.pnums[hprs->splitfaces[j][1]-1],
+			 el.pnums[hprs->splitfaces[j][2]-1]);
+
+	      if (i3.I2() > i3.I3()) Swap (i3.I2(), i3.I3());
+	      
+	      if (!newfacepts.Used (i3))
+		{
+		  Point<3> np; 
+		  	for( int l=0;l<3;l++)
+			  np(l) = (1-2*fac1)*mesh.Point(i3.I1())(l) 
+			    + fac1*mesh.Point(i3.I2())(l)  + fac1*mesh.Point(i3.I3())(l);  
+		  int npi = mesh.AddPoint (np);
+		  newfacepts.Set (i3, npi);
+		}
+	      j++;
+	    }
+      }
+     
+    for (int i = 0; i < oldelsize; i++)
+      {
+	HPRefElement el = elements[i];
+	HPRef_Struct * hprs = Get_HPRef_Struct (el.type);
+	int newlevel = el.levelx + 1;
+
+	int oldnp = 0;
+	switch (hprs->geom)
+	  {
+	  case HP_SEGM: oldnp = 2; break;
+	  case HP_TRIG: oldnp = 3; break;
+	  case HP_QUAD: oldnp = 4; break;
+	  case HP_TET: oldnp = 4; break;
+	  case HP_PYRAMID: oldnp = 5; break;
+	  case HP_PRISM: oldnp = 6; break;
+	  case HP_HEX: oldnp = 8; break;
+            
+          default:
+            cerr << "HPRefElement: illegal type (3) " << hprs->geom << endl;
+            throw NgException ("HPRefElement::SetType: illegal type (3)");
+	  }
+
+
+	if (el.type == HP_SEGM ||
+	    el.type == HP_TRIG ||
+	    el.type == HP_QUAD ||
+	    el.type == HP_TET ||
+	    el.type == HP_PRISM ||
+	    el.type == HP_HEX || 
+	    el.type == HP_PYRAMID)
+	  newlevel = el.levelx;
+
+	if (!hprs) continue;
+
+	int newpnums[64];
+	double newparam[64][3];
+
+	int j;
+	for (j = 0; j < oldnp; j++)
+	  {
+	    newpnums[j] = el.pnums[j];
+	    for (int l = 0; l < 3; l++)
+	      newparam[j][l] = el.param[j][l];
+	  }
+
+	// split edges, incl. transferring curvature
+	j = 0;
+	while (hprs->splitedges[j][0])
+	  {
+	    INDEX_2 i2(el.pnums[hprs->splitedges[j][0]-1],
+		       el.pnums[hprs->splitedges[j][1]-1]);
+
+	    int npi = newpts.Get(i2);
+	    newpnums[hprs->splitedges[j][2]-1] = npi;
+
+	    for (int l = 0; l < 3; l++)
+	      newparam[hprs->splitedges[j][2]-1][l] =
+		(1-fac1) * el.param[hprs->splitedges[j][0]-1][l] + 
+		fac1 * el.param[hprs->splitedges[j][1]-1][l];
+	      
+	    j++;
+	  }
+
+	// split faces
+	j = 0;
+	if (hprs->splitfaces)
+	  while (hprs->splitfaces[j][0])
+	    {
+	      INDEX_3 i3(el.pnums[hprs->splitfaces[j][0]-1],
+			 el.pnums[hprs->splitfaces[j][1]-1],
+			 el.pnums[hprs->splitfaces[j][2]-1]);
+	      if (i3.I2() > i3.I3())
+		Swap (i3.I2(), i3.I3());
+	      int npi = newfacepts.Get(i3);
+	      newpnums[hprs->splitfaces[j][3]-1] = npi;
+	    
+
+	      for (int l = 0; l < 3; l++)
+		newparam[hprs->splitfaces[j][3]-1][l] =
+		  (1-2*fac1) * el.param[hprs->splitfaces[j][0]-1][l] + 
+		  fac1 * el.param[hprs->splitfaces[j][1]-1][l] + 
+		  fac1 * el.param[hprs->splitfaces[j][2]-1][l];
+	      j++;
+	    }
+	// split elements
+	j = 0;
+	if (hprs->splitelements)
+	  while (hprs->splitelements[j][0])
+	    {
+	      //int pi1 = el.pnums[hprs->splitelements[j][0]-1];
+	      Point<3> np; 
+	      	for( int l=0;l<3;l++)
+		  np(l) = (1-3*fac1)* mesh.Point(el.pnums[hprs->splitelements[j][0]-1])(l) 
+		    + fac1* mesh.Point(el.pnums[hprs->splitelements[j][1]-1])(l)
+		    + fac1* mesh.Point(el.pnums[hprs->splitelements[j][2]-1])(l)
+		    + fac1* mesh.Point(el.pnums[hprs->splitelements[j][3]-1])(l); 
+	      
+	      int npi = mesh.AddPoint (np);
+	      
+	      newpnums[hprs->splitelements[j][4]-1] = npi;
+	      
+	  	    
+	      for (int l = 0; l  < 3; l++)
+		newparam[hprs->splitelements[j][4]-1][l] =
+		  (1-3*fac1) * el.param[hprs->splitelements[j][0]-1][l] + 
+		  fac1 * el.param[hprs->splitelements[j][1]-1][l] + 
+		  fac1 * el.param[hprs->splitelements[j][2]-1][l] + 
+		  fac1 * el.param[hprs->splitelements[j][3]-1][l];
+
+	      j++;
+	    }
+ 
+	j = 0;
+
+	/*
+	*testout << " newpnums = ";
+	for (int hi = 0; hi < 64; hi++)
+	  *testout << newpnums[hi] << " ";
+	*testout << endl;
+	*/
+
+	while (hprs->neweltypes[j])
+	  {
+	    HPRef_Struct * hprsnew = Get_HPRef_Struct (hprs->neweltypes[j]);
+	    HPRefElement newel(el);
+
+	    newel.type = hprs->neweltypes[j]; 
+            
+	    // newel.index = elements[i].index;
+	    // newel.coarse_elnr = elements[i].coarse_elnr;
+	    newel.levelx = newel.levely = newel.levelz = newlevel;
+            switch(hprsnew->geom) 
+	      {
+	      case HP_SEGM: newel.np=2; break; 
+	      case HP_QUAD: newel.np=4; break; 
+	      case HP_TRIG: newel.np=3; break; 
+	      case HP_HEX: newel.np=8; break; 
+	      case HP_PRISM: newel.np=6; break;
+	      case HP_TET: newel.np=4; break; 
+	      case HP_PYRAMID: newel.np=5; break; 
+              default:
+                throw NgException (string("hprefinement.cpp: illegal type"));
+	      }
+
+	    for (int k = 0; k < newel.np; k++)
+	      newel.pnums[k] = newpnums[hprs->newels[j][k]-1];
+	    
+	    /*
+	    *testout  << " newel pnums " ; 
+	    for (int k = 0; k < newel.np; k++)  
+	      *testout  << newel.pnums[k] << "\t"; 
+	    *testout << endl; 
+	    */
+
+	    for (int k = 0; k < newel.np; k++)  
+	      { 
+		for (int l = 0; l < 3; l++)
+		  { 
+		    newel.param[k][l] = newparam[hprs->newels[j][k]-1][l];
+		    //    *testout << newel.param[k][l] << " \t ";
+		  } 
+		// *testout << endl; 
+	      } 
+	    
+	    if (j == 0) 
+	      elements[i] = newel; // overwrite old element
+	    else        
+	      elements.Append (newel);
+	    j++;
+	  }
+      } 
+  }
+
+
+
+
+
+
+  /* ************************** DoRefineDummies ******************************** */
+
+  void DoRefineDummies (Mesh & mesh, Array<HPRefElement> & elements,
+			Refinement * ref)
+  {
+    int oldelsize = elements.Size();
+
+    for (int i = 0; i < oldelsize; i++)
+      {
+	HPRefElement el = elements[i];
+
+	HPRef_Struct * hprs = Get_HPRef_Struct (el.type);
+	if (!hprs) continue;
+
+	if (el.type != HP_DUMMY_QUAD_SINGCORNER &&
+	    el.type != HP_PYRAMID_EDGES &&
+	    el.type != HP_PYRAMID_0E_1V &&
+	    el.type != HP_HEX_0E_1V &&
+	    el.type != HP_HEX_1E_1V &&
+	    el.type != HP_HEX_1E_0V &&
+	    el.type != HP_HEX_3E_0V
+	    ) continue;
+
+	int newlevel = el.levelx;
+
+	int newpnums[8];
+	int j;
+	for (j = 0; j < 8; j++)
+	  newpnums[j] = el.pnums[j];
+
+	double newparam[8][3];
+	for (j = 0; j < 8; j++)
+	  for (int k = 0; k < 3; k++)
+	    newparam[j][k] = el.param[j][k];
+
+	j = 0;
+	while (hprs->neweltypes[j])
+	  {
+	    HPRef_Struct * hprsnew = Get_HPRef_Struct (hprs->neweltypes[j]);
+	    HPRefElement newel(el);
+	    switch(hprsnew->geom)
+	      {
+	      case HP_SEGM: newel.np=2; break; 
+	      case HP_QUAD: newel.np=4; break; 
+	      case HP_TRIG: newel.np=3; break; 
+	      case HP_HEX: newel.np=8; break; 
+	      case HP_PRISM: newel.np=6; break;
+	      case HP_TET: newel.np=4; break; 
+	      case HP_PYRAMID: newel.np=5; break; 
+
+              default:
+                cerr << "HPRefElement: illegal type (4) " << hprsnew->geom << endl;
+                throw NgException ("HPRefElement: illegal type (4)");
+                
+	      }
+	    newel.type = hprs->neweltypes[j];
+	    for (int k = 0; k < 8; k++)
+	      newel.pnums[k] = newpnums[hprs->newels[j][k]-1];
+	    newel.index = el.index;
+	    newel.coarse_elnr = el.coarse_elnr;
+	    newel.levelx = newel.levely = newel.levelz = newlevel;
+
+	    for (int k = 0; k < 8; k++)
+	      for (int l = 0; l < 3; l++)
+		newel.param[k][l] = newparam[hprs->newels[j][k]-1][l];
+		
+	    if (j == 0)
+	      elements[i] = newel;
+	    else
+	      elements.Append (newel);
+	    j++;
+	  }
+      }
+  }
+
+
+
+
+
+
+
+  void SubdivideDegeneratedHexes (Mesh & mesh, Array<HPRefElement> & elements, double fac1)
+  {
+    int oldne = elements.Size();
+    for (int i = 0; i < oldne; i++)
+      if (Get_HPRef_Struct (elements[i].type)->geom == HP_HEX)
+	{
+	  bool common = 0;
+	  for (int j = 0; j < 8; j++)
+	    for (int k = 0; k < j; k++)
+	      if (elements[i].pnums[j] == elements[i].pnums[k])
+		common = 1;
+	  if (common)
+	    {
+
+               
+	      cout << " Degenerate Hex found " << endl; 
+              *testout << " Degenerate Hex found " << endl; 
+	      HPRefElement el = elements[i];
+	      HPRefElement newel = el;
+
+	      Point<3> center(0,0,0);
+	      double newparam[3] = { 0, 0, 0 };
+
+	      for (int j = 0; j < 8; j++)
+		{
+		  
+		  
+		  center += 0.125 * Vec<3>(mesh[el.pnums[j]]); 
+		  // 0.125 originates form 8 points not from fac1;
+                  
+		  for (int l = 0; l < 3; l++)
+		    newparam[l] += 0.125 * el.param[j][l];
+                  
+		}
+
+	      int npi = mesh.AddPoint (center);
+
+	      const ELEMENT_FACE * faces = MeshTopology::GetFaces1 (HEX);
+
+	      for (int j = 0; j < 6; j++)  
+		{
+		  Array<int> pts;
+		  for (int k = 0; k < 4; k++)
+		    {
+		      bool same = 0;
+		      for (int l = 0; l < pts.Size(); l++)
+			if (el.pnums[pts[l]] == el.pnums[faces[j][k]-1])
+			  same = 1;
+		      if (!same)
+			pts.Append (faces[j][k]-1);
+
+		    }
+		  
+		  
+		  if (pts.Size() == 3) // TrigFace -> TET 
+		    {
+		      
+		      for (int k = 0; k < 3; k++)
+			{
+			  newel.pnums[k] = el.pnums[pts[2-k]];
+			  for (int l = 0; l < 3; l++)
+			    newel.param[k][l] = el.param[pts[2-k]][l];
+			}
+		      newel.pnums[3] = npi;
+		      for (int l = 0; l < 3; l++)
+			newel.param[3][l] = newparam[l];
+
+		      newel.type = HP_TET;
+		      newel.np = 4; 
+		    }
+		  else
+		    {
+		      for (int k = 0; k < 4; k++)
+			{
+			  newel.pnums[k] = el.pnums[pts[3-k]];
+			  for (int l = 0; l < 3; l++)
+			    newel.param[k][l] = el.param[pts[3-k]][l];
+			}
+
+		      newel.pnums[4] = npi;
+		      for (int l = 0; l < 3; l++)
+			newel.param[4][l] = newparam[l];
+
+		      newel.type = HP_PYRAMID;
+		      newel.np = 5;
+		    }
+		  
+		  if (j == 0)
+		    elements[i] = newel;
+		  else
+		    elements.Append (newel); 
+
+		  
+		}
+
+	      /*     const ELEMENT_EDGE * edges = MeshTopology::GetEdges (HEX);
+	       
+		for(int k=0;k<12;k++) 
+		  { 
+		    int e[2];  
+		    for(int l=0;l<2;l++) e[l] = edges[k][l]-1; 
+		    if(el.PNum(e[0]+1)!=el.PNum(e[1]+1)) 
+		      { 
+			newel.SetType(HP_SEGM);
+			for(int l=0;l<2;l++) 
+			  { 
+			    newel.pnums[0] = el.PNum(e[l]+1); 
+			    newel.pnums[1] = npi; 
+			    for(int j=0;j<3;j++) 
+			      {
+				//	newel.param[0][j] = el.param[e[l]][j]; 
+				//	newel.param[1][j] = newparam[j]; 
+			      } 
+			    
+			    elements.Append(newel);
+			  }
+			newel.SetType(HP_TRIG);
+			newel.pnums[0] = el.PNum(e[0]+1); 			
+			newel.pnums[1] = el.PNum(e[1]+1); 			
+			newel.pnums[2] = npi; 
+			
+			*testout << "DEGHEX TRIG :: newpnums " << newel.pnums[0] << "\t"  << newel.pnums[1] << "\t"  << newel.pnums[2] << endl;  
+	cout << "DEGHEX TRIG :: newpnums " << newel.pnums[0] << "\t"  << newel.pnums[1] << "\t"  << newel.pnums[2] << endl;  
+			for(int j=0;j<3;j++) 
+			  {
+			    // newel.param[0][j] = el.param[e[0]][j]; 
+			    //   newel.param[1][j] = el.param[e[1]][j]; 
+			    //   newel.param[2][j] = newparam[j]; 
+			  } 
+			
+			elements.Append(newel);
+		      }
+			
+		      }*/
+	    }
+	}
+  }
+
+
+  void CalcStatistics (Array<HPRefElement> & elements)
+  {
+    return;
+#ifdef ABC    
+    int i, p;
+    int nsegm = 0, ntrig = 0, nquad = 0;
+    int nhex = 0, nprism = 0, npyramid = 0, ntet = 0;
+    int maxlevel = 0;
+
+    for (i = 1; i <= elements.Size(); i++)
+      {
+	const HPRefElement & el = elements.Get(i);
+	maxlevel = max2 (el.level, maxlevel);
+	switch (Get_HPRef_Struct (el.type)->geom)
+	  {
+	  case HP_SEGM:
+
+	    {
+	      nsegm++;
+	      break;
+	    }
+	  case HP_TRIG:
+	    {
+	      ntrig ++;
+	      break;
+	    }
+	  case HP_QUAD:
+	    {
+	      nquad++;
+	      break;
+	    }
+	  case HP_TET:
+	    {
+	      ntet++;
+	      break;
+	    }
+
+	  case HP_PRISM:
+	    {
+	      nprism++;
+	      break;
+	    }
+
+	  case HP_PYRAMID:
+	    {
+	      npyramid++;
+	      break;
+	    }
+
+	  case HP_HEX:
+	    {	
+	      nhex++;
+	      break;
+	    }
+
+	  default:
+	    {
+	      cerr << "statistics error, unknown element type" << endl;
+	    }
+	  }
+      }
+
+    cout << "level = " << maxlevel << endl;
+    cout << "nsegm = " << nsegm << endl;
+    cout << "ntrig = " << ntrig << ", nquad = " << nquad << endl;
+    cout << "ntet = " << ntet << ", npyr = " << npyramid
+	 << ", nprism = " << nprism << ", nhex = " << nhex << endl;
+
+    return;
+
+    double memcost = 0, cpucost = 0;
+    for (p = 1; p <= 20; p++)
+      {
+	memcost = (ntet + nprism + nhex) * pow (static_cast<double>(p), 6.0);
+	cpucost = (ntet + nprism + nhex) * pow (static_cast<double>(p), 9.0);
+	cout << "costs for p = " << p << ": mem = " << memcost << ", cpu = " << cpucost << endl;
+      }
+
+    double memcosttet = 0;
+    double memcostprism = 0;
+    double memcosthex = 0;
+    double memcostsctet = 0; 
+    double memcostscprism = 0;
+    double memcostschex = 0;
+    double cpucosttet = 0;
+    double cpucostprism = 0;
+    double cpucosthex = 0;
+
+    for (i = 1; i <= elements.Size(); i++)
+      {
+	const HPRefElement & el = elements.Get(i);
+	switch (el.type)
+	  {
+	  case HP_TET:
+	  case HP_TET_0E_1V:
+	  case HP_TET_1E_0V:
+	  case HP_TET_1E_1VA:
+	    {
+	      int p1 = maxlevel - el.level + 1;
+	      (*testout) << "p1 = " << p1 << ", P1^6 = " << pow (static_cast<double>(p1), 6.0)
+			 << " (p1-3)^6 = " << pow ( static_cast<double>(max2(p1-3, 0)), 6.0) 
+			 << " p1^3 = " << pow ( static_cast<double>(p1), 3.0) 
+			 << " (p1-3)^3 = " << pow ( static_cast<double>(p1-3), 3.0) 
+			 << " [p1^3-(p1-3)^3]^2 = " << sqr (pow (static_cast<double>(p1),3.0) - pow ( static_cast<double>(p1-3), 3.0))
+			 << endl;
+
+	      p1 /= 2 +1;
+	      memcosttet += pow (static_cast<double>(p1), 6.0);
+	      memcostsctet += pow (static_cast<double>(p1), 6.0) - pow ( static_cast<double>(max2(p1-3, 1)), 6.0);
+	      cpucosttet += pow (static_cast<double>(p1), 9.0);
+	      break;
+	    }
+	  case HP_PRISM:
+	  case HP_PRISM_SINGEDGE:
+	    {
+	      int p1 = maxlevel - el.level + 1;
+	      p1 /= 2 +1;
+	      memcostprism += pow (static_cast<double>(p1), 6.0);
+	      memcostscprism += pow (static_cast<double>(p1), 6.0) - pow ( static_cast<double>(max2(p1-3, 1)), 6.0);
+	      cpucostprism += pow (static_cast<double>(p1), 9.0);
+	      break;
+	    }
+	  case HP_HEX:
+	    {	
+	      int p1 = maxlevel - el.level + 1;
+	      int p2 = maxlevel;
+	      p1 /= 2 +1;
+	      p2 /= 2 +1;
+	      memcosthex += pow (static_cast<double>(p1), 4.0) * pow (static_cast<double>(p2), 2.0);
+	      memcostschex += pow (static_cast<double>(p1), 6.0) - pow ( static_cast<double>(max2(p1-2, 0)), 6.0);
+	      cpucosthex += pow (static_cast<double>(p1), 6.0) * pow (static_cast<double>(p2), 3.0);
+	      break;
+	    }
+	  default:
+	    ;
+	  }
+      }
+    cout << "TET: hp-memcost = " << memcosttet 
+	 << ", scmemcost = " << memcostsctet
+	 << ", cpucost = " << cpucosttet
+	 << endl;
+    cout << "PRI: hp-memcost = " << memcostprism
+	 << ", scmemcost = " << memcostscprism
+	 << ", cpucost = " << cpucostprism << endl;
+    cout << "HEX: hp-memcost = " << memcosthex
+	 << ", scmemcost = " << memcostschex
+	 << ", cpucost = " << cpucosthex << endl;
+#endif
+  }
+
+
+
+  void ReorderPoints (Mesh & mesh, Array<HPRefElement> & hpelements)
+  {
+    Array<int, 1> map (mesh.GetNP());
+    
+    for (int i = 1; i <= mesh.GetNP(); i++)
+      map[i] = i;
+
+    int nwrong(0), nright(0);
+    for (int k = 0; k < 5; k++)
+      {
+        nwrong = nright = 0;
+        for (int i = 0; i < hpelements.Size(); i++)
+          {
+            const HPRefElement & hpel = hpelements[i];
+            
+            if (Get_HPRef_Struct (hpel.type) -> geom == HP_PRISM)
+              {
+                int minbot = 0, mintop = 0;
+                for (int j = 0; j < 3; j++)
+                  {
+                    if (map[hpel.pnums[j]] < map[hpel.pnums[minbot]]) minbot = j;
+                    if (map[hpel.pnums[j+3]] < map[hpel.pnums[mintop+3]]) mintop = j;
+                  }
+                if (minbot != mintop) 
+                  nwrong++;
+                else
+                  nright++;
+                
+                if (minbot != mintop)
+                  {
+                    if (map[hpel.pnums[minbot]] < map[hpel.pnums[mintop+3]])
+                      swap (map[hpel.pnums[3+minbot]], map[hpel.pnums[3+mintop]]);
+                    else
+                      swap (map[hpel.pnums[minbot]], map[hpel.pnums[mintop]]);
+                  }
+              }
+          }
+        // cout << nwrong << " wrong prisms, " << nright << " right prisms" << endl;
+      }
+
+    cout << nwrong << " wrong prisms, " << nright << " right prisms" << endl;
+
+
+    Array<MeshPoint, 1> hpts(mesh.GetNP());
+
+    for (int i = 1; i <= mesh.GetNP(); i++)
+      hpts[map[i]] = mesh.Point(i);
+
+    for (int i = 1; i <= mesh.GetNP(); i++)
+      mesh.Point(i) = hpts[i];
+
+    for (int i = 0; i < hpelements.Size(); i++)
+      {
+        HPRefElement & hpel = hpelements[i];
+        for (int j = 0; j < hpel.np; j++)
+          hpel.pnums[j] = map[hpel.pnums[j]];
+      }
+  }
+
+
+
+  /* ***************************** HPRefinement ********************************** */
+
+  void HPRefinement (Mesh & mesh, Refinement * ref, int levels, double fac1, bool setorders, bool reflevels)
+  {
+    PrintMessage (1, "HP Refinement called, levels = ", levels);
+
+ 
+    // NgLock mem_lock (mem_mutex,1);
+
+    mesh.coarsemesh = new Mesh; 
+    *mesh.coarsemesh = mesh;
+    
+    // #ifdef CURVEDELEMS_NEW
+    const_cast<CurvedElements&> (mesh.coarsemesh->GetCurvedElements() ).
+      BuildCurvedElements (ref, mesh.GetCurvedElements().GetOrder());
+    // #endif
+
+
+    delete mesh.hpelements;
+    mesh.hpelements = new Array<HPRefElement>;
+        
+    Array<HPRefElement> & hpelements = *mesh.hpelements; 
+        
+    InitHPElements(mesh,hpelements); 
+    
+    Array<int> nplevel;
+    nplevel.Append (mesh.GetNP());
+    
+    int act_ref=1;
+    bool sing = ClassifyHPElements (mesh,hpelements, act_ref, levels); 
+
+    sing = true; // iterate at least once
+    while(sing) 
+      {
+	cout << " Start new hp-refinement: step " <<  act_ref  << endl; 
+		
+	DoRefinement (mesh, hpelements, ref, fac1); 
+	DoRefineDummies (mesh, hpelements, ref);
+	
+	nplevel.Append (mesh.GetNP());
+	CalcStatistics (hpelements);
+	
+	SubdivideDegeneratedHexes (mesh, hpelements,fac1);
+
+        ReorderPoints (mesh, hpelements);
+
+	mesh.ClearSegments();
+	mesh.ClearSurfaceElements();
+  	mesh.ClearVolumeElements();
+
+	for (int i = 0; i < hpelements.Size(); i++)
+	  {
+	    HPRefElement & hpel = hpelements[i];
+	    if (Get_HPRef_Struct (hpel.type))
+	      switch (Get_HPRef_Struct (hpel.type) -> geom)
+		{
+		case HP_SEGM:
+		  {
+		    Segment seg;
+		    seg[0] = hpel.pnums[0];
+		    seg[1] = hpel.pnums[1];
+		    // NOTE: only for less than 10000 elements (HACK) !!!
+		    seg.edgenr = hpel.index % 10000;
+		    seg.si     = hpel.index / 10000;
+
+                    /*
+                    seg.epgeominfo[0].dist = hpel.param[0][0]; // he: war hpel.param[0][0]
+                    seg.epgeominfo[1].dist = hpel.param[1][0]; // he: war hpel.param[1][0]
+                    */
+                    
+                    const Segment & coarseseg = mesh.coarsemesh->LineSegment(hpel.coarse_elnr+1);
+                    double d1 = coarseseg.epgeominfo[0].dist;
+                    double d2 = coarseseg.epgeominfo[1].dist;
+
+                    // seg.epgeominfo[0].dist = hpel.param[0][0]; // he: war hpel.param[0][0]
+                    // seg.epgeominfo[1].dist = hpel.param[1][0]; // he: war hpel.param[1][0]
+
+                    seg.epgeominfo[0].dist = d1 + hpel.param[0][0] * (d2-d1); // JS, June 08
+                    seg.epgeominfo[1].dist = d1 + hpel.param[1][0] * (d2-d1); 
+
+
+		    seg.epgeominfo[0].edgenr = seg.edgenr;
+		    seg.epgeominfo[1].edgenr = seg.edgenr;
+                    seg.domin = hpel.domin; seg.domout=hpel.domout; // he: needed for segments!
+		    seg.hp_elnr = i;
+		    seg.singedge_left = hpel.singedge_left; 
+		    seg.singedge_right = hpel.singedge_right; 
+		    mesh.AddSegment (seg); 
+		    break;
+		  }
+		  
+		case HP_TRIG: 
+		case HP_QUAD: 
+		  { 
+		    Element2d el(hpel.np); 
+		    for(int j=0;j<hpel.np;j++) 
+		      el.PNum(j+1) = hpel.pnums[j]; 
+		    el.hp_elnr = i; 
+		    el.SetIndex(hpel.index);
+		    if(setorders)
+		      el.SetOrder(act_ref+1,act_ref+1,0); 
+		    mesh.AddSurfaceElement(el);
+		    break; 
+		  } 
+		case HP_HEX:
+		case HP_TET:
+		case HP_PRISM:
+		case HP_PYRAMID:
+		  { 
+		    Element el(hpel.np); 
+		    for(int j=0;j<hpel.np;j++) 
+		      el.PNum(j+1) = hpel.pnums[j]; 
+		    el.SetIndex(hpel.index); 
+		    el.hp_elnr = i; 
+		    if(setorders)
+		      el.SetOrder(act_ref+1,act_ref+1,act_ref+1);
+		    mesh.AddVolumeElement(el); 
+		    break;
+		  } 
+		      
+		default:
+		  PrintSysError ("hpref, backconversion failed for element ", 
+				 int(Get_HPRef_Struct (hpel.type) -> geom));
+		}
+	  }
+	cout << " Start with Update Topology " << endl; 
+	mesh.UpdateTopology();
+	cout << " Mesh Update Topology done " << endl; 
+
+	act_ref++; 
+	
+	sing = ClassifyHPElements(mesh,hpelements, act_ref, levels); 
+      }
+
+    cout << " HP-Refinement done with " << --act_ref << " refinement steps." << endl; 
+
+    if(act_ref>=1)
+      { 
+	for(ElementIndex i=0;i<mesh.GetNE(); i++) 
+	  { 
+	    Element el = mesh[i] ;
+	    HPRefElement & hpel = hpelements[mesh[i].hp_elnr];
+	    const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (mesh[i].GetType());
+	    double dist[3] = {0,0,0}; 
+	    int ord_dir[3] = {0,0,0}; 
+	    int edge_dir[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; 
+	    int ned = 4; 
+	    
+	    switch (mesh[i].GetType())
+	      {
+	      case TET: 
+		/* cout << " TET " ; 
+		for(int k=0;k<4;k++) cout << el[k] << "\t" ; 
+		cout << endl; */ 
+		break; 
+	      case PRISM:
+		/* cout << " PRISM " ; 
+		for(int k=0;k<6;k++) cout << el[k] << "\t" ; 
+		cout << endl;  */ 
+		for(int l=6;l<9;l++) edge_dir[l] = 2; 
+		ord_dir[2] = 2; 
+		ned = 9; 
+		break; 
+	      case HEX: 
+		/* cout << " HEX " ; 
+		for(int k=0;k<8;k++) cout << el[k] << "\t" ; 
+		cout << endl; */
+		for(int l=8;l<12; l++) edge_dir[l] = 2; 
+		edge_dir[2] = edge_dir[3] = edge_dir[6] = edge_dir[7] = 1;
+		ord_dir[1] = 1; 
+		ord_dir[2] = 2; 
+		ned = 12; 
+		break;  
+	      case PYRAMID: 
+		/*	cout << " PYRAMID " ; 
+		for(int k=0;k<5;k++) cout << el[k] << "\t" ; 
+		cout << endl; */ 
+		for(int l=4;l<8;l++) edge_dir[l] = 2; 
+		edge_dir[2] = edge_dir[3] = 1; 
+		ord_dir[1] = 1; 
+		ord_dir[2] = 2; 
+		ned = 8;  
+		break; 
+
+
+              default:
+                cerr << "HPRefElement: illegal elementtype (2) " << mesh[i].GetType() << endl;
+                throw NgException ("HPRefElement: illegal elementtype (2)");
+                
+	      }
+	
+	    for (int j=0;j<ned;j++) 
+	      { 
+			
+		Vec<3> v(hpel.param[edges[j][0]-1][0]-hpel.param[edges[j][1]-1][0],
+			    hpel.param[edges[j][0]-1][1]-hpel.param[edges[j][1]-1][1],
+			    hpel.param[edges[j][0]-1][2]-hpel.param[edges[j][1]-1][2]);
+		dist[edge_dir[j]] = max(v.Length(),dist[edge_dir[j]]);
+	      }
+	    
+	    int refi[3];  
+	    for(int j=0;j<3;j++) 
+	      refi[j] = int(max(double(floor(log(dist[ord_dir[j]]/sqrt(2.))/log(fac1))),0.)); 	
+	    
+	    // cout << " ref " << refi[0] << "\t" << refi[1] << "\t" << refi[2] << endl; 
+	    // cout << " order " << act_ref +1 - refi[0] << "\t" << act_ref +1 - refi[1] << "\t" << act_ref +1 - refi[2] << endl; 
+	   	      
+	    if(setorders)
+	      mesh[i].SetOrder(act_ref+1-refi[0],act_ref+1-refi[1],act_ref+1-refi[2]); 
+	  }
+	for(SurfaceElementIndex i=0;i<mesh.GetNSE(); i++) 
+	  { 
+	    Element2d el = mesh[i] ;
+	    HPRefElement & hpel = hpelements[mesh[i].hp_elnr];
+	    const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (mesh[i].GetType());
+	    double dist[3] = {0,0,0}; 
+	    int ord_dir[3] = {0,0,0}; 
+	    int  edge_dir[4] = {0,0,0,0} ; 
+	    int ned = 3; 
+	   
+	    if(mesh[i].GetType() == QUAD)
+	      {
+		/*	cout << " QUAD " ; 
+		for(int k=0;k<4;k++) cout << el[k] << "\t" ; 
+		cout << endl; 	*/ 
+ 
+		edge_dir[2] = edge_dir[3] = 1; 
+		ord_dir[1] = 1; 
+		ned = 4; 
+	      }
+	    /*  else 
+	      { 
+		cout << " TRIG " ; 
+		for(int k=0;k<3;k++) cout << el[k] << "\t" ; 
+		cout << endl; 
+		} */ 
+	    
+	    for (int j=0;j<ned;j++) 
+	      { 
+		Vec<3> v(hpel.param[edges[j][0]-1][0]-hpel.param[edges[j][1]-1][0],
+			    hpel.param[edges[j][0]-1][1]-hpel.param[edges[j][1]-1][1],
+			    hpel.param[edges[j][0]-1][2]-hpel.param[edges[j][1]-1][2]);
+		dist[edge_dir[j]] = max(v.Length(),dist[edge_dir[j]]);
+	      }
+	    
+	    int refi[3]; 
+	    for(int j=0;j<3;j++) 
+	      refi[j] = int(max(double(floor(log(dist[ord_dir[j]]/sqrt(2.))/log(fac1))),0.)); 	
+	    
+	    if(setorders)
+	      mesh[i].SetOrder(act_ref+1-refi[0],act_ref+1-refi[1],act_ref+1-refi[2]); 
+
+	      // cout << " ref " << refi[0] << "\t" << refi[1] << endl; 
+	      // cout << " order " << act_ref +1 - refi[0] << "\t" << act_ref +1 - refi[1] << endl; 
+	  }
+      }
+  }
+
+bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE<int> & edges, INDEX_2_HASHTABLE<int> & edgepoint_dom, 
+		       BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE<int> & faces, INDEX_2_HASHTABLE<int> & face_edges, 
+			INDEX_2_HASHTABLE<int> & surf_edges, Array<int, PointIndex::BASE> & facepoint, int & levels, int & act_ref)
+{ 
+  bool sing = 0; 
+  if (mesh.GetDimension() == 3)
+    {
+	/*
+	// check, if point has as least 3 different surfs:
+
+	Array<INDEX_3, PointIndex::BASE> surfonpoint(mesh.GetNP());
+  	surfonpoint = INDEX_3(0,0,0);
+
+	for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++)
+	  {
+	    const Element2d & el = mesh[sei];
+	    int ind = el.GetIndex();
+	    for (int j = 0; j < el.GetNP(); j++)
+	      {
+		INDEX_3 & i3 = surfonpoint[el[j]];
+		if (ind != i3.I1() && ind != i3.I2() && ind != i3.I3())
+		  {
+		    i3.I1() = i3.I2();
+		    i3.I2() = i3.I3();
+		    i3.I3() = ind;
+		  }
+	      }
+	  }
+	for (int i = 1; i <= mesh.GetNP(); i++)
+	  if (surfonpoint.Get(i).I1())
+	    cornerpoint.Set(i);
+	*/
+	cornerpoint.Clear();
+	
+	for (int i = 1; i <= mesh.GetNP(); i++)
+	  {
+	    if (mesh.Point(i).Singularity() * levels >= act_ref)
+	      {
+		cornerpoint.Set(i);
+		sing = 1; 
+	      } 
+	  }
+	cout << endl; 
+
+	for (int i = 1; i <= mesh.GetNSeg(); i++)
+	  if (mesh.LineSegment(i).singedge_left * levels >= act_ref)
+	    {
+	      INDEX_2 i2 (mesh.LineSegment(i)[0], 
+			  mesh.LineSegment(i)[1]);
+
+	      /*
+		// before
+	      edges.Set (i2, 1);
+	      i2.Sort();   
+	      INDEX_2 i2s(i2.I2(), i2.I1());
+	      edges.Set (i2s, 1);
+	      */
+
+	      edges.Set (i2, 1);
+	      INDEX_2 i2s(i2.I2(), i2.I1());
+	      edges.Set (i2s, 1);
+
+
+	      edgepoint.Set (i2.I1());
+	      edgepoint.Set (i2.I2());
+	      sing = 1; 
+	    }
+
+	// if 2 adjacent edges of an element are singular, the 
+	// commen point must be a singular point
+	for (int i = 1; i <= mesh.GetNE(); i++)
+	  {
+	    const Element & el = mesh.VolumeElement(i);
+	    const ELEMENT_EDGE * eledges = MeshTopology::GetEdges1 (el.GetType());
+	    int nedges = MeshTopology::GetNEdges (el.GetType());
+	    for (int j = 0; j < nedges; j++)
+	      for (int k = 0; k < nedges; k++)
+		if (j != k)
+		  {
+		    INDEX_2 ej(el.PNum(eledges[j][0]), el.PNum(eledges[j][1]));
+		    ej.Sort();
+		    INDEX_2 ek(el.PNum(eledges[k][0]), el.PNum(eledges[k][1]));
+		    ek.Sort();
+		    if (edges.Used(ej) && edges.Used(ek))
+		      {
+			if (ej.I1() == ek.I1()) cornerpoint.Set (ek.I1());
+			if (ej.I1() == ek.I2()) cornerpoint.Set (ek.I2());
+			if (ej.I2() == ek.I1()) cornerpoint.Set (ek.I1());
+			if (ej.I2() == ek.I2()) cornerpoint.Set (ek.I2());
+		      }
+		  }
+	  }
+
+	edgepoint.Or (cornerpoint);
+	(*testout) << "cornerpoint = " << endl << cornerpoint << endl;
+	(*testout) << "edgepoint = " << endl << edgepoint << endl;
+
+	facepoint = 0;
+	for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++)
+	  {
+	    const Element2d & el = mesh[sei];
+	    const FaceDescriptor & fd = mesh.GetFaceDescriptor (el.GetIndex());
+	  
+	    int domnr = 0;
+	    if (fd.DomainInSingular() * levels < act_ref && fd.DomainOutSingular() * levels < act_ref) 
+	      { domnr=0;  continue;}
+	    
+	    if (fd.DomainInSingular() * levels >= act_ref) 
+	      {
+		domnr = fd.DomainIn();
+		sing = 1;
+	      }
+	    if (fd.DomainOutSingular() * levels >= act_ref)
+	      {
+		domnr = fd.DomainOut();
+		sing = 1; 
+	      } 
+	    if (fd.DomainInSingular() * levels >= act_ref 
+		&& fd.DomainOutSingular() * levels >= act_ref) 
+	      {
+		domnr = -1;
+		sing = 1;
+	      } 
+  
+	    INDEX_3 i3;
+	    if (el.GetNP() == 3) 
+	      i3 = INDEX_3::Sort (el[0], el[1], el[2]);
+	    else
+	      {
+		INDEX_4 i4 (el[0], el[1], el[2], el[3]);
+		i4.Sort();
+		i3 = INDEX_3(i4.I1(), i4.I2(), i4.I3());
+	      }
+	    faces.Set (i3, domnr);
+	
+	    for (int j = 0; j < el.GetNP(); j++)
+	      {
+		face_edges.Set (INDEX_2::Sort (el[j], el[(j+1)%el.GetNP()]), domnr);
+	
+		surf_edges.Set (INDEX_2::Sort (el[j], el[(j+1)%el.GetNP()]), fd.SurfNr()+1);
+		
+		facepoint[el[j]] = domnr;
+	      }
+	   
+	  }
+	(*testout) << "singular faces = " << faces << endl;
+	(*testout) << "singular faces_edges = " << face_edges << endl;
+      }
+    else
+      {
+	// 2D case
+
+	// check, if point has as least 3 different surfs:
+	Array<INDEX_3, PointIndex::BASE> surfonpoint(mesh.GetNP());
+
+	for (int i = 1; i <= mesh.GetNP(); i++)
+	  surfonpoint.Elem(i) = INDEX_3(0,0,0);
+	
+	for (int i = 1; i <= mesh.GetNSeg(); i++)
+	  {
+	    const Segment & seg = mesh.LineSegment(i);
+	    int ind = seg.edgenr;
+	    
+	    if (seg.singedge_left * levels >= act_ref)
+	      {
+		INDEX_2 i2 (mesh.LineSegment(i)[0], 
+			    mesh.LineSegment(i)[1]);
+		edges.Set(i2,1); 
+		edgepoint.Set(i2.I1());
+		edgepoint.Set(i2.I2());
+		*testout << " singleft " << endl;  
+		*testout << " mesh.LineSegment(i).domout " << mesh.LineSegment(i).domout << endl;      
+		*testout << " mesh.LineSegment(i).domin " << mesh.LineSegment(i).domin << endl;      
+		edgepoint_dom.Set (INDEX_2(mesh.LineSegment(i).domin, i2.I1()), 1);
+		edgepoint_dom.Set (INDEX_2(mesh.LineSegment(i).domin, i2.I2()), 1);
+		sing = 1; 
+		
+	      }
+	    
+	    if (seg.singedge_right * levels >= act_ref)
+	      {
+		INDEX_2 i2 (mesh.LineSegment(i)[1], 
+			    mesh.LineSegment(i)[0]);  
+		edges.Set (i2, 1);
+		edgepoint.Set(i2.I1());
+		edgepoint.Set(i2.I2());
+		
+		*testout << " singright " << endl;  
+		*testout << " mesh.LineSegment(i).domout " << mesh.LineSegment(i).domout << endl;      
+		*testout << " mesh.LineSegment(i).domin " << mesh.LineSegment(i).domin << endl;      
+		
+		edgepoint_dom.Set (INDEX_2(mesh.LineSegment(i).domout, i2.I1()), 1);
+		edgepoint_dom.Set (INDEX_2(mesh.LineSegment(i).domout, i2.I2()), 1);
+		sing = 1;
+	      }
+	    
+	    // (*testout) << "seg = " << ind << ", " << seg[0] << "-" << seg[1] << endl;
+	    
+
+	    if (seg.singedge_left * levels >= act_ref
+		|| seg.singedge_right* levels >= act_ref)
+	      {
+		for (int j = 0; j < 2; j++)
+		  {
+		    int pi = (j == 0) ? seg[0] : seg[1];
+		    INDEX_3 & i3 = surfonpoint.Elem(pi);
+		    if (ind != i3.I1() &&
+			ind != i3.I2())
+		      {
+			i3.I1() = i3.I2();
+			i3.I2() = ind;
+		      }
+		  }
+	      }
+	  }
+
+
+	for (int i = 1; i <= mesh.GetNP(); i++)
+	  {
+	    // mark points for refinement that are in corners between two anisotropic edges 
+	    if (surfonpoint.Get(i).I1())
+	      {
+		// cornerpoint.Set(i);    // disabled by JS, Aug 2009
+		edgepoint.Set(i);
+	      }
+	
+	    // mark points for refinement that are explicity specified in input file
+	    if (mesh.Point(i).Singularity()*levels >= act_ref)
+	      {
+		cornerpoint.Set(i);
+		edgepoint.Set(i);
+		sing =  1; 
+	      }
+	  }
+
+	edgepoint.Or (cornerpoint);
+
+	(*testout) << "2d sing edges: " << endl << edges << endl;
+	(*testout) << "2d cornerpoints: " << endl << cornerpoint << endl
+		   << "2d edgepoints: " << endl << edgepoint << endl;
+	
+	facepoint = 0;
+      }
+
+    if (!sing)
+      cout << "PrepareElements no more to do for actual refinement " << act_ref << endl; 
+
+    return(sing); 
+}
+
+
+
+  bool ClassifyHPElements (Mesh & mesh, Array<HPRefElement> & elements, int & act_ref, int & levels)
+  {
+    INDEX_2_HASHTABLE<int> edges(mesh.GetNSeg()+1);
+    BitArray edgepoint(mesh.GetNP());
+    INDEX_2_HASHTABLE<int> edgepoint_dom(mesh.GetNSeg()+1);
+
+    edgepoint.Clear();
+    BitArray cornerpoint(mesh.GetNP());
+    cornerpoint.Clear();
+
+    // value = nr > 0 ... refine elements in domain nr
+    // value = -1   ..... refine elements in any domain
+    INDEX_3_HASHTABLE<int> faces(mesh.GetNSE()+1);
+    INDEX_2_HASHTABLE<int> face_edges(mesh.GetNSE()+1);
+    INDEX_2_HASHTABLE<int> surf_edges(mesh.GetNSE()+1);
+    Array<int, PointIndex::BASE> facepoint(mesh.GetNP());
+
+    bool sing = CheckSingularities(mesh, edges, edgepoint_dom, 
+			      cornerpoint, edgepoint, faces, face_edges, 
+			      surf_edges, facepoint, levels, act_ref); 
+  
+    if(sing==0) return(sing); 
+
+    int cnt_undef = 0, cnt_nonimplement = 0;
+    Array<int> misses(10000);
+    misses = 0;
+
+    (*testout) << "edgepoint_dom = " << endl << edgepoint_dom << endl;
+
+    for( int i = 0; i<elements.Size(); i++) 
+      {
+	// *testout << "classify element " << i << endl;
+
+	HPRefElement & hpel = elements[i]; 
+	HPRef_Struct * hprs = Get_HPRef_Struct (hpel.type);
+	HPRefElement old_el = elements[i]; 
+	int dd=3; 
+
+
+	if(act_ref !=1 && (hpel.type == HP_HEX || hpel.type == HP_PRISM || hpel.type == HP_TET 
+			   || hpel.type == HP_PYRAMID || hpel.type == HP_QUAD || hpel.type == HP_TRIG || hpel.type == HP_SEGM)) 
+	  continue; 
+	
+	sing = 1; 
+	switch (hprs->geom)
+	  {
+	  case HP_TET:
+	    {
+	      hpel.type = ClassifyTet(hpel, edges, edgepoint_dom, cornerpoint, edgepoint, faces,face_edges, surf_edges, facepoint); 
+	      break;
+	    }
+	  case HP_PRISM:
+	    {
+	      hpel.type = ClassifyPrism(hpel, edges, edgepoint_dom, cornerpoint, edgepoint, faces,
+					face_edges, surf_edges, facepoint); 	    	    
+	 
+	    
+	      break;
+	    }
+	  case HP_HEX:
+	    { 
+	      hpel.type = hpel.type = ClassifyHex(hpel, edges, edgepoint_dom, cornerpoint, edgepoint, faces,
+						  face_edges, surf_edges, facepoint); 	    	    
+	      break; 
+	    } 
+	  case HP_TRIG: 
+	    { 
+	      int dim = mesh.GetDimension(); 
+	      const FaceDescriptor & fd = mesh.GetFaceDescriptor (hpel.GetIndex());	
+	      
+	      hpel.type = ClassifyTrig(hpel, edges, edgepoint_dom, cornerpoint, edgepoint, 
+				       faces, face_edges, surf_edges, facepoint, dim, fd);    
+	     
+	      dd = 2; 
+	      break; 
+	    } 
+	  case HP_QUAD: 
+	    { 
+	      int dim = mesh.GetDimension(); 
+	      const FaceDescriptor & fd = mesh.GetFaceDescriptor (hpel.GetIndex());	
+	      hpel.type = ClassifyQuad(hpel, edges, edgepoint_dom, cornerpoint, edgepoint, 
+				  faces, face_edges, surf_edges, facepoint, dim, fd);    
+
+	      dd = 2; 
+	      break; 
+	    }
+	  case HP_SEGM: 
+	    {
+	      hpel.type = ClassifySegm(hpel, edges, edgepoint_dom, cornerpoint, edgepoint, 
+				       faces, face_edges, surf_edges, facepoint);    
+	      dd = 1; 
+	      break; 
+	    }
+	  case HP_PYRAMID: 
+	    {
+	      hpel.type = ClassifyPyramid(hpel, edges, edgepoint_dom, cornerpoint, edgepoint, faces,
+						  face_edges, surf_edges, facepoint); 	    	    
+	      
+	      cout << " ** Pyramid classified  " << hpel.type << endl; 
+	      break; 
+	    }
+	  default:
+	    {
+	      cout << "illegal element type for hp-prepare elements " << hpel.type << endl;
+	      throw NgException ("hprefinement.cpp: don't know how to set parameters");
+	    }
+	  }
+	    
+	if(hpel.type == HP_NONE) 
+	  cnt_undef++; 
+
+	//else 
+	//cout << "elem " << i << " classified type " << hpel.type << endl; 
+
+	
+	
+	if (!Get_HPRef_Struct (hpel.type)) 
+	  {
+	    (*testout) << "hp-element-type " << hpel.type << " not implemented   " << endl;
+	    (*testout) << " elType " << hprs->geom << endl; 
+ (cout) << " elType " << hprs->geom << endl;        
+	    cnt_nonimplement++;
+	    misses[hpel.type]++;
+	  }
+	
+  
+	for(int j=0; j<hpel.np; j++)
+	  {
+	    for( int k=0; k<hpel.np; k++) 
+	      if(hpel[j] == old_el.pnums[k]) 
+		{ 
+		  for(int l=0;l<dd;l++) 
+		    hpel.param[j][l] = old_el.param[k][l];
+		  break;
+		}
+	  } 
+
+      }
+    
+    
+    cout << "undefined elements update classification: " << cnt_undef << endl;
+    cout << "non-implemented in update classification: " << cnt_nonimplement << endl;
+
+    for (int i = 0; i < misses.Size(); i++)
+      if (misses[i])
+	cout << " in update classification missing case " << i << " occured " << misses[i] << " times" << endl;
+
+    return(sing); 
+  }
+}
+  
diff --git a/contrib/Netgen/libsrc/meshing/hprefinement.hpp b/contrib/Netgen/libsrc/meshing/hprefinement.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f299ae8aa15beb4cf85d5d2315fd0d41e468fd97
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hprefinement.hpp
@@ -0,0 +1,319 @@
+#ifndef FILE_HPREFINEMENT
+#define FILE_HPREFINEMENT
+
+/**************************************************************************/
+/* File:   hprefinement.hh                                                */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   27. Oct. 2000                                                  */
+/**************************************************************************/
+
+/*
+  HP Refinement
+*/
+
+
+
+
+enum HPREF_ELEMENT_TYPE {
+  HP_NONE=0,
+
+  HP_SEGM = 1,
+  HP_SEGM_SINGCORNERL,
+  HP_SEGM_SINGCORNERR,
+  HP_SEGM_SINGCORNERS,
+
+  HP_TRIG = 10,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_SINGCORNER12,
+  HP_TRIG_SINGCORNER123,
+  HP_TRIG_SINGCORNER123_2D,   // not rotational symmetric
+  HP_TRIG_SINGEDGE = 20,
+  HP_TRIG_SINGEDGECORNER1,   // E = 100, V = 100
+  HP_TRIG_SINGEDGECORNER2,   // E = 100, V = 010
+  HP_TRIG_SINGEDGECORNER12,  // E = 100, V = 110
+  HP_TRIG_SINGEDGECORNER3,
+  HP_TRIG_SINGEDGECORNER13,
+  HP_TRIG_SINGEDGECORNER23,
+  HP_TRIG_SINGEDGECORNER123,
+  HP_TRIG_SINGEDGES = 30,
+  HP_TRIG_SINGEDGES2,
+  HP_TRIG_SINGEDGES3,
+  HP_TRIG_SINGEDGES23,
+  HP_TRIG_3SINGEDGES = 40,
+
+  HP_QUAD = 50,
+  HP_QUAD_SINGCORNER,
+  HP_DUMMY_QUAD_SINGCORNER,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_0E_2VA,  // V = 1100
+  HP_QUAD_0E_2VB,  // V = 1010
+  HP_QUAD_0E_3V,
+  HP_QUAD_0E_4V,
+
+  // one edge: marked edge is always edge from vertex 1 to vertex 2 (E = 1000)
+  HP_QUAD_1E_1VA,  // vertex on beginning of edge: V = 1000
+  HP_QUAD_1E_1VB,  // vertex on end of edge: V = 0100
+  HP_QUAD_1E_1VC,  // V = 0010
+  HP_QUAD_1E_1VD,  // V = 0001
+
+  HP_QUAD_1E_2VA,  // V = 1100
+  HP_QUAD_1E_2VB,  // V = 1010
+  HP_QUAD_1E_2VC,  // V = 1001
+  HP_QUAD_1E_2VD,  // V = 0110
+  HP_QUAD_1E_2VE,  // V = 0101
+  HP_QUAD_1E_2VF,  // V = 0011
+
+  HP_QUAD_1E_3VA,  // V = 1110
+  HP_QUAD_1E_3VB,  // V = 1101
+  HP_QUAD_1E_3VC,  // V = 1011
+  HP_QUAD_1E_3VD,  // V = 0111
+
+  HP_QUAD_1E_4V,   // V = 1111
+
+
+  HP_QUAD_2E,      // E = 1001, V = 1000
+  HP_QUAD_2E_1VA,  // E = 1001, V = 1100
+  HP_QUAD_2E_1VB,  // E = 1001, V = 1010
+  HP_QUAD_2E_1VC,  // E = 1001, V = 1001
+  HP_QUAD_2E_2VA,  // E = 1001, V = 1110
+  HP_QUAD_2E_2VB,  // E = 1001, V = 1101
+  HP_QUAD_2E_2VC,  // E = 1001, V = 1011
+  HP_QUAD_2E_3V,   // E = 1001, V = 1111
+
+  HP_QUAD_2EB_0V,   // E = 1010, V = 0000
+  HP_QUAD_2EB_1VA,  // E = 1010, V = 1000
+  HP_QUAD_2EB_1VB,  // E = 1010, V = 0100
+  HP_QUAD_2EB_2VA,  // E = 1010, V = 1100
+  HP_QUAD_2EB_2VB,  // E = 1010, V = 1010
+  HP_QUAD_2EB_2VC,  // E = 1010, V = 1001
+  HP_QUAD_2EB_2VD,  // E = 1010, V = 0101
+  HP_QUAD_2EB_3VA,  // E = 1010, V = 1110
+  HP_QUAD_2EB_3VB,  // E = 1010, V = 1101
+
+  HP_QUAD_2EB_4V,
+
+
+  HP_QUAD_3E,      // E = 1101, V = 1100
+  HP_QUAD_3E_3VA,  // E = 1101, V = 1110
+  HP_QUAD_3E_3VB,  // E = 1101, V = 1101
+  HP_QUAD_3E_4V,   // E = 1101, V = 1111
+
+  HP_QUAD_4E,
+
+
+  HP_TET = 100,     // no singular vertex/edge
+  HP_TET_0E_1V,     // V1
+  HP_TET_0E_2V,     // V1,2
+  HP_TET_0E_3V,     // V1,2,3  
+  HP_TET_0E_4V,     // V1,2,3,4
+  HP_TET_1E_0V = 200,   // E1-2
+  HP_TET_1E_1VA,    // V1
+  HP_TET_1E_1VB,    // V3
+  HP_TET_1E_2VA,    // V1,2
+  HP_TET_1E_2VB,    // V1,3
+  HP_TET_1E_2VC,    // V1,4
+  HP_TET_1E_2VD,    // V3,4
+  HP_TET_1E_3VA,    // V1,2,3
+  HP_TET_1E_3VB,    // V1,3,4
+  HP_TET_1E_4V,     // V1,2,3,4
+
+
+  // 2 connected edges, additonally marked Vs
+  HP_TET_2EA_0V = 220,    // E1-2, E1-3
+  HP_TET_2EA_1VA,   // V2
+  HP_TET_2EA_1VB,   // V3
+  HP_TET_2EA_1VC,   // V4
+  HP_TET_2EA_2VA,   // V2,3
+  HP_TET_2EA_2VB,   // V2,4
+  HP_TET_2EA_2VC,   // V3,4
+  HP_TET_2EA_3V,    // V2,3,4
+
+  // 2 opposite edges
+  HP_TET_2EB_0V = 230,    // E1-2, E3-4
+  HP_TET_2EB_1V,    // V1
+  HP_TET_2EB_2VA,   // V1,2
+  HP_TET_2EB_2VB,   // V1,3
+  HP_TET_2EB_2VC,   // V1,4
+  HP_TET_2EB_3V,    // V1,2,3
+  HP_TET_2EB_4V,    // V1,2,3,4
+
+  HP_TET_3EA_0V = 400,  // E1-2, E1-3, E1-4, 3 edges connected
+  HP_TET_3EA_1V,        // V2
+  HP_TET_3EA_2V,        // V2,3
+  HP_TET_3EA_3V,        // V2,3,4
+
+  HP_TET_3EB_0V = 420,  // E1-2, E1-4, E2-3  3 edges chain
+  HP_TET_3EB_1V,        // 
+  HP_TET_3EB_2V,        // 
+  HP_TET_3EC_0V = 430,  // 3 edges chain, alter
+  HP_TET_3EC_1V,        // 3 edges chain, alter
+  HP_TET_3EC_2V,        // 3 edges chain, alter
+
+
+  HP_TET_1F_0E_0V = 500,  // 1 singular face
+  HP_TET_1F_0E_1VA,       // 1 sing vertex in face (V2)
+  HP_TET_1F_0E_1VB,       // 1 sing vertex not in face (V1)
+  HP_TET_1F_1EA_0V,       // 1 sing edge not in face
+  HP_TET_1F_1EB_0V,       // 1 sing edge in face
+  HP_TET_2F_0E_0V = 600,  // 2 singular faces
+
+  HP_PRISM = 1000,
+  HP_PRISM_SINGEDGE,
+  HP_PRISM_SINGEDGE_V12,
+  HP_PRISM_SINGEDGE_H1,
+  HP_PRISM_SINGEDGE_H12,
+
+  HP_PRISM_1FA_0E_0V,     // 1 singular trig face
+  HP_PRISM_2FA_0E_0V,     // 2 singular trig faces
+  HP_PRISM_1FB_0E_0V,     // 1 singular quad face  1-2-4-5
+
+  HP_PRISM_1FB_1EA_0V,     // 1 singular quad face, edge is 1-2
+  HP_PRISM_1FA_1E_0V, 
+  HP_PRISM_2FA_1E_0V, 
+  HP_PRISM_1FA_1FB_0E_0V, 
+  HP_PRISM_2FA_1FB_0E_0V,
+  HP_PRISM_1FA_1FB_1EA_0V, 
+  HP_PRISM_1FA_1FB_1EB_0V, 
+  HP_PRISM_2FA_1FB_1EA_0V,
+  HP_PRISM_1FB_1EC_0V, 
+  HP_PRISM_1FA_1FB_1EC_0V, 
+  HP_PRISM_2FA_1FB_1EC_0V,
+  HP_PRISM_1FB_2EA_0V, 
+  HP_PRISM_1FA_1FB_2EA_0V, 
+  HP_PRISM_2FA_1FB_2EA_0V,
+  HP_PRISM_1FB_2EB_0V,
+  HP_PRISM_1FA_1FB_2EB_0V,  
+  HP_PRISM_1FA_1FB_2EC_0V, 
+  HP_PRISM_2FA_1FB_2EB_0V, 
+  HP_PRISM_1FB_3E_0V, 
+  HP_PRISM_1FA_1FB_3E_0V, 
+  HP_PRISM_2FA_1FB_3E_0V, 
+  HP_PRISM_2FB_0E_0V, 
+  HP_PRISM_1FA_2FB_0E_0V, 
+  HP_PRISM_2FA_2FB_0E_0V,
+  HP_PRISM_2FB_1EC_0V, 
+  HP_PRISM_1FA_2FB_1EC_0V,
+  HP_PRISM_1FA_2FB_1EB_0V,
+  HP_PRISM_2FA_2FB_1EC_0V,
+  HP_PRISM_2FB_3E_0V, 
+  HP_PRISM_1FA_2FB_3E_0V, 
+  HP_PRISM_2FA_2FB_3E_0V, 
+  HP_PRISM_1FA_2E_0V, 
+  HP_PRISM_2FA_2E_0V,
+  HP_PRISM_3E_0V, 
+  HP_PRISM_1FA_3E_0V, 
+  HP_PRISM_2FA_3E_0V,  
+  HP_PRISM_3FB_0V, 
+  HP_PRISM_1FA_3FB_0V, 
+  HP_PRISM_2FA_3FB_0V,  
+  HP_PRISM_3E_4EH,
+  
+ 
+
+  /*  HP_PRISM_1FB_1EA_0V,     // 1 singular quad face, edge is 1-4
+  HP_PRISM_1FB_1EB_0V,     // 1 singular quad face, edge is 2-5
+  HP_PRISM_2F_0E_0V,      // 2 singular quad faces
+  */
+
+  HP_PYRAMID = 2000,
+  HP_PYRAMID_0E_1V,
+  HP_PYRAMID_EDGES,
+  HP_PYRAMID_1FB_0E_1VA,  // 1 trig face, top vertex
+
+  HP_HEX = 3000,
+  HP_HEX_0E_1V,
+  HP_HEX_1E_1V,
+  HP_HEX_1E_0V,
+  HP_HEX_3E_0V,
+  HP_HEX_1F_0E_0V,
+  HP_HEX_1FA_1FB_0E_0V
+};
+
+
+
+struct HPRef_Struct {
+  HPREF_ELEMENT_TYPE geom;
+  int (*splitedges)[3];
+  int (*splitfaces)[4];
+  int (*splitelements)[5];
+  HPREF_ELEMENT_TYPE * neweltypes;
+  int (*newels)[8];
+};
+
+
+
+
+class HPRefElement
+{
+private:
+  void Reset(void);
+
+public:
+  HPRefElement (); 
+  HPRefElement(Element & el);
+  HPRefElement(Element2d & el);
+  HPRefElement(Segment & el);	
+  HPRefElement(HPRefElement & el);
+
+  void SetType( HPREF_ELEMENT_TYPE t);
+  // HPRefElement(HPRefElement & el, HPREF_ELEMENT_TYPE t); 
+	       
+  /* HPRefElement(HPRefElement & el, HPREF_ELEMENT_TYPE t)
+  { 
+    type = t; 
+    HPRef_Struct * hprs = Get_HPRef_Struct(t);
+    for (int i=0; i<np ; i++) 
+      {
+	pnums[i] = el[i];
+	for(int l=0; l<np; l++) param[i][l] = el.param[i][l]; 
+      }
+    switch(hprs->geom)
+      {
+      case HP_SEGM: np=2; sing_edge_left=0; sing_edge_right=0; break; 
+      case HP_QUAD: np=4; break; 
+      case HP_TRIG: np=3; break; 
+      case HP_HEX: np=8; break; 
+      case HP_PRISM: np=6; break;
+      case HP_TET: np=4; break; 
+      case HP_PYRAMID: np=5; break; 
+      }
+    index = el.index; 
+    levelx = el.levelx; 
+    levely = el.levely; 
+    levelz = el.levelz; 
+    type = el.type; 
+    coarse_elnr = el.coarse_elnr;
+    singedge_left = el.singedge_left; 
+    singedge_right = el.singedge_left; 
+    } */ 
+  
+  HPREF_ELEMENT_TYPE type;
+  PointIndex pnums[8];
+  double param[8][3];
+  int index;
+  int levelx;
+  int levely;
+  int levelz;
+  int np; 
+  int coarse_elnr;
+  int domin, domout; // he: needed for segment!! in 3d there should be surf1, surf2!!
+  // int coarse_hpelnr; 
+  PointIndex & operator[](int i) { return(pnums[i]);}
+  PointIndex & PNumMod(int i) { return pnums[(i-1) % np]; };
+  PointIndex & PNum(int i) {return pnums[(i-1)]; };
+  int GetIndex () const { return index; }; 
+  double singedge_left, singedge_right; 
+  
+
+  //  EdgePointGeomInfo epgeominfo[2];
+  
+};
+
+
+
+extern void HPRefinement (Mesh & mesh, Refinement * ref, int levels, 
+			  double fac1=0.125, bool setorders=true, bool ref_level = false);
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/meshing/improve2.cpp b/contrib/Netgen/libsrc/meshing/improve2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eea6c51137a38e272856f369bbb93f31e64b1c8b
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve2.cpp
@@ -0,0 +1,854 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+#include <opti.hpp>
+
+#ifndef SMALLLIB
+//#ifndef NOTCL
+//#include <visual.hpp>
+//#endif
+#endif
+
+namespace netgen
+{
+
+  class Neighbour
+  {
+    int nr[3];
+    int orient[3];
+
+  public:
+    Neighbour () { ; } 
+
+    void SetNr (int side, int anr) { nr[side] = anr; }
+    int GetNr (int side) { return nr[side]; }
+
+    void SetOrientation (int side, int aorient) { orient[side] = aorient; }
+    int GetOrientation (int side) { return orient[side]; }
+
+
+
+    /*
+      void SetNr1 (int side, int anr) { nr[side-1] = anr; }
+      int GetNr1 (int side) { return nr[side-1]; }
+
+      void SetOrientation1 (int side, int aorient) { orient[side-1] = aorient; }
+      int GetOrientation1 (int side) { return orient[side-1]; }
+    */
+  };
+
+
+
+
+  class trionedge
+  {
+  public:
+    int tnr;
+    int sidenr;
+
+    trionedge () { tnr = 0; sidenr = 0; }
+    trionedge (int atnr, int asidenr)
+    { tnr = atnr; sidenr = asidenr; }
+  };
+
+
+
+ 
+  void MeshOptimize2d :: EdgeSwapping (Mesh & mesh, int usemetric)
+  {
+    if (!faceindex)
+      {
+	if (usemetric)
+	  PrintMessage (3, "Edgeswapping, metric");
+	else
+	  PrintMessage (3, "Edgeswapping, topological");
+
+	for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++)
+	  {
+	    EdgeSwapping (mesh, usemetric);
+
+	    if (multithread.terminate)
+	      throw NgException ("Meshing stopped");
+	  }
+
+	faceindex = 0;
+	mesh.CalcSurfacesOfNode();
+	return;
+      }
+
+
+    static int timer = NgProfiler::CreateTimer ("EdgeSwapping 2D");
+    NgProfiler::RegionTimer reg1 (timer);
+
+    static int timerstart = NgProfiler::CreateTimer ("EdgeSwapping 2D start");
+    NgProfiler::StartTimer (timerstart);
+
+
+    Array<SurfaceElementIndex> seia;
+    mesh.GetSurfaceElementsOfFace (faceindex, seia);
+
+    for (int i = 0; i < seia.Size(); i++)
+      if (mesh[seia[i]].GetNP() != 3)
+	{
+	  GenericImprove (mesh);
+	  return;
+	}
+
+    int surfnr = mesh.GetFaceDescriptor (faceindex).SurfNr();
+
+    Array<Neighbour> neighbors(mesh.GetNSE());
+    INDEX_2_HASHTABLE<trionedge> other(seia.Size() + 2);
+
+
+    Array<char> swapped(mesh.GetNSE());
+    Array<int,PointIndex::BASE> pdef(mesh.GetNP());
+    Array<double,PointIndex::BASE> pangle(mesh.GetNP());
+
+
+    // int e;
+    // double d;
+    // Vec3d nv1, nv2;
+
+    // double loch(-1);
+    static const double minangle[] = { 0, 1.481, 2.565, 3.627, 4.683, 5.736, 7, 9 };
+
+
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & sel = mesh[seia[i]];
+	for (int j = 0; j < 3; j++)
+	  pangle[sel[j]] = 0.0;
+      }
+    // pangle = 0;
+
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & sel = mesh[seia[i]];
+	for (int j = 0; j < 3; j++)
+	  {
+	    POINTTYPE typ = mesh[sel[j]].Type();
+	    if (typ == FIXEDPOINT || typ == EDGEPOINT)
+	      {
+		pangle[sel[j]] +=
+		  Angle (mesh[sel[(j+1)%3]] - mesh[sel[j]],
+			 mesh[sel[(j+2)%3]] - mesh[sel[j]]);
+	      }
+	  }
+      }
+
+    // for (PointIndex pi = PointIndex::BASE; 
+    // pi < mesh.GetNP()+PointIndex::BASE; pi++)
+    
+    // pdef = 0;
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & sel = mesh[seia[i]];
+	for (int j = 0; j < 3; j++)
+	  {
+	    PointIndex pi = sel[j];
+	    if (mesh[pi].Type() == INNERPOINT || mesh[pi].Type() == SURFACEPOINT)
+	      pdef[pi] = -6;
+	    else
+	      for (int j = 0; j < 8; j++)
+		if (pangle[pi] >= minangle[j])
+		  pdef[pi] = -1-j;
+	  }
+      }
+
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & sel = mesh[seia[i]];
+	for (int j = 0; j < 3; j++)
+	  pdef[sel[j]]++;
+      }
+
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	for (int j = 0; j < 3; j++)
+	  {
+	    neighbors[seia[i]].SetNr (j, -1);
+	    neighbors[seia[i]].SetOrientation (j, 0);
+	  }
+      }
+
+    /*
+      Array<Vec3d> normals(mesh.GetNP());
+      for (i = 1; i <= mesh.GetNSE(); i++)
+      {
+      Element2d & hel = mesh.SurfaceElement(i);
+      if (hel.GetIndex() == faceindex)
+      for (k = 1; k <= 3; k++)
+      {
+      int pi = hel.PNum(k);
+      SelectSurfaceOfPoint (mesh.Point(pi), hel.GeomInfoPi(k));
+      int surfi = mesh.GetFaceDescriptor(faceindex).SurfNr();
+      GetNormalVector (surfi, mesh.Point(pi), normals.Elem(pi));
+      normals.Elem(pi) /= normals.Elem(pi).Length();
+      }
+      }
+    */	    
+
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & sel = mesh[seia[i]];
+
+	for (int j = 0; j < 3; j++)
+	  {
+	    PointIndex pi1 = sel.PNumMod(j+2);
+	    PointIndex pi2 = sel.PNumMod(j+3);
+	  
+	    //	    double loch = mesh.GetH(mesh[pi1]);
+	    
+	    INDEX_2 edge(pi1, pi2);
+	    edge.Sort();
+	  
+	    if (mesh.IsSegment (pi1, pi2))
+	      continue;
+	  
+	    /*
+	      if (segments.Used (edge))
+	      continue;
+	    */
+	    INDEX_2 ii2 (pi1, pi2);
+	    if (other.Used (ii2))
+	      {
+		// INDEX_2 i2s(ii2);
+		// i2s.Sort();
+	      
+		int i2 = other.Get(ii2).tnr;
+		int j2 = other.Get(ii2).sidenr;
+		
+		neighbors[seia[i]].SetNr (j, i2);
+		neighbors[seia[i]].SetOrientation (j, j2);
+		neighbors[i2].SetNr (j2, seia[i]);
+		neighbors[i2].SetOrientation (j2, j);
+	      }
+	    else
+	      {
+		other.Set (INDEX_2 (pi2, pi1), trionedge (seia[i], j));
+	      }
+	  }
+      }
+
+    for (int i = 0; i < seia.Size(); i++)
+      swapped[seia[i]] = 0;
+
+    NgProfiler::StopTimer (timerstart);
+  
+
+
+    int t = 4;
+    int done = 0;
+    while (!done && t >= 2)
+      {
+	for (int i = 0; i < seia.Size(); i++)
+	  {
+	    SurfaceElementIndex t1 = seia[i];
+
+	    if (mesh[t1].IsDeleted())
+	      continue;
+
+	    if (mesh[t1].GetIndex() != faceindex)
+	      continue;
+
+	    if (multithread.terminate)
+	      throw NgException ("Meshing stopped");
+
+	    for (int o1 = 0; o1 < 3; o1++)
+	      {
+		bool should;
+
+
+		SurfaceElementIndex t2 = neighbors[t1].GetNr (o1);
+		int o2 = neighbors[t1].GetOrientation (o1);
+
+		if (t2 == -1) continue;
+		if (swapped[t1] || swapped[t2]) continue;
+	      
+
+		PointIndex pi1 = mesh[t1].PNumMod(o1+1+1);
+		PointIndex pi2 = mesh[t1].PNumMod(o1+1+2);
+		PointIndex pi3 = mesh[t1].PNumMod(o1+1);
+		PointIndex pi4 = mesh[t2].PNumMod(o2+1);
+	      
+		PointGeomInfo gi1 = mesh[t1].GeomInfoPiMod(o1+1+1);
+		PointGeomInfo gi2 = mesh[t1].GeomInfoPiMod(o1+1+2);
+		PointGeomInfo gi3 = mesh[t1].GeomInfoPiMod(o1+1);
+		PointGeomInfo gi4 = mesh[t2].GeomInfoPiMod(o2+1);
+	    
+		bool allowswap = true;
+
+		Vec<3> auxvec1 = mesh[pi3]-mesh[pi4];
+		Vec<3> auxvec2 = mesh[pi1]-mesh[pi4];
+
+		allowswap = allowswap && fabs(1.-(auxvec1*auxvec2)/(auxvec1.Length()*auxvec2.Length())) > 1e-4;
+
+		if(!allowswap)
+		  continue;
+
+		// normal of new
+		Vec<3> nv1 = Cross (auxvec1, auxvec2);
+
+		auxvec1 = mesh.Point(pi4)-mesh.Point(pi3);
+		auxvec2 = mesh.Point(pi2)-mesh.Point(pi3);
+		allowswap = allowswap && fabs(1.-(auxvec1*auxvec2)/(auxvec1.Length()*auxvec2.Length())) > 1e-4;
+
+
+		if(!allowswap)
+		  continue;
+
+		Vec<3> nv2 = Cross (auxvec1, auxvec2);
+
+	      
+		// normals of original
+		Vec<3> nv3 = Cross (mesh[pi1]-mesh[pi4], mesh[pi2]-mesh[pi4]);
+		Vec<3> nv4 = Cross (mesh[pi2]-mesh[pi3], mesh[pi1]-mesh[pi3]);
+	      
+		nv3 *= -1;
+		nv4 *= -1;
+		nv3.Normalize();
+		nv4.Normalize();
+
+		nv1.Normalize();
+		nv2.Normalize();
+	    
+		Vec<3> nvp3, nvp4;
+		SelectSurfaceOfPoint (mesh.Point(pi3), gi3);
+		GetNormalVector (surfnr, mesh.Point(pi3), gi3, nvp3);
+
+		nvp3.Normalize();
+
+		SelectSurfaceOfPoint (mesh.Point(pi4), gi4);
+		GetNormalVector (surfnr, mesh.Point(pi4), gi4, nvp4);
+	    
+		nvp4.Normalize();
+	      
+	      
+	      
+		double critval = cos (M_PI / 6);  // 30 degree
+		allowswap = allowswap &&
+		  (nv1 * nvp3 > critval) && 
+		  (nv1 * nvp4 > critval) && 
+		  (nv2 * nvp3 > critval) && 
+		  (nv2 * nvp4 > critval) &&
+		  (nvp3 * nv3 > critval) && 
+		  (nvp4 * nv4 > critval);
+	      
+
+		double horder = Dist (mesh.Point(pi1), mesh.Point(pi2));
+
+		if ( // nv1 * nv2 >= 0 &&
+		    nv1.Length() > 1e-3 * horder * horder &&
+		    nv2.Length() > 1e-3 * horder * horder &&
+		    allowswap )
+		  {
+		    if (!usemetric)
+		      {
+			int e = pdef[pi1] + pdef[pi2] - pdef[pi3] - pdef[pi4];
+			double d = 
+			  Dist2 (mesh.Point(pi1), mesh.Point(pi2)) - 
+			  Dist2 (mesh.Point(pi3), mesh.Point(pi4));
+		      
+			should = e >= t && (e > 2 || d > 0);
+		      }
+		    else
+		      {
+			double loch = mesh.GetH(mesh[pi1]);
+			should = 
+			  CalcTriangleBadness (mesh.Point(pi4), mesh.Point(pi3), mesh.Point(pi1), 
+					       metricweight, loch) +
+			  CalcTriangleBadness (mesh.Point(pi3), mesh.Point(pi4), mesh.Point(pi2), 
+					       metricweight, loch) <
+			  CalcTriangleBadness (mesh.Point(pi1), mesh.Point(pi2), mesh.Point(pi3), 
+					       metricweight, loch) +
+			  CalcTriangleBadness (mesh.Point(pi2), mesh.Point(pi1), mesh.Point(pi4), 
+					       metricweight, loch);
+
+		      }
+		  
+		    if (allowswap)
+		      {
+			Element2d sw1 (pi4, pi3, pi1);
+			Element2d sw2 (pi3, pi4, pi2);
+
+			int legal1 = 
+			  mesh.LegalTrig (mesh.SurfaceElement (t1)) + 
+			  mesh.LegalTrig (mesh.SurfaceElement (t2));
+			int legal2 = 
+			  mesh.LegalTrig (sw1) + mesh.LegalTrig (sw2);
+
+			if (legal1 < legal2) should = 1;
+			if (legal2 < legal1) should = 0;
+		      }
+		  
+		    if (should)
+		      {
+			// do swapping !
+		      
+			done = 1;
+		      
+			mesh[t1].PNum(1) = pi1;
+			mesh[t1].PNum(2) = pi4;
+			mesh[t1].PNum(3) = pi3;
+		      
+			mesh[t2].PNum(1) = pi2;
+			mesh[t2].PNum(2) = pi3;
+			mesh[t2].PNum(3) = pi4;
+		      
+			mesh[t1].GeomInfoPi(1) = gi1;
+			mesh[t1].GeomInfoPi(2) = gi4;
+			mesh[t1].GeomInfoPi(3) = gi3;
+		      
+			mesh[t2].GeomInfoPi(1) = gi2;
+			mesh[t2].GeomInfoPi(2) = gi3;
+			mesh[t2].GeomInfoPi(3) = gi4;
+		      
+			pdef[pi1]--;
+			pdef[pi2]--;
+			pdef[pi3]++;
+			pdef[pi4]++;
+		      
+			swapped[t1] = 1;
+			swapped[t2] = 1;
+		      }
+		  }
+	      }
+	  }
+	t--;
+      }
+
+    mesh.SetNextTimeStamp();
+  }
+
+
+
+
+
+
+
+ 
+  void MeshOptimize2d :: CombineImprove (Mesh & mesh)
+  {
+    if (!faceindex)
+      {
+	PrintMessage (3, "Combine improve");
+
+	for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++)
+	  {
+	    CombineImprove (mesh);
+
+	    if (multithread.terminate)
+	      throw NgException ("Meshing stopped");
+	  }
+	faceindex = 0;
+	return;
+      }
+
+
+    static int timer = NgProfiler::CreateTimer ("Combineimprove 2D");
+    NgProfiler::RegionTimer reg (timer);
+
+    static int timerstart = NgProfiler::CreateTimer ("Combineimprove 2D start");
+    NgProfiler::StartTimer  (timerstart);
+
+
+    static int timerstart1 = NgProfiler::CreateTimer ("Combineimprove 2D start1");
+    NgProfiler::StartTimer  (timerstart1);
+
+
+
+    // int i, j, k, l;
+    // PointIndex pi;
+    // SurfaceElementIndex sei;
+
+
+    Array<SurfaceElementIndex> seia;
+    mesh.GetSurfaceElementsOfFace (faceindex, seia);
+
+
+    for (int i = 0; i < seia.Size(); i++)
+      if (mesh[seia[i]].GetNP() != 3)
+	return;
+
+
+
+    int surfnr = 0;
+    if (faceindex)
+      surfnr = mesh.GetFaceDescriptor (faceindex).SurfNr();
+
+
+    // PointIndex pi1, pi2;
+    // MeshPoint p1, p2, pnew;
+    double bad1, bad2;
+    Vec<3> nv;
+
+    int np = mesh.GetNP();
+    //int nse = mesh.GetNSE();
+
+    TABLE<SurfaceElementIndex,PointIndex::BASE> elementsonnode(np); 
+    Array<SurfaceElementIndex> hasonepi, hasbothpi;
+
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	Element2d & el = mesh[seia[i]];
+	for (int j = 0; j < el.GetNP(); j++)
+	  elementsonnode.Add (el[j], seia[i]);
+      }
+
+    Array<bool,PointIndex::BASE> fixed(np);
+    fixed = false;
+
+    NgProfiler::StopTimer  (timerstart1);
+
+    /*
+    for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+      {
+	INDEX_2 i2(mesh[si][0], mesh[si][1]);
+	fixed[i2.I1()] = true;
+	fixed[i2.I2()] = true;
+      }
+    */
+
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	Element2d & sel = mesh[seia[i]];
+	for (int j = 0; j < sel.GetNP(); j++)
+	  {
+	    PointIndex pi1 = sel.PNumMod(j+2);
+	    PointIndex pi2 = sel.PNumMod(j+3);
+	    if (mesh.IsSegment (pi1, pi2))
+	      {	
+		fixed[pi1] = true;
+		fixed[pi2] = true;
+	      }
+	  }
+      }
+
+
+
+    for(int i = 0; i < mesh.LockedPoints().Size(); i++)
+      fixed[mesh.LockedPoints()[i]] = true;
+
+
+
+    Array<Vec<3>,PointIndex::BASE> normals(np);
+
+    for (PointIndex pi = PointIndex::BASE; 
+	 pi < np + PointIndex::BASE; pi++)
+      {
+	if (elementsonnode[pi].Size())
+	  {
+	    Element2d & hel = mesh[elementsonnode[pi][0]];
+	    for (int k = 0; k < 3; k++)
+	      if (hel[k] == pi)
+		{
+		  SelectSurfaceOfPoint (mesh[pi], hel.GeomInfoPi(k+1));
+		  GetNormalVector (surfnr, mesh[pi], hel.GeomInfoPi(k+1), normals[pi]);
+		  break;
+		}
+	  }
+      }
+
+    NgProfiler::StopTimer  (timerstart);
+
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	SurfaceElementIndex sei = seia[i];
+	Element2d & elem = mesh[sei];
+	if (elem.IsDeleted()) continue;
+
+	for (int j = 0; j < 3; j++)
+	  {
+	    PointIndex pi1 = elem[j];
+	    PointIndex pi2 = elem[(j+1) % 3];
+
+	    if (pi1 < PointIndex::BASE || 
+		pi2 < PointIndex::BASE)
+	      continue;
+
+	    /*
+	      INDEX_2 i2(pi1, pi2);
+	      i2.Sort();
+	      if (segmentht.Used(i2))
+	      continue;
+	    */
+
+	    bool debugflag = 0;
+
+	    if (debugflag)
+	      {
+		(*testout) << "Combineimprove, face = " << faceindex 
+			   << "pi1 = " << pi1 << " pi2 = " << pi2 << endl;
+	      }
+
+	    /*
+	    // save version:
+	    if (fixed.Get(pi1) || fixed.Get(pi2)) 
+	    continue;
+	    if (pi2 < pi1) swap (pi1, pi2);
+	    */
+
+	    // more general 
+	    if (fixed[pi2]) 
+	      Swap (pi1, pi2);
+
+	    if (fixed[pi2])  
+	      continue;
+
+	    double loch = mesh.GetH (mesh[pi1]);
+
+	    INDEX_2 si2 (pi1, pi2);
+	    si2.Sort();
+
+	    /*	  
+	      if (edgetested.Used (si2))
+	      continue;
+	      edgetested.Set (si2, 1);
+	    */
+
+	    hasonepi.SetSize(0);
+	    hasbothpi.SetSize(0);
+
+	    for (int k = 0; k < elementsonnode[pi1].Size(); k++)
+	      {
+		const Element2d & el2 = mesh[elementsonnode[pi1][k]];
+
+		if (el2.IsDeleted()) continue;
+
+		if (el2[0] == pi2 || el2[1] == pi2 || el2[2] == pi2)
+		  {
+		    hasbothpi.Append (elementsonnode[pi1][k]);
+		    nv = Cross (Vec3d (mesh[el2[0]], mesh[el2[1]]),
+				Vec3d (mesh[el2[0]], mesh[el2[2]]));
+		  }
+		else
+		  {
+		    hasonepi.Append (elementsonnode[pi1][k]);
+		  }
+	      } 
+
+
+	    Element2d & hel = mesh[hasbothpi[0]];
+	    for (int k = 0; k < 3; k++)
+	      if (hel[k] == pi1)
+		{
+		  SelectSurfaceOfPoint (mesh[pi1],
+					hel.GeomInfoPi(k+1));
+		  GetNormalVector (surfnr, mesh[pi1], hel.GeomInfoPi(k+1), nv);
+		  break;
+		}
+
+	    //	  nv = normals.Get(pi1);
+
+
+
+	    for (int k = 0; k < elementsonnode[pi2].Size(); k++)
+	      {
+		const Element2d & el2 = mesh[elementsonnode[pi2][k]];
+		if (el2.IsDeleted()) continue;
+
+		if (el2[0] == pi1 || el2[1] == pi1 || el2[2] == pi1)
+		  ;
+		else
+		  hasonepi.Append (elementsonnode[pi2][k]);
+	      } 
+
+	    bad1 = 0;
+	    int illegal1 = 0, illegal2 = 0;
+	    for (int k = 0; k < hasonepi.Size(); k++)
+	      {
+		const Element2d & el = mesh[hasonepi[k]];
+		bad1 += CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]],
+					     nv, -1, loch);
+		illegal1 += 1-mesh.LegalTrig(el);
+	      }
+	  
+	    for (int k = 0; k < hasbothpi.Size(); k++)
+	      {
+		const Element2d & el = mesh[hasbothpi[k]];
+		bad1 += CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]],
+					     nv, -1, loch);
+		illegal1 += 1-mesh.LegalTrig(el);
+	      }
+	    bad1 /= (hasonepi.Size()+hasbothpi.Size());
+
+	    MeshPoint p1 = mesh[pi1];
+	    MeshPoint p2 = mesh[pi2];
+
+	    MeshPoint pnew = p1;
+	    mesh[pi1] = pnew;
+	    mesh[pi2] = pnew;
+
+	    bad2 = 0;
+	    for (int k = 0; k < hasonepi.Size(); k++)
+	      {
+		Element2d & el = mesh[hasonepi[k]];
+		double err = 
+		  CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]],
+				       nv, -1, loch);
+		bad2 += err;
+
+		Vec<3> hnv = Cross (Vec3d (mesh[el[0]],
+					   mesh[el[1]]),
+				    Vec3d (mesh[el[0]],
+					   mesh[el[2]]));
+		if (hnv * nv < 0)
+		  bad2 += 1e10;
+              
+		for (int l = 0; l < 3; l++)
+		  if ( (normals[el[l]] * nv) < 0.5)
+		    bad2 += 1e10;
+
+		illegal2 += 1-mesh.LegalTrig(el);
+	      }
+	    bad2 /= hasonepi.Size();
+
+	    mesh[pi1] = p1;
+	    mesh[pi2] = p2;
+	  
+       
+	    if (debugflag)
+	      {
+		(*testout) << "bad1 = " << bad1 << ", bad2 = " << bad2 << endl;
+	      }
+
+
+	    bool should = (bad2 < bad1 && bad2 < 1e4);
+	    if (bad2 < 1e4)
+	      {
+		if (illegal1 > illegal2) should = 1;
+		if (illegal2 > illegal1) should = 0;
+	      }
+	  
+
+	    if (should)
+	      {
+		// (*testout) << "combine !" << endl;
+		// (*testout) << "bad1 = " << bad1 << ", bad2 = " << bad2 << endl;
+
+
+		mesh[pi1] = pnew;
+		PointGeomInfo gi;
+		bool gi_set(false);
+	      
+	      
+		Element2d *el1p(NULL);
+		int l = 0;
+		while(mesh[elementsonnode[pi1][l]].IsDeleted() && l<elementsonnode.EntrySize(pi1)) l++;
+		if(l<elementsonnode.EntrySize(pi1))
+		  el1p = &mesh[elementsonnode[pi1][l]];
+		else
+		  cerr << "OOPS!" << endl;
+
+		for (l = 0; l < el1p->GetNP(); l++)
+		  if ((*el1p)[l] == pi1)
+		    {
+		      gi = el1p->GeomInfoPi (l+1);
+		      gi_set = true;
+		    }
+
+		// (*testout) << "Connect point " << pi2 << " to " << pi1 << "\n";
+		for (int k = 0; k < elementsonnode[pi2].Size(); k++)
+		  {
+		    Element2d & el = mesh[elementsonnode[pi2][k]];
+		    if(el.IsDeleted()) continue;
+		    elementsonnode.Add (pi1, elementsonnode[pi2][k]);
+
+		    bool haspi1 = 0;
+		    for (l = 0; l < el.GetNP(); l++)
+		      if (el[l] == pi1)
+			haspi1 = 1;
+		    if (haspi1) continue;
+
+		    for (int l = 0; l < el.GetNP(); l++)
+		      {
+			if (el[l] == pi2)
+			  {
+			    el[l] = pi1;
+			    el.GeomInfoPi (l+1) = gi;
+			  }
+
+			fixed[el[l]] = true;
+		      }
+		  }
+
+		/*
+		  for (k = 0; k < hasbothpi.Size(); k++)
+		  {
+		  cout << mesh[hasbothpi[k]] << endl;
+		  for (l = 0; l < 3; l++)
+		  cout << mesh[mesh[hasbothpi[k]][l]] << " ";
+		  cout << endl;
+		  }
+		*/
+
+		for (int k = 0; k < hasbothpi.Size(); k++)
+		  {
+		    mesh[hasbothpi[k]].Delete();
+		    /*
+		      for (l = 0; l < 4; l++)
+		      mesh[hasbothpi[k]][l] = PointIndex::BASE-1;
+		    */
+		  }
+
+	      }
+	  }
+      }
+
+    //  mesh.Compress();
+    mesh.SetNextTimeStamp();
+  }
+
+
+  void MeshOptimize2d :: CheckMeshApproximation (Mesh & mesh)
+  {
+    // Check angles between elements and normals at corners
+    /*
+  
+    int i, j;
+    int ne = mesh.GetNSE();
+    int surfnr;
+  
+    Vec3d n, ng;
+    Array<Vec3d> ngs(3);
+
+    (*mycout) << "Check Surface Approxiamtion" << endl;
+    (*testout) << "Check Surface Approxiamtion" << endl;
+
+    for (i = 1; i <= ne; i++)
+    {
+    const Element2d & el = mesh.SurfaceElement(i);
+    surfnr = mesh.GetFaceDescriptor (el.GetIndex()).SurfNr();
+    Vec3d n = Cross (mesh.Point (el.PNum(1)) - mesh.Point (el.PNum(2)),
+    mesh.Point (el.PNum(1)) - mesh.Point (el.PNum(3)));
+    n /= n.Length();
+
+    for (j = 1; j <= el.GetNP(); j++)
+    {
+    SelectSurfaceOfPoint (mesh.Point(el.PNum(j)), el.GeomInfoPi(j));
+    GetNormalVector (surfnr, mesh.Point(el.PNum(j)), ng);
+    ng /= ng.Length();
+    ngs.Elem(j) = ng;
+
+    double angle =  (180.0 / M_PI) * Angle (n, ng);
+    if (angle > 60)
+    {
+    (*testout) << "el " << i << " node " << el.PNum(j)
+    << "has angle = " << angle << endl;
+    }
+    }	
+
+    for (j = 1; j <= 3; j++)
+    {
+    double angle =  (180.0 / M_PI) * Angle (ngs.Get(j), ngs.Get(j%3+1));
+    if (angle > 60)
+    {
+    (*testout) << "el " << i << " node-node " 
+    << ngs.Get(j) << " - " << ngs.Get(j%3+1)
+    << " has angle = " << angle << endl;
+    }
+    }
+    }
+    */
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/improve2.hpp b/contrib/Netgen/libsrc/meshing/improve2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4a6e49ea5ebd757b473fabbbe801e4094ad69d9d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve2.hpp
@@ -0,0 +1,102 @@
+#ifndef FILE_IMPROVE2
+#define FILE_IMPROVE2
+
+
+
+///
+class MeshOptimize2d
+{
+  int faceindex;
+  int improveedges;
+  double metricweight;
+  int writestatus;
+
+public:
+  ///
+  MeshOptimize2d ();
+  ///
+  void ImproveMesh (Mesh & mesh2d, const MeshingParameters & mp);
+  void ImproveMeshJacobian (Mesh & mesh2d, const MeshingParameters & mp);
+  void ImproveVolumeMesh (Mesh & mesh);
+  void ProjectBoundaryPoints(Array<int> & surfaceindex, 
+			     const Array<Point<3>* > & from, Array<Point<3>* > & dest);
+
+  void EdgeSwapping (Mesh & mesh, int usemetric);
+  void CombineImprove (Mesh & mesh);
+
+  void GenericImprove (Mesh & mesh);
+
+
+  void SetFaceIndex (int fi) { faceindex = fi; }
+  void SetImproveEdges (int ie) { improveedges = ie; }
+  void SetMetricWeight (double mw) { metricweight = mw; }
+  void SetWriteStatus (int ws) { writestatus = ws; }
+
+
+
+  ///
+  virtual void SelectSurfaceOfPoint (const Point<3> & p,
+				     const PointGeomInfo & gi);
+  ///
+  virtual void ProjectPoint (INDEX /* surfind */, Point<3> & /* p */) const { };
+
+  /// project point, use gi as initial value, and compute new gi
+  virtual int ProjectPointGI (INDEX surfind, Point<3> & p, PointGeomInfo & gi) const 
+  { ProjectPoint (surfind, p); return CalcPointGeomInfo (surfind, gi, p); }
+
+  ///
+  virtual void ProjectPoint2 (INDEX /* surfind */, INDEX /* surfind2 */, Point<3> & /* p */) const { };
+
+  /// liefert zu einem 3d-Punkt die geominfo (Dreieck) und liefert 1, wenn erfolgreich, 
+  /// 0, wenn nicht (Punkt ausserhalb von chart)
+  virtual int CalcPointGeomInfo(PointGeomInfo& gi, const Point<3> & /*p3*/) const
+    { gi.trignum = 1; return 1;};
+
+  virtual int CalcPointGeomInfo(int /* surfind */, PointGeomInfo& gi, const Point<3> & p3) const
+    { return CalcPointGeomInfo (gi, p3); }
+
+  ///
+  virtual void GetNormalVector(INDEX surfind, const Point<3>  & p, PointGeomInfo & gi, Vec<3> & n) const;
+  virtual void GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const;
+
+  void CheckMeshApproximation (Mesh & mesh);
+
+
+  ///
+  friend class Opti2SurfaceMinFunction;
+  ///
+  friend class Opti2EdgeMinFunction;
+  ///
+  friend double Opti2FunctionValueGrad (const Vector & x, Vector & grad);
+  ///
+  friend double Opti2EdgeFunctionValueGrad (const Vector & x, Vector & grad);
+
+
+
+};
+
+
+extern void CalcTriangleBadness (double x2, double x3, double y3, 
+				 double metricweight,
+				 double h, double & badness, 
+				 double & g1x, double & g1y);
+
+
+
+
+extern double CalcTriangleBadness (const Point3d & p1, 
+				   const Point3d & p2, 
+				   const Point3d & p3,
+				   double metricweight,
+				   double h);
+
+extern double CalcTriangleBadness (const Point3d & p1, 
+				   const Point3d & p2, 
+				   const Point3d & p3,
+				   const Vec3d & n,
+				   double metricweight,
+				   double h);
+
+#endif
+
+
diff --git a/contrib/Netgen/libsrc/meshing/improve2gen.cpp b/contrib/Netgen/libsrc/meshing/improve2gen.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e5b65308a40b012add0c28deb0a70ab63fae9ea9
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve2gen.cpp
@@ -0,0 +1,455 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+#include <opti.hpp>
+
+namespace netgen
+{
+
+  class ImprovementRule
+  {
+  public:
+    Array<Element2d> oldels;
+    Array<Element2d> newels;
+    Array<INDEX_2> deledges;
+    Array<int> incelsonnode;
+    Array<int> reused;
+    int bonus;
+    int onp;
+  };
+
+
+  void MeshOptimize2d :: GenericImprove (Mesh & mesh)
+  {
+    if (!faceindex)
+      {
+	if (writestatus)
+	  PrintMessage (3, "Generic Improve");
+
+	for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++)
+	  GenericImprove (mesh);
+      
+	faceindex = 0;
+      }
+
+    // int j, k, l, ri;
+    int np = mesh.GetNP();
+    int ne = mesh.GetNSE();
+    //    SurfaceElementIndex sei;
+
+    
+//     for (SurfaceElementIndex sei = 0; sei < ne; sei++)
+//       {
+// 	const Element2d & el = mesh[sei];
+// 	(*testout) << "element " << sei << ": " <<flush;
+// 	for(int j=0; j<el.GetNP(); j++)
+// 	  (*testout) << el[j] << " " << flush;
+// 	(*testout) << "IsDeleted() " << el.IsDeleted()<< endl;
+//       }
+
+    bool ok;
+    int olddef, newdef;
+
+    Array<ImprovementRule*> rules;
+    Array<SurfaceElementIndex> elmap;
+    Array<int> elrot;
+    Array<PointIndex> pmap;
+    Array<PointGeomInfo> pgi;
+
+    int surfnr = mesh.GetFaceDescriptor (faceindex).SurfNr();
+  
+    ImprovementRule * r1;
+
+    // 2 triangles to quad
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3));
+    r1->oldels.Append (Element2d (3, 2, 4));
+    r1->newels.Append (Element2d (1, 2, 4, 3));
+    r1->deledges.Append (INDEX_2 (2,3));
+    r1->onp = 4;
+    r1->bonus = 2;
+    rules.Append (r1);
+
+    // 2 quad to 1 quad
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (4, 3, 2, 5));
+    r1->newels.Append (Element2d (1, 2, 5, 4));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->deledges.Append (INDEX_2 (3, 4));
+    r1->onp = 5;
+    r1->bonus = 0;
+    rules.Append (r1);
+
+    // swap quads
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (3, 2, 5, 6));
+    r1->newels.Append (Element2d (1, 6, 3, 4));
+    r1->newels.Append (Element2d (1, 2, 5, 6));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->onp = 6;
+    r1->bonus = 0;
+    rules.Append (r1);
+
+    // three quads to 2
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (2, 5, 6, 3));
+    r1->oldels.Append (Element2d (3, 6, 7, 4));
+    r1->newels.Append (Element2d (1, 2, 5, 4));
+    r1->newels.Append (Element2d (4, 5, 6, 7));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->deledges.Append (INDEX_2 (3, 4));
+    r1->deledges.Append (INDEX_2 (3, 6));
+    r1->onp = 7;
+    r1->bonus = -1;
+    rules.Append (r1);
+
+    // quad + 2 connected trigs to quad
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (2, 5, 3));
+    r1->oldels.Append (Element2d (3, 5, 4));
+    r1->newels.Append (Element2d (1, 2, 5, 4));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->deledges.Append (INDEX_2 (3, 4));
+    r1->deledges.Append (INDEX_2 (3, 5));
+    r1->onp = 5;
+    r1->bonus = 0;
+    rules.Append (r1);
+
+    // quad + 2 non-connected trigs to quad (a and b)
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (2, 6, 3));
+    r1->oldels.Append (Element2d (1, 4, 5));
+    r1->newels.Append (Element2d (1, 3, 4, 5));
+    r1->newels.Append (Element2d (1, 2, 6, 3));
+    r1->deledges.Append (INDEX_2 (1, 4));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->onp = 6;
+    r1->bonus = 0;
+    rules.Append (r1);
+
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (2, 6, 3));
+    r1->oldels.Append (Element2d (1, 4, 5));
+    r1->newels.Append (Element2d (1, 2, 4, 5));
+    r1->newels.Append (Element2d (4, 2, 6, 3));
+    r1->deledges.Append (INDEX_2 (1, 4));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->onp = 6;
+    r1->bonus = 0;
+    rules.Append (r1);
+
+    // two quad + trig -> one quad + trig
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (2, 5, 6, 3));
+    r1->oldels.Append (Element2d (4, 3, 6));
+    r1->newels.Append (Element2d (1, 2, 6, 4));
+    r1->newels.Append (Element2d (2, 5, 6));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->deledges.Append (INDEX_2 (3, 4));
+    r1->deledges.Append (INDEX_2 (3, 6));
+    r1->onp = 6;
+    r1->bonus = -1;
+    rules.Append (r1);
+
+    // swap quad + trig (a and b)
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (2, 5, 3));
+    r1->newels.Append (Element2d (2, 5, 3, 4));
+    r1->newels.Append (Element2d (1, 2, 4));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->onp = 5;
+    r1->bonus = 0;
+    rules.Append (r1);
+
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (2, 5, 3));
+    r1->newels.Append (Element2d (1, 2, 5, 3));
+    r1->newels.Append (Element2d (1, 3, 4));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->onp = 5;
+    r1->bonus = 0;
+    rules.Append (r1);
+
+
+    // 2 quads to quad + 2 trigs
+    r1 = new ImprovementRule;
+    r1->oldels.Append (Element2d (1, 2, 3, 4));
+    r1->oldels.Append (Element2d (3, 2, 5, 6));
+    r1->newels.Append (Element2d (1, 5, 6, 4));
+    r1->newels.Append (Element2d (1, 2, 5));
+    r1->newels.Append (Element2d (4, 6, 3));
+    r1->deledges.Append (INDEX_2 (2, 3));
+    r1->onp = 6;
+    r1->bonus = 0;
+    //    rules.Append (r1);
+
+
+
+
+    Array<int> mapped(rules.Size());
+    Array<int> used(rules.Size());
+    used = 0;
+    mapped = 0;
+
+  
+
+    for (int ri = 0; ri < rules.Size(); ri++)
+      {
+	ImprovementRule & rule = *rules[ri];
+	rule.incelsonnode.SetSize (rule.onp);
+	rule.reused.SetSize (rule.onp);
+
+	for (int j = 1; j <= rule.onp; j++)
+	  {
+	    rule.incelsonnode.Elem(j) = 0;
+	    rule.reused.Elem(j) = 0;
+	  }
+
+	for (int j = 1; j <= rule.oldels.Size(); j++)
+	  {
+	    const Element2d & el = rule.oldels.Elem(j);
+	    for (int k = 1; k <= el.GetNP(); k++)
+	      rule.incelsonnode.Elem(el.PNum(k))--;
+	  }
+
+	for (int j = 1; j <= rule.newels.Size(); j++)
+	  {
+	    const Element2d & el = rule.newels.Elem(j);
+	    for (int k = 1; k <= el.GetNP(); k++)
+	      {
+		rule.incelsonnode.Elem(el.PNum(k))++;
+		rule.reused.Elem(el.PNum(k)) = 1;
+	      }
+	  }
+      }
+
+
+
+  
+    TABLE<int,PointIndex::BASE> elonnode(np);
+    Array<int,PointIndex::BASE> nelonnode(np);
+    TABLE<SurfaceElementIndex> nbels(ne);
+
+    nelonnode = -4;
+
+    for (SurfaceElementIndex sei = 0; sei < ne; sei++)
+      {
+	const Element2d & el = mesh[sei];
+
+	if (el.GetIndex() == faceindex && !el.IsDeleted())
+	  {
+	    for (int j = 0; j < el.GetNP(); j++)
+	      elonnode.Add (el[j], sei);
+	  }
+	if(!el.IsDeleted())
+	  {
+	    for (int j = 0; j < el.GetNP(); j++)
+	      nelonnode[el[j]]++;
+	  }
+      }
+
+    for (SurfaceElementIndex sei = 0; sei < ne; sei++)
+      {
+	const Element2d & el = mesh[sei];
+	if (el.GetIndex() == faceindex && !el.IsDeleted())
+	  {
+	    for (int j = 0; j < el.GetNP(); j++)
+	      {
+		for (int k = 0; k < elonnode[el[j]].Size(); k++)
+		  {
+		    int nbel = elonnode[el[j]] [k];
+		    bool inuse = false;
+		    for (int l = 0; l < nbels[sei].Size(); l++)
+		      if (nbels[sei][l] == nbel)
+			inuse = true;
+		    if (!inuse)
+		      nbels.Add (sei, nbel);
+		  }
+	      }
+	  }
+      }
+
+
+    for (int ri = 0; ri < rules.Size(); ri++)
+      {
+	const ImprovementRule & rule = *rules[ri];
+      
+	elmap.SetSize (rule.oldels.Size());
+	elrot.SetSize (rule.oldels.Size());
+	pmap.SetSize (rule.onp);
+	pgi.SetSize (rule.onp);
+
+
+	for (SurfaceElementIndex sei = 0; sei < ne; sei++)
+	  {
+	    if (multithread.terminate)
+	      break;
+	    if (mesh[sei].IsDeleted()) continue;
+
+	    elmap[0] = sei;
+	    FlatArray<SurfaceElementIndex> neighbours = nbels[sei];
+	    
+	    for (elrot[0] = 0; elrot[0] < mesh[sei].GetNP(); elrot[0]++)
+	      {
+		const Element2d & el0 = mesh[sei];
+		const Element2d & rel0 = rule.oldels[0];
+
+		if (el0.GetIndex() != faceindex) continue;
+		if (el0.IsDeleted()) continue;
+		if (el0.GetNP() != rel0.GetNP()) continue;
+
+
+		pmap = -1;
+ 
+		for (int k = 0; k < el0.GetNP(); k++)
+		  {
+		    pmap.Elem(rel0[k]) = el0.PNumMod(k+elrot[0]+1);
+		    pgi.Elem(rel0[k]) = el0.GeomInfoPiMod(k+elrot[0]+1);
+		  }
+		
+		ok = 1;
+		for (int i = 1; i < elmap.Size(); i++)
+		  {
+		    // try to find a mapping for reference-element i
+
+		    const Element2d & rel = rule.oldels[i];
+		    bool possible = 0;
+
+		    for (elmap[i] = 0; elmap[i] < neighbours.Size(); elmap[i]++)
+		      {
+			const Element2d & el = mesh[neighbours[elmap[i]]];
+			if (el.IsDeleted()) continue;
+			if (el.GetNP() != rel.GetNP()) continue;
+
+			for (elrot[i] = 0; elrot[i] < rel.GetNP(); elrot[i]++)
+			  {
+			    possible = 1;
+
+			    for (int k = 0; k < rel.GetNP(); k++)
+			      if (pmap.Elem(rel[k]) != -1 &&
+				  pmap.Elem(rel[k]) != el.PNumMod (k+elrot[i]+1))
+				possible = 0;
+
+			    if (possible) 
+			      {
+				for (int k = 0; k < el.GetNP(); k++)
+				  {
+				    pmap.Elem(rel[k]) = el.PNumMod(k+elrot[i]+1);
+				    pgi.Elem(rel[k]) = el.GeomInfoPiMod(k+elrot[i]+1);
+				  }
+				break;
+			      }
+			  }
+			if (possible) break;
+		      }
+
+		    if (!possible) 
+		      {
+			ok = 0;
+			break;
+		      }
+
+		    elmap[i] = neighbours[elmap[i]];
+		  }
+
+		for(int i=0; ok && i<rule.deledges.Size(); i++)
+		  {
+		    ok = !mesh.IsSegment(pmap.Elem(rule.deledges[i].I1()),
+					 pmap.Elem(rule.deledges[i].I2()));
+		  }
+								    
+								    
+		
+		
+		if (!ok) continue;
+
+		mapped[ri]++;
+
+		olddef = 0;
+		for (int j = 1; j <= pmap.Size(); j++)
+		  olddef += sqr (nelonnode[pmap.Get(j)]);
+		olddef += rule.bonus;
+
+		newdef = 0;
+		for (int j = 1; j <= pmap.Size(); j++)
+		  if (rule.reused.Get(j))
+		    newdef += sqr (nelonnode[pmap.Get(j)] + 
+				   rule.incelsonnode.Get(j));
+
+		if (newdef > olddef)
+		  continue;
+
+		// calc metric badness
+		double bad1 = 0, bad2 = 0;
+		Vec<3> n;
+
+		SelectSurfaceOfPoint (mesh.Point(pmap.Get(1)), pgi.Get(1));
+		GetNormalVector (surfnr, mesh.Point(pmap.Get(1)), pgi.Elem(1), n);
+		  
+		for (int j = 1; j <= rule.oldels.Size(); j++)
+		  bad1 += mesh.SurfaceElement(elmap.Get(j)).CalcJacobianBadness (mesh.Points(), n);
+		  
+		// check new element:
+		for (int j = 1; j <= rule.newels.Size(); j++)
+		  {
+		    const Element2d & rnel = rule.newels.Get(j);
+		    Element2d nel(rnel.GetNP());
+		    for (int k = 1; k <= rnel.GetNP(); k++)
+		      nel.PNum(k) = pmap.Get(rnel.PNum(k));
+
+		    bad2 += nel.CalcJacobianBadness (mesh.Points(), n);
+		  }
+
+		if (bad2 > 1e3) continue;
+
+		if (newdef == olddef && bad2 > bad1) continue;
+		  
+
+		// generate new element:
+		for (int j = 1; j <= rule.newels.Size(); j++)
+		  {
+		    const Element2d & rnel = rule.newels.Get(j);
+		    Element2d nel(rnel.GetNP());
+		    nel.SetIndex (faceindex);
+		    for (int k = 1; k <= rnel.GetNP(); k++)
+		      {
+			nel.PNum(k) = pmap.Get(rnel.PNum(k));
+			nel.GeomInfoPi(k) = pgi.Get(rnel.PNum(k));
+		      }
+		      
+		    mesh.AddSurfaceElement(nel);
+		  }
+		  
+		for (int j = 0; j < rule.oldels.Size(); j++)
+		  mesh.DeleteSurfaceElement ( elmap[j] );
+
+		for (int j = 1; j <= pmap.Size(); j++)
+		  nelonnode[pmap.Get(j)] += rule.incelsonnode.Get(j);
+
+		used[ri]++;
+	      }
+	  }
+      }
+
+    mesh.Compress();
+
+    for (int ri = 0; ri < rules.Size(); ri++)
+      {
+	PrintMessage (5, "rule ", ri+1, " ",
+		      mapped[ri], "/", used[ri], " mapped/used");
+      }
+  }
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/improve3.cpp b/contrib/Netgen/libsrc/meshing/improve3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a236aa2014fed690a35ded07da97b46238602b4
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve3.cpp
@@ -0,0 +1,2779 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+
+#ifdef SOLIDGEOM
+#include <csg.hpp>
+#endif
+#include <opti.hpp>
+
+namespace netgen
+{
+
+/*
+  Combine two points to one.
+  Set new point into the center, if both are
+  inner points.
+  Connect inner point to boundary point, if one
+  point is inner point.
+*/
+
+void MeshOptimize3d :: CombineImprove (Mesh & mesh,
+				       OPTIMIZEGOAL goal)
+{
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+
+  TABLE<ElementIndex, PointIndex::BASE> elementsonnode(np); 
+  Array<ElementIndex> hasonepi, hasbothpi;
+
+  Array<double> oneperr;
+  Array<double> elerrs (ne);
+
+  PrintMessage (3, "CombineImprove");
+  (*testout)  << "Start CombineImprove" << "\n";
+
+  //  mesh.CalcSurfacesOfNode ();
+  const char * savetask = multithread.task;
+  multithread.task = "Combine Improve";
+
+
+  double totalbad = 0;
+  for (ElementIndex ei = 0; ei < ne; ei++)
+    {
+      double elerr = CalcBad (mesh.Points(), mesh[ei], 0);
+      totalbad += elerr;
+      elerrs[ei] = elerr;
+    }
+
+  if (goal == OPT_QUALITY)
+    {
+      totalbad = CalcTotalBad (mesh.Points(), mesh.VolumeElements());
+      (*testout) << "Total badness = " << totalbad << endl;
+      PrintMessage (5, "Total badness = ", totalbad);
+    }
+
+  for (ElementIndex ei = 0; ei < ne; ei++)
+    if (!mesh[ei].IsDeleted())
+      for (int j = 0; j < mesh[ei].GetNP(); j++)
+	elementsonnode.Add (mesh[ei][j], ei);
+  
+  INDEX_2_HASHTABLE<int> edgetested (np+1);
+
+  int cnt = 0;
+
+  for (ElementIndex ei = 0; ei < ne; ei++)
+    {
+      if (multithread.terminate)
+	break;
+      
+      multithread.percent = 100.0 * (ei+1) / ne;
+
+      if (mesh.ElementType(ei) == FIXEDELEMENT)
+	continue;
+
+      for (int j = 0; j < 6; j++)
+	{
+	  Element & elemi = mesh[ei];
+	  if (elemi.IsDeleted()) continue;
+	  
+	  static const int tetedges[6][2] =
+	    { { 0, 1 }, { 0, 2 }, { 0, 3 },
+	      { 1, 2 }, { 1, 3 }, { 2, 3 } };
+
+	  PointIndex pi1 = elemi[tetedges[j][0]];
+	  PointIndex pi2 = elemi[tetedges[j][1]];
+
+	  if (pi2 < pi1) Swap (pi1, pi2);
+	  
+	  INDEX_2 si2 (pi1, pi2);
+	  si2.Sort();
+	  
+	  if (edgetested.Used (si2)) continue;
+	  edgetested.Set (si2, 1);
+
+
+	  // hasonepoint.SetSize(0);
+	  //	  hasbothpoints.SetSize(0);
+	  hasonepi.SetSize(0);
+	  hasbothpi.SetSize(0);
+
+	  FlatArray<ElementIndex> row1 = elementsonnode[pi1];
+	  for (int k = 0; k < row1.Size(); k++)
+	    {
+	      Element & elem = mesh[row1[k]];
+	      if (elem.IsDeleted()) continue;
+
+	      if (elem[0] == pi2 || elem[1] == pi2 ||
+		  elem[2] == pi2 || elem[3] == pi2)
+		{
+		  hasbothpi.Append (row1[k]);
+		}
+	      else
+		{
+		  hasonepi.Append (row1[k]);
+		}
+	    } 
+	  
+	  FlatArray<ElementIndex> row2 = elementsonnode[pi2];	  
+	  for (int k = 0; k < row2.Size(); k++)
+	    {
+	      Element & elem = mesh[row2[k]];
+	      if (elem.IsDeleted()) continue;
+
+	      if (elem[0] == pi1 || elem[1] == pi1 ||
+		  elem[2] == pi1 || elem[3] == pi1)
+		;
+	      else
+		{
+		  hasonepi.Append (row2[k]);
+		}
+	    } 
+	  
+	  double bad1 = 0;
+	  for (int k = 0; k < hasonepi.Size(); k++)
+	    bad1 += elerrs[hasonepi[k]];
+	  for (int k = 0; k < hasbothpi.Size(); k++)
+	    bad1 += elerrs[hasbothpi[k]];
+	  
+	  MeshPoint p1 = mesh[pi1];
+	  MeshPoint p2 = mesh[pi2];
+	  
+
+	  // if (mesh.PointType(pi2) != INNERPOINT)
+	  if (p2.Type() != INNERPOINT)
+	    continue;
+	  
+	  MeshPoint pnew;
+	  // if (mesh.PointType(pi1) != INNERPOINT)
+	  if (p1.Type() != INNERPOINT)
+	    pnew = p1;
+	  else
+	    pnew = Center (p1, p2);
+	    
+	  mesh[pi1] = pnew;
+	  mesh[pi2] = pnew;
+
+	  oneperr.SetSize (hasonepi.Size());
+
+	  double bad2 = 0;
+	  for (int k = 0; k < hasonepi.Size(); k++)
+	    {
+	      const Element & elem = mesh[hasonepi[k]];
+	      double err = CalcBad (mesh.Points(), elem, 0);
+	      // CalcTetBadness (mesh[elem[0]], mesh[elem[1]],  
+	      // mesh[elem[2]], mesh[elem[3]], 0, mparam);
+	      bad2 += err;
+	      oneperr[k] = err;
+	    }
+	  
+	  mesh[pi1] = p1;
+	  mesh[pi2] = p2;
+
+	  // if (mesh.PointType(pi1) != INNERPOINT)
+	  if (p1.Type() != INNERPOINT)
+	    {
+	      for (int k = 0; k < hasonepi.Size(); k++)
+		{
+		  Element & elem = mesh[hasonepi[k]];
+		  int l;
+		  for (l = 0; l < 4; l++)
+		    if (elem[l] == pi2)
+		      {
+			elem[l] = pi1;
+			break;
+		      }
+		      
+		  elem.flags.illegal_valid = 0;
+		  if (!mesh.LegalTet(elem))
+		    bad2 += 1e4;
+		  
+		  if (l < 4)
+		    {
+		      elem.flags.illegal_valid = 0;
+		      elem[l] = pi2;
+		    }
+		}
+	    }
+
+	  if (bad2 / hasonepi.Size()  < 
+	      bad1 / (hasonepi.Size()+hasbothpi.Size()))
+	    {
+	      mesh[pi1] = pnew;
+	      cnt++;
+
+	      FlatArray<ElementIndex> row = elementsonnode[pi2];
+	      for (int k = 0; k < row.Size(); k++)
+		{
+		  Element & elem = mesh[row[k]];
+		  if (elem.IsDeleted()) continue;
+
+		  elementsonnode.Add (pi1, row[k]);
+		  for (int l = 0; l < elem.GetNP(); l++)
+		    if (elem[l] == pi2)
+		      elem[l] = pi1;
+		  
+		  elem.flags.illegal_valid = 0;
+		  if (!mesh.LegalTet (elem))
+		    (*testout) << "illegal tet " << elementsonnode[pi2][k] << endl;
+		}
+
+	      for (int k = 0; k < hasonepi.Size(); k++)
+		elerrs[hasonepi[k]] = oneperr[k];
+	      
+	      for (int k = 0; k < hasbothpi.Size(); k++)
+		{
+		  mesh[hasbothpi[k]].flags.illegal_valid = 0;
+		  mesh[hasbothpi[k]].Delete();
+		}
+	    }
+	}
+    }
+
+  mesh.Compress();
+  mesh.MarkIllegalElements();
+
+  PrintMessage (5, cnt, " elements combined");
+  (*testout) << "CombineImprove done" << "\n";
+
+  totalbad = 0;
+  for (ElementIndex ei = 0; ei < mesh.GetNE(); ei++)
+    totalbad += CalcBad (mesh.Points(), mesh[ei], 0);
+
+  if (goal == OPT_QUALITY)
+    {
+      totalbad = CalcTotalBad (mesh.Points(), mesh.VolumeElements());
+      (*testout) << "Total badness = " << totalbad << endl;
+
+      int cntill = 0;
+      for (ElementIndex ei = 0; ei < ne; ei++)
+	if (!mesh.LegalTet (mesh[ei]))
+	  cntill++;
+
+      PrintMessage (5, cntill, " illegal tets");
+    }
+  multithread.task = savetask;
+} 
+
+
+
+
+
+/*
+  Mesh improvement by edge splitting.
+  If mesh quality is improved by inserting a node into an inner edge,
+  the edge is split into two parts.
+*/
+void MeshOptimize3d :: SplitImprove (Mesh & mesh,
+				     OPTIMIZEGOAL goal)
+{
+  int j, k, l;
+  Point3d p1, p2, pnew;
+
+  ElementIndex ei;
+  SurfaceElementIndex sei;
+  PointIndex pi1, pi2;
+
+  double bad1, bad2, badmax, badlimit;
+
+
+  int cnt = 0;
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+
+  TABLE<ElementIndex,PointIndex::BASE> elementsonnode(np); 
+  Array<ElementIndex> hasbothpoints;
+
+  BitArray origpoint(np), boundp(np);
+  origpoint.Set();
+
+  Array<double> elerrs(ne);
+  BitArray illegaltet(ne);
+  illegaltet.Clear();
+
+  const char * savetask = multithread.task;
+  multithread.task = "Split Improve";
+
+
+  PrintMessage (3, "SplitImprove");
+  (*testout)  << "start SplitImprove" << "\n";
+
+  Array<INDEX_3> locfaces;
+
+  INDEX_2_HASHTABLE<int> edgetested (np);
+
+  bad1 = 0;
+  badmax = 0;
+  for (ei = 0; ei < ne; ei++)
+    {
+      elerrs[ei] = CalcBad (mesh.Points(), mesh[ei], 0);
+      bad1 += elerrs[ei];
+      if (elerrs[ei] > badmax) badmax = elerrs[ei];
+    }
+
+  PrintMessage (5, "badmax = ", badmax);
+  badlimit = 0.5 * badmax;
+
+
+  boundp.Clear();
+  for (sei = 0; sei < mesh.GetNSE(); sei++)
+    for (j = 0; j < 3; j++)
+      boundp.Set (mesh[sei][j]);
+
+  if (goal == OPT_QUALITY)
+    {
+      bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements());
+      (*testout) << "Total badness = " << bad1 << endl;
+    }
+
+  for (ei = 0; ei < ne; ei++)
+    for (j = 0; j < mesh[ei].GetNP(); j++)
+      elementsonnode.Add (mesh[ei][j], ei);
+
+
+  mesh.MarkIllegalElements();
+  if (goal == OPT_QUALITY || goal == OPT_LEGAL)
+    {
+      int cntill = 0;
+      for (ei = 0; ei < ne; ei++)
+	{
+	  //	  if (!LegalTet (volelements.Get(i)))
+	  if (mesh[ei].flags.illegal)
+	    {
+	      cntill++;
+	      illegaltet.Set (ei+1);
+	    }
+	}
+      //      (*mycout) << cntill << " illegal tets" << endl;
+    }
+
+
+  for (ei = 0; ei < ne; ei++)
+    {
+      if (multithread.terminate)
+	break;
+
+      multithread.percent = 100.0 * (ei+1) / ne;
+
+      bool ltestmode = 0;
+
+
+      if (elerrs[ei] < badlimit && !illegaltet.Test(ei+1)) continue;
+
+      if ((goal == OPT_LEGAL) &&
+	  !illegaltet.Test(ei+1) &&
+	  CalcBad (mesh.Points(), mesh[ei], 0) < 1e3) 
+	continue;
+
+      
+      Element & elem = mesh[ei];
+
+      if (ltestmode)
+	{
+	  (*testout) << "test el " << ei << endl;
+	  for (j = 0; j < 4; j++)
+	    (*testout) << elem[j] << " ";
+	  (*testout) << endl;
+	}
+
+
+      for (j = 0; j < 6; j++)
+	{
+
+	  static const int tetedges[6][2] =
+	    { { 0, 1 }, { 0, 2 }, { 0, 3 },
+	      { 1, 2 }, { 1, 3 }, { 2, 3 } };
+
+	  pi1 = elem[tetedges[j][0]];
+	  pi2 = elem[tetedges[j][1]];
+
+	  if (pi2 < pi1) Swap (pi1, pi2);
+	  if (pi2 > elementsonnode.Size()) continue;
+
+	  if (!origpoint.Test(pi1) || !origpoint.Test(pi2))
+	    continue;
+
+  
+	  INDEX_2 i2(pi1, pi2);
+	  i2.Sort();
+
+	  if (mesh.BoundaryEdge (pi1, pi2)) continue;
+
+	  if (edgetested.Used (i2) && !illegaltet.Test(ei+1)) continue;
+	  edgetested.Set (i2, 1);
+
+	  hasbothpoints.SetSize (0);
+	  for (k = 1; k <= elementsonnode.EntrySize(pi1); k++)
+	    {
+	      bool has1 = 0, has2 = 0;
+
+	      ElementIndex elnr = elementsonnode.Get(pi1, k);
+	      Element & el = mesh[elnr];
+
+	      for (l = 0; l < el.GetNP(); l++)
+		{
+		  if (el[l] == pi1) has1 = 1;
+		  if (el[l] == pi2) has2 = 1;
+		}
+	      if (has1 && has2) 
+		{ // only once
+		  for (l = 0; l < hasbothpoints.Size(); l++)
+		    if (hasbothpoints[l] == elnr)
+		      has1 = 0;
+		  
+		  if (has1)
+		    hasbothpoints.Append (elnr);
+		}
+	    }
+	  
+	  bad1 = 0;
+	  for (k = 0; k < hasbothpoints.Size(); k++)
+	    bad1 += CalcBad (mesh.Points(), mesh[hasbothpoints[k]], 0);
+	  
+
+	  bool puretet = 1;
+	  for (k = 0; k < hasbothpoints.Size(); k++)
+	    if (mesh[hasbothpoints[k]].GetType() != TET)
+	      puretet = 0;
+	  if (!puretet) continue;
+
+	  p1 = mesh[pi1];
+	  p2 = mesh[pi2];
+
+	  /*
+	    pnew = Center (p1, p2);
+	  
+	    points.Elem(pi1) = pnew;
+	    bad2 = 0;
+	    for (k = 1; k <= hasbothpoints.Size(); k++)
+	    bad2 += CalcBad (points, 
+	    volelements.Get(hasbothpoints.Get(k)), 0);
+
+	    points.Elem(pi1) = p1;
+	    points.Elem(pi2) = pnew;
+	      
+	    for (k = 1; k <= hasbothpoints.Size(); k++)
+	    bad2 += CalcBad (points, 
+	    volelements.Get(hasbothpoints.Get(k)), 0);
+	    points.Elem(pi2) = p2;
+	  */
+
+
+	  locfaces.SetSize (0);
+	  for (k = 0; k < hasbothpoints.Size(); k++)
+	    {
+	      const Element & el = mesh[hasbothpoints[k]];
+
+	      for (l = 0; l < 4; l++)
+		if (el[l] == pi1 || el[l] == pi2)
+		  {
+		    INDEX_3 i3;
+		    Element2d face;
+		    el.GetFace (l+1, face);
+		    for (int kk = 1; kk <= 3; kk++)
+		      i3.I(kk) = face.PNum(kk);
+		    locfaces.Append (i3);
+		  }
+	    }
+
+	  PointFunction1 pf (mesh.Points(), locfaces, mp, -1);
+	  OptiParameters par;
+	  par.maxit_linsearch = 50;
+	  par.maxit_bfgs = 20;
+
+	  pnew = Center (p1, p2);
+	  Vector px(3);
+	  px(0) = pnew.X();
+	  px(1) = pnew.Y();
+	  px(2) = pnew.Z();
+
+	  if (elerrs[ei] > 0.1 * badmax)
+	    BFGS (px, pf, par);
+
+	  bad2 = pf.Func (px);
+
+	  pnew.X() = px(0);
+	  pnew.Y() = px(1);
+	  pnew.Z() = px(2);
+
+
+	  int hpinew = mesh.AddPoint (pnew);
+	  //	  ptyps.Append (INNERPOINT);
+
+	  for (k = 0; k < hasbothpoints.Size(); k++)
+	    {
+	      Element & oldel = mesh[hasbothpoints[k]];
+	      Element newel1 = oldel;
+	      Element newel2 = oldel;
+
+	      oldel.flags.illegal_valid = 0;
+	      newel1.flags.illegal_valid = 0;
+	      newel2.flags.illegal_valid = 0;
+	      
+	      for (l = 0; l < 4; l++)
+		{
+		  if (newel1[l] == pi2) newel1[l] = hpinew;
+		  if (newel2[l] == pi1) newel2[l] = hpinew;
+		}
+	      
+	      if (!mesh.LegalTet (oldel)) bad1 += 1e6;
+	      if (!mesh.LegalTet (newel1)) bad2 += 1e6;
+	      if (!mesh.LegalTet (newel2)) bad2 += 1e6;
+	    }	  
+	  
+	  // mesh.PointTypes().DeleteLast();
+	  mesh.Points().DeleteLast();
+
+	  if (bad2 < bad1) 
+	    /*	      (bad1 > 1e4 && boundp.Test(pi1) && boundp.Test(pi2)) */ 
+	    {
+	      cnt++;
+
+	      PointIndex pinew = mesh.AddPoint (pnew);
+	      
+	      for (k = 0; k < hasbothpoints.Size(); k++)
+		{
+		  Element & oldel = mesh[hasbothpoints[k]];
+		  Element newel = oldel;
+
+		  newel.flags.illegal_valid = 0;
+		  oldel.flags.illegal_valid = 0;
+
+		  for (l = 0; l < 4; l++)
+		    {
+		      origpoint.Clear (oldel[l]);
+
+		      if (oldel[l] == pi2) oldel[l] = pinew;
+		      if (newel[l] == pi1) newel[l] = pinew;
+		    }
+		  mesh.AddVolumeElement (newel);
+		}
+	      
+	      j = 10;
+	    }
+	}
+    }
+
+
+  mesh.Compress();
+  PrintMessage (5, cnt, " splits performed");
+
+  (*testout) << "Splitt - Improve done" << "\n";
+
+  if (goal == OPT_QUALITY)
+    {
+      bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements());
+      (*testout) << "Total badness = " << bad1 << endl;
+
+      int cntill = 0;
+      ne = mesh.GetNE();
+      for (ei = 0; ei < ne; ei++)
+	{
+	  if (!mesh.LegalTet (mesh[ei]))
+	    cntill++;
+	}
+      //      cout << cntill << " illegal tets" << endl;
+    }
+
+  multithread.task = savetask;
+}
+
+
+      
+  
+
+void MeshOptimize3d :: SwapImprove (Mesh & mesh, OPTIMIZEGOAL goal,
+				    const BitArray * working_elements)
+{
+  PointIndex pi1(0), pi2(0), pi3(0), pi4(0), pi5(0), pi6(0);
+  int cnt = 0;
+
+  Element el21(TET), el22(TET), el31(TET), el32(TET), el33(TET);
+  Element el1(TET), el2(TET), el3(TET), el4(TET);
+  Element el1b(TET), el2b(TET), el3b(TET), el4b(TET);
+  
+  double bad1, bad2, bad3;
+
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+  
+  // contains at least all elements at node
+  TABLE<ElementIndex,PointIndex::BASE> elementsonnode(np);
+
+  Array<ElementIndex> hasbothpoints;
+
+  PrintMessage (3, "SwapImprove ");
+  (*testout) << "\n" << "Start SwapImprove" << endl;
+
+  const char * savetask = multithread.task;
+  multithread.task = "Swap Improve";
+  
+  //  mesh.CalcSurfacesOfNode ();
+    
+  INDEX_3_HASHTABLE<int> faces(mesh.GetNOpenElements()/3 + 2);
+  if (goal == OPT_CONFORM)
+    {
+      for (int i = 1; i <= mesh.GetNOpenElements(); i++)
+	{
+	  const Element2d & hel = mesh.OpenElement(i);
+	  INDEX_3 face(hel[0], hel[1], hel[2]);
+	  face.Sort();
+	  faces.Set (face, 1);
+	}
+    }
+  
+  // Calculate total badness
+  if (goal == OPT_QUALITY)
+    {
+      bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements());
+      (*testout) << "Total badness = " << bad1 << endl;
+    }
+  
+  // find elements on node
+  for (ElementIndex ei = 0; ei < ne; ei++)
+    for (int j = 0; j < mesh[ei].GetNP(); j++)
+      elementsonnode.Add (mesh[ei][j], ei);
+
+
+  // INDEX_2_HASHTABLE<int> edgeused(2 * ne + 5);
+  INDEX_2_CLOSED_HASHTABLE<int> edgeused(12 * ne + 5);
+  
+  for (ElementIndex ei = 0; ei < ne; ei++)
+    {
+      if (multithread.terminate)
+	break;
+      
+      multithread.percent = 100.0 * (ei+1) / ne;
+
+      if ((mesh.ElementType(ei)) == FIXEDELEMENT)
+	continue;
+
+      if(working_elements && 
+	 ei < working_elements->Size() &&
+	 !working_elements->Test(ei))
+	continue;
+
+      if (mesh[ei].IsDeleted())
+	continue;
+
+      if ((goal == OPT_LEGAL) && 
+	  mesh.LegalTet (mesh[ei]) &&
+	  CalcBad (mesh.Points(), mesh[ei], 0) < 1e3)
+	continue;
+
+      //      int onlybedges = 1;
+
+      for (int j = 0; j < 6; j++)
+	{
+	  // loop over edges
+
+	  const Element & elemi = mesh[ei];
+	  if (elemi.IsDeleted()) continue;
+
+
+	  //	  (*testout) << "check element " << elemi << endl;
+
+	  int mattyp = elemi.GetIndex();
+	  
+	  static const int tetedges[6][2] =
+	    { { 0, 1 }, { 0, 2 }, { 0, 3 },
+	      { 1, 2 }, { 1, 3 }, { 2, 3 } };
+
+	  pi1 = elemi[tetedges[j][0]];
+	  pi2 = elemi[tetedges[j][1]];
+	  
+
+	  if (pi2 < pi1) Swap (pi1, pi2);
+	 	  
+	  if (mesh.BoundaryEdge (pi1, pi2)) continue;
+
+
+	  INDEX_2 i2 (pi1, pi2);
+	  i2.Sort();
+	  if (edgeused.Used(i2)) continue;
+	  edgeused.Set (i2, 1);
+	  
+	  hasbothpoints.SetSize (0);
+	  for (int k = 0; k < elementsonnode[pi1].Size(); k++)
+	    {
+	      bool has1 = 0, has2 = 0;
+	      ElementIndex elnr = elementsonnode[pi1][k];
+	      const Element & elem = mesh[elnr];
+	      
+	      if (elem.IsDeleted()) continue;
+	      
+	      for (int l = 0; l < elem.GetNP(); l++)
+		{
+		  if (elem[l] == pi1) has1 = 1;
+		  if (elem[l] == pi2) has2 = 1;
+		}
+
+	      if (has1 && has2) 
+		{ // only once
+		  for (int l = 0; l < hasbothpoints.Size(); l++)
+		    if (hasbothpoints[l] == elnr)
+		      has1 = 0;
+		  
+		  if (has1)
+		    hasbothpoints.Append (elnr);
+		}
+	    }
+	  
+	  bool puretet = 1;
+	  for (int k = 0; k < hasbothpoints.Size(); k++)
+	    if (mesh[hasbothpoints[k]].GetType () != TET)
+	      puretet = 0;
+	  if (!puretet)
+	    continue;
+
+	  int nsuround = hasbothpoints.Size();
+
+	  if ( nsuround == 3 )
+	    {
+	      Element & elem = mesh[hasbothpoints[0]];
+	      for (int l = 0; l < 4; l++)
+		if (elem[l] != pi1 && elem[l] != pi2)
+		  {
+		    pi4 = pi3;
+		    pi3 = elem[l];
+		  }
+	      
+	      el31[0] = pi1;
+	      el31[1] = pi2;
+	      el31[2] = pi3;
+	      el31[3] = pi4;
+	      el31.SetIndex (mattyp);
+	      
+	      if (WrongOrientation (mesh.Points(), el31))
+		{
+		  Swap (pi3, pi4);
+		  el31[2] = pi3;
+		  el31[3] = pi4;
+		}
+	      
+	      pi5 = 0;
+	      for (int k = 1; k < 3; k++)
+		{
+		  const Element & elemk = mesh[hasbothpoints[k]];
+		  bool has1 = 0;
+		  for (int l = 0; l < 4; l++)
+		    if (elemk[l] == pi4)
+		      has1 = 1;
+		  if (has1)
+		    {
+		      for (int l = 0; l < 4; l++)
+			if (elemk[l] != pi1 && elemk[l] != pi2 && elemk[l] != pi4)
+			  pi5 = elemk[l];
+		    }
+		}
+
+	      if(pi5 == 0)
+                throw NgException("Illegal state observed in SwapImprove");
+
+
+	      
+	      el32[0] = pi1;  
+	      el32[1] = pi2;  
+	      el32[2] = pi4;  
+	      el32[3] = pi5;  
+	      el32.SetIndex (mattyp);
+	      
+	      el33[0] = pi1;  
+	      el33[1] = pi2;  
+	      el33[2] = pi5;  
+	      el33[3] = pi3;  
+	      el33.SetIndex (mattyp);
+	      
+	      elementsonnode.Add (pi4, hasbothpoints[1]);
+	      elementsonnode.Add (pi3, hasbothpoints[2]);
+	      
+	      bad1 = CalcBad (mesh.Points(), el31, 0) + 
+		CalcBad (mesh.Points(), el32, 0) +
+		CalcBad (mesh.Points(), el33, 0);
+
+	      el31.flags.illegal_valid = 0;
+	      el32.flags.illegal_valid = 0;
+	      el33.flags.illegal_valid = 0;
+
+	      if (!mesh.LegalTet(el31) ||
+		  !mesh.LegalTet(el32) ||
+		  !mesh.LegalTet(el33))
+		bad1 += 1e4;
+	      
+	      el21[0] = pi3;
+	      el21[1] = pi4;
+	      el21[2] = pi5;
+	      el21[3] = pi2;
+	      el21.SetIndex (mattyp);
+	      
+	      el22[0] = pi5;
+	      el22[1] = pi4;
+	      el22[2] = pi3;
+	      el22[3] = pi1;
+	      el22.SetIndex (mattyp);	      
+
+	      bad2 = CalcBad (mesh.Points(), el21, 0) + 
+		CalcBad (mesh.Points(), el22, 0);
+
+	      el21.flags.illegal_valid = 0;
+	      el22.flags.illegal_valid = 0;
+
+	      if (!mesh.LegalTet(el21) ||
+		  !mesh.LegalTet(el22))
+		bad2 += 1e4;
+
+
+	      if (goal == OPT_CONFORM && bad2 < 1e4)
+		{
+		  INDEX_3 face(pi3, pi4, pi5);
+		  face.Sort();
+		  if (faces.Used(face))
+		    {
+		      // (*testout) << "3->2 swap, could improve conformity, bad1 = " << bad1
+		      //				 << ", bad2 = " << bad2 << endl;
+		      if (bad2 < 1e4)
+			bad1 = 2 * bad2;
+		    }
+		  /*
+		    else
+		    {
+		    INDEX_2 hi1(pi3, pi4);
+		    hi1.Sort();
+		    INDEX_2 hi2(pi3, pi5);
+		    hi2.Sort();
+		    INDEX_2 hi3(pi4, pi5);
+		    hi3.Sort();
+
+		    if (boundaryedges->Used (hi1) ||
+		    boundaryedges->Used (hi2) ||
+		    boundaryedges->Used (hi3) )
+		    bad1 = 2 * bad2;
+		    }
+		  */
+		}
+
+	      if (bad2 < bad1)
+		{
+		  //		  (*mycout) << "3->2 " << flush;
+		  //		  (*testout) << "3->2 conversion" << endl;
+		  cnt++;
+		  
+
+		  /*
+		  (*testout) << "3->2 swap, old els = " << endl
+			     << mesh[hasbothpoints[0]] << endl
+			     << mesh[hasbothpoints[1]] << endl
+			     << mesh[hasbothpoints[2]] << endl
+			     << "new els = " << endl
+			     << el21 << endl
+			     << el22 << endl;
+		  */
+                  
+		  el21.flags.illegal_valid = 0;
+		  el22.flags.illegal_valid = 0;
+		  mesh[hasbothpoints[0]] = el21;
+		  mesh[hasbothpoints[1]] = el22;
+		  for (int l = 0; l < 4; l++)
+		    mesh[hasbothpoints[2]][l] = 0;
+		  mesh[hasbothpoints[2]].Delete();
+
+		  for (int k = 0; k < 2; k++)
+		    for (int l = 0; l < 4; l++)
+		      elementsonnode.Add (mesh[hasbothpoints[k]][l], hasbothpoints[k]);
+		}
+	    }	  
+          
+	  if (nsuround == 4)
+	    {
+	      const Element & elem1 = mesh[hasbothpoints[0]];
+	      for (int l = 0; l < 4; l++)
+		if (elem1[l] != pi1 && elem1[l] != pi2)
+		  {
+		    pi4 = pi3;
+		    pi3 = elem1[l];
+		  }
+	      
+	      el1[0] = pi1; el1[1] = pi2;
+	      el1[2] = pi3; el1[3] = pi4;
+	      el1.SetIndex (mattyp);
+
+	      if (WrongOrientation (mesh.Points(), el1))
+		{
+		  Swap (pi3, pi4);
+		  el1[2] = pi3;
+		  el1[3] = pi4;
+		}
+	      
+	      pi5 = 0;
+	      for (int k = 1; k < 4; k++)
+		{
+		  const Element & elem = mesh[hasbothpoints[k]];
+		  bool has1 = 0;
+		  for (int l = 0; l < 4; l++)
+		    if (elem[l] == pi4)
+		      has1 = 1;
+		  if (has1)
+		    {
+		      for (int l = 0; l < 4; l++)
+			if (elem[l] != pi1 && elem[l] != pi2 && elem[l] != pi4)
+			  pi5 = elem[l];
+		    }
+		}
+	      
+	      pi6 = 0;
+	      for (int k = 1; k < 4; k++)
+		{
+		  const Element & elem = mesh[hasbothpoints[k]];
+		  bool has1 = 0;
+		  for (int l = 0; l < 4; l++)
+		    if (elem[l] == pi3)
+		      has1 = 1;
+		  if (has1)
+		    {
+		      for (int l = 0; l < 4; l++)
+			if (elem[l] != pi1 && elem[l] != pi2 && elem[l] != pi3)
+			  pi6 = elem[l];
+		    }
+		}
+	      
+	      /*
+	      INDEX_2 i22(pi3, pi5);
+	      i22.Sort();
+	      INDEX_2 i23(pi4, pi6);
+	      i23.Sort();
+	      */
+	      
+	      el1[0] = pi1; el1[1] = pi2;  
+	      el1[2] = pi3; el1[3] = pi4; 
+	      el1.SetIndex (mattyp);
+	      
+	      el2[0] = pi1; el2[1] = pi2;  
+	      el2[2] = pi4; el2[3] = pi5;  
+	      el2.SetIndex (mattyp);
+	      
+	      el3[0] = pi1; el3[1] = pi2;  
+	      el3[2] = pi5; el3[3] = pi6;  
+	      el3.SetIndex (mattyp);
+	      
+	      el4[0] = pi1; el4[1] = pi2;  
+	      el4[2] = pi6; el4[3] = pi3;  
+	      el4.SetIndex (mattyp);
+	      
+	      //        elementsonnode.Add (pi4, hasbothpoints.Elem(2));
+	      //        elementsonnode.Add (pi3, hasbothpoints.Elem(3));
+	      
+	      bad1 = CalcBad (mesh.Points(), el1, 0) + 
+		CalcBad (mesh.Points(), el2, 0) +
+		CalcBad (mesh.Points(), el3, 0) + 
+		CalcBad (mesh.Points(), el4, 0);
+	      
+
+	      el1.flags.illegal_valid = 0;
+	      el2.flags.illegal_valid = 0;
+	      el3.flags.illegal_valid = 0;
+	      el4.flags.illegal_valid = 0;
+
+
+	      if (goal != OPT_CONFORM)
+		{
+		  if (!mesh.LegalTet(el1) ||
+		      !mesh.LegalTet(el2) ||
+		      !mesh.LegalTet(el3) ||
+		      !mesh.LegalTet(el4))
+		    bad1 += 1e4;
+		}
+	      
+	      el1[0] = pi3; el1[1] = pi5;  
+	      el1[2] = pi2; el1[3] = pi4; 
+	      el1.SetIndex (mattyp);
+	 
+	      el2[0] = pi3; el2[1] = pi5;  
+	      el2[2] = pi4; el2[3] = pi1;  
+	      el2.SetIndex (mattyp);
+	      
+	      el3[0] = pi3; el3[1] = pi5;  
+	      el3[2] = pi1; el3[3] = pi6;  
+	      el3.SetIndex (mattyp);
+	      
+	      el4[0] = pi3; el4[1] = pi5;  
+	      el4[2] = pi6; el4[3] = pi2;  	
+	      el4.SetIndex (mattyp);
+      
+	      bad2 = CalcBad (mesh.Points(), el1, 0) + 
+		CalcBad (mesh.Points(), el2, 0) +
+		CalcBad (mesh.Points(), el3, 0) + 
+		CalcBad (mesh.Points(), el4, 0);
+
+	      el1.flags.illegal_valid = 0;
+	      el2.flags.illegal_valid = 0;
+	      el3.flags.illegal_valid = 0;
+	      el4.flags.illegal_valid = 0;
+
+	      if (goal != OPT_CONFORM)
+		{
+		  if (!mesh.LegalTet(el1) ||
+		      !mesh.LegalTet(el2) ||
+		      !mesh.LegalTet(el3) ||
+		      !mesh.LegalTet(el4))
+		    bad2 += 1e4;
+		}
+
+	      
+	      el1b[0] = pi4; el1b[1] = pi6;  
+	      el1b[2] = pi3; el1b[3] = pi2; 
+	      el1b.SetIndex (mattyp);
+	      
+	      el2b[0] = pi4; el2b[1] = pi6;  
+	      el2b[2] = pi2; el2b[3] = pi5;  
+	      el2b.SetIndex (mattyp);
+	      
+	      el3b[0] = pi4; el3b[1] = pi6;  
+	      el3b[2] = pi5; el3b[3] = pi1;  
+	      el3b.SetIndex (mattyp);
+	      
+	      el4b[0] = pi4; el4b[1] = pi6;  
+	      el4b[2] = pi1; el4b[3] = pi3;  
+	      el4b.SetIndex (mattyp);
+	      
+	      bad3 = CalcBad (mesh.Points(), el1b, 0) + 
+		CalcBad (mesh.Points(), el2b, 0) +
+		CalcBad (mesh.Points(), el3b, 0) +
+		CalcBad (mesh.Points(), el4b, 0);
+	      
+	      el1b.flags.illegal_valid = 0;
+	      el2b.flags.illegal_valid = 0;
+	      el3b.flags.illegal_valid = 0;
+	      el4b.flags.illegal_valid = 0;
+
+	      if (goal != OPT_CONFORM)
+		{
+		  if (!mesh.LegalTet(el1b) ||
+		      !mesh.LegalTet(el2b) ||
+		      !mesh.LegalTet(el3b) ||
+		      !mesh.LegalTet(el4b))
+		    bad3 += 1e4;
+		}
+
+
+	      /*
+	      int swap2 = (bad2 < bad1) && (bad2 < bad3);
+	      int swap3 = !swap2 && (bad3 < bad1);
+	      
+	      if ( ((bad2 < 10 * bad1) || 
+		    (bad2 < 1e6)) && mesh.BoundaryEdge (pi3, pi5))
+		swap2 = 1;
+	      else  if ( ((bad3 < 10 * bad1) || 
+			  (bad3 < 1e6)) && mesh.BoundaryEdge (pi4, pi6))
+		{
+		  swap3 = 1;
+		  swap2 = 0;
+		}
+	      */
+	      bool swap2, swap3;
+
+	      if (goal != OPT_CONFORM)
+		{
+		  swap2 = (bad2 < bad1) && (bad2 < bad3);
+		  swap3 = !swap2 && (bad3 < bad1);
+		}
+	      else
+		{
+		  if (mesh.BoundaryEdge (pi3, pi5)) bad2 /= 1e6;
+		  if (mesh.BoundaryEdge (pi4, pi6)) bad3 /= 1e6;
+
+		  swap2 = (bad2 < bad1) && (bad2 < bad3);
+		  swap3 = !swap2 && (bad3 < bad1);
+		}
+		
+
+	      if (swap2 || swap3)
+		{
+		  // (*mycout) << "4->4 " << flush;
+		  cnt++;
+		  //		  (*testout) << "4->4 conversion" << "\n";
+		  /*
+		    (*testout) << "bad1 = " << bad1 
+		    << " bad2 = " << bad2
+		    << " bad3 = " << bad3 << "\n";
+		  
+		    (*testout) << "Points: " << pi1 << " " << pi2 << " " << pi3 
+		    << " " << pi4 << " " << pi5 << " " << pi6 << "\n";
+		    (*testout) << "Elements: " 
+		    << hasbothpoints.Get(1) << "  "
+		    << hasbothpoints.Get(2) << "  "
+		    << hasbothpoints.Get(3) << "  "
+		    << hasbothpoints.Get(4) << "  " << "\n";
+		  */
+
+		  /*
+		    {
+		    int i1, j1;
+		    for (i1 = 1; i1 <= 4; i1++)
+		    {
+		    for (j1 = 1; j1 <= 4; j1++)
+		    (*testout) << volelements.Get(hasbothpoints.Get(i1)).PNum(j1)
+		    << "  ";
+		    (*testout) << "\n";
+		    }
+		    }
+		  */
+		}
+	      
+
+	      if (swap2)
+		{
+		  //		  (*mycout) << "bad1 = " << bad1 << " bad2 = " << bad2 << "\n";
+
+
+		  /*
+		  (*testout) << "4->4 swap A, old els = " << endl
+			     << mesh[hasbothpoints[0]] << endl
+			     << mesh[hasbothpoints[1]] << endl
+			     << mesh[hasbothpoints[2]] << endl
+			     << mesh[hasbothpoints[3]] << endl
+			     << "new els = " << endl
+			     << el1 << endl
+			     << el2 << endl
+			     << el3 << endl
+			     << el4 << endl;
+		  */
+
+
+
+		  el1.flags.illegal_valid = 0;
+		  el2.flags.illegal_valid = 0;
+		  el3.flags.illegal_valid = 0;
+		  el4.flags.illegal_valid = 0;
+		  
+		  mesh[hasbothpoints[0]] = el1;
+		  mesh[hasbothpoints[1]] = el2;
+		  mesh[hasbothpoints[2]] = el3;
+		  mesh[hasbothpoints[3]] = el4;
+		  
+		  for (int k = 0; k < 4; k++)
+		    for (int l = 0; l < 4; l++)
+		      elementsonnode.Add (mesh[hasbothpoints[k]][l], hasbothpoints[k]);
+		}
+	      else if (swap3)
+		{
+		  // (*mycout) << "bad1 = " << bad1 << " bad3 = " << bad3 << "\n";
+		  el1b.flags.illegal_valid = 0;
+		  el2b.flags.illegal_valid = 0;
+		  el3b.flags.illegal_valid = 0;
+		  el4b.flags.illegal_valid = 0;
+
+
+		  /*
+		  (*testout) << "4->4 swap A, old els = " << endl
+			     << mesh[hasbothpoints[0]] << endl
+			     << mesh[hasbothpoints[1]] << endl
+			     << mesh[hasbothpoints[2]] << endl
+			     << mesh[hasbothpoints[3]] << endl
+			     << "new els = " << endl
+			     << el1b << endl
+			     << el2b << endl
+			     << el3b << endl
+			     << el4b << endl;
+		  */
+
+
+		  mesh[hasbothpoints[0]] = el1b;
+		  mesh[hasbothpoints[1]] = el2b;
+		  mesh[hasbothpoints[2]] = el3b;
+		  mesh[hasbothpoints[3]] = el4b;
+
+
+		  for (int k = 0; k < 4; k++)
+		    for (int l = 0; l < 4; l++)
+		      elementsonnode.Add (mesh[hasbothpoints[k]][l], hasbothpoints[k]);
+		}
+	    }
+
+	  if (nsuround >= 5) 
+	    {
+	      Element hel(TET);
+
+	      ArrayMem<PointIndex, 50> suroundpts(nsuround);
+	      ArrayMem<char, 50> tetused(nsuround);
+
+	      Element & elem = mesh[hasbothpoints[0]];
+
+	      for (int l = 0; l < 4; l++)
+		if (elem[l] != pi1 && elem[l] != pi2)
+		  {
+		    pi4 = pi3;
+		    pi3 = elem[l];
+		  }
+
+	      hel[0] = pi1;
+	      hel[1] = pi2;
+	      hel[2] = pi3;
+	      hel[3] = pi4;
+	      hel.SetIndex (mattyp);
+	      
+	      if (WrongOrientation (mesh.Points(), hel))
+		{
+		  Swap (pi3, pi4);
+		  hel[2] = pi3;
+		  hel[3] = pi4;
+		}
+
+	      
+	      // suroundpts.SetSize (nsuround);
+	      suroundpts[0] = pi3;
+	      suroundpts[1] = pi4;
+
+	      tetused = 0;
+	      tetused[0] = 1;
+
+	      for (int l = 2; l < nsuround; l++)
+		{
+		  int oldpi = suroundpts[l-1];
+		  int newpi = 0;
+
+		  for (int k = 0; k < nsuround && !newpi; k++)
+		    if (!tetused[k])
+		      {
+			const Element & nel = mesh[hasbothpoints[k]];
+
+			for (int k2 = 0; k2 < 4 && !newpi; k2++)
+			  if (nel[k2] == oldpi)
+			    {
+			      newpi = 
+				nel[0] + nel[1] + nel[2] + nel[3] 
+				- pi1 - pi2 - oldpi;
+			      
+			      tetused[k] = 1; 
+			      suroundpts[l] = newpi;
+			    }			
+		      }
+		}
+
+	      
+	      bad1 = 0;
+	      for (int k = 0; k < nsuround; k++)
+		{
+		  hel[0] = pi1;
+		  hel[1] = pi2;
+		  hel[2] = suroundpts[k];
+		  hel[3] = suroundpts[(k+1) % nsuround];
+		  hel.SetIndex (mattyp);
+
+		  bad1 += CalcBad (mesh.Points(), hel, 0);
+		}
+
+	      //  (*testout) << "nsuround = " << nsuround << " bad1 = " << bad1 << endl;
+
+
+	      int bestl = -1;
+	      int confface = -1;
+	      int confedge = -1;
+	      double badopt = bad1;
+
+	      for (int l = 0; l < nsuround; l++)
+		{
+		  bad2 = 0;
+
+		  for (int k = l+1; k <= nsuround + l - 2; k++)
+		    {
+		      hel[0] = suroundpts[l];
+		      hel[1] = suroundpts[k % nsuround];
+		      hel[2] = suroundpts[(k+1) % nsuround];
+		      hel[3] = pi2;
+
+		      bad2 += CalcBad (mesh.Points(), hel, 0);
+		      hel.flags.illegal_valid = 0;
+		      if (!mesh.LegalTet(hel)) bad2 += 1e4;
+
+		      hel[2] = suroundpts[k % nsuround];
+		      hel[1] = suroundpts[(k+1) % nsuround];
+		      hel[3] = pi1;
+
+		      bad2 += CalcBad (mesh.Points(), hel, 0);
+
+		      hel.flags.illegal_valid = 0;
+		      if (!mesh.LegalTet(hel)) bad2 += 1e4;
+		    }
+		  // (*testout) << "bad2," << l << " = " << bad2 << endl;
+		  
+		  if ( bad2 < badopt )
+		    {
+		      bestl = l;
+		      badopt = bad2;
+		    }
+		  
+		  
+		  if (goal == OPT_CONFORM)
+		       // (bad2 <= 100 * bad1 || bad2 <= 1e6))
+		    {
+		      bool nottoobad =
+			(bad2 <= bad1) ||
+			(bad2 <= 100 * bad1 && bad2 <= 1e18) ||
+			(bad2 <= 1e8);
+		      
+		      for (int k = l+1; k <= nsuround + l - 2; k++)
+			{
+			  INDEX_3 hi3(suroundpts[l],
+				      suroundpts[k % nsuround],
+				      suroundpts[(k+1) % nsuround]);
+			  hi3.Sort();
+			  if (faces.Used(hi3))
+			    {
+			      // (*testout) << "could improve face conformity, bad1 = " << bad1
+			      // << ", bad 2 = " << bad2 << ", nottoobad = " << nottoobad << endl;
+			      if (nottoobad)
+				confface = l;
+			    }
+			}
+
+		      for (int k = l+2; k <= nsuround+l-2; k++)
+			{
+			  if (mesh.BoundaryEdge (suroundpts[l],
+						 suroundpts[k % nsuround]))
+			    {
+			      /*
+			      *testout << "could improve edge conformity, bad1 = " << bad1
+				   << ", bad 2 = " << bad2 << ", nottoobad = " << nottoobad << endl;
+			      */
+			      if (nottoobad)
+				confedge = l;
+			    }
+			}
+		    }
+		}
+	      
+	      if (confedge != -1)
+		bestl = confedge;
+	      if (confface != -1)
+		bestl = confface;
+
+	      if (bestl != -1)
+		{
+		  // (*mycout) << nsuround << "->" << 2 * (nsuround-2) << " " << flush;
+		  cnt++;
+		  
+		  for (int k = bestl+1; k <= nsuround + bestl - 2; k++)
+		    {
+		      int k1;
+
+		      hel[0] = suroundpts[bestl];
+		      hel[1] = suroundpts[k % nsuround];
+		      hel[2] = suroundpts[(k+1) % nsuround];
+		      hel[3] = pi2;
+		      hel.flags.illegal_valid = 0;
+
+		      /*
+		      (*testout) << nsuround << "-swap, new el,top = "
+				 << hel << endl;
+		      */
+		      mesh.AddVolumeElement (hel);
+
+		      for (k1 = 0; k1 < 4; k1++)
+			elementsonnode.Add (hel[k1], mesh.GetNE()-1);
+		      
+
+		      hel[2] = suroundpts[k % nsuround];
+		      hel[1] = suroundpts[(k+1) % nsuround];
+		      hel[3] = pi1;
+
+		      /*
+		      (*testout) << nsuround << "-swap, new el,bot = "
+				 << hel << endl;
+		      */
+
+		      mesh.AddVolumeElement (hel);
+
+		      for (k1 = 0; k1 < 4; k1++)
+			elementsonnode.Add (hel[k1], mesh.GetNE()-1);
+		    }		  
+		  
+		  for (int k = 0; k < nsuround; k++)
+		    {
+		      Element & rel = mesh[hasbothpoints[k]];
+		      /*
+		      (*testout) << nsuround << "-swap, old el = "
+				 << rel << endl;
+		      */
+		      rel.Delete();
+		      for (int k1 = 0; k1 < 4; k1++)
+			rel[k1] = 0;
+
+		    }
+		}
+	    }
+	}
+
+      /*
+	if (onlybedges)
+	{
+	(*testout) << "bad tet: " 
+	<< volelements.Get(i)[0] 
+	<< volelements.Get(i)[1] 
+	<< volelements.Get(i)[2] 
+	<< volelements.Get(i)[3] << "\n";
+
+	if (!mesh.LegalTet (volelements.Get(i)))
+	cerr << "Illegal tet" << "\n";
+	}
+      */
+    }
+  //  (*mycout) << endl;
+
+  /*  
+      cout << "edgeused: ";
+      edgeused.PrintMemInfo(cout);
+  */
+  PrintMessage (5, cnt, " swaps performed");
+
+
+
+
+
+  mesh.Compress ();
+
+  /*
+  if (goal == OPT_QUALITY)
+    {
+      bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements());
+      //      (*testout) << "Total badness = " << bad1 << endl;
+    }
+  */
+
+  /*
+    for (i = 1; i <= GetNE(); i++)
+    if (volelements.Get(i)[0])
+    if (!mesh.LegalTet (volelements.Get(i)))
+    {
+    cout << "detected illegal tet, 2" << endl;
+    (*testout) << "detected illegal tet1: " << i << endl;
+    }
+  */
+
+  multithread.task = savetask;
+}
+  
+
+
+
+
+
+void MeshOptimize3d :: SwapImproveSurface (Mesh & mesh, OPTIMIZEGOAL goal,
+					   const BitArray * working_elements,
+					   const Array< Array<int,PointIndex::BASE>* > * idmaps)
+{
+  Array< Array<int,PointIndex::BASE>* > locidmaps;
+  const Array< Array<int,PointIndex::BASE>* > * used_idmaps;
+
+  if(idmaps)
+    used_idmaps = idmaps;
+  else
+    {
+      used_idmaps = &locidmaps;
+      
+      for(int i=1; i<=mesh.GetIdentifications().GetMaxNr(); i++)
+	{
+	  if(mesh.GetIdentifications().GetType(i) == Identifications::PERIODIC)
+	    {
+	      locidmaps.Append(new Array<int,PointIndex::BASE>);
+	      mesh.GetIdentifications().GetMap(i,*locidmaps.Last(),true);
+	    }
+	}
+    }
+
+
+  PointIndex pi1, pi2, pi3, pi4, pi5, pi6;
+  PointIndex pi1other, pi2other;
+  int cnt = 0;
+
+  //double bad1, bad2, bad3, sbad;
+  double bad1, sbad;
+  double h;
+
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+  int nse = mesh.GetNSE();
+
+  int mattype, othermattype;
+
+  
+  // contains at least all elements at node
+  TABLE<ElementIndex,PointIndex::BASE> elementsonnode(np);
+  TABLE<SurfaceElementIndex,PointIndex::BASE> surfaceelementsonnode(np);
+  TABLE<int,PointIndex::BASE> surfaceindicesonnode(np);
+
+  Array<ElementIndex> hasbothpoints;
+  Array<ElementIndex> hasbothpointsother;
+
+  PrintMessage (3, "SwapImproveSurface ");
+  (*testout) << "\n" << "Start SwapImproveSurface" << endl;
+
+  const char * savetask = multithread.task;
+  multithread.task = "Swap Improve Surface";
+    
+      
+  
+  // find elements on node
+  for (ElementIndex ei = 0; ei < ne; ei++)
+    for (int j = 0; j < mesh[ei].GetNP(); j++)
+      elementsonnode.Add (mesh[ei][j], ei);
+
+  for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+    for(int j=0; j<mesh[sei].GetNP(); j++)
+      {
+	surfaceelementsonnode.Add(mesh[sei][j], sei);
+	if(!surfaceindicesonnode[mesh[sei][j]].Contains(mesh[sei].GetIndex()))
+	  surfaceindicesonnode.Add(mesh[sei][j],mesh[sei].GetIndex());
+      }
+
+  bool periodic;
+  int idnum(-1);
+
+  // INDEX_2_HASHTABLE<int> edgeused(2 * ne + 5);
+  INDEX_2_CLOSED_HASHTABLE<int> edgeused(12 * ne + 5);
+
+  for (ElementIndex ei = 0; ei < ne; ei++)
+    {
+      if (multithread.terminate)
+	break;
+      
+      multithread.percent = 100.0 * (ei+1) / ne;
+
+      if (mesh.ElementType(ei) == FIXEDELEMENT)
+	continue;
+      
+      if(working_elements && 
+	 ei < working_elements->Size() &&
+	 !working_elements->Test(ei))
+	continue;
+
+      if (mesh[ei].IsDeleted())
+	continue;
+
+      if ((goal == OPT_LEGAL) && 
+	  mesh.LegalTet (mesh[ei]) &&
+	  CalcBad (mesh.Points(), mesh[ei], 0) < 1e3)
+	continue;
+
+      const Element & elemi = mesh[ei];
+      //Element elemi = mesh[ei];
+      if (elemi.IsDeleted()) continue;
+
+
+      mattype = elemi.GetIndex();
+
+      bool swapped = false;
+
+      for (int j = 0; !swapped && j < 6; j++)
+	{
+	  // loop over edges
+
+	  
+	  static const int tetedges[6][2] =
+	    { { 0, 1 }, { 0, 2 }, { 0, 3 },
+	      { 1, 2 }, { 1, 3 }, { 2, 3 } };
+
+	  pi1 = elemi[tetedges[j][0]];
+	  pi2 = elemi[tetedges[j][1]];
+
+	  
+	  if (pi2 < pi1)
+	    Swap (pi1, pi2);
+	    	  
+	  
+	  bool found = false;
+	  for(int k=0; !found && k<used_idmaps->Size(); k++)
+	    {
+	      if(pi2 < (*used_idmaps)[k]->Size() + PointIndex::BASE)
+		{
+		  pi1other = (*(*used_idmaps)[k])[pi1];
+		  pi2other = (*(*used_idmaps)[k])[pi2];
+		  found = (pi1other != 0 && pi2other != 0 && pi1other != pi1 && pi2other != pi2);
+		  if(found)
+		    idnum = k;
+		}
+	    }
+	  if(found)
+	    periodic = true;
+	  else
+	    {
+	      periodic = false;
+	      pi1other = pi1; pi2other = pi2;
+	    }
+
+
+	 	  
+	  if (!mesh.BoundaryEdge (pi1, pi2) ||
+	      mesh.IsSegment(pi1, pi2)) continue;
+
+	  othermattype = -1;
+
+	  
+	  INDEX_2 i2 (pi1, pi2);
+	  i2.Sort();
+	  if (edgeused.Used(i2)) continue;
+	  edgeused.Set (i2, 1);
+	  if(periodic)
+	    {
+	      i2.I1() = pi1other;
+	      i2.I2() = pi2other;
+	      i2.Sort();
+	      edgeused.Set(i2,1);
+	    }
+	  
+	  
+	  hasbothpoints.SetSize (0);
+	  hasbothpointsother.SetSize (0);
+	  for (int k = 0; k < elementsonnode[pi1].Size(); k++)
+	    {
+	      bool has1 = false, has2 = false;
+	      ElementIndex elnr = elementsonnode[pi1][k];
+	      const Element & elem = mesh[elnr];
+	      
+	      if (elem.IsDeleted()) continue;
+	      
+	      for (int l = 0; l < elem.GetNP(); l++)
+		{
+		  if (elem[l] == pi1) has1 = true;
+		  if (elem[l] == pi2) has2 = true;
+		}
+
+	      if (has1 && has2) 
+		{ 
+		  if(othermattype == -1 && elem.GetIndex() != mattype)
+		    othermattype = elem.GetIndex();
+
+		  if(elem.GetIndex() == mattype)
+		    {
+		      // only once
+		      for (int l = 0; l < hasbothpoints.Size(); l++)
+			if (hasbothpoints[l] == elnr)
+			  has1 = 0;
+		      
+		      if (has1)
+			hasbothpoints.Append (elnr);
+		    }
+		  else if(elem.GetIndex() == othermattype)
+		    {
+		      // only once
+		      for (int l = 0; l < hasbothpointsother.Size(); l++)
+			if (hasbothpointsother[l] == elnr)
+			  has1 = 0;
+		      
+		      if (has1)
+			hasbothpointsother.Append (elnr);
+		    }
+		  else
+		    {
+		      cout << "problem with domain indices" << endl;
+		      (*testout) << "problem: mattype = " << mattype << ", othermattype = " << othermattype 
+				 << " elem " << elem << " mt " << elem.GetIndex() << endl
+				 << " pi1 " << pi1 << " pi2 " << pi2 << endl;
+		      (*testout) << "hasbothpoints:" << endl;
+		      for(int ii=0; ii < hasbothpoints.Size(); ii++)
+			(*testout) << mesh[hasbothpoints[ii]] << endl;
+		      (*testout) << "hasbothpointsother:" << endl;
+		      for(int ii=0; ii < hasbothpointsother.Size(); ii++)
+			(*testout) << mesh[hasbothpointsother[ii]] << endl;
+		    }
+		}
+	    }
+
+	  if(hasbothpointsother.Size() > 0 && periodic)
+	    throw NgException("SwapImproveSurface: Assumption about interface/periodicity wrong!");
+
+	  if(periodic)
+	    {
+	      for (int k = 0; k < elementsonnode[pi1other].Size(); k++)
+		{
+		  bool has1 = false, has2 = false;
+		  ElementIndex elnr = elementsonnode[pi1other][k];
+		  const Element & elem = mesh[elnr];
+	      
+		  if (elem.IsDeleted()) continue;
+	      
+		  for (int l = 0; l < elem.GetNP(); l++)
+		    {
+		      if (elem[l] == pi1other) has1 = true;
+		      if (elem[l] == pi2other) has2 = true;
+		    }
+		  
+		  if (has1 && has2) 
+		    { 
+		      if(othermattype == -1)
+			othermattype = elem.GetIndex();
+
+		      // only once
+		      for (int l = 0; l < hasbothpointsother.Size(); l++)
+			if (hasbothpointsother[l] == elnr)
+			  has1 = 0;
+		      
+		      if (has1)
+			hasbothpointsother.Append (elnr);
+		    }
+		}
+	    }
+
+
+	  //for(k=0; k<hasbothpoints.Size(); k++)
+	  //  (*testout) << "hasbothpoints["<<k<<"]: " << mesh[hasbothpoints[k]] << endl;
+
+	  
+	  SurfaceElementIndex sel1=-1,sel2=-1;
+	  SurfaceElementIndex sel1other=-1,sel2other=-1;
+	  for(int k = 0; k < surfaceelementsonnode[pi1].Size(); k++)
+	    {
+	      bool has1 = false, has2 = false;
+	      SurfaceElementIndex elnr = surfaceelementsonnode[pi1][k];
+	      const Element2d & elem = mesh[elnr];
+
+	      if (elem.IsDeleted()) continue;
+
+	      for (int l = 0; l < elem.GetNP(); l++)
+		{
+		  if (elem[l] == pi1) has1 = true;
+		  if (elem[l] == pi2) has2 = true;
+		}
+
+	      if(has1 && has2 && elnr != sel2)
+		{
+		  sel1 = sel2;
+		  sel2 = elnr;
+		}
+	    }
+
+	  if(periodic)
+	    {
+	      for(int k = 0; k < surfaceelementsonnode[pi1other].Size(); k++)
+		{
+		  bool has1 = false, has2 = false;
+		  SurfaceElementIndex elnr = surfaceelementsonnode[pi1other][k];
+		  const Element2d & elem = mesh[elnr];
+
+		  if (elem.IsDeleted()) continue;
+
+		  for (int l = 0; l < elem.GetNP(); l++)
+		    {
+		      if (elem[l] == pi1other) has1 = true;
+		      if (elem[l] == pi2other) has2 = true;
+		    }
+
+		  if(has1 && has2 && elnr != sel2other)
+		    {
+		      sel1other = sel2other;
+		      sel2other = elnr;
+		    }
+		}
+	    }
+	  else
+	    {
+	      sel1other = sel1; sel2other = sel2;
+	    }
+
+	  //(*testout) << "sel1 " << sel1 << " sel2 " << sel2 << " el " << mesh[sel1] << " resp. " << mesh[sel2] << endl;
+
+	  PointIndex sp1(0), sp2(0);
+	  PointIndex sp1other, sp2other;
+	  for(int l=0; l<mesh[sel1].GetNP(); l++)
+	    if(mesh[sel1][l] != pi1 && mesh[sel1][l] != pi2)
+	      sp1 = mesh[sel1][l];
+	  for(int l=0; l<mesh[sel2].GetNP(); l++)
+	    if(mesh[sel2][l] != pi1 && mesh[sel2][l] != pi2)
+	      sp2 = mesh[sel2][l];
+
+	  if(periodic)
+	    {
+	      sp1other = (*(*used_idmaps)[idnum])[sp1];
+	      sp2other = (*(*used_idmaps)[idnum])[sp2];
+
+	      bool change = false;
+	      for(int l=0; !change && l<mesh[sel1other].GetNP(); l++)
+		change = (sp2other == mesh[sel1other][l]);
+	      
+	      if(change)
+		{
+		  SurfaceElementIndex aux = sel1other;
+		  sel1other = sel2other;
+		  sel2other = aux;
+		}
+
+	    }
+	  else
+	    {
+	      sp1other = sp1; sp2other = sp2;
+	    }
+	  
+	  Vec<3> v1 = mesh[sp1]-mesh[pi1],
+	    v2 = mesh[sp2]-mesh[pi1],
+	    v3 = mesh[sp1]-mesh[pi2],
+	    v4 = mesh[sp2]-mesh[pi2];
+	  double vol = 0.5*(Cross(v1,v2).Length() + Cross(v3,v4).Length());
+	  h = sqrt(vol);
+	  h = 0;
+
+	  sbad = CalcTriangleBadness (mesh[pi1],mesh[pi2],mesh[sp1],0,0) + 
+	    CalcTriangleBadness (mesh[pi2],mesh[pi1],mesh[sp2],0,0);
+	  
+
+
+	  bool puretet = true;
+	  for (int k = 0; puretet && k < hasbothpoints.Size(); k++)
+	    if (mesh[hasbothpoints[k]].GetType () != TET)
+	      puretet = false;
+	  for (int k = 0; puretet && k < hasbothpointsother.Size(); k++)
+	    if (mesh[hasbothpointsother[k]].GetType () != TET)
+	      puretet = false;
+	  if (!puretet)
+	    continue;
+
+	  int nsuround = hasbothpoints.Size();
+	  int nsuroundother = hasbothpointsother.Size();
+
+	  Array < int > outerpoints(nsuround+1);
+	  outerpoints[0] = sp1;
+
+	  for(int i=0; i<nsuround; i++)
+	    {
+	      bool done = false;
+	      for(int jj=i; !done && jj<hasbothpoints.Size(); jj++)
+		{
+		  for(int k=0; !done && k<4; k++)
+		    if(mesh[hasbothpoints[jj]][k] == outerpoints[i])
+		      {
+			done = true;
+			for(int l=0; l<4; l++)
+			  if(mesh[hasbothpoints[jj]][l] != pi1 &&
+			     mesh[hasbothpoints[jj]][l] != pi2 &&
+			     mesh[hasbothpoints[jj]][l] != outerpoints[i])
+			    outerpoints[i+1] = mesh[hasbothpoints[jj]][l];
+		      }
+		  if(done)
+		    {
+		      ElementIndex aux = hasbothpoints[i];
+		      hasbothpoints[i] = hasbothpoints[jj];
+		      hasbothpoints[jj] = aux;
+		    }
+		}
+	    }
+	  if(outerpoints[nsuround] != sp2)
+	    {
+	      cerr << "OJE OJE OJE" << endl;
+	      (*testout) << "OJE OJE OJE" << endl;
+	      (*testout) << "hasbothpoints: " << endl;
+	      for(int ii=0; ii < hasbothpoints.Size(); ii++)
+		{
+		  (*testout) << mesh[hasbothpoints[ii]] << endl;
+		  for(int jj=0; jj<mesh[hasbothpoints[ii]].GetNP(); jj++)
+		    if(mesh.mlbetweennodes[mesh[hasbothpoints[ii]][jj]][0] > 0)
+		      (*testout) << mesh[hasbothpoints[ii]][jj] << " between "
+				 << mesh.mlbetweennodes[mesh[hasbothpoints[ii]][jj]][0] << " and "
+				 << mesh.mlbetweennodes[mesh[hasbothpoints[ii]][jj]][1] << endl;
+		}
+	      (*testout) << "outerpoints: " << outerpoints << endl;
+	      (*testout) << "sel1 " << mesh[sel1] << endl
+			 << "sel2 " << mesh[sel2] << endl;
+	      for(int ii=0; ii<3; ii++)
+		{
+		  if(mesh.mlbetweennodes[mesh[sel1][ii]][0] > 0)
+		    (*testout) << mesh[sel1][ii] << " between "
+			       << mesh.mlbetweennodes[mesh[sel1][ii]][0] << " and "
+			       << mesh.mlbetweennodes[mesh[sel1][ii]][1] << endl;
+		  if(mesh.mlbetweennodes[mesh[sel2][ii]][0] > 0)
+		    (*testout) << mesh[sel2][ii] << " between "
+			       << mesh.mlbetweennodes[mesh[sel2][ii]][0] << " and "
+			       << mesh.mlbetweennodes[mesh[sel2][ii]][1] << endl;
+		}
+	    }
+
+	  
+	  Array < int > outerpointsother;
+
+	  if(nsuroundother > 0)
+	    {
+	      outerpointsother.SetSize(nsuroundother+1);
+	      outerpointsother[0] = sp2other;
+	    }
+
+	  for(int i=0; i<nsuroundother; i++)
+	    {
+	      bool done = false;
+	      for(int jj=i; !done && jj<hasbothpointsother.Size(); jj++)
+		{
+		  for(int k=0; !done && k<4; k++)
+		    if(mesh[hasbothpointsother[jj]][k] == outerpointsother[i])
+		      {
+			done = true;
+			for(int l=0; l<4; l++)
+			  if(mesh[hasbothpointsother[jj]][l] != pi1other &&
+			     mesh[hasbothpointsother[jj]][l] != pi2other &&
+			     mesh[hasbothpointsother[jj]][l] != outerpointsother[i])
+			    outerpointsother[i+1] = mesh[hasbothpointsother[jj]][l];
+		      }
+		  if(done)
+		    {
+		      ElementIndex aux = hasbothpointsother[i];
+		      hasbothpointsother[i] = hasbothpointsother[jj];
+		      hasbothpointsother[jj] = aux;
+		    }
+		}
+	    }
+	  if(nsuroundother > 0 && outerpointsother[nsuroundother] != sp1other)
+	    {
+	      cerr << "OJE OJE OJE (other)" << endl;
+	      (*testout) << "OJE OJE OJE (other)" << endl;
+	      (*testout) << "pi1 " << pi1 << " pi2 " << pi2 << " sp1 " << sp1 << " sp2 " << sp2 << endl;
+	      (*testout) << "hasbothpoints: " << endl;
+	      for(int ii=0; ii < hasbothpoints.Size(); ii++)
+		{
+		  (*testout) << mesh[hasbothpoints[ii]] << endl;
+		  for(int jj=0; jj<mesh[hasbothpoints[ii]].GetNP(); jj++)
+		    if(mesh.mlbetweennodes[mesh[hasbothpoints[ii]][jj]][0] > 0)
+		      (*testout) << mesh[hasbothpoints[ii]][jj] << " between "
+				 << mesh.mlbetweennodes[mesh[hasbothpoints[ii]][jj]][0] << " and "
+				 << mesh.mlbetweennodes[mesh[hasbothpoints[ii]][jj]][1] << endl;
+		}
+	      (*testout) << "outerpoints: " << outerpoints << endl;
+	      (*testout) << "sel1 " << mesh[sel1] << endl
+			 << "sel2 " << mesh[sel2] << endl;
+	      for(int ii=0; ii<3; ii++)
+		{
+		  if(mesh.mlbetweennodes[mesh[sel1][ii]][0] > 0)
+		    (*testout) << mesh[sel1][ii] << " between "
+			       << mesh.mlbetweennodes[mesh[sel1][ii]][0] << " and "
+			       << mesh.mlbetweennodes[mesh[sel1][ii]][1] << endl;
+		  if(mesh.mlbetweennodes[mesh[sel2][ii]][0] > 0)
+		    (*testout) << mesh[sel2][ii] << " between "
+			       << mesh.mlbetweennodes[mesh[sel2][ii]][0] << " and "
+			       << mesh.mlbetweennodes[mesh[sel2][ii]][1] << endl;
+		}
+		  
+	      (*testout) << "pi1other " << pi1other << " pi2other " << pi2other << " sp1other " << sp1other << " sp2other " << sp2other << endl;
+	      (*testout) << "hasbothpointsother: " << endl;
+	      for(int ii=0; ii < hasbothpointsother.Size(); ii++)
+		{
+		  (*testout) << mesh[hasbothpointsother[ii]] << endl;
+		  for(int jj=0; jj<mesh[hasbothpointsother[ii]].GetNP(); jj++)
+		    if(mesh.mlbetweennodes[mesh[hasbothpointsother[ii]][jj]][0] > 0)
+		      (*testout) << mesh[hasbothpointsother[ii]][jj] << " between "
+				 << mesh.mlbetweennodes[mesh[hasbothpointsother[ii]][jj]][0] << " and "
+				 << mesh.mlbetweennodes[mesh[hasbothpointsother[ii]][jj]][1] << endl;
+		}
+	      (*testout) << "outerpoints: " << outerpointsother << endl;
+	      (*testout) << "sel1other " << mesh[sel1other] << endl
+			 << "sel2other " << mesh[sel2other] << endl;
+	      for(int ii=0; ii<3; ii++)
+		{
+		  if(mesh.mlbetweennodes[mesh[sel1other][ii]][0] > 0)
+		    (*testout) << mesh[sel1other][ii] << " between "
+			       << mesh.mlbetweennodes[mesh[sel1other][ii]][0] << " and "
+			       << mesh.mlbetweennodes[mesh[sel1other][ii]][1] << endl;
+		  if(mesh.mlbetweennodes[mesh[sel2other][ii]][0] > 0)
+		    (*testout) << mesh[sel2other][ii] << " between "
+			       << mesh.mlbetweennodes[mesh[sel2other][ii]][0] << " and "
+			       << mesh.mlbetweennodes[mesh[sel2other][ii]][1] << endl;
+		}
+	    }
+
+	  bad1=0;
+	  for(int i=0; i<hasbothpoints.Size(); i++)
+	    bad1 += CalcBad(mesh.Points(), mesh[hasbothpoints[i]],h);
+	  for(int i=0; i<hasbothpointsother.Size(); i++)
+	    bad1 += CalcBad(mesh.Points(), mesh[hasbothpointsother[i]],h);
+	  bad1 /= double(hasbothpoints.Size() + hasbothpointsother.Size());
+
+	  
+	  int startpoints,startpointsother;
+
+
+	  if(outerpoints.Size() == 3)
+	    startpoints = 1;
+	  else if(outerpoints.Size() == 4)
+	    startpoints = 2;
+	  else
+	    startpoints = outerpoints.Size();
+	  
+	  if(outerpointsother.Size() == 3)
+	    startpointsother = 1;
+	  else if(outerpointsother.Size() == 4)
+	    startpointsother = 2;
+	  else
+	    startpointsother = outerpointsother.Size();
+	  
+
+	  Array < Array < Element* > * > newelts(startpoints);
+	  Array < Array < Element* > * > neweltsother(startpointsother);
+
+	  double minbad = 1e50, minbadother = 1e50, currbad;
+	  int minpos = -1, minposother = -1;
+
+	  //(*testout) << "pi1 " << pi1 << " pi2 " << pi2 << " outerpoints " << outerpoints << endl;
+
+	  for(int i=0; i<startpoints; i++)
+	    {
+	      newelts[i] = new Array <Element*>(2*(nsuround-1));
+	      
+	      for(int jj=0; jj<nsuround-1; jj++)
+		{
+		  (*newelts[i])[2*jj] = new Element(TET);
+		  (*newelts[i])[2*jj+1] = new Element(TET);
+		  Element & newel1 = *((*newelts[i])[2*jj]);
+		  Element & newel2 = *((*newelts[i])[2*jj+1]);
+
+		  newel1[0] = pi1;
+		  newel1[1] = outerpoints[i];
+		  newel1[2] = outerpoints[(i+jj+1)%outerpoints.Size()];
+		  newel1[3] = outerpoints[(i+jj+2)%outerpoints.Size()];
+
+		  newel2[0] = pi2;
+		  newel2[1] = outerpoints[i];
+		  newel2[2] = outerpoints[(i+jj+2)%outerpoints.Size()];
+		  newel2[3] = outerpoints[(i+jj+1)%outerpoints.Size()];
+		  
+
+		  //(*testout) << "j " << j << " newel1 " << newel1[0] << " "<< newel1[1] << " "<< newel1[2] << " "<< newel1[3] << endl
+		  //     << " newel2 " << newel2[0] << " "<< newel2[1] << " "<< newel2[2] << " "<< newel2[3] << endl;
+		  
+		  newel1.SetIndex(mattype);
+		  newel2.SetIndex(mattype);
+
+		}
+
+	      bool wrongorientation = true;
+	      for(int jj = 0; wrongorientation && jj<newelts[i]->Size(); jj++)
+		wrongorientation = wrongorientation && WrongOrientation(mesh.Points(), *(*newelts[i])[jj]);
+	      
+	      currbad = 0;
+
+	      for(int jj=0; jj<newelts[i]->Size(); jj++)
+		{
+		  if(wrongorientation)
+		    Swap((*(*newelts[i])[jj])[2],(*(*newelts[i])[jj])[3]);
+
+
+		  // not two new faces on same surface
+		  Array<int> face_index;
+		  for(int k = 0; k<surfaceindicesonnode[(*(*newelts[i])[jj])[0]].Size(); k++)
+		    face_index.Append(surfaceindicesonnode[(*(*newelts[i])[jj])[0]][k]);
+
+		  for(int k=1; k<4; k++)
+		    {
+		      for(int l=0; l<face_index.Size(); l++)
+			{
+			  if(face_index[l] != -1 && 
+			     !(surfaceindicesonnode[(*(*newelts[i])[jj])[k]].Contains(face_index[l])))
+			    face_index[l] = -1;
+			}
+
+		    }
+		      
+		  for(int k=0; k<face_index.Size(); k++)
+		    if(face_index[k] != -1)
+		      currbad += 1e12;
+
+
+		  currbad += CalcBad(mesh.Points(),*(*newelts[i])[jj],h);
+
+
+		}  
+
+	      //currbad /= double(newelts[i]->Size());
+		    
+
+
+	      if(currbad < minbad)
+		{
+		  minbad = currbad;
+		  minpos = i;
+		}
+
+	    }
+
+	  if(startpointsother == 0)
+	    minbadother = 0;
+
+	  for(int i=0; i<startpointsother; i++)
+	    {
+	      neweltsother[i] = new Array <Element*>(2*(nsuroundother));
+	      
+	      for(int jj=0; jj<nsuroundother; jj++)
+		{
+		  (*neweltsother[i])[2*jj] = new Element(TET);
+		  (*neweltsother[i])[2*jj+1] = new Element(TET);
+		  Element & newel1 = *((*neweltsother[i])[2*jj]);
+		  Element & newel2 = *((*neweltsother[i])[2*jj+1]);
+
+		  newel1[0] = pi1other;
+		  newel1[1] = outerpointsother[i];
+		  newel1[2] = outerpointsother[(i+jj+1)%outerpointsother.Size()];
+		  newel1[3] = outerpointsother[(i+jj+2)%outerpointsother.Size()];
+
+		  newel2[0] = pi2other;
+		  newel2[1] = outerpointsother[i];
+		  newel2[2] = outerpointsother[(i+jj+2)%outerpointsother.Size()];
+		  newel2[3] = outerpointsother[(i+jj+1)%outerpointsother.Size()];
+		  
+
+		  //(*testout) << "j " << j << " newel1 " << newel1[0] << " "<< newel1[1] << " "<< newel1[2] << " "<< newel1[3] << endl
+		  //	     << " newel2 " << newel2[0] << " "<< newel2[1] << " "<< newel2[2] << " "<< newel2[3] << endl;
+		  
+		  newel1.SetIndex(othermattype);
+		  newel2.SetIndex(othermattype);
+
+		}
+
+	      bool wrongorientation = true;
+	      for(int jj = 0; wrongorientation && jj<neweltsother[i]->Size(); jj++)
+		wrongorientation = wrongorientation && WrongOrientation(mesh.Points(), *(*neweltsother[i])[jj]);
+	      
+	      currbad = 0;
+
+	      for(int jj=0; jj<neweltsother[i]->Size(); jj++)
+		{
+		  if(wrongorientation)
+		    Swap((*(*neweltsother[i])[jj])[2],(*(*neweltsother[i])[jj])[3]);
+
+		  currbad += CalcBad(mesh.Points(),*(*neweltsother[i])[jj],h);
+		}  
+
+	      //currbad /= double(neweltsother[i]->Size());
+		    
+
+
+	      if(currbad < minbadother)
+		{
+		  minbadother = currbad;
+		  minposother = i;
+		}
+
+	    }
+
+	  //(*testout) << "minbad " << minbad << " bad1 " << bad1 << endl;
+
+	  
+	  double sbadnew = CalcTriangleBadness (mesh[pi1],mesh[sp2],mesh[sp1],0,0) + 
+	    CalcTriangleBadness (mesh[pi2],mesh[sp1],mesh[sp2],0,0);
+	  
+
+	  int denom = newelts[minpos]->Size();
+	  if(minposother >= 0)
+	    denom += neweltsother[minposother]->Size();
+	  
+
+	  if((minbad+minbadother)/double(denom) < bad1 && 
+	     sbadnew < sbad)
+	    {
+	      cnt++;
+
+	      swapped = true;
+
+
+	      int start1 = -1;
+	      for(int l=0; l<3; l++)
+		if(mesh[sel1][l] == pi1)
+		  start1 = l;
+	      if(mesh[sel1][(start1+1)%3] == pi2)
+		{
+		  mesh[sel1][0] = pi1;
+		  mesh[sel1][1] = sp2;
+		  mesh[sel1][2] = sp1;
+		  mesh[sel2][0] = pi2;
+		  mesh[sel2][1] = sp1;
+		  mesh[sel2][2] = sp2;
+		}
+	      else
+		{
+		  mesh[sel1][0] = pi2;
+		  mesh[sel1][1] = sp2;
+		  mesh[sel1][2] = sp1;
+		  mesh[sel2][0] = pi1;
+		  mesh[sel2][1] = sp1;
+		  mesh[sel2][2] = sp2;
+		}
+	      //(*testout) << "changed surface element " << sel1 << " to " << mesh[sel1] << ", " << sel2 << " to " << mesh[sel2] << endl;
+
+	      for(int l=0; l<3; l++)
+		{
+		  surfaceelementsonnode.Add(mesh[sel1][l],sel1);
+		  surfaceelementsonnode.Add(mesh[sel2][l],sel2);
+		}
+	      
+
+
+	      if(periodic)
+		{
+		  start1 = -1;
+		  for(int l=0; l<3; l++)
+		    if(mesh[sel1other][l] == pi1other)
+		      start1 = l;
+		  
+
+
+		  //(*testout) << "changed surface elements " << mesh[sel1other] << " and " << mesh[sel2other] << endl;
+		  if(mesh[sel1other][(start1+1)%3] == pi2other)
+		    {
+		      mesh[sel1other][0] = pi1other;
+		      mesh[sel1other][1] = sp2other;
+		      mesh[sel1other][2] = sp1other;
+		      mesh[sel2other][0] = pi2other;
+		      mesh[sel2other][1] = sp1other;
+		      mesh[sel2other][2] = sp2other;
+		      //(*testout) << "       with rule 1" << endl;
+		    }
+		  else
+		    {
+		      mesh[sel1other][0] = pi2other;
+		      mesh[sel1other][1] = sp2other;
+		      mesh[sel1other][2] = sp1other;
+		      mesh[sel2other][0] = pi1other;
+		      mesh[sel2other][1] = sp1other;
+		      mesh[sel2other][2] = sp2other;
+		      //(*testout) << "       with rule 2" << endl;
+		    }
+		  //(*testout) << "         to " << mesh[sel1other] << " and " << mesh[sel2other] << endl;
+		  
+		  //(*testout) << "  and surface element " << sel1other << " to " << mesh[sel1other] << ", " << sel2other << " to " << mesh[sel2other] << endl;
+
+		  for(int l=0; l<3; l++)
+		    {
+		      surfaceelementsonnode.Add(mesh[sel1other][l],sel1other);
+		      surfaceelementsonnode.Add(mesh[sel2other][l],sel2other);
+		    }
+		}
+
+
+
+
+	      for(int i=0; i<hasbothpoints.Size(); i++)
+		{
+		  mesh[hasbothpoints[i]] = *(*newelts[minpos])[i];
+
+		  for(int l=0; l<4; l++)
+		    elementsonnode.Add((*(*newelts[minpos])[i])[l],hasbothpoints[i]);
+		}
+
+	      for(int i=hasbothpoints.Size(); i<(*newelts[minpos]).Size(); i++)
+		{
+		  ElementIndex ni = mesh.AddVolumeElement(*(*newelts[minpos])[i]);
+		  
+		  for(int l=0; l<4; l++)
+		    elementsonnode.Add((*(*newelts[minpos])[i])[l],ni);
+		}
+
+	      if(hasbothpointsother.Size() > 0)
+		{
+		  for(int i=0; i<hasbothpointsother.Size(); i++)
+		    {
+		      mesh[hasbothpointsother[i]] = *(*neweltsother[minposother])[i];
+		      for(int l=0; l<4; l++)
+			elementsonnode.Add((*(*neweltsother[minposother])[i])[l],hasbothpointsother[i]);
+		    }
+		  
+		  for(int i=hasbothpointsother.Size(); i<(*neweltsother[minposother]).Size(); i++)
+		    {
+		      ElementIndex ni = mesh.AddVolumeElement(*(*neweltsother[minposother])[i]);
+		      for(int l=0; l<4; l++)
+			elementsonnode.Add((*(*neweltsother[minposother])[i])[l],ni);
+		    }
+		}
+
+	      
+
+	    }
+
+	  for(int i=0; i<newelts.Size(); i++)
+	    {
+	      for(int jj=0; jj<newelts[i]->Size(); jj++)
+		delete (*newelts[i])[jj];
+	      delete newelts[i];
+	    }
+
+	  for(int i=0; i<neweltsother.Size(); i++)
+	    {
+	      for(int jj=0; jj<neweltsother[i]->Size(); jj++)
+		delete (*neweltsother[i])[jj];
+	      delete neweltsother[i];
+	    }
+	
+	}
+    }
+
+  PrintMessage (5, cnt, " swaps performed");
+
+
+  for(int i=0; i<locidmaps.Size(); i++)
+    delete locidmaps[i];
+
+
+  mesh.Compress ();
+
+  multithread.task = savetask;
+}
+  
+
+
+
+
+
+
+
+/*
+  2 -> 3 conversion
+*/
+
+
+void MeshOptimize3d :: SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal)
+{
+  PointIndex pi1(0), pi2(0), pi3(0), pi4(0), pi5(0);
+  Element el21(TET), el22(TET), el31(TET), el32(TET), el33(TET);
+
+  int cnt = 0;
+  double bad1, bad2;
+
+  int np = mesh.GetNP();
+  int ne = mesh.GetNE();
+  int nse = mesh.GetNSE();
+
+  if (goal == OPT_CONFORM) return;
+
+  // contains at least all elements at node
+  TABLE<ElementIndex, PointIndex::BASE> elementsonnode(np); 
+  TABLE<SurfaceElementIndex, PointIndex::BASE> belementsonnode(np);
+
+  PrintMessage (3, "SwapImprove2 ");
+  (*testout) << "\n" << "Start SwapImprove2" << "\n";
+  //  TestOk();
+
+
+  /*
+    CalcSurfacesOfNode ();
+    for (i = 1; i <= GetNE(); i++)
+    if (volelements.Get(i)[0])
+    if (!mesh.LegalTet (volelements.Get(i)))
+    {
+    cout << "detected illegal tet, 1" << endl;
+    (*testout) << "detected illegal tet1: " << i << endl;
+    }
+  */
+
+  
+  // Calculate total badness
+
+  bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements());
+  (*testout) << "Total badness = " << bad1 << endl;
+  //  cout << "tot bad = " << bad1 << endl;
+
+  // find elements on node
+
+  for (ElementIndex ei = 0; ei < ne; ei++)
+    for (int j = 0; j < mesh[ei].GetNP(); j++)
+      elementsonnode.Add (mesh[ei][j], ei);
+
+  for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+    for (int j = 0; j < 3; j++)
+      belementsonnode.Add (mesh[sei][j], sei);
+
+  for (ElementIndex eli1 = 0; eli1 < ne; eli1++)
+    {
+      if (multithread.terminate)
+	break;
+
+      if (mesh.ElementType (eli1) == FIXEDELEMENT)
+	continue;
+
+      if (mesh[eli1].GetType() != TET)
+	continue;
+
+      if ((goal == OPT_LEGAL) && 
+	  mesh.LegalTet (mesh[eli1]) &&
+	  CalcBad (mesh.Points(), mesh[eli1], 0) < 1e3)
+	continue;
+
+      // cout << "eli = " << eli1 << endl;
+      //      (*testout) << "swapimp2, eli = " << eli1 << "; el = " << mesh[eli1] << endl;
+
+      for (int j = 0; j < 4; j++)
+	{
+	  // loop over faces
+      
+	  Element & elem = mesh[eli1];
+	  // if (elem[0] < PointIndex::BASE) continue;
+	  if (elem.IsDeleted()) continue;
+
+	  int mattyp = elem.GetIndex();
+	  
+	  switch (j)
+	    {
+	    case 0:
+	      pi1 = elem.PNum(1); pi2 = elem.PNum(2); 
+	      pi3 = elem.PNum(3); pi4 = elem.PNum(4);
+	      break;
+	    case 1:
+	      pi1 = elem.PNum(1); pi2 = elem.PNum(4); 
+	      pi3 = elem.PNum(2); pi4 = elem.PNum(3);
+	      break;
+	    case 2:
+	      pi1 = elem.PNum(1); pi2 = elem.PNum(3); 
+	      pi3 = elem.PNum(4); pi4 = elem.PNum(2);
+	      break;
+	    case 3:
+	      pi1 = elem.PNum(2); pi2 = elem.PNum(4); 
+	      pi3 = elem.PNum(3); pi4 = elem.PNum(1);
+	      break;
+	    }
+	  
+
+	  bool bface = 0;
+	  for (int k = 0; k < belementsonnode[pi1].Size(); k++)
+	    {
+	      const Element2d & bel = 
+		mesh[belementsonnode[pi1][k]];
+
+	      bool bface1 = 1;
+	      for (int l = 0; l < 3; l++)
+		if (bel[l] != pi1 && bel[l] != pi2 && bel[l] != pi3)
+		  {
+		    bface1 = 0;
+		    break;
+		  }
+
+	      if (bface1) 
+		{
+		  bface = 1;
+		  break;
+		}
+	    }
+	  
+	  if (bface) continue;
+
+
+	  FlatArray<ElementIndex> row = elementsonnode[pi1];
+	  for (int k = 0; k < row.Size(); k++)
+	    {
+	      ElementIndex eli2 = row[k];
+
+	      // cout << "\rei1 = " << eli1 << ", pi1 = " << pi1 << ", k = " << k << ", ei2 = " << eli2 
+	      // << ", getne = " << mesh.GetNE();
+
+	      if ( eli1 != eli2 )
+		{
+		  Element & elem2 = mesh[eli2];
+		  if (elem2.IsDeleted()) continue;
+		  if (elem2.GetType() != TET)
+		    continue;
+		  
+		  int comnodes=0;
+		  for (int l = 1; l <= 4; l++)
+		    if (elem2.PNum(l) == pi1 || elem2.PNum(l) == pi2 ||
+			elem2.PNum(l) == pi3)
+		      {
+			comnodes++;
+		      }
+		    else
+		      {
+			pi5 = elem2.PNum(l);
+		      }
+		  
+		  if (comnodes == 3)
+		    {
+		      bad1 = CalcBad (mesh.Points(), elem, 0) + 
+			CalcBad (mesh.Points(), elem2, 0); 
+		      
+		      if (!mesh.LegalTet(elem) || 
+			  !mesh.LegalTet(elem2))
+			bad1 += 1e4;
+
+		      
+		      el31.PNum(1) = pi1;
+		      el31.PNum(2) = pi2;
+		      el31.PNum(3) = pi5;
+		      el31.PNum(4) = pi4;
+		      el31.SetIndex (mattyp);
+		      
+		      el32.PNum(1) = pi2;
+		      el32.PNum(2) = pi3;
+		      el32.PNum(3) = pi5;
+		      el32.PNum(4) = pi4;
+		      el32.SetIndex (mattyp);
+		      
+		      el33.PNum(1) = pi3;
+		      el33.PNum(2) = pi1;
+		      el33.PNum(3) = pi5;
+		      el33.PNum(4) = pi4;
+		      el33.SetIndex (mattyp);
+		      
+		      bad2 = CalcBad (mesh.Points(), el31, 0) + 
+			CalcBad (mesh.Points(), el32, 0) +
+			CalcBad (mesh.Points(), el33, 0); 
+		      
+
+		      el31.flags.illegal_valid = 0;
+		      el32.flags.illegal_valid = 0;
+		      el33.flags.illegal_valid = 0;
+
+		      if (!mesh.LegalTet(el31) || 
+			  !mesh.LegalTet(el32) ||
+			  !mesh.LegalTet(el33))
+			bad2 += 1e4;
+
+
+		      bool do_swap = (bad2 < bad1);
+
+		      if ( ((bad2 < 1e6) || (bad2 < 10 * bad1)) &&
+			   mesh.BoundaryEdge (pi4, pi5))
+			do_swap = 1;
+			   
+		      if (do_swap)
+			{
+			  //			  cout << "do swap, eli1 = " << eli1 << "; eli2 = " << eli2 << endl;
+			  //			  (*mycout) << "2->3 " << flush;
+			  cnt++;
+
+			  el31.flags.illegal_valid = 0;
+			  el32.flags.illegal_valid = 0;
+			  el33.flags.illegal_valid = 0;
+
+			  mesh[eli1] = el31;
+			  mesh[eli2] = el32;
+			  
+			  ElementIndex neli =
+			    mesh.AddVolumeElement (el33);
+			  
+			  /*
+			    if (!LegalTet(el31) || !LegalTet(el32) ||
+			    !LegalTet(el33))
+			    {
+			    cout << "Swap to illegal tets !!!" << endl;
+			    }
+			  */
+			  // cout << "neli = " << neli << endl;
+			  for (int l = 0; l < 4; l++)
+			    {
+			      elementsonnode.Add (el31[l], eli1);
+			      elementsonnode.Add (el32[l], eli2);
+			      elementsonnode.Add (el33[l], neli);
+			    }
+
+			  break;
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+
+  PrintMessage (5, cnt, " swaps performed");
+
+
+
+  /*
+    CalcSurfacesOfNode ();
+    for (i = 1; i <= GetNE(); i++)
+    if (volelements.Get(i).PNum(1))
+    if (!LegalTet (volelements.Get(i)))
+    {
+    cout << "detected illegal tet, 2" << endl;
+    (*testout) << "detected illegal tet2: " << i << endl;
+    }
+  */
+
+
+  bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements());
+  (*testout) << "Total badness = " << bad1 << endl;
+  (*testout) << "swapimprove2 done" << "\n";
+  //  (*mycout) << "Vol = " << CalcVolume (points, volelements) << "\n";
+}
+
+
+/*
+  void Mesh :: SwapImprove2 (OPTIMIZEGOAL goal)
+  {
+  int i, j;
+  int eli1, eli2;
+  int mattyp;
+
+  Element el31(4), el32(4), el33(4);
+  double bad1, bad2;
+
+
+  INDEX_3_HASHTABLE<INDEX_2> elsonface (GetNE());
+
+  (*mycout) << "SwapImprove2 " << endl;
+  (*testout) << "\n" << "Start SwapImprove2" << "\n";
+
+  // Calculate total badness
+
+  if (goal == OPT_QUALITY)
+  {
+  double bad1 = CalcTotalBad (points, volelements);
+  (*testout) << "Total badness = " << bad1 << endl;
+  }
+
+  // find elements on node
+
+
+  Element2d face;
+  for (i = 1; i <= GetNE(); i++)
+  if ( (i > eltyps.Size()) || (eltyps.Get(i) != FIXEDELEMENT) )
+  {
+  const Element & el = VolumeElement(i);
+  if (!el.PNum(1)) continue;
+
+  for (j = 1; j <= 4; j++)
+  {
+  el.GetFace (j, face);
+  INDEX_3 i3 (face.PNum(1), face.PNum(2), face.PNum(3));
+  i3.Sort();
+
+
+  int bnr, posnr;
+  if (!elsonface.PositionCreate (i3, bnr, posnr))
+  {
+  INDEX_2 i2;
+  elsonface.GetData (bnr, posnr, i3, i2);
+  i2.I2() = i;
+  elsonface.SetData (bnr, posnr, i3, i2);
+  }
+  else
+  {
+  INDEX_2 i2 (i, 0);
+  elsonface.SetData (bnr, posnr, i3, i2);
+  }
+
+  //  	    if (elsonface.Used (i3))
+  //  	      {
+  //  		INDEX_2 i2 = elsonface.Get(i3);
+  //  		i2.I2() = i;
+  //  		elsonface.Set (i3, i2);
+  //  	      }
+  //  	    else
+  //  	      {
+  //  		INDEX_2 i2 (i, 0);
+  //  		elsonface.Set (i3, i2);
+  //  	      }
+
+  }
+  }
+
+  BitArray original(GetNE());
+  original.Set();
+
+  for (i = 1; i <= GetNSE(); i++)
+  {
+  const Element2d & sface = SurfaceElement(i);
+  INDEX_3 i3 (sface.PNum(1), sface.PNum(2), sface.PNum(3));
+  i3.Sort();
+  INDEX_2 i2(0,0);
+  elsonface.Set (i3, i2);
+  }
+
+
+  for (i = 1; i <= elsonface.GetNBags(); i++)
+  for (j = 1; j <= elsonface.GetBagSize(i); j++)
+  {
+  INDEX_3 i3;
+  INDEX_2 i2;
+  elsonface.GetData (i, j, i3, i2);
+
+
+  int eli1 = i2.I1();
+  int eli2 = i2.I2();
+
+  if (eli1 && eli2 && original.Test(eli1) && original.Test(eli2) )
+  {
+  Element & elem = volelements.Elem(eli1);
+  Element & elem2 = volelements.Elem(eli2);
+
+  int pi1 = i3.I1();
+  int pi2 = i3.I2();
+  int pi3 = i3.I3();
+
+  int pi4 = elem.PNum(1) + elem.PNum(2) + elem.PNum(3) + elem.PNum(4) - pi1 - pi2 - pi3;
+  int pi5 = elem2.PNum(1) + elem2.PNum(2) + elem2.PNum(3) + elem2.PNum(4) - pi1 - pi2 - pi3;
+
+
+
+
+
+
+  el31.PNum(1) = pi1;
+  el31.PNum(2) = pi2;
+  el31.PNum(3) = pi3;
+  el31.PNum(4) = pi4;
+  el31.SetIndex (mattyp);
+	    
+  if (WrongOrientation (points, el31))
+  swap (pi1, pi2);
+
+
+  bad1 = CalcBad (points, elem, 0) + 
+  CalcBad (points, elem2, 0); 
+	    
+  //	    if (!LegalTet(elem) || !LegalTet(elem2))
+  //	      bad1 += 1e4;
+
+	    
+  el31.PNum(1) = pi1;
+  el31.PNum(2) = pi2;
+  el31.PNum(3) = pi5;
+  el31.PNum(4) = pi4;
+  el31.SetIndex (mattyp);
+	    
+  el32.PNum(1) = pi2;
+  el32.PNum(2) = pi3;
+  el32.PNum(3) = pi5;
+  el32.PNum(4) = pi4;
+  el32.SetIndex (mattyp);
+		      
+  el33.PNum(1) = pi3;
+  el33.PNum(2) = pi1;
+  el33.PNum(3) = pi5;
+  el33.PNum(4) = pi4;
+  el33.SetIndex (mattyp);
+	    
+  bad2 = CalcBad (points, el31, 0) + 
+  CalcBad (points, el32, 0) +
+  CalcBad (points, el33, 0); 
+	    
+  //	    if (!LegalTet(el31) || !LegalTet(el32) ||
+  //		!LegalTet(el33))
+  //	      bad2 += 1e4;
+	    
+	    
+  int swap = (bad2 < bad1);
+
+  INDEX_2 hi2b(pi4, pi5);
+  hi2b.Sort();
+	    
+  if ( ((bad2 < 1e6) || (bad2 < 10 * bad1)) &&
+  boundaryedges->Used (hi2b) )
+  swap = 1;
+	    
+  if (swap)
+  {
+  (*mycout) << "2->3 " << flush;
+		
+  volelements.Elem(eli1) = el31;
+  volelements.Elem(eli2) = el32;
+  volelements.Append (el33);
+		
+  original.Clear (eli1);
+  original.Clear (eli2);
+  }
+  }
+  }
+  
+  (*mycout) << endl;
+
+  if (goal == OPT_QUALITY)
+  {
+  bad1 = CalcTotalBad (points, volelements);
+  (*testout) << "Total badness = " << bad1 << endl;
+  }
+
+  //  FindOpenElements ();
+
+  (*testout) << "swapimprove2 done" << "\n";
+  }
+
+*/
+}
diff --git a/contrib/Netgen/libsrc/meshing/improve3.hpp b/contrib/Netgen/libsrc/meshing/improve3.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..41001e8426feda880b2c94a6eaff5d7638f549b7
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve3.hpp
@@ -0,0 +1,125 @@
+#ifndef FILE_IMPROVE3
+#define FILE_IMPROVE3
+
+
+extern double CalcTotalBad (const Mesh::T_POINTS & points, 
+			    const Mesh::T_VOLELEMENTS & elements,
+			    const MeshingParameters & mp);
+
+
+///
+class MeshOptimize3d
+{
+  const MeshingParameters & mp;
+public:
+  MeshOptimize3d (const MeshingParameters & amp) : mp(amp) { ; }
+  void CombineImprove (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY);
+  void SplitImprove (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY);
+  void SwapImprove (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY,
+		    const BitArray * working_elements = NULL);
+  void SwapImproveSurface (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY,
+			   const BitArray * working_elements = NULL,
+			   const Array< Array<int,PointIndex::BASE>* > * idmaps = NULL);
+  void SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY);
+
+  double 
+  CalcBad (const Mesh::T_POINTS & points, const Element & elem, double h)
+  {
+    if (elem.GetType() == TET)
+      return CalcTetBadness (points[elem[0]], points[elem[1]],  
+			     points[elem[2]], points[elem[3]], h, mp);  
+    return 0;
+  }
+
+
+  double CalcTotalBad (const Mesh::T_POINTS & points, 
+		       const Mesh::T_VOLELEMENTS & elements)
+  {
+    return netgen::CalcTotalBad (points, elements, mp);
+  }
+
+};
+
+
+inline double 
+CalcBad (const Mesh::T_POINTS & points, const Element & elem, double h, const MeshingParameters & mp)
+{
+  if (elem.GetType() == TET)
+    return CalcTetBadness (points[elem[0]], points[elem[1]],  
+			   points[elem[2]], points[elem[3]], h, mp);  
+  return 0;
+}
+
+
+
+extern int WrongOrientation (const Mesh::T_POINTS & points, const Element & el);
+
+
+/* Functional depending of inner point inside triangular surface */
+
+
+class MinFunctionSum : public MinFunction
+{
+protected:
+  Array<MinFunction*> functions;
+ 
+public:
+  
+  virtual double Func (const Vector & x) const;
+  virtual void Grad (const Vector & x, Vector & g) const;
+  virtual double FuncGrad (const Vector & x, Vector & g) const;
+  virtual double FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const;
+  virtual double GradStopping (const Vector & x) const;
+
+  void AddFunction(MinFunction & fun);
+  
+  const MinFunction & Function(int i) const;
+  MinFunction & Function(int i);  
+};
+  
+
+class PointFunction1 : public MinFunction
+{
+  Mesh::T_POINTS & points;
+  const Array<INDEX_3> & faces;
+  const MeshingParameters & mp;
+  double h;
+public:
+  PointFunction1 (Mesh::T_POINTS & apoints, 
+		  const Array<INDEX_3> & afaces,
+		  const MeshingParameters & amp,
+		  double ah);
+  
+  virtual double Func (const Vector & x) const;
+  virtual double FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const;
+  virtual double FuncGrad (const Vector & x, Vector & g) const;
+  virtual double GradStopping (const Vector & x) const;
+};
+
+class JacobianPointFunction : public MinFunction
+{
+public:
+  Mesh::T_POINTS & points;
+  const Mesh::T_VOLELEMENTS & elements;
+  TABLE<INDEX> elementsonpoint;
+  PointIndex actpind;
+
+  bool onplane;
+  Vec<3> nv;
+  
+public:
+  JacobianPointFunction (Mesh::T_POINTS & apoints, 
+			 const Mesh::T_VOLELEMENTS & aelements);
+  
+  virtual void SetPointIndex (PointIndex aactpind);
+  virtual double Func (const Vector & x) const;
+  virtual double FuncGrad (const Vector & x, Vector & g) const;
+  virtual double FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const;
+
+  inline void SetNV(const Vec<3> & anv) {nv = anv; onplane = true;}
+  inline void UnSetNV(void) {onplane = false;}
+};
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/localh.cpp b/contrib/Netgen/libsrc/meshing/localh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99767360be0a4c3641c74f7361da7851bbf6a24d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/localh.cpp
@@ -0,0 +1,708 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+
+  GradingBox :: GradingBox (const double * ax1, const double * ax2)
+  {
+    h2 = 0.5 * (ax2[0] - ax1[0]);
+    for (int i = 0; i < 3; i++)
+      xmid[i] = 0.5 * (ax1[i] + ax2[i]);
+
+    for (int i = 0; i < 8; i++)
+      childs[i] = NULL;
+    father = NULL;
+
+    flags.cutboundary = 0;
+    flags.isinner = 0;
+    flags.oldcell = 0;
+    flags.pinner = 0;
+
+    hopt = 2 * h2;
+  }
+
+
+  BlockAllocator GradingBox :: ball(sizeof (GradingBox));
+
+  void * GradingBox :: operator new(size_t)
+  {
+    return ball.Alloc();
+  }
+
+  void GradingBox :: operator delete (void * p)
+  {
+    ball.Free (p);
+  }
+
+
+
+
+
+  void GradingBox :: DeleteChilds()
+  {
+    for (int i = 0; i < 8; i++)
+      if (childs[i])
+	{
+	  childs[i]->DeleteChilds();
+	  delete childs[i];
+	  childs[i] = NULL;
+	}
+  }
+  
+
+  LocalH :: LocalH (const Point3d & pmin, const Point3d & pmax, double agrading)
+  {
+    double x1[3], x2[3];
+    double hmax;
+
+    boundingbox = Box3d (pmin, pmax);
+    grading = agrading;
+
+    // a small enlargement, non-regular points 
+    double val = 0.0879;
+    for (int i = 1; i <= 3; i++)
+      {
+	x1[i-1] = (1 + val * i) * pmin.X(i) - val * i * pmax.X(i);
+	x2[i-1] = 1.1 * pmax.X(i) - 0.1 * pmin.X(i);
+      }
+
+    hmax = x2[0] - x1[0];
+    for (int i = 1; i <= 2; i++)
+      if (x2[i] - x1[i] > hmax)
+	hmax = x2[i] - x1[i];
+
+    for (int i = 0; i <= 2; i++)
+      x2[i] = x1[i] + hmax;
+
+    root = new GradingBox (x1, x2);
+    boxes.Append (root);
+  }
+
+
+  LocalH :: LocalH (const Box<3> & box, double agrading)
+  {
+    Point3d pmin = box.PMin();
+    Point3d pmax = box.PMax();
+
+    double x1[3], x2[3];
+    double hmax;
+
+    boundingbox = Box3d (pmin, pmax);
+    grading = agrading;
+
+    // a small enlargement, non-regular points 
+    double val = 0.0879;
+    for (int i = 1; i <= 3; i++)
+      {
+	x1[i-1] = (1 + val * i) * pmin.X(i) - val * i * pmax.X(i);
+	x2[i-1] = 1.1 * pmax.X(i) - 0.1 * pmin.X(i);
+      }
+
+    hmax = x2[0] - x1[0];
+    for (int i = 1; i <= 2; i++)
+      if (x2[i] - x1[i] > hmax)
+	hmax = x2[i] - x1[i];
+
+    for (int i = 0; i <= 2; i++)
+      x2[i] = x1[i] + hmax;
+
+    root = new GradingBox (x1, x2);
+    boxes.Append (root);
+  }
+
+
+
+
+  LocalH :: ~LocalH ()
+  {
+    root->DeleteChilds();
+    delete root;
+  }
+
+  void LocalH :: Delete ()
+  {
+    root->DeleteChilds();
+  }
+
+  void LocalH :: SetH (const Point3d & p, double h)
+  {
+    /*
+      (*testout) << "Set h at " << p << " to " << h << endl;
+      if (h < 1e-8)
+      {
+      cout << "do not set h to " << h << endl;
+      return;
+      }
+    */
+
+    if (fabs (p.X() - root->xmid[0]) > root->h2 ||
+	fabs (p.Y() - root->xmid[1]) > root->h2 ||
+	fabs (p.Z() - root->xmid[2]) > root->h2)
+      return;
+
+    /*      
+	    if (p.X() < root->x1[0] || p.X() > root->x2[0] ||
+	    p.Y() < root->x1[1] || p.Y() > root->x2[1] ||
+	    p.Z() < root->x1[2] || p.Z() > root->x2[2])
+	    return;
+    */
+
+
+    if (GetH(p) <= 1.2 * h) return;
+
+
+    GradingBox * box = root;
+    GradingBox * nbox = root;
+    GradingBox * ngb;
+    int childnr;
+    double x1[3], x2[3];
+
+    while (nbox)
+      {
+	box = nbox;
+	childnr = 0;
+	if (p.X() > box->xmid[0]) childnr += 1;
+	if (p.Y() > box->xmid[1]) childnr += 2;
+	if (p.Z() > box->xmid[2]) childnr += 4;
+	nbox = box->childs[childnr];
+      };
+
+
+    while (2 * box->h2 > h)
+      {
+	childnr = 0;
+	if (p.X() > box->xmid[0]) childnr += 1;
+	if (p.Y() > box->xmid[1]) childnr += 2;
+	if (p.Z() > box->xmid[2]) childnr += 4;
+
+	double h2 = box->h2;
+	if (childnr & 1)
+	  {
+	    x1[0] = box->xmid[0];
+	    x2[0] = x1[0]+h2;   // box->x2[0];
+	  }
+	else
+	  {
+	    x2[0] = box->xmid[0];
+	    x1[0] = x2[0]-h2;   // box->x1[0];
+	  }
+
+	if (childnr & 2)
+	  {
+	    x1[1] = box->xmid[1];
+	    x2[1] = x1[1]+h2;   // box->x2[1];
+	  }
+	else
+	  {
+	    x2[1] = box->xmid[1];
+	    x1[1] = x2[1]-h2;   // box->x1[1];
+	  }
+
+	if (childnr & 4)
+	  {
+	    x1[2] = box->xmid[2];
+	    x2[2] = x1[2]+h2;  // box->x2[2];
+	  }
+	else
+	  {
+	    x2[2] = box->xmid[2];
+	    x1[2] = x2[2]-h2;  // box->x1[2];
+	  }
+
+	ngb = new GradingBox (x1, x2);
+	box->childs[childnr] = ngb;
+	ngb->father = box;
+
+	boxes.Append (ngb);
+	box = box->childs[childnr];
+      }
+
+    box->hopt = h;
+
+
+    double hbox = 2 * box->h2;  // box->x2[0] - box->x1[0];
+    double hnp = h + grading * hbox;
+
+    Point3d np;
+    for (int i = 1; i <= 3; i++)
+      {
+	np = p;
+	np.X(i) = p.X(i) + hbox;
+	SetH (np, hnp);
+
+	np.X(i) = p.X(i) - hbox;
+	SetH (np, hnp);
+      }
+  }
+
+
+
+  double LocalH :: GetH (const Point3d & x) const
+  {
+    const GradingBox * box = root;
+
+    while (1)
+      {
+	int childnr = 0;
+	if (x.X() > box->xmid[0]) childnr += 1;
+	if (x.Y() > box->xmid[1]) childnr += 2;
+	if (x.Z() > box->xmid[2]) childnr += 4;
+
+	if (box->childs[childnr])
+	  box = box->childs[childnr];
+	else
+	  return box->hopt;
+      }
+  }
+
+
+  /// minimal h in box (pmin, pmax)
+  double LocalH :: GetMinH (const Point3d & pmin, const Point3d & pmax) const
+  { 
+    Point3d pmin2, pmax2;
+    for (int j = 1; j <= 3; j++)
+      if (pmin.X(j) < pmax.X(j))
+	{ pmin2.X(j) = pmin.X(j); pmax2.X(j) = pmax.X(j); }
+      else
+	{ pmin2.X(j) = pmax.X(j); pmax2.X(j) = pmin.X(j); }
+
+    return GetMinHRec (pmin2, pmax2, root); 
+  }
+
+
+  double LocalH :: GetMinHRec (const Point3d & pmin, const Point3d & pmax,
+			       const GradingBox * box) const
+  {
+    double h2 = box->h2;
+    if (pmax.X() < box->xmid[0]-h2 || pmin.X() > box->xmid[0]+h2 ||
+	pmax.Y() < box->xmid[1]-h2 || pmin.Y() > box->xmid[1]+h2 ||
+	pmax.Z() < box->xmid[2]-h2 || pmin.Z() > box->xmid[2]+h2)
+      return 1e8;
+      
+    double hmin = 2 * box->h2; // box->x2[0] - box->x1[0];
+  
+    for (int i = 0; i < 8; i++)
+      if (box->childs[i])
+	hmin = min2 (hmin, GetMinHRec (pmin, pmax, box->childs[i]));
+
+    return hmin;
+  }
+
+
+  void LocalH :: CutBoundaryRec (const Point3d & pmin, const Point3d & pmax,
+				 GradingBox * box)
+  {
+    double h2 = box->h2;
+    if (pmax.X() < box->xmid[0]-h2 || pmin.X() > box->xmid[0]+h2 ||
+	pmax.Y() < box->xmid[1]-h2 || pmin.Y() > box->xmid[1]+h2 ||
+	pmax.Z() < box->xmid[2]-h2 || pmin.Z() > box->xmid[2]+h2)
+      return;
+
+
+    box->flags.cutboundary = 1;
+    for (int i = 0; i < 8; i++)
+      if (box->childs[i])
+	CutBoundaryRec (pmin, pmax, box->childs[i]);
+  }
+
+
+
+
+  void LocalH :: FindInnerBoxes (AdFront3 * adfront,
+				 int (*testinner)(const Point3d & p1))
+  {
+    int nf = adfront->GetNF();
+
+    for (int i = 0; i < boxes.Size(); i++)
+      boxes[i] -> flags.isinner = 0;
+
+    root->flags.isinner = 0;
+
+    Point3d rpmid(root->xmid[0], root->xmid[1], root->xmid[2]);
+    Vec3d rv(root->h2, root->h2, root->h2);
+    Point3d rx2 = rpmid + rv;
+    // Point3d rx1 = rpmid - rv;
+
+
+    root->flags.pinner = !adfront->SameSide (rpmid, rx2);
+  
+    if (testinner)
+      (*testout) << "inner = " << root->flags.pinner << " =?= " 
+		 << testinner(Point3d(root->xmid[0], root->xmid[1], root->xmid[2])) << endl;
+  
+    Array<int> faceinds(nf);
+    Array<Box3d> faceboxes(nf);
+
+    for (int i = 1; i <= nf; i++)
+      {
+	faceinds.Elem(i) = i;
+	adfront->GetFaceBoundingBox(i, faceboxes.Elem(i));
+      }
+  
+    for (int i = 0; i < 8; i++)
+      FindInnerBoxesRec2 (root->childs[i], adfront, faceboxes, faceinds, nf);
+  }
+
+
+  void LocalH :: 
+  FindInnerBoxesRec2 (GradingBox * box,
+		      class AdFront3 * adfront, 
+		      Array<Box3d> & faceboxes,
+		      Array<int> & faceinds, int nfinbox)
+  {
+    if (!box) return;
+  
+    GradingBox * father = box -> father;
+  
+    Point3d c(box->xmid[0], box->xmid[1], box->xmid[2]);
+    Vec3d v(box->h2, box->h2, box->h2);
+    Box3d boxc(c-v, c+v);
+
+    Point3d fc(father->xmid[0], father->xmid[1], father->xmid[2]);
+    Vec3d fv(father->h2, father->h2, father->h2);
+    Box3d fboxc(fc-fv, fc+fv);
+
+    Box3d boxcfc(c,fc);
+
+    ArrayMem<int, 100> faceused;
+    ArrayMem<int, 100> faceused2;
+    ArrayMem<int, 100> facenotused;
+
+    /*
+    faceused.SetSize(0);
+    facenotused.SetSize(0);
+    faceused2.SetSize(0);
+    */
+
+    for (int j = 1; j <= nfinbox; j++)
+      {
+	//      adfront->GetFaceBoundingBox (faceinds.Get(j), facebox);
+	const Box3d & facebox = faceboxes.Get(faceinds.Get(j));
+  
+	if (boxc.Intersect (facebox))
+	  faceused.Append(faceinds.Get(j));
+	else
+	  facenotused.Append(faceinds.Get(j));
+
+	if (boxcfc.Intersect (facebox))
+	  faceused2.Append (faceinds.Get(j));
+      }
+  
+    for (int j = 1; j <= faceused.Size(); j++)
+      faceinds.Elem(j) = faceused.Get(j);
+    for (int j = 1; j <= facenotused.Size(); j++)
+      faceinds.Elem(j+faceused.Size()) = facenotused.Get(j);
+
+  
+    if (!father->flags.cutboundary)
+      {
+	box->flags.isinner = father->flags.isinner;
+	box->flags.pinner = father->flags.pinner;
+      }
+    else
+      {
+	Point3d cf(father->xmid[0], father->xmid[1], father->xmid[2]);
+      
+	if (father->flags.isinner)
+	  box->flags.pinner = 1;
+	else
+	  {
+	    if (adfront->SameSide (c, cf, &faceused2))
+	      box->flags.pinner = father->flags.pinner;
+	    else
+	      box->flags.pinner = 1 - father->flags.pinner;
+	  }
+      
+	if (box->flags.cutboundary)
+	  box->flags.isinner = 0;
+	else
+	  box->flags.isinner = box->flags.pinner;
+      }
+
+    // cout << "faceused: " << faceused.Size() << ", " << faceused2.Size() << ", " << facenotused.Size() << endl;
+
+    int nf = faceused.Size();
+    for (int i = 0; i < 8; i++)
+      FindInnerBoxesRec2 (box->childs[i], adfront, faceboxes, faceinds, nf);
+  }
+
+
+
+  void LocalH :: FindInnerBoxesRec ( int (*inner)(const Point3d & p),
+				     GradingBox * box)
+  {
+    if (box->flags.cutboundary)
+      {
+	for (int i = 0; i < 8; i++)
+	  if (box->childs[i])
+	    FindInnerBoxesRec (inner, box->childs[i]);
+      }
+    else
+      {
+	if (inner (box->PMid()))
+	  SetInnerBoxesRec (box);
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  void LocalH :: FindInnerBoxes (AdFront2 * adfront,
+				 int (*testinner)(const Point<2> & p1))
+  {
+    int nf = adfront->GetNFL();
+
+    for (int i = 0; i < boxes.Size(); i++)
+      boxes[i] -> flags.isinner = 0;
+
+    root->flags.isinner = 0;
+
+    Point<2> rpmid(root->xmid[0], root->xmid[1], root->xmid[2]);
+    Vec<2> rv(root->h2, root->h2);
+    Point<2> rx2 = rpmid + rv;
+    // Point<2> rx1 = rpmid - rv;
+
+
+    root->flags.pinner = !adfront->SameSide (rpmid, rx2);
+  
+    if (testinner)
+      (*testout) << "inner = " << root->flags.pinner << " =?= "
+		 << testinner(rpmid) << endl;
+  
+    Array<int> faceinds(nf);
+    Array<Box<3> > faceboxes(nf);
+
+    for (int i = 0; i < nf; i++)
+      {
+	faceinds[i] = i;
+	// adfront->GetFaceBoundingBox(i, faceboxes.Elem(i));
+
+	const FrontLine & line = adfront->GetLine(i);
+	faceboxes[i].Set (adfront->GetPoint (line.L().I1()));
+	faceboxes[i].Add (adfront->GetPoint (line.L().I2()));
+
+      }
+  
+    for (int i = 0; i < 8; i++)
+      FindInnerBoxesRec2 (root->childs[i], adfront, faceboxes, faceinds, nf);
+  }
+
+
+  void LocalH :: 
+  FindInnerBoxesRec2 (GradingBox * box,
+		      class AdFront2 * adfront, 
+		      Array<Box<3> > & faceboxes,
+		      Array<int> & faceinds, int nfinbox)
+  {
+    if (!box) return;
+  
+    GradingBox * father = box -> father;
+  
+    Point3d c(box->xmid[0], box->xmid[1], box->xmid[2]);
+    Vec3d v(box->h2, box->h2, box->h2);
+    Box3d boxc(c-v, c+v);
+
+    Point3d fc(father->xmid[0], father->xmid[1], father->xmid[2]);
+    Vec3d fv(father->h2, father->h2, father->h2);
+    Box3d fboxc(fc-fv, fc+fv);
+
+    Box3d boxcfc(c,fc);
+
+    ArrayMem<int, 100> faceused;
+    ArrayMem<int, 100> faceused2;
+    ArrayMem<int, 100> facenotused;
+
+
+    for (int j = 1; j <= nfinbox; j++)
+      {
+	//      adfront->GetFaceBoundingBox (faceinds.Get(j), facebox);
+	const Box3d & facebox = faceboxes.Get(faceinds.Get(j));
+  
+	if (boxc.Intersect (facebox))
+	  faceused.Append(faceinds.Get(j));
+	else
+	  facenotused.Append(faceinds.Get(j));
+
+	if (boxcfc.Intersect (facebox))
+	  faceused2.Append (faceinds.Get(j));
+      }
+  
+    for (int j = 1; j <= faceused.Size(); j++)
+      faceinds.Elem(j) = faceused.Get(j);
+    for (int j = 1; j <= facenotused.Size(); j++)
+      faceinds.Elem(j+faceused.Size()) = facenotused.Get(j);
+
+  
+    if (!father->flags.cutboundary)
+      {
+	box->flags.isinner = father->flags.isinner;
+	box->flags.pinner = father->flags.pinner;
+      }
+    else
+      {
+	Point3d cf(father->xmid[0], father->xmid[1], father->xmid[2]);
+      
+	if (father->flags.isinner)
+	  box->flags.pinner = 1;
+	else
+	  {
+	    Point<2> c2d (c.X(), c.Y());
+	    Point<2> cf2d (cf.X(), cf.Y());
+	    if (adfront->SameSide (c2d, cf2d, &faceused2))
+	      box->flags.pinner = father->flags.pinner;
+	    else
+	      box->flags.pinner = 1 - father->flags.pinner;
+	  }
+      
+	if (box->flags.cutboundary)
+	  box->flags.isinner = 0;
+	else
+	  box->flags.isinner = box->flags.pinner;
+      }
+
+    // cout << "faceused: " << faceused.Size() << ", " << faceused2.Size() << ", " << facenotused.Size() << endl;
+
+    int nf = faceused.Size();
+    for (int i = 0; i < 8; i++)
+      FindInnerBoxesRec2 (box->childs[i], adfront, faceboxes, faceinds, nf);
+  }
+
+
+
+  void LocalH :: FindInnerBoxesRec ( int (*inner)(const Point<2> & p),
+				     GradingBox * box)
+  {
+    if (box->flags.cutboundary)
+      {
+	for (int i = 0; i < 8; i++)
+	  if (box->childs[i])
+	    FindInnerBoxesRec (inner, box->childs[i]);
+      }
+    else
+      {
+	Point<2> p2d(box->PMid()(0), box->PMid()(1)); 
+	if (inner (p2d))
+	  SetInnerBoxesRec (box);
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  void LocalH :: SetInnerBoxesRec (GradingBox * box)
+  {
+    box->flags.isinner = 1;
+    for (int i = 0; i < 8; i++)
+      if (box->childs[i])
+	ClearFlagsRec (box->childs[i]);
+  }
+
+  void LocalH :: ClearFlagsRec (GradingBox * box)
+  {
+    box->flags.cutboundary = 0;
+    box->flags.isinner = 0;
+    for (int i = 0; i < 8; i++)
+      if (box->childs[i])
+	ClearFlagsRec (box->childs[i]);
+  }
+
+
+  void LocalH :: WidenRefinement ()
+  {
+    for (int i = 0; i < boxes.Size(); i++)
+      {
+	double h = boxes[i]->hopt;
+	Point3d c = boxes[i]->PMid();
+      
+	for (int i1 = -1; i1 <= 1; i1++)
+	  for (int i2 = -1; i2 <= 1; i2++)
+	    for (int i3 = -1; i3 <= 1; i3++)
+	      SetH (Point3d (c.X() + i1 * h, 
+			     c.Y() + i2 * h,
+			     c.Z() + i3 * h), 1.001 * h);     
+      }
+  }
+
+  void LocalH :: GetInnerPoints (Array<Point<3> > & points)
+  {
+    for (int i = 0; i < boxes.Size(); i++)
+      if (boxes[i] -> flags.isinner)
+	points.Append ( boxes[i] -> PMid() );
+  }
+
+
+  void LocalH :: GetOuterPoints (Array<Point<3> > & points)
+  {
+    for (int i = 0; i < boxes.Size(); i++)
+      if (!boxes[i]->flags.isinner && !boxes[i]->flags.cutboundary)
+	points.Append ( boxes[i] -> PMid());
+  }
+
+
+  void LocalH :: Convexify ()
+  {
+    ConvexifyRec (root);
+  }
+
+  void LocalH :: ConvexifyRec (GradingBox * box)
+  {
+    Point<3> center = box -> PMid();
+
+    double size = 2 * box->h2; // box->x2[0] - box->x1[0];
+    double dx = 0.6 * size;
+
+    double maxh = box->hopt;
+  
+    for (int i = 0; i < 3; i++)
+      {
+	Point<3> hp = center;
+	hp(i) += dx;
+	maxh = max2 (maxh, GetH(hp));
+	hp(i) = center(i)-dx;
+	maxh = max2 (maxh, GetH(hp));
+      }
+
+    if (maxh < 0.95 * box->hopt)
+      SetH (center, maxh);
+
+    for (int i = 0; i < 8; i++)
+      if (box->childs[i])
+	ConvexifyRec (box->childs[i]);  
+  }
+
+  void LocalH :: PrintMemInfo (ostream & ost) const
+  {
+    ost << "LocalH: " << boxes.Size() << " boxes of " << sizeof(GradingBox)
+	<< " bytes = " << boxes.Size()*sizeof(GradingBox) << " bytes" << endl;
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/localh.hpp b/contrib/Netgen/libsrc/meshing/localh.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ee279c8696d42bfb16ece0c209febd67a2a2315c
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/localh.hpp
@@ -0,0 +1,187 @@
+#ifndef LOCALH
+#define LOCALH
+
+/**************************************************************************/
+/* File:   localh.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   29. Jan. 97                                                    */
+/**************************************************************************/
+
+
+namespace netgen
+{
+
+
+  /// box for grading
+  class GradingBox
+  {
+    /// xmid
+    float xmid[3];
+    /// half edgelength
+    float h2;
+    ///
+    GradingBox * childs[8];
+    ///
+    GradingBox * father;
+    ///
+    double hopt;
+    ///
+  public:
+
+    struct 
+    {
+      unsigned int cutboundary:1;
+      unsigned int isinner:1;
+      unsigned int oldcell:1;
+      unsigned int pinner:1;
+    } flags;
+
+    ///
+    GradingBox (const double * ax1, const double * ax2);
+    ///
+    void DeleteChilds();
+    ///
+
+    Point<3> PMid() const { return Point<3> (xmid[0], xmid[1], xmid[2]); }
+    double H2() const { return h2; }
+
+    friend class LocalH;
+
+    static BlockAllocator ball;
+    void * operator new(size_t);
+    void operator delete (void *);
+  };
+
+
+
+
+  /**
+     Control of 3D mesh grading
+  */
+  class LocalH 
+  {
+    ///
+    GradingBox * root;
+    ///
+    double grading;
+    ///
+    Array<GradingBox*> boxes;
+    ///
+    Box3d boundingbox;
+  public:
+    ///
+    LocalH (const Point3d & pmin, const Point3d & pmax, double grading);
+    ///
+    LocalH (const Box<3> & box, double grading);
+    ///
+    ~LocalH();
+    ///
+    void Delete();
+    ///
+    void SetGrading (double agrading) { grading = agrading; }
+    ///
+    void SetH (const Point3d & x, double h);
+    ///
+    double GetH (const Point3d & x) const;
+    /// minimal h in box (pmin, pmax)
+    double GetMinH (const Point3d & pmin, const Point3d & pmax) const;
+
+    /// mark boxes intersecting with boundary-box
+    // void CutBoundary (const Point3d & pmin, const Point3d & pmax)
+    // { CutBoundaryRec (pmin, pmax, root); }
+    void CutBoundary (const Box<3> & box)
+    { CutBoundaryRec (box.PMin(), box.PMax(), root); }
+  
+    /// find inner boxes
+    void FindInnerBoxes (class AdFront3 * adfront,
+			 int (*testinner)(const Point3d & p1));
+
+    void FindInnerBoxes (class AdFront2 * adfront,
+			 int (*testinner)(const Point<2> & p1));
+
+
+    /// clears all flags 
+    void ClearFlags ()
+    { ClearFlagsRec(root); }
+
+    /// widen refinement zone
+    void WidenRefinement ();
+
+    /// get points in inner elements
+    void GetInnerPoints (Array<Point<3> > & points);
+
+    /// get points in outer closure
+    void GetOuterPoints (Array<Point<3> > & points);
+
+    ///
+    void Convexify ();
+    ///
+    int GetNBoxes () { return boxes.Size(); } 
+    const Box3d & GetBoundingBox () const
+    { return boundingbox; }
+    ///
+    void PrintMemInfo (ostream & ost) const;
+  private:
+    /// 
+    double GetMinHRec (const Point3d & pmin, const Point3d & pmax,
+		       const GradingBox * box) const;
+    ///
+    void CutBoundaryRec (const Point3d & pmin, const Point3d & pmax,
+			 GradingBox * box);
+
+    ///
+    void FindInnerBoxesRec ( int (*inner)(const Point3d & p),
+			     GradingBox * box);
+
+    ///
+    void FindInnerBoxesRec2 (GradingBox * box,
+			     class AdFront3 * adfront,
+			     Array<Box3d> & faceboxes,
+			     Array<int> & finds, int nfinbox);
+
+
+
+    void FindInnerBoxesRec ( int (*inner)(const Point<2> & p),
+			     GradingBox * box);
+
+    ///
+    void FindInnerBoxesRec2 (GradingBox * box,
+			     class AdFront2 * adfront,
+			     Array<Box<3> > & faceboxes,
+			     Array<int> & finds, int nfinbox);
+
+
+
+    ///
+    void SetInnerBoxesRec (GradingBox * box);
+
+    ///
+    void ClearFlagsRec (GradingBox * box);
+  
+    ///
+    void ConvexifyRec (GradingBox * box);
+
+    friend ostream & operator<< (ostream & ost, const LocalH & loch);
+  };
+
+
+
+
+  inline ostream & operator<< (ostream & ost, const GradingBox & box)
+  {
+    ost << "gradbox, pmid = " << box.PMid() << ", h2 = " << box.H2() 
+	<< " cutbound = " << box.flags.cutboundary << " isinner = " << box.flags.isinner 
+	<< endl;
+    return ost;
+  }
+
+  inline ostream & operator<< (ostream & ost, const LocalH & loch)
+  {
+    for (int i = 0; i < loch.boxes.Size(); i++)
+      ost << "box[" << i << "] = " << *(loch.boxes[i]);
+    return ost;
+  }
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/meshclass.cpp b/contrib/Netgen/libsrc/meshing/meshclass.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..430a95ff85b74479147e7beb6b0083a2458a72b1
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshclass.cpp
@@ -0,0 +1,5648 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+  Mesh :: Mesh ()
+  {
+    // volelements.SetName ("vol elements");
+    // surfelements.SetName ("surf elements");
+    // points.SetName ("meshpoints");
+
+    boundaryedges = NULL;
+    surfelementht = NULL; 
+    segmentht = NULL;
+
+    lochfunc = NULL;
+    mglevels = 1;
+    elementsearchtree = NULL;
+    elementsearchtreets = NextTimeStamp();
+    majortimestamp = timestamp = NextTimeStamp();
+    hglob = 1e10;
+    hmin = 0;
+    numvertices = -1;
+    dimension = 3;
+
+    topology = new MeshTopology (*this);
+    curvedelems = new CurvedElements (*this);
+    clusters = new AnisotropicClusters (*this);
+    ident = new Identifications (*this);
+
+    hpelements = NULL;
+    coarsemesh = NULL;
+
+    ps_startelement = 0;
+
+    geomtype = NO_GEOM;
+
+    bcnames.SetSize(0);
+
+#ifdef PARALLEL
+    paralleltop = new ParallelMeshTopology (*this);
+#endif
+  }
+
+
+  Mesh :: ~Mesh()
+  {
+    delete lochfunc;
+    delete boundaryedges;
+    delete surfelementht;
+    delete segmentht;
+    delete curvedelems;
+    delete clusters;
+    delete topology;
+    delete ident;
+    delete elementsearchtree;
+    delete coarsemesh;
+    delete hpelements;
+
+    for (int i = 0; i < materials.Size(); i++)
+      delete [] materials[i];
+
+    for(int i = 0; i < userdata_int.Size(); i++)
+      delete userdata_int[i];
+    for(int i = 0; i < userdata_double.Size(); i++)
+      delete userdata_double[i];
+
+    for (int i = 0; i < bcnames.Size(); i++ )
+      if ( bcnames[i] ) delete bcnames[i];
+
+#ifdef PARALLEL
+    delete paralleltop;
+#endif
+  }
+
+
+  Mesh & Mesh :: operator= (const Mesh & mesh2)
+  {
+    points = mesh2.points;
+    // eltyps = mesh2.eltyps;
+    segments = mesh2.segments;
+    surfelements = mesh2.surfelements;
+    volelements = mesh2.volelements;
+    lockedpoints = mesh2.lockedpoints;
+    facedecoding = mesh2.facedecoding;
+    dimension = mesh2.dimension;
+
+    bcnames.SetSize( mesh2.bcnames.Size() );
+    for ( int i = 0; i < mesh2.bcnames.Size(); i++ )
+      if ( mesh2.bcnames[i] ) bcnames[i] = new string ( *mesh2.bcnames[i] );
+      else bcnames[i] = 0;
+
+    return *this;
+  }
+
+
+  void Mesh :: DeleteMesh()
+  {
+    NgLock lock(mutex);
+    lock.Lock();
+
+    points.SetSize(0);
+    segments.SetSize(0);
+    surfelements.SetSize(0);
+    volelements.SetSize(0);
+    lockedpoints.SetSize(0);
+    surfacesonnode.SetSize(0);
+
+    delete boundaryedges;
+    boundaryedges = NULL;
+
+    openelements.SetSize(0);
+    facedecoding.SetSize(0);
+
+    delete ident;
+    ident = new Identifications (*this);
+    delete topology;
+    topology = new MeshTopology (*this);
+    delete curvedelems;
+    curvedelems = new CurvedElements (*this);
+    delete clusters;
+    clusters = new AnisotropicClusters (*this);
+
+    for ( int i = 0; i < bcnames.Size(); i++ )
+      if ( bcnames[i] ) delete bcnames[i];
+
+#ifdef PARALLEL
+    delete paralleltop;
+    paralleltop = new ParallelMeshTopology (*this);
+#endif
+
+    lock.UnLock();
+
+    timestamp = NextTimeStamp();
+  }
+
+
+  void Mesh :: ClearSurfaceElements()
+  { 
+    surfelements.SetSize(0); 
+    for (int i = 0; i < facedecoding.Size(); i++)
+      facedecoding[i].firstelement = -1;
+
+    timestamp = NextTimeStamp();
+  }
+
+
+
+  PointIndex Mesh :: AddPoint (const Point3d & p, int layer)
+  { 
+    NgLock lock(mutex);
+    lock.Lock();
+
+    timestamp = NextTimeStamp();
+
+    PointIndex pi = points.Size() + PointIndex::BASE;
+    points.Append ( MeshPoint (p, layer, INNERPOINT) ); 
+
+#ifdef PARALLEL
+    points.Last().SetGhost(0);
+#endif
+
+    lock.UnLock();
+
+    return pi;
+  }
+
+  PointIndex Mesh :: AddPoint (const Point3d & p, int layer, POINTTYPE type)
+  { 
+    NgLock lock(mutex);
+    lock.Lock();
+
+    timestamp = NextTimeStamp();
+
+    PointIndex pi = points.Size() + PointIndex::BASE;
+    points.Append ( MeshPoint (p, layer, type) ); 
+
+#ifdef PARALLEL
+    points.Last().SetGhost(0);
+#endif
+
+    lock.UnLock();
+
+    return pi;
+  }
+
+
+#ifdef PARALLEL
+  PointIndex Mesh :: AddPoint (const Point3d & p, bool isghost,  int layer)
+  { 
+    NgLock lock(mutex);
+    lock.Lock();
+
+    timestamp = NextTimeStamp();
+
+    PointIndex pi = points.Size() + PointIndex::BASE;
+    points.Append ( MeshPoint (p, layer, INNERPOINT) ); 
+
+    points.Last().SetGhost(isghost);
+
+    lock.UnLock();
+
+    return pi;
+  }
+
+  PointIndex Mesh :: AddPoint (const Point3d & p, bool isghost, int layer, POINTTYPE type)
+  { 
+    NgLock lock(mutex);
+    lock.Lock();
+
+    timestamp = NextTimeStamp();
+
+    PointIndex pi = points.Size() + PointIndex::BASE;
+    points.Append ( MeshPoint (p, layer, type) ); 
+
+    points.Last().SetGhost(isghost);
+
+    lock.UnLock();
+
+    return pi;
+  }
+
+#endif
+
+
+
+  SegmentIndex Mesh :: AddSegment (const Segment & s)
+  { 
+    NgLock lock(mutex);	
+    lock.Lock();
+    timestamp = NextTimeStamp();
+
+    int maxn = max2 (s[0], s[1]);
+    maxn += 1-PointIndex::BASE;
+
+    /*
+      if (maxn > ptyps.Size())
+      {
+      int maxo = ptyps.Size();
+      ptyps.SetSize (maxn);
+      for (int i = maxo; i < maxn; i++)
+      ptyps[i] = INNERPOINT;
+      }
+
+      if (ptyps[s[0]] > EDGEPOINT) ptyps[s[0]] = EDGEPOINT;
+      if (ptyps[s[1]] > EDGEPOINT) ptyps[s[1]] = EDGEPOINT;
+    */
+
+    if (maxn <= points.Size())
+      {
+        if (points[s[0]].Type() > EDGEPOINT)
+          points[s[0]].SetType (EDGEPOINT);
+        if (points[s[1]].Type() > EDGEPOINT)
+          points[s[1]].SetType (EDGEPOINT);
+      }
+    /*
+      else
+      {
+      cerr << "edge points nrs > points.Size" << endl;
+      }
+    */
+
+    SegmentIndex si = segments.Size();
+    segments.Append (s); 
+
+    lock.UnLock();
+    return si;
+  }
+
+  SurfaceElementIndex Mesh :: AddSurfaceElement (const Element2d & el)
+  {     
+    NgLock lock(mutex);
+    lock.Lock();
+    timestamp = NextTimeStamp();
+
+    int maxn = el[0];
+    for (int i = 1; i < el.GetNP(); i++)
+      if (el[i] > maxn) maxn = el[i];
+
+    maxn += 1-PointIndex::BASE;
+
+    /*
+      if (maxn > ptyps.Size())
+      {
+      int maxo = ptyps.Size();
+      ptyps.SetSize (maxn);
+      for (i = maxo+PointIndex::BASE; 
+      i < maxn+PointIndex::BASE; i++)
+      ptyps[i] = INNERPOINT;
+
+      }
+    */
+    if (maxn <= points.Size())
+      {
+        for (int i = 0; i < el.GetNP(); i++)
+          if (points[el[i]].Type() > SURFACEPOINT)
+            points[el[i]].SetType(SURFACEPOINT);
+      }
+    /*
+      else
+      {
+      cerr << "surf points nrs > points.Size" << endl;      
+      }
+    */
+
+    SurfaceElementIndex si = surfelements.Size();
+    surfelements.Append (el); 
+
+    if (el.index > facedecoding.Size())
+      cerr << "has no facedecoding: fd.size = " << facedecoding.Size() << ", ind = " << el.index << endl;
+
+    surfelements.Last().next = facedecoding[el.index-1].firstelement;
+    facedecoding[el.index-1].firstelement = si;
+
+#ifdef PARALLEL
+    surfelements.Last().SetGhost ( el.IsGhost() );
+#endif
+
+    lock.UnLock();
+    return si;
+  }
+
+
+  ElementIndex Mesh :: AddVolumeElement (const Element & el)
+  { 
+    NgLock lock(mutex);
+    lock.Lock();
+
+    int maxn = el[0];
+    for (int i = 1; i < el.GetNP(); i++)
+      if (el[i] > maxn) maxn = el[i];
+
+    maxn += 1-PointIndex::BASE;
+
+    /*
+      if (maxn > ptyps.Size())
+      {
+      int maxo = ptyps.Size();
+      ptyps.SetSize (maxn);
+      for (i = maxo+PointIndex::BASE; 
+      i < maxn+PointIndex::BASE; i++)
+      ptyps[i] = INNERPOINT;
+      }
+    */
+    /*
+      if (maxn > points.Size())
+      {
+      cerr << "add vol element before point" << endl;
+      }
+    */
+
+    int ve = volelements.Size();
+
+    volelements.Append (el); 
+    volelements.Last().flags.illegal_valid = 0;
+
+#ifdef PARALLEL
+    volelements.Last().SetGhost ( el.IsGhost() );
+#endif
+
+    // while (volelements.Size() > eltyps.Size())
+    // eltyps.Append (FREEELEMENT);
+
+    timestamp = NextTimeStamp();
+
+    lock.UnLock();
+    return ve;
+  }
+
+
+
+
+
+
+  void Mesh :: Save (const string & filename) const
+  {
+
+    ofstream outfile(filename.c_str());
+
+    Save(outfile);
+  }
+
+
+
+  void Mesh :: Save (ostream & outfile) const
+  {
+    int i, j;
+
+    double scale = 1;  // globflags.GetNumFlag ("scale", 1);
+    int inverttets = 0;  // globflags.GetDefineFlag ("inverttets");
+    int invertsurf = 0;  // globflags.GetDefineFlag ("invertsurfacemesh");
+
+
+
+    outfile << "mesh3d" << "\n";
+
+    outfile << "dimension\n" << GetDimension() << "\n";
+
+    outfile << "geomtype\n" << int(geomtype) << "\n";
+
+
+    outfile << "\n";
+    outfile << "# surfnr    bcnr   domin  domout      np      p1      p2      p3"
+            << "\n";
+
+
+    switch (geomtype)
+      {
+      case GEOM_STL:
+        outfile << "surfaceelementsgi" << "\n";
+        break;
+      case GEOM_OCC: case GEOM_ACIS:
+        outfile << "surfaceelementsuv" << "\n";
+        break;
+      default:
+        outfile << "surfaceelements" << "\n";
+      }
+
+    outfile << GetNSE() << "\n";
+
+    SurfaceElementIndex sei;
+    for (sei = 0; sei < GetNSE(); sei++)
+      {
+        if ((*this)[sei].GetIndex())
+          {
+            outfile.width(8);
+            outfile << GetFaceDescriptor((*this)[sei].GetIndex ()).SurfNr()+1;
+            outfile.width(8);
+            outfile << GetFaceDescriptor((*this)[sei].GetIndex ()).BCProperty();
+            outfile.width(8);	  
+            outfile << GetFaceDescriptor((*this)[sei].GetIndex ()).DomainIn();
+            outfile.width(8);	  
+            outfile << GetFaceDescriptor((*this)[sei].GetIndex ()).DomainOut();
+          }
+        else
+          outfile << "       0       0       0";
+
+        Element2d sel = (*this)[sei];
+        if (invertsurf)
+          sel.Invert();
+
+        outfile.width(8);
+        outfile << sel.GetNP();
+
+        for (j = 0; j < sel.GetNP(); j++)
+          {
+            outfile.width(8);	  
+            outfile << sel[j];
+          }
+
+
+        switch (geomtype)
+          {
+          case GEOM_STL:
+            for (j = 1; j <= sel.GetNP(); j++)
+              {
+                outfile.width(7);	  
+                outfile << " " << sel.GeomInfoPi(j).trignum;
+              }
+            break;
+          case GEOM_OCC: case GEOM_ACIS:
+            for (j = 1; j <= sel.GetNP(); j++)
+              {
+                outfile.width(7);	  
+                outfile << " " << sel.GeomInfoPi(j).u;
+                outfile << " " << sel.GeomInfoPi(j).v;
+              }
+            break;
+          default:
+            ; // outfile << "\n";
+          }
+
+
+        outfile << "\n";
+      }
+
+    outfile << "\n" << "\n";
+    outfile << "#  matnr      np      p1      p2      p3      p4" << "\n";
+    outfile << "volumeelements" << "\n";
+    outfile << GetNE() << "\n";
+
+    for (ElementIndex ei = 0; ei < GetNE(); ei++)
+      {
+        outfile.width(8);
+        outfile << (*this)[ei].GetIndex();
+        outfile.width(8);
+        outfile << (*this)[ei].GetNP();
+
+        Element el = (*this)[ei];
+        if (inverttets)
+          el.Invert();
+
+        /*
+          for (j = 0; j < el.GetNP(); j++)
+          for (int k = 0; k < el.GetNP()-1; k++)
+          if (el[k] > el[k+1]) swap (el[k], el[k+1]);
+        */
+
+        for (j = 0; j < el.GetNP(); j++)
+          {
+            outfile.width(8);
+            outfile << el[j];
+          }
+        outfile << "\n";
+      }
+
+
+    outfile << "\n" << "\n";
+    //     outfile << "   surf1   surf2      p1      p2" << "\n";
+    outfile << "# surfid  0   p1   p2   trignum1    trignum2   domin/surfnr1    domout/surfnr2   ednr1   dist1   ednr2   dist2 \n";
+    outfile << "edgesegmentsgi2" << "\n";
+    outfile << GetNSeg() << "\n";
+
+    for (i = 1; i <= GetNSeg(); i++)
+      {
+        const Segment & seg = LineSegment (i);
+        outfile.width(8);
+        outfile << seg.si; // 2D: bc number, 3D: wievielte Kante
+        outfile.width(8);
+        outfile << 0;
+        outfile.width(8);
+        outfile << seg[0];
+        outfile.width(8);
+        outfile << seg[1];
+        outfile << " ";
+        outfile.width(8);
+        outfile << seg.geominfo[0].trignum;  // stl dreiecke
+        outfile << " ";
+        outfile.width(8);
+        outfile << seg.geominfo[1].trignum; // << endl;  // stl dreieck
+
+        if (dimension == 3)
+          {
+            outfile << " ";
+            outfile.width(8);
+            outfile << seg.surfnr1+1;
+            outfile << " ";
+            outfile.width(8);
+            outfile << seg.surfnr2+1;
+          }
+        else
+          {
+            outfile << " ";
+            outfile.width(8);
+            outfile << seg.domin;
+            outfile << " ";
+            outfile.width(8);
+            outfile << seg.domout;
+          }
+
+        outfile << " ";
+        outfile.width(8);
+        outfile << seg.edgenr;
+        outfile << " ";
+        outfile.width(12);
+        outfile.precision(16);
+        outfile << seg.epgeominfo[0].dist;  // splineparameter (2D)
+        outfile << " ";
+        outfile.width(8);
+        outfile.precision(16);
+        outfile << seg.epgeominfo[1].edgenr;  // geometry dependent
+        outfile << " ";
+        outfile.width(12);
+        outfile << seg.epgeominfo[1].dist;
+
+        outfile << "\n";
+      }
+
+
+    outfile << "\n" << "\n";
+    outfile << "#          X             Y             Z" << "\n";
+    outfile << "points" << "\n";
+    outfile << GetNP() << "\n";
+    outfile.precision(16);
+    outfile.setf (ios::fixed, ios::floatfield);
+    outfile.setf (ios::showpoint);
+
+    PointIndex pi;
+    for (pi = PointIndex::BASE; 
+         pi < GetNP()+PointIndex::BASE; pi++)
+      {
+        outfile.width(22);
+        outfile << (*this)[pi](0)/scale << "  ";
+        outfile.width(22);
+        outfile << (*this)[pi](1)/scale << "  ";
+        outfile.width(22);
+        outfile << (*this)[pi](2)/scale << "\n";
+      }      
+
+    if (ident -> GetMaxNr() > 0)
+      {
+        outfile << "identifications\n";
+        Array<INDEX_2> identpairs;
+        int cnt = 0;
+        for (i = 1; i <= ident -> GetMaxNr(); i++)
+          {
+            ident -> GetPairs (i, identpairs);
+            cnt += identpairs.Size();
+          }
+        outfile << cnt << "\n";
+        for (i = 1; i <= ident -> GetMaxNr(); i++)
+          {
+            ident -> GetPairs (i, identpairs);
+            for (j = 1; j <= identpairs.Size(); j++)
+              {
+                outfile.width (8);
+                outfile << identpairs.Get(j).I1();
+                outfile.width (8);
+                outfile << identpairs.Get(j).I2();
+                outfile.width (8);
+                outfile << i << "\n";
+              }
+          }
+
+        outfile << "identificationtypes\n";
+        outfile << ident -> GetMaxNr() << "\n";
+        for (i = 1; i <= ident -> GetMaxNr(); i++)
+          {
+            int type = ident -> GetType(i);
+            outfile << " " << type;
+          }
+        outfile << "\n";
+      }
+
+    int cntmat = 0;
+    for (i = 1; i <= materials.Size(); i++)
+      if (materials.Get(i) && strlen (materials.Get(i)))
+        cntmat++;
+
+    if (cntmat)
+      {
+        outfile << "materials" << endl;
+        outfile << cntmat << endl;
+        for (i = 1; i <= materials.Size(); i++)
+          if (materials.Get(i) && strlen (materials.Get(i)))
+            outfile << i << " " << materials.Get(i) << endl;
+      }
+
+
+    int cntbcnames = 0;
+    for ( int ii = 0; ii < bcnames.Size(); ii++ )
+      if ( bcnames[ii] ) cntbcnames++;
+
+    if ( cntbcnames )
+      {
+        outfile << "\n\nbcnames" << endl << bcnames.Size() << endl;
+        for ( i = 0; i < bcnames.Size(); i++ )
+          outfile << i+1 << "\t" << GetBCName(i) << endl;
+        outfile << endl << endl;
+      }
+
+    /*
+      if ( GetDimension() == 2 )
+      {
+      for (i = 1; i <= GetNSeg(); i++)
+      {
+      const Segment & seg = LineSegment (i);
+      if ( ! bcprops.Contains(seg.si) && seg.GetBCName() != "" )
+      {
+      bcprops.Append(seg.si);
+      cntbcnames++;
+      }
+      }
+      }
+      else
+      {
+      for (sei = 0; sei < GetNSE(); sei++)
+      {
+      if ((*this)[sei].GetIndex())
+      {
+      int bcp = GetFaceDescriptor((*this)[sei].GetIndex ()).BCProperty();
+      string name = GetFaceDescriptor((*this)[sei].GetIndex ()).BCName();
+      if ( !bcprops.Contains(bcp) &&
+      name != "" )
+      {
+      bcprops.Append(bcp);
+      cntbcnames++;
+      }
+      }
+      }
+      }
+
+      bcprops.SetSize(0);
+      if ( cntbcnames )
+      {
+      outfile << "\nbcnames" << endl << cntbcnames << endl;
+      if ( GetDimension() == 2 )
+      {
+      for (i = 1; i <= GetNSeg(); i++)
+      {
+      const Segment & seg = LineSegment (i);
+      if ( ! bcprops.Contains(seg.si) && seg.GetBCName() != "" )
+      {
+      bcprops.Append(seg.si);
+      outfile << seg.si << "\t" << seg.GetBCName() << endl;
+      }
+      }
+      }
+      else
+      {
+      for (sei = 0; sei < GetNSE(); sei++)
+      {
+      if ((*this)[sei].GetIndex())
+      {
+      int bcp = GetFaceDescriptor((*this)[sei].GetIndex ()).BCProperty();
+      string name = GetFaceDescriptor((*this)[sei].GetIndex ()).BCName();
+      if ( !bcprops.Contains(bcp) &&
+      name != "" )
+      {
+      bcprops.Append(bcp);
+      outfile << bcp << "\t" << name << endl;
+      }
+      }
+      }
+      }
+      outfile << endl << endl;
+      }
+    */
+
+    int cnt_sing = 0;
+    for (PointIndex pi = PointIndex::BASE; pi < GetNP()+PointIndex::BASE; pi++)
+      if ((*this)[pi].Singularity()>=1.) cnt_sing++;
+
+    if (cnt_sing)
+      {
+        outfile << "singular_points" << endl << cnt_sing << endl;
+        for (PointIndex pi = PointIndex::BASE; pi < GetNP()+PointIndex::BASE; pi++)
+          if ((*this)[pi].Singularity()>=1.) 
+            outfile << int(pi) << "\t" << (*this)[pi].Singularity() << endl;
+      }
+
+    cnt_sing = 0;
+    for (SegmentIndex si = 0; si < GetNSeg(); si++)
+      if ( segments[si].singedge_left ) cnt_sing++;
+    if (cnt_sing)
+      {
+        outfile << "singular_edge_left" << endl << cnt_sing << endl;
+        for (SegmentIndex si = 0; si < GetNSeg(); si++)
+          if ( segments[si].singedge_left )
+            outfile << int(si) << "\t" << segments[si].singedge_left << endl;
+      }
+
+    cnt_sing = 0;
+    for (SegmentIndex si = 0; si < GetNSeg(); si++)
+      if ( segments[si].singedge_right ) cnt_sing++;
+    if (cnt_sing)
+      {
+        outfile << "singular_edge_right" << endl << cnt_sing << endl;
+        for (SegmentIndex si = 0; si < GetNSeg(); si++)
+          if ( segments[si].singedge_right  )
+            outfile << int(si) << "\t" << segments[si].singedge_right << endl;
+      }
+
+
+    cnt_sing = 0;
+    for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
+      if ( GetFaceDescriptor ((*this)[sei].GetIndex()).domin_singular) 
+        cnt_sing++;
+
+    if (cnt_sing)
+      {
+        outfile << "singular_face_inside" << endl << cnt_sing << endl;
+        for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
+          if ( GetFaceDescriptor ((*this)[sei].GetIndex()).domin_singular) 
+            outfile << int(sei)  << "\t" << 
+              GetFaceDescriptor ((*this)[sei].GetIndex()).domin_singular  << endl;
+      }
+
+    cnt_sing = 0;
+    for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
+      if ( GetFaceDescriptor ((*this)[sei].GetIndex()).domout_singular) cnt_sing++;
+    if (cnt_sing)
+      {
+        outfile << "singular_face_outside" << endl << cnt_sing << endl;
+        for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
+          if ( GetFaceDescriptor ((*this)[sei].GetIndex()).domout_singular) 
+            outfile << int(sei) << "\t" 
+                    << GetFaceDescriptor ((*this)[sei].GetIndex()).domout_singular << endl;
+      }
+
+
+    // Philippose - 09/07/2009
+    // Add mesh face colours to Netgen Vol file format
+    // The colours are saved in RGB triplets
+    int cnt_facedesc = GetNFD();
+    if (cnt_facedesc)
+    {
+       outfile << endl << endl << "#   Surfnr     Red     Green     Blue" << endl;
+       outfile << "face_colours" << endl << cnt_facedesc << endl;
+
+       outfile.precision(8);
+       outfile.setf(ios::fixed, ios::floatfield);
+       outfile.setf(ios::showpoint);
+
+       for(i = 1; i <= cnt_facedesc; i++)
+       {
+          outfile.width(8);
+          outfile << GetFaceDescriptor(i).SurfNr()+1 << " ";
+          outfile.width(12);
+          outfile << GetFaceDescriptor(i).SurfColour().X() << " ";
+          outfile.width(12);
+          outfile << GetFaceDescriptor(i).SurfColour().Y() << " ";
+          outfile.width(12);
+          outfile << GetFaceDescriptor(i).SurfColour().Z();
+          outfile << endl;
+       }
+    }
+
+  }
+
+
+
+  void Mesh :: Load (const string & filename)
+  {
+
+    ifstream infile(filename.c_str());
+    if (!infile.good())
+      throw NgException ("mesh file not found");
+
+    Load(infile);
+  }
+
+
+
+
+  void Mesh :: Load (istream & infile)
+  {
+
+    char str[100];
+    int i, n;
+
+    double scale = 1;  // globflags.GetNumFlag ("scale", 1);
+    int inverttets = 0;  // globflags.GetDefineFlag ("inverttets");
+    int invertsurf = 0;  // globflags.GetDefineFlag ("invertsurfacemesh");
+
+
+    facedecoding.SetSize(0);
+
+    bool endmesh = false;
+
+    while (infile.good() && !endmesh)
+      {
+        infile >> str;
+
+        if (strcmp (str, "dimension") == 0)
+          {
+            infile >> dimension;
+          }
+
+        if (strcmp (str, "geomtype") == 0)
+          {
+            int hi;
+            infile >> hi;
+            geomtype = GEOM_TYPE(hi);
+          }
+
+
+        if (strcmp (str, "surfaceelements") == 0 || strcmp (str, "surfaceelementsgi")==0 || strcmp (str, "surfaceelementsuv") == 0)
+          {
+            infile >> n;
+            PrintMessage (3, n, " surface elements");
+            for (i = 1; i <= n; i++)
+              {
+                int surfnr, bcp, domin, domout, nep, faceind = 0;
+
+                infile >> surfnr >> bcp >> domin >> domout;
+                surfnr--;
+
+		bool invert_el = false;
+		/*
+		if (domin == 0) 
+		  {
+		    invert_el = true;
+		    Swap (domin, domout);
+		  }
+		*/
+
+                for (int j = 1; j <= facedecoding.Size(); j++)
+                  if (GetFaceDescriptor(j).SurfNr() == surfnr &&
+                      GetFaceDescriptor(j).BCProperty() == bcp &&
+                      GetFaceDescriptor(j).DomainIn() == domin &&
+                      GetFaceDescriptor(j).DomainOut() == domout)
+                    faceind = j;
+
+                if (!faceind)
+                  {
+                    faceind = AddFaceDescriptor (FaceDescriptor(surfnr, domin, domout, 0));
+                    GetFaceDescriptor(faceind).SetBCProperty (bcp);
+                  }
+
+                infile >> nep;
+                if (!nep) nep = 3;
+
+                Element2d tri(nep);
+                tri.SetIndex(faceind);
+
+                for (int j = 1; j <= nep; j++)
+                  infile >> tri.PNum(j);
+
+                if (strcmp (str, "surfaceelementsgi") == 0)
+                  for (int j = 1; j <= nep; j++)
+                    infile >> tri.GeomInfoPi(j).trignum;
+
+                if (strcmp (str, "surfaceelementsuv") == 0)
+                  for (int j = 1; j <= nep; j++)
+                    infile >> tri.GeomInfoPi(j).u >> tri.GeomInfoPi(j).v;
+
+                if (invertsurf)
+                  tri.Invert();
+		if (invert_el)
+		  tri.Invert();
+
+                AddSurfaceElement (tri);
+              }
+          }
+
+        if (strcmp (str, "volumeelements") == 0)
+          {
+            infile >> n;
+            PrintMessage (3, n, " volume elements");
+            for (i = 1; i <= n; i++)
+              {
+                Element el;
+                int hi, nep;
+                infile >> hi;
+                if (hi == 0) hi = 1;
+                el.SetIndex(hi);
+                infile >> nep;
+                el.SetNP(nep);
+
+                for (int j = 0; j < nep; j++)
+                  infile >> (int&)(el[j]);
+
+                if (inverttets)
+                  el.Invert();
+
+                AddVolumeElement (el);
+              }
+          }
+
+
+        if (strcmp (str, "edgesegments") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                Segment seg;
+                int hi;
+                infile >> seg.si >> hi >> seg[0] >> seg[1];
+                AddSegment (seg);
+              }
+          }
+
+
+
+        if (strcmp (str, "edgesegmentsgi") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                Segment seg;
+                int hi;
+                infile >> seg.si >> hi >> seg[0] >> seg[1]
+                       >> seg.geominfo[0].trignum
+                       >> seg.geominfo[1].trignum;
+                AddSegment (seg);
+              }
+          }
+
+        if (strcmp (str, "edgesegmentsgi2") == 0)
+          {
+            int a; 
+            infile >> a;
+            n=a; 
+
+            PrintMessage (3, n, " curve elements");
+
+            for (i = 1; i <= n; i++)
+              {
+                Segment seg;
+                int hi;
+                infile >> seg.si >> hi >> seg[0] >> seg[1]
+                       >> seg.geominfo[0].trignum
+                       >> seg.geominfo[1].trignum
+                       >> seg.surfnr1 >> seg.surfnr2
+                       >> seg.edgenr
+                       >> seg.epgeominfo[0].dist
+                       >> seg.epgeominfo[1].edgenr
+                       >> seg.epgeominfo[1].dist;
+
+                seg.epgeominfo[0].edgenr = seg.epgeominfo[1].edgenr;
+
+                seg.domin = seg.surfnr1;
+                seg.domout = seg.surfnr2;
+
+                seg.surfnr1--;
+                seg.surfnr2--;
+
+                AddSegment (seg);
+              }
+          }
+
+        if (strcmp (str, "points") == 0)
+          {
+            infile >> n;
+            PrintMessage (3, n, " points");
+            for (i = 1; i <= n; i++)
+              {
+                Point3d p;
+                infile >> p.X() >> p.Y() >> p.Z();
+                p.X() *= scale;
+                p.Y() *= scale;
+                p.Z() *= scale;
+                AddPoint (p);
+              }
+          }
+
+        if (strcmp (str, "identifications") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                PointIndex pi1, pi2;
+                int ind;
+                infile >> pi1 >> pi2 >> ind;
+                ident -> Add (pi1, pi2, ind);
+              }
+          }
+
+        if (strcmp (str, "identificationtypes") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                int type;
+                infile >> type;
+                ident -> SetType(i,Identifications::ID_TYPE(type));
+              }
+          }
+
+        if (strcmp (str, "materials") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                int nr;
+                string mat;
+                infile >> nr >> mat;
+                SetMaterial (nr, mat.c_str());
+              }
+          }
+
+        if ( strcmp (str, "bcnames" ) == 0 )
+          {
+            infile >> n;
+            Array<int,0> bcnrs(n);
+            SetNBCNames(n);
+            for ( i = 1; i <= n; i++ )
+              {
+                string nextbcname;
+                infile >> bcnrs[i-1] >> nextbcname;
+                bcnames[bcnrs[i-1]-1] = new string(nextbcname);
+              }
+
+            if ( GetDimension() == 2 )
+              {
+                for (i = 1; i <= GetNSeg(); i++)
+                  {
+                    Segment & seg = LineSegment (i);
+                    if ( seg.si <= n )
+                      seg.SetBCName (bcnames[seg.si-1]);
+                    else
+                      seg.SetBCName(0);
+                  }
+              }
+            else
+              {
+                for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
+                  {
+                    if ((*this)[sei].GetIndex())
+                      {
+                        int bcp = GetFaceDescriptor((*this)[sei].GetIndex ()).BCProperty();
+                        if ( bcp <= n )
+                          GetFaceDescriptor((*this)[sei].GetIndex ()).SetBCName(bcnames[bcp-1]);
+                        else
+                          GetFaceDescriptor((*this)[sei].GetIndex ()).SetBCName(0);
+
+                      }
+                  }
+
+              }
+
+
+          }
+
+        if (strcmp (str, "singular_points") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                PointIndex pi;
+                double s; 
+                infile >> pi;
+                infile >> s; 
+                (*this)[pi].Singularity (s);
+              }
+          }
+
+        if (strcmp (str, "singular_edge_left") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                SegmentIndex si;
+                double s; 
+                infile >> si;
+                infile >> s; 
+                (*this)[si].singedge_left = s;
+              }
+          }
+        if (strcmp (str, "singular_edge_right") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                SegmentIndex si;
+                double s; 
+                infile >> si;
+                infile >> s; 
+                (*this)[si].singedge_right = s;
+              }
+          }
+
+        if (strcmp (str, "singular_face_inside") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                SurfaceElementIndex sei;
+                double s; 
+                infile >> sei;
+                infile >> s; 
+                GetFaceDescriptor((*this)[sei].GetIndex()).domin_singular = s;
+              }
+          }
+
+        if (strcmp (str, "singular_face_outside") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                SurfaceElementIndex sei;
+                double s; 
+                infile >> sei;
+                infile >> s; 
+                GetFaceDescriptor((*this)[sei].GetIndex()).domout_singular = s;
+              }
+          }
+
+        // Philippose - 09/07/2009
+        // Add mesh face colours to Netgen Vol file format
+        // The colours are read in as RGB triplets
+        if (strcmp (str, "face_colours") == 0)
+        {
+           int cnt_facedesc = GetNFD();
+           infile >> n;
+           if(n == cnt_facedesc)
+           {
+              for(i = 1; i <= n; i++)
+              {
+                 int surfnr = 0;
+                 Vec3d surfcolour(0.0,1.0,0.0);
+
+                 infile >> surfnr 
+                        >> surfcolour.X() 
+                        >> surfcolour.Y() 
+                        >> surfcolour.Z();
+
+                 surfnr--;
+
+                 if(surfnr > 0) 
+                 {
+                    for(int facedesc = 1; facedesc <= cnt_facedesc; facedesc++)
+                    {
+                       if(surfnr == GetFaceDescriptor(facedesc).SurfNr())
+                       {
+                          GetFaceDescriptor(facedesc).SetSurfColour(surfcolour);
+                       }
+                    }
+                 }
+              }
+           }
+        }
+
+        if (strcmp (str, "endmesh") == 0)
+          endmesh = true;
+
+
+
+        strcpy (str, "");
+      }
+
+    CalcSurfacesOfNode ();
+    //  BuildConnectedNodes ();
+    topology -> Update();
+    clusters -> Update();
+
+    SetNextMajorTimeStamp();
+    //  PrintMemInfo (cout);
+
+
+#ifdef PARALLEL
+    if ( ntasks > 1 )
+      {
+        // for parallel processing
+        Distribute ();
+        return;
+      }
+#endif
+
+  }
+
+
+
+
+
+  void Mesh :: Merge (const string & filename, const int surfindex_offset)
+  {
+    ifstream infile(filename.c_str());
+    if (!infile.good())
+      throw NgException ("mesh file not found");
+
+    Merge(infile,surfindex_offset);
+
+  }
+
+
+
+  void Mesh :: Merge (istream & infile, const int surfindex_offset)
+  {
+    char str[100];
+    int i, n;
+
+
+    int inverttets = 0;  // globflags.GetDefineFlag ("inverttets");
+
+    int oldnp = GetNP();
+    int oldne = GetNSeg();
+    int oldnd = GetNDomains();
+
+    for(SurfaceElementIndex si = 0; si < GetNSE(); si++)
+      for(int j=1; j<=(*this)[si].GetNP(); j++) (*this)[si].GeomInfoPi(j).trignum = -1;
+
+    int max_surfnr = 0;
+    for (i = 1; i <= GetNFD(); i++)
+      max_surfnr = max2 (max_surfnr, GetFaceDescriptor(i).SurfNr());
+    max_surfnr++;
+
+    if(max_surfnr < surfindex_offset) max_surfnr = surfindex_offset;
+
+
+    bool endmesh = false;
+
+    while (infile.good() && !endmesh)
+      {
+        infile >> str;
+
+        if (strcmp (str, "surfaceelementsgi") == 0 || strcmp (str, "surfaceelements") == 0)
+          {
+            infile >> n;
+            PrintMessage (3, n, " surface elements");
+            for (i = 1; i <= n; i++)
+              {
+                int j;
+                int surfnr, bcp, domin, domout, nep, faceind = 0;
+                infile >> surfnr >> bcp >> domin >> domout;
+
+                surfnr--;
+
+                if(domin > 0) domin += oldnd;
+                if(domout > 0) domout += oldnd;
+                surfnr += max_surfnr;
+
+
+                for (j = 1; j <= facedecoding.Size(); j++)
+                  if (GetFaceDescriptor(j).SurfNr() == surfnr &&
+                      GetFaceDescriptor(j).BCProperty() == bcp &&
+                      GetFaceDescriptor(j).DomainIn() == domin &&
+                      GetFaceDescriptor(j).DomainOut() == domout)
+                    faceind = j;
+
+                if (!faceind)
+                  {
+                    faceind = AddFaceDescriptor (FaceDescriptor(surfnr, domin, domout, 0));
+                    if(GetDimension() == 2) bcp++;
+                    GetFaceDescriptor(faceind).SetBCProperty (bcp);
+                  }
+
+                infile >> nep;
+                if (!nep) nep = 3;
+
+                Element2d tri(nep);
+                tri.SetIndex(faceind);
+
+                for (j = 1; j <= nep; j++)
+                  {
+                    infile >> tri.PNum(j);
+                    tri.PNum(j) = tri.PNum(j) + oldnp;
+                  }
+
+
+                if (strcmp (str, "surfaceelementsgi") == 0)
+                  for (j = 1; j <= nep; j++)
+                    {
+                      infile >> tri.GeomInfoPi(j).trignum;
+                      tri.GeomInfoPi(j).trignum = -1;
+                    }
+
+                AddSurfaceElement (tri);
+              }
+          }
+
+
+        if (strcmp (str, "edgesegments") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                Segment seg;
+                int hi;
+                infile >> seg.si >> hi >> seg[0] >> seg[1];
+                seg[0] = seg[0] + oldnp;
+                seg[1] = seg[1] + oldnp;
+                AddSegment (seg);
+              }
+          }
+
+
+
+        if (strcmp (str, "edgesegmentsgi") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                Segment seg;
+                int hi;
+                infile >> seg.si >> hi >> seg[0] >> seg[1]
+                       >> seg.geominfo[0].trignum
+                       >> seg.geominfo[1].trignum;
+                seg[0] = seg[0] + oldnp;
+                seg[1] = seg[1] + oldnp;
+                AddSegment (seg);
+              }
+          }
+        if (strcmp (str, "edgesegmentsgi2") == 0)
+          {
+            infile >> n;
+            PrintMessage (3, n, " curve elements");
+
+            for (i = 1; i <= n; i++)
+              {
+                Segment seg;
+                int hi;
+                infile >> seg.si >> hi >> seg[0] >> seg[1]
+                       >> seg.geominfo[0].trignum
+                       >> seg.geominfo[1].trignum
+                       >> seg.surfnr1 >> seg.surfnr2
+                       >> seg.edgenr
+                       >> seg.epgeominfo[0].dist
+                       >> seg.epgeominfo[1].edgenr
+                       >> seg.epgeominfo[1].dist;
+                seg.epgeominfo[0].edgenr = seg.epgeominfo[1].edgenr;
+
+                seg.surfnr1--;
+                seg.surfnr2--;
+
+                if(seg.surfnr1 >= 0)  seg.surfnr1 = seg.surfnr1 + max_surfnr;
+                if(seg.surfnr2 >= 0)  seg.surfnr2 = seg.surfnr2 + max_surfnr;
+                seg[0] = seg[0] +oldnp;
+                seg[1] = seg[1] +oldnp;
+                seg.edgenr = seg.edgenr + oldne;
+                seg.epgeominfo[1].edgenr = seg.epgeominfo[1].edgenr + oldne;
+
+                AddSegment (seg);
+              }
+          }
+
+        if (strcmp (str, "volumeelements") == 0)
+          {
+            infile >> n;
+            PrintMessage (3, n, " volume elements");
+            for (i = 1; i <= n; i++)
+              {
+                Element el;
+                int hi, nep;
+                infile >> hi;
+                if (hi == 0) hi = 1;
+                el.SetIndex(hi+oldnd);
+                infile >> nep;
+                el.SetNP(nep);
+
+                for (int j = 0; j < nep; j++)
+                  {
+                    infile >> (int&)(el[j]);
+                    el[j] = el[j]+oldnp;
+                  }
+
+                if (inverttets)
+                  el.Invert();
+
+                AddVolumeElement (el);
+              }
+          }
+
+
+        if (strcmp (str, "points") == 0)
+          {
+            infile >> n;
+            PrintMessage (3, n, " points");
+            for (i = 1; i <= n; i++)
+              {
+                Point3d p;
+                infile >> p.X() >> p.Y() >> p.Z();
+                AddPoint (p);
+              }
+          }
+
+
+        if (strcmp (str, "endmesh") == 0)
+          {
+            endmesh = true;
+          }
+
+
+        if (strcmp (str, "materials") == 0)
+          {
+            infile >> n;
+            for (i = 1; i <= n; i++)
+              {
+                int nr;
+                string mat;
+                infile >> nr >> mat;
+                SetMaterial (nr+oldnd, mat.c_str());
+              }
+          }
+
+
+        strcpy (str, "");
+      }
+
+    CalcSurfacesOfNode ();
+
+    topology -> Update();
+    clusters -> Update();
+
+    SetNextMajorTimeStamp();
+  }
+
+
+
+
+
+
+
+
+
+
+  bool Mesh :: TestOk () const
+  {
+    for (ElementIndex ei = 0; ei < volelements.Size(); ei++)
+      {
+        for (int j = 0; j < 4; j++)
+          if ( (*this)[ei][j] <= PointIndex::BASE-1)
+            {
+              (*testout) << "El " << ei << " has 0 nodes: ";
+              for (int k = 0; k < 4; k++)
+                (*testout) << (*this)[ei][k];
+              break;
+            }
+      }
+    CheckMesh3D (*this);
+    return 1;
+  }
+
+  void Mesh :: SetAllocSize(int nnodes, int nsegs, int nsel, int nel)
+  {
+    points.SetAllocSize(nnodes);
+    segments.SetAllocSize(nsegs);
+    surfelements.SetAllocSize(nsel);
+    volelements.SetAllocSize(nel);
+  }
+
+
+  void Mesh :: BuildBoundaryEdges(void)
+  {
+    delete boundaryedges;
+
+    boundaryedges = new INDEX_2_CLOSED_HASHTABLE<int>
+      (3 * (GetNSE() + GetNOpenElements()) + GetNSeg() + 1);
+
+
+    for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
+      {
+        const Element2d & sel = surfelements[sei];
+        if (sel.IsDeleted()) continue;
+
+        // int si = sel.GetIndex();
+
+        for (int j = 0; j < sel.GetNP(); j++)
+          {
+            INDEX_2 i2;
+            i2.I1() = sel.PNumMod(j+1);
+            i2.I2() = sel.PNumMod(j+2);
+            i2.Sort();
+            if (sel.GetNP() <= 4)
+              boundaryedges->Set (i2, 1);
+          }
+      }
+
+
+    for (int i = 0; i < openelements.Size(); i++)
+      {
+        const Element2d & sel = openelements[i];
+        for (int j = 0; j < sel.GetNP(); j++)
+          {
+            INDEX_2 i2;
+            i2.I1() = sel.PNumMod(j+1);
+            i2.I2() = sel.PNumMod(j+2);
+            i2.Sort();
+            boundaryedges->Set (i2, 1);
+
+            points[sel[j]].SetType(FIXEDPOINT);
+          }
+      }
+
+    for (int i = 0; i < GetNSeg(); i++)
+      {
+        const Segment & seg = segments[i];
+        INDEX_2 i2(seg[0], seg[1]);
+        i2.Sort();
+
+        boundaryedges -> Set (i2, 2);
+        //segmentht -> Set (i2, i);
+      }
+
+
+  }
+
+  void Mesh :: CalcSurfacesOfNode ()
+  {
+    int i, j, k;
+    SurfaceElementIndex sei;
+
+    surfacesonnode.SetSize (GetNP());
+
+    delete boundaryedges;
+    boundaryedges = NULL;
+
+    delete surfelementht;
+    delete segmentht;
+
+    /*
+      surfelementht = new INDEX_3_HASHTABLE<int> (GetNSE()/4 + 1);
+      segmentht = new INDEX_2_HASHTABLE<int> (GetNSeg() + 1);
+    */
+
+    surfelementht = new INDEX_3_CLOSED_HASHTABLE<int> (3*GetNSE() + 1);
+    segmentht = new INDEX_2_CLOSED_HASHTABLE<int> (3*GetNSeg() + 1);
+
+    for (sei = 0; sei < GetNSE(); sei++)
+      {
+        const Element2d & sel = surfelements[sei];
+        if (sel.IsDeleted()) continue;
+
+        int si = sel.GetIndex();
+
+        for (j = 0; j < sel.GetNP(); j++)
+          {
+            PointIndex pi = sel[j];
+            bool found = 0;
+            for (k = 0; k < surfacesonnode[pi].Size(); k++)
+              if (surfacesonnode[pi][k] == si)
+                {
+                  found = 1;
+                  break;
+                }
+
+            if (!found)
+              surfacesonnode.Add (pi, si);
+
+          }
+      }
+    /*
+      for (sei = 0; sei < GetNSE(); sei++)
+      {
+      const Element2d & sel = surfelements[sei];
+      if (sel.IsDeleted()) continue;
+
+      INDEX_3 i3;
+      i3.I1() = sel.PNum(1);
+      i3.I2() = sel.PNum(2);
+      i3.I3() = sel.PNum(3);
+      i3.Sort();
+      surfelementht -> PrepareSet (i3);
+      }
+
+      surfelementht -> AllocateElements();
+    */
+    for (sei = 0; sei < GetNSE(); sei++)
+      {
+        const Element2d & sel = surfelements[sei];
+        if (sel.IsDeleted()) continue;
+
+        INDEX_3 i3;
+        i3.I1() = sel.PNum(1);
+        i3.I2() = sel.PNum(2);
+        i3.I3() = sel.PNum(3);
+        i3.Sort();
+        surfelementht -> Set (i3, sei);   // war das wichtig ???    sel.GetIndex());
+      }
+
+    int np = GetNP();
+
+    if (dimension == 3)
+      {
+        for (PointIndex pi = PointIndex::BASE; 
+             pi < np+PointIndex::BASE; pi++)
+          points[pi].SetType (INNERPOINT);
+
+        if (GetNFD() == 0) 
+          {
+            for (sei = 0; sei < GetNSE(); sei++)
+              {
+                const Element2d & sel = surfelements[sei];
+                if (sel.IsDeleted()) continue;
+                for (j = 0;  j < sel.GetNP(); j++)
+                  {
+                    PointIndex pi = SurfaceElement(sei)[j];
+                    points[pi].SetType(FIXEDPOINT);
+                  }
+              }
+          }
+        else
+          {
+            for (sei = 0; sei < GetNSE(); sei++)
+              {
+                const Element2d & sel = surfelements[sei];
+                if (sel.IsDeleted()) continue;
+                for (j = 0; j < sel.GetNP(); j++)
+                  {
+                    PointIndex pi = sel[j];
+                    int ns = surfacesonnode[pi].Size();
+                    if (ns == 1)
+                      points[pi].SetType(SURFACEPOINT);
+                    if (ns == 2)
+                      points[pi].SetType(EDGEPOINT);
+                    if (ns >= 3)
+                      points[pi].SetType(FIXEDPOINT);
+                  }      
+              }
+          }
+
+        for (i = 0; i < segments.Size(); i++)
+          {
+            const Segment & seg = segments[i];
+            for (j = 1; j <= 2; j++)
+              {
+                PointIndex hi = (j == 1) ? seg[0] : seg[1];
+
+                if (points[hi].Type() == INNERPOINT ||
+                    points[hi].Type() == SURFACEPOINT)
+                  points[hi].SetType(EDGEPOINT);
+              }
+          }
+
+
+        for (i = 0; i < lockedpoints.Size(); i++)
+          points[lockedpoints[i]].SetType(FIXEDPOINT);
+      }
+
+
+    /*
+      for (i = 0; i < openelements.Size(); i++)
+      {
+      const Element2d & sel = openelements[i];
+      for (j = 0; j < sel.GetNP(); j++)
+      {
+      INDEX_2 i2;
+      i2.I1() = sel.PNumMod(j+1);
+      i2.I2() = sel.PNumMod(j+2);
+      i2.Sort();
+      boundaryedges->Set (i2, 1);
+
+      points[sel[j]].SetType(FIXEDPOINT);
+      }
+      }
+    */
+
+    // eltyps.SetSize (GetNE());
+    // eltyps = FREEELEMENT;
+
+    for (i = 0; i < GetNSeg(); i++)
+      {
+        const Segment & seg = segments[i];
+        INDEX_2 i2(seg[0], seg[1]);
+        i2.Sort();
+
+        //boundaryedges -> Set (i2, 2);
+        segmentht -> Set (i2, i);
+      }
+  }
+
+
+  void Mesh :: FixPoints (const BitArray & fixpoints)
+  {
+    if (fixpoints.Size() != GetNP())
+      {
+        cerr << "Mesh::FixPoints: sizes don't fit" << endl;
+        return;
+      }
+    int np = GetNP();
+    for (int i = 1; i <= np; i++)
+      if (fixpoints.Test(i))
+        {
+          points.Elem(i).SetType (FIXEDPOINT);
+        }
+  }
+
+
+  void Mesh :: FindOpenElements (int dom)
+  {
+    static int timer = NgProfiler::CreateTimer ("Mesh::FindOpenElements");
+    NgProfiler::RegionTimer reg (timer);
+
+    int np = GetNP();
+    int ne = GetNE();
+    int nse = GetNSE();
+
+    Array<int,PointIndex::BASE> numonpoint(np);
+
+    numonpoint = 0;
+
+    for (ElementIndex ei = 0; ei < ne; ei++)
+      {
+        const Element & el = (*this)[ei];
+        if (dom == 0 || dom == el.GetIndex())
+          {
+            if (el.GetNP() == 4)
+              {
+                INDEX_4 i4(el[0], el[1], el[2], el[3]);
+                i4.Sort();
+                numonpoint[i4.I1()]++;
+                numonpoint[i4.I2()]++;
+              }
+            else
+              for (int j = 0; j < el.GetNP(); j++)
+                numonpoint[el[j]]++;
+          }
+      }
+
+    TABLE<ElementIndex,PointIndex::BASE> elsonpoint(numonpoint);
+    for (ElementIndex ei = 0; ei < ne; ei++)
+      {
+        const Element & el = (*this)[ei];
+        if (dom == 0 || dom == el.GetIndex())
+          {
+            if (el.GetNP() == 4)
+              {
+                INDEX_4 i4(el[0], el[1], el[2], el[3]);
+                i4.Sort();
+                elsonpoint.Add (i4.I1(), ei);
+                elsonpoint.Add (i4.I2(), ei);
+              }
+            else
+              for (int j = 0; j < el.GetNP(); j++)
+                elsonpoint.Add (el[j], ei);
+          }
+      }
+
+
+    Array<char, 1> hasface(GetNFD());
+
+    int i;
+    for (i = 1; i <= GetNFD(); i++)
+      {
+        int domin = GetFaceDescriptor(i).DomainIn();
+        int domout = GetFaceDescriptor(i).DomainOut();
+        hasface[i] = 
+          ( dom == 0 && (domin != 0 || domout != 0) ) ||
+          ( dom != 0 && (domin == dom || domout == dom) );
+      }
+
+    numonpoint = 0;
+    for (SurfaceElementIndex sii = 0; sii < nse; sii++)
+      {
+        int ind = surfelements[sii].GetIndex();
+        /*
+          if (
+          GetFaceDescriptor(ind).DomainIn() && 
+          (dom == 0 || dom == GetFaceDescriptor(ind).DomainIn())
+          ||
+          GetFaceDescriptor(ind).DomainOut() && 
+          (dom == 0 || dom == GetFaceDescriptor(ind).DomainOut())
+          )
+        */
+        if (hasface[ind])
+          {
+            /*
+              Element2d hel = surfelements[i];
+              hel.NormalizeNumbering();	  
+              numonpoint[hel[0]]++;
+            */
+            const Element2d & hel = surfelements[sii];
+            int mini = 0;
+            for (int j = 1; j < hel.GetNP(); j++)
+              if (hel[j] < hel[mini])
+                mini = j;
+            numonpoint[hel[mini]]++;
+          }
+      }
+
+    TABLE<SurfaceElementIndex,PointIndex::BASE> selsonpoint(numonpoint);
+    for (SurfaceElementIndex sii = 0; sii < nse; sii++)
+      {
+        int ind = surfelements[sii].GetIndex();
+
+        /*
+          if (
+          GetFaceDescriptor(ind).DomainIn() && 
+          (dom == 0 || dom == GetFaceDescriptor(ind).DomainIn())
+          ||
+          GetFaceDescriptor(ind).DomainOut() && 
+          (dom == 0 || dom == GetFaceDescriptor(ind).DomainOut())
+          )
+        */
+        if (hasface[ind])
+          {
+            /*
+              Element2d hel = surfelements[i];
+              hel.NormalizeNumbering();	  
+              selsonpoint.Add (hel[0], i);
+            */
+            const Element2d & hel = surfelements[sii];
+            int mini = 0;
+            for (int j = 1; j < hel.GetNP(); j++)
+              if (hel[j] < hel[mini])
+                mini = j;
+            selsonpoint.Add (hel[mini], sii);
+          }
+      }
+
+
+    int ii;
+    PointIndex pi;
+    SurfaceElementIndex sei;
+    Element2d hel;
+
+
+    INDEX_3_CLOSED_HASHTABLE<INDEX_2> faceht(100);   
+    openelements.SetSize(0);
+
+    for (PointIndex pi = PointIndex::BASE; pi < np+PointIndex::BASE; pi++)
+      if (selsonpoint[pi].Size()+elsonpoint[pi].Size())
+        {
+          faceht.SetSize (2 * selsonpoint[pi].Size() + 4 * elsonpoint[pi].Size());
+
+          FlatArray<SurfaceElementIndex> row = selsonpoint[pi];
+          for (ii = 0; ii < row.Size(); ii++)
+            {
+              hel = SurfaceElement(row[ii]);
+              int ind = hel.GetIndex();	  
+
+              if (GetFaceDescriptor(ind).DomainIn() && 
+                  (dom == 0 || dom == GetFaceDescriptor(ind).DomainIn()) )
+                {
+                  hel.NormalizeNumbering();
+                  if (hel.PNum(1) == pi)
+                    {
+                      INDEX_3 i3(hel[0], hel[1], hel[2]);
+                      INDEX_2 i2 (GetFaceDescriptor(ind).DomainIn(), 
+                                  (hel.GetNP() == 3) 
+                                  ? PointIndex (PointIndex::BASE-1)
+                                  : hel.PNum(4));
+                      faceht.Set (i3, i2);
+                    }
+                }
+              if (GetFaceDescriptor(ind).DomainOut() &&
+                  (dom == 0 || dom == GetFaceDescriptor(ind).DomainOut()) )
+                {
+                  hel.Invert();
+                  hel.NormalizeNumbering();
+                  if (hel.PNum(1) == pi)
+                    {
+                      INDEX_3 i3(hel[0], hel[1], hel[2]);
+                      INDEX_2 i2 (GetFaceDescriptor(ind).DomainOut(), 
+                                  (hel.GetNP() == 3) 
+                                  ? PointIndex (PointIndex::BASE-1)
+                                  : hel.PNum(4));
+                      faceht.Set (i3, i2);
+                    }
+                }
+            }
+
+
+          FlatArray<ElementIndex> rowel = elsonpoint[pi];
+          for (ii = 0; ii < rowel.Size(); ii++)
+            {
+              const Element & el = VolumeElement(rowel[ii]);
+
+              if (dom == 0 || el.GetIndex() == dom)
+                {
+                  for (int j = 1; j <= el.GetNFaces(); j++)
+                    {
+                      el.GetFace (j, hel);
+                      hel.Invert();
+                      hel.NormalizeNumbering();
+
+                      if (hel[0] == pi)
+                        {
+                          INDEX_3 i3(hel[0], hel[1], hel[2]);
+
+                          if (faceht.Used (i3))
+                            {
+                              INDEX_2 i2 = faceht.Get(i3);
+                              if (i2.I1() == el.GetIndex())
+                                {
+                                  i2.I1() = PointIndex::BASE-1;
+                                  faceht.Set (i3, i2);
+                                }
+                              else
+                                {
+                                  if (i2.I1() == 0)
+                                    {
+                                      PrintSysError ("more elements on face");
+                                      (*testout)  << "more elements on face!!!" << endl;
+                                      (*testout) << "el = " << el << endl;
+                                      (*testout) << "hel = " << hel << endl;
+                                      (*testout) << "face = " << i3 << endl;
+                                      (*testout) << "points = " << endl;
+                                      for (int jj = 1; jj <= 3; jj++)
+                                        (*testout) << "p = " << Point(i3.I(jj)) << endl;
+                                    }
+                                }
+                            }
+                          else
+                            {
+                              hel.Invert();
+                              hel.NormalizeNumbering();
+                              INDEX_3 i3(hel[0], hel[1], hel[2]);
+                              INDEX_2 i2(el.GetIndex(), 
+                                         (hel.GetNP() == 3) 
+                                         ? PointIndex (PointIndex::BASE-1)
+                                         : hel[3]);
+                              faceht.Set (i3, i2);
+                            }
+                        }
+                    }
+                }
+            }
+          for (int i = 0; i < faceht.Size(); i++)
+            if (faceht.UsedPos (i))
+              {
+                INDEX_3 i3;
+                INDEX_2 i2;
+                faceht.GetData (i, i3, i2);
+                if (i2.I1() != PointIndex::BASE-1)
+                  {
+                    Element2d tri;
+                    tri.SetType ( (i2.I2() == PointIndex::BASE-1) ? TRIG : QUAD);
+                    for (int l = 0; l < 3; l++)
+                      tri[l] = i3.I(l+1);
+                    tri.PNum(4) = i2.I2();
+                    tri.SetIndex (i2.I1());
+
+                    //	tri.Invert();
+
+                    openelements.Append (tri);
+                  }
+              }
+        }
+
+    int cnt3 = 0;
+    for (i = 0; i < openelements.Size(); i++)
+      if (openelements[i].GetNP() == 3)
+        cnt3++;
+
+    int cnt4 = openelements.Size() - cnt3;
+
+
+    MyStr treequad;
+    if (cnt4)
+      treequad = MyStr(" (") + MyStr(cnt3) + MyStr (" + ") + 
+        MyStr(cnt4) + MyStr(")");
+
+    PrintMessage (5, openelements.Size(), treequad, " open elements");
+
+    BuildBoundaryEdges();
+
+
+    for (int i = 1; i <= openelements.Size(); i++)
+      {
+        const Element2d & sel = openelements.Get(i);
+
+        if (boundaryedges)
+          for (int j = 1; j <= sel.GetNP(); j++)
+            {
+              INDEX_2 i2;
+              i2.I1() = sel.PNumMod(j);
+              i2.I2() = sel.PNumMod(j+1);
+              i2.Sort();
+              boundaryedges->Set (i2, 1);
+            }
+
+        for (int j = 1; j <= 3; j++)
+          {
+            int pi = sel.PNum(j);
+            if (pi < points.Size()+PointIndex::BASE)
+              points[pi].SetType (FIXEDPOINT);
+          }
+      }
+
+
+
+    /*
+      for (i = 1; i <= GetNSeg(); i++)
+      {
+      const Segment & seg = LineSegment(i);
+      INDEX_2 i2(seg[0], seg[1]);
+      i2.Sort();
+
+      if (!boundaryedges->Used (i2))
+      cerr << "WARNING: no boundedge, but seg edge: " << i2 << endl;
+
+      boundaryedges -> Set (i2, 2);
+      segmentht -> Set (i2, i-1);
+      }
+    */
+  }
+
+  bool Mesh :: HasOpenQuads () const
+  {
+    int no = GetNOpenElements();
+    for (int i = 0; i < no; i++)
+      if (openelements[i].GetNP() == 4)
+        return true;
+    return false;
+  }
+
+
+
+
+
+  void Mesh :: FindOpenSegments (int surfnr)
+  {
+    int i, j, k;
+
+    // new version, general elemetns
+    // hash index: pnum1-2
+    // hash data : surfnr,  surfel-nr (pos) or segment nr(neg)
+    INDEX_2_HASHTABLE<INDEX_2> faceht(4 * GetNSE()+GetNSeg()+1);   
+
+    PrintMessage (5, "Test Opensegments");
+    for (i = 1; i <= GetNSeg(); i++)
+      {
+        const Segment & seg = LineSegment (i);
+
+        if (surfnr == 0 || seg.si == surfnr)
+          {
+            INDEX_2 key(seg[0], seg[1]);
+            INDEX_2 data(seg.si, -i);
+
+            if (faceht.Used (key))
+              {
+                cerr << "ERROR: Segment " << seg << " already used" << endl;
+                (*testout) << "ERROR: Segment " << seg << " already used" << endl;
+              }
+
+            faceht.Set (key, data);
+          }
+      }
+
+
+    for (i = 1; i <= GetNSeg(); i++)
+      {
+        const Segment & seg = LineSegment (i);
+
+        if (surfnr == 0 || seg.si == surfnr)
+          {
+            INDEX_2 key(seg[1], seg[0]);
+            if (!faceht.Used(key))
+              {
+                cerr << "ERROR: Segment " << seg << " brother not used" << endl;
+                (*testout) << "ERROR: Segment " << seg << " brother not used" << endl;
+              }
+          }
+      }
+
+
+    for (i = 1; i <= GetNSE(); i++)
+      {
+        const Element2d & el = SurfaceElement(i);
+        if (el.IsDeleted()) continue;
+
+        if (surfnr == 0 || el.GetIndex() == surfnr)
+          {
+            for (j = 1; j <= el.GetNP(); j++)
+              {
+                INDEX_2 seg (el.PNumMod(j), el.PNumMod(j+1));
+                INDEX_2 data;
+
+                if (seg.I1() <= 0 || seg.I2() <= 0)
+                  cerr << "seg = " << seg << endl;
+
+                if (faceht.Used(seg))
+                  {
+                    data = faceht.Get(seg);
+                    if (data.I1() == el.GetIndex())
+                      {
+                        data.I1() = 0;
+                        faceht.Set (seg, data);
+                      }
+                    else
+                      {
+                        PrintSysError ("hash table si not fitting for segment: ",
+                                       seg.I1(), "-", seg.I2(), " other = ",
+                                       data.I2());
+                      }
+                  }
+                else
+                  {
+                    Swap (seg.I1(), seg.I2());
+                    data.I1() = el.GetIndex();
+                    data.I2() = i;
+
+                    faceht.Set (seg, data);
+                  }
+              }
+          }
+      }  
+
+    (*testout) << "open segments: " << endl;
+    opensegments.SetSize(0);
+    for (i = 1; i <= faceht.GetNBags(); i++)
+      for (j = 1; j <= faceht.GetBagSize(i); j++)
+        {
+          INDEX_2 i2;
+          INDEX_2 data;
+          faceht.GetData (i, j, i2, data);
+          if (data.I1())  // surfnr
+            {
+              Segment seg;
+              seg[0] = i2.I1();
+              seg[1] = i2.I2();
+              seg.si = data.I1();
+
+              // find geomdata:
+              if (data.I2() > 0)
+                {
+                  // segment due to triangle
+                  const Element2d & el = SurfaceElement (data.I2());
+                  for (k = 1; k <= el.GetNP(); k++)
+                    {
+                      if (seg[0] == el.PNum(k))
+                        seg.geominfo[0] = el.GeomInfoPi(k);
+                      if (seg[1] == el.PNum(k))
+                        seg.geominfo[1] = el.GeomInfoPi(k);
+                    }
+
+                  (*testout) << "trig seg: ";
+                }
+              else
+                {
+                  // segment due to line
+                  const Segment & lseg = LineSegment (-data.I2());
+                  seg.geominfo[0] = lseg.geominfo[0];
+                  seg.geominfo[1] = lseg.geominfo[1];
+
+                  (*testout) << "line seg: ";
+                }
+
+              (*testout) << seg[0] << " - " << seg[1] 
+                         << " len = " << Dist (Point(seg[0]), Point(seg[1]))
+                         << endl;
+
+              opensegments.Append (seg);
+              if (seg.geominfo[0].trignum <= 0 || seg.geominfo[1].trignum <= 0)
+                {
+                  (*testout) << "Problem with open segment: " << seg << endl;
+                }
+
+            }
+        }
+
+    PrintMessage (3, opensegments.Size(), " open segments found");
+    (*testout) << opensegments.Size() << " open segments found" << endl;
+
+    /*
+      ptyps.SetSize (GetNP());
+      for (i = 1; i <= ptyps.Size(); i++)
+      ptyps.Elem(i) = SURFACEPOINT;
+
+      for (i = 1; i <= GetNSeg(); i++)
+      {
+      const Segment & seg = LineSegment (i);
+      ptyps.Elem(seg[0]) = EDGEPOINT;
+      ptyps.Elem(seg[1]) = EDGEPOINT;
+      }
+      for (i = 1; i <= GetNOpenSegments(); i++)
+      {
+      const Segment & seg = GetOpenSegment (i);
+      ptyps.Elem(seg[0]) = EDGEPOINT;
+      ptyps.Elem(seg[1]) = EDGEPOINT;
+      }
+    */
+    for (i = 1; i <= points.Size(); i++)
+      points.Elem(i).SetType(SURFACEPOINT);
+
+    for (i = 1; i <= GetNSeg(); i++)
+      {
+        const Segment & seg = LineSegment (i);
+        points[seg[0]].SetType(EDGEPOINT);
+        points[seg[1]].SetType(EDGEPOINT);
+      }
+    for (i = 1; i <= GetNOpenSegments(); i++)
+      {
+        const Segment & seg = GetOpenSegment (i);
+        points[seg[0]].SetType (EDGEPOINT);
+        points[seg[1]].SetType (EDGEPOINT);
+      }
+
+
+
+    /*
+
+    for (i = 1; i <= openelements.Size(); i++)
+    {
+    const Element2d & sel = openelements.Get(i);
+
+    if (boundaryedges)
+    for (j = 1; j <= sel.GetNP(); j++)
+    {
+    INDEX_2 i2;
+    i2.I1() = sel.PNumMod(j);
+    i2.I2() = sel.PNumMod(j+1);
+    i2.Sort();
+    boundaryedges->Set (i2, 1);
+    }
+
+    for (j = 1; j <= 3; j++)
+    {
+    int pi = sel.PNum(j);
+    if (pi <= ptyps.Size())
+    ptyps.Elem(pi) = FIXEDPOINT;
+    }
+    }
+    */
+  }
+
+
+  void Mesh :: RemoveOneLayerSurfaceElements ()
+  {
+    int i, j;
+    int np = GetNP();
+
+    FindOpenSegments();
+    BitArray frontpoints(np);
+
+    frontpoints.Clear();
+    for (i = 1; i <= GetNOpenSegments(); i++)
+      {
+        const Segment & seg = GetOpenSegment(i);
+        frontpoints.Set (seg[0]);
+        frontpoints.Set (seg[1]);
+      }
+
+    for (i = 1; i <= GetNSE(); i++)
+      {
+        Element2d & sel = surfelements.Elem(i);
+        int remove = 0;
+        for (j = 1; j <= sel.GetNP(); j++)
+          if (frontpoints.Test(sel.PNum(j)))
+            remove = 1;
+        if (remove)
+          sel.PNum(1) = 0;
+      }
+
+    for (i = surfelements.Size(); i >= 1; i--)
+      {
+        if (surfelements.Elem(i).PNum(1) == 0)
+          {
+            surfelements.Elem(i) = surfelements.Last();
+            surfelements.DeleteLast();
+          }
+      }
+
+    RebuildSurfaceElementLists ();
+    /*
+    for (int i = 0; i < facedecoding.Size(); i++)
+      facedecoding[i].firstelement = -1;
+    for (int i = surfelements.Size()-1; i >= 0; i--)
+      {
+        int ind = surfelements[i].GetIndex();
+        surfelements[i].next = facedecoding[ind-1].firstelement;
+        facedecoding[ind-1].firstelement = i;
+      }
+    */
+
+    timestamp = NextTimeStamp();
+    //  Compress();
+  }
+
+
+
+
+
+  void Mesh :: FreeOpenElementsEnvironment (int layers)
+  {
+    int i, j, k;
+    PointIndex pi;
+    const int large = 9999;
+    Array<int,PointIndex::BASE> dist(GetNP());
+
+    dist = large;
+
+    for (int i = 1; i <= GetNOpenElements(); i++)
+      {
+        const Element2d & face = OpenElement(i);
+        for (j = 0; j < face.GetNP(); j++)
+          dist[face[j]] = 1;
+      }
+
+    for (k = 1; k <= layers; k++)
+      for (i = 1; i <= GetNE(); i++)
+        {
+          const Element & el = VolumeElement(i);
+          if (el[0] == -1 || el.IsDeleted()) continue;
+
+          int elmin = large;
+          for (j = 0; j < el.GetNP(); j++)
+            if (dist[el[j]] < elmin)
+              elmin = dist[el[j]];
+
+          if (elmin < large)
+            {
+              for (j = 0; j < el.GetNP(); j++)
+                if (dist[el[j]] > elmin+1)
+                  dist[el[j]] = elmin+1;
+            }
+        }
+
+    int cntfree = 0;
+    for (i = 1; i <= GetNE(); i++)
+      {
+        Element & el = VolumeElement(i);
+        if (el[0] == -1 || el.IsDeleted()) continue;
+
+        int elmin = large;
+        for (j = 0; j < el.GetNP(); j++)
+          if (dist[el[j]] < elmin)
+            elmin = dist[el[j]];
+
+        el.flags.fixed = elmin > layers;
+        // eltyps.Elem(i) = (elmin <= layers) ? 
+        // FREEELEMENT : FIXEDELEMENT;
+        if (elmin <= layers)
+          cntfree++;
+      }
+
+    PrintMessage (5, "free: ", cntfree, ", fixed: ", GetNE()-cntfree);
+    (*testout) << "free: " << cntfree << ", fixed: " << GetNE()-cntfree << endl;
+
+    for (pi = PointIndex::BASE; 
+         pi < GetNP()+PointIndex::BASE; pi++)
+      {
+        if (dist[pi] > layers+1)
+          points[pi].SetType(FIXEDPOINT);
+      }
+  }
+
+
+
+  void Mesh :: SetLocalH (const Point3d & pmin, const Point3d & pmax, double grading)
+  {
+    Point3d c = Center (pmin, pmax);
+    double d = max3 (pmax.X()-pmin.X(),
+                     pmax.Y()-pmin.Y(),
+                     pmax.Z()-pmin.Z());
+    d /= 2;
+    Point3d pmin2 = c - Vec3d (d, d, d);
+    Point3d pmax2 = c + Vec3d (d, d, d);
+
+
+    delete lochfunc;
+    lochfunc = new LocalH (pmin2, pmax2, grading);
+  }
+
+  void Mesh :: RestrictLocalH (const Point3d & p, double hloc)
+  {
+    if(hloc < hmin)
+      hloc = hmin;
+
+    //cout << "restrict h in " << p << " to " << hloc << endl;
+    if (!lochfunc)
+      {
+        PrintWarning("RestrictLocalH called, creating mesh-size tree");
+
+        Point3d boxmin, boxmax;
+        GetBox (boxmin, boxmax);
+        SetLocalH (boxmin, boxmax, 0.8);
+      }
+
+    lochfunc -> SetH (p, hloc);
+  }
+
+  void Mesh :: RestrictLocalHLine (const Point3d & p1, 
+                                   const Point3d & p2,
+                                   double hloc)
+  {
+    if(hloc < hmin)
+      hloc = hmin;
+
+    // cout << "restrict h along " << p1 << " - " << p2 << " to " << hloc << endl;
+    int i;
+    int steps = int (Dist (p1, p2) / hloc) + 2;
+    Vec3d v(p1, p2);
+
+    for (i = 0; i <= steps; i++)
+      {
+        Point3d p = p1 + (double(i)/double(steps) * v);
+        RestrictLocalH (p, hloc);
+      }
+  }
+
+
+  void Mesh :: SetMinimalH (double h)
+  {
+    hmin = h;
+  }
+
+
+  void Mesh :: SetGlobalH (double h)
+  {
+    hglob = h;
+  }
+
+  double Mesh :: MaxHDomain (int dom) const
+  {
+    if (maxhdomain.Size())
+      return maxhdomain.Get(dom);
+    else
+      return 1e10;
+  }
+
+  void Mesh :: SetMaxHDomain (const Array<double> & mhd)
+  {
+    maxhdomain.SetSize(mhd.Size());
+    for (int i = 1; i <= mhd.Size(); i++)
+      maxhdomain.Elem(i) = mhd.Get(i);
+  }
+
+
+  double Mesh :: GetH (const Point3d & p) const
+  {
+    double hmin = hglob;
+    if (lochfunc)
+      {
+        double hl = lochfunc->GetH (p);
+        if (hl < hglob)
+          hmin = hl;
+      }
+    return hmin;
+  }
+
+  double Mesh :: GetMinH (const Point3d & pmin, const Point3d & pmax)
+  {
+    double hmin = hglob;
+    if (lochfunc)
+      {
+        double hl = lochfunc->GetMinH (pmin, pmax);
+        if (hl < hmin)
+          hmin = hl;
+      }
+    return hmin;
+  }
+
+
+
+
+
+  double Mesh :: AverageH (int surfnr) const
+  {
+    int i, j, n;
+    double hi, hsum;
+    double maxh = 0, minh = 1e10;
+
+    hsum = 0;
+    n = 0;
+    for (i = 1; i <= GetNSE(); i++)
+      {
+        const Element2d & el = SurfaceElement(i);
+        if (surfnr == 0 || el.GetIndex() == surfnr)
+          {
+            for (j = 1; j <= 3; j++)
+              {
+                hi = Dist (Point (el.PNumMod(j)), 
+                           Point (el.PNumMod(j+1)));
+
+                hsum += hi;
+
+                if (hi > maxh) maxh = hi;
+                if (hi < minh) minh = hi;
+                n++;
+              }
+          }
+      }
+
+    PrintMessage (5, "minh = ", minh, " avh = ", (hsum/n), " maxh = ", maxh);
+    return (hsum / n);
+  }
+
+
+
+  void Mesh :: CalcLocalH (double grading) 
+  {
+    if (!lochfunc)
+      {
+        Point3d pmin, pmax;
+        GetBox (pmin, pmax);
+        // SetLocalH (pmin, pmax, mparam.grading);
+	SetLocalH (pmin, pmax, grading);
+      }
+
+    PrintMessage (3,
+                  "CalcLocalH: ", 
+                  GetNP(), " Points ", 
+                  GetNE(), " Elements ", 
+                  GetNSE(), " Surface Elements");
+
+
+    for (int i = 0; i < GetNSE(); i++)
+      {
+        const Element2d & el = surfelements[i];
+        int j;
+
+        if (el.GetNP() == 3)
+          {
+            double hel = -1;
+            for (j = 1; j <= 3; j++)
+              {
+                const Point3d & p1 = points[el.PNumMod(j)];
+                const Point3d & p2 = points[el.PNumMod(j+1)];
+
+                /*
+                  INDEX_2 i21(el.PNumMod(j), el.PNumMod(j+1));
+                  INDEX_2 i22(el.PNumMod(j+1), el.PNumMod(j));
+                  if (! identifiedpoints->Used (i21) &&
+                  ! identifiedpoints->Used (i22) )
+                */
+                if (!ident -> UsedSymmetric (el.PNumMod(j),
+                                             el.PNumMod(j+1)))
+                  {
+                    double hedge = Dist (p1, p2);
+                    if (hedge > hel)
+                      hel = hedge;
+                    //		  lochfunc->SetH (Center (p1, p2), 2 * Dist (p1, p2));
+                    //		  (*testout) << "trigseth, p1,2 = " << el.PNumMod(j) << ", " << el.PNumMod(j+1) 
+                    //			     << " h = " << (2 * Dist(p1, p2)) << endl;
+                  }
+              }
+
+            if (hel > 0)
+              {
+                const Point3d & p1 = points[el.PNum(1)];
+                const Point3d & p2 = points[el.PNum(2)];
+                const Point3d & p3 = points[el.PNum(3)];
+                lochfunc->SetH (Center (p1, p2, p3), hel);
+              }
+          }
+        else
+          {
+            {
+              const Point3d & p1 = points[el.PNum(1)];
+              const Point3d & p2 = points[el.PNum(2)];
+              lochfunc->SetH (Center (p1, p2), 2 * Dist (p1, p2));
+            }
+            {
+              const Point3d & p1 = points[el.PNum(3)];
+              const Point3d & p2 = points[el.PNum(4)];
+              lochfunc->SetH (Center (p1, p2), 2 * Dist (p1, p2));
+            }
+          }
+      }
+
+    for (int i = 0; i < GetNSeg(); i++)
+      {
+        const Segment & seg = segments[i];
+        const Point3d & p1 = points[seg[0]];
+        const Point3d & p2 = points[seg[1]];
+        /*
+          INDEX_2 i21(seg[0], seg[1]);
+          INDEX_2 i22(seg[1], seg[0]);
+          if (identifiedpoints)
+          if (!identifiedpoints->Used (i21) && !identifiedpoints->Used (i22))
+        */
+        if (!ident -> UsedSymmetric (seg[0], seg[1]))
+          {
+            lochfunc->SetH (Center (p1, p2), Dist (p1, p2));
+          }
+      }
+    /*
+      cerr << "do vol" << endl;
+      for (i = 1; i <= GetNE(); i++)
+      {
+      const Element & el = VolumeElement(i);
+      if (el.GetType() == TET)
+      {
+      int j, k;
+      for (j = 2; j <= 4; j++)
+      for (k = 1; k < j; k++)  
+      {
+      const Point3d & p1 = Point (el.PNum(j));
+      const Point3d & p2 = Point (el.PNum(k));
+      lochfunc->SetH (Center (p1, p2), 2 * Dist (p1, p2));
+      (*testout) << "set vol h to " << (2 * Dist (p1, p2)) << endl;
+
+      }
+      }
+      }
+    */
+
+    /*
+      const char * meshsizefilename = 
+      globflags.GetStringFlag ("meshsize", NULL);
+      if (meshsizefilename)
+      {
+      ifstream msf(meshsizefilename);
+      if (msf)
+      {
+      int nmsp;
+      msf >> nmsp;
+      for (i = 1; i <= nmsp; i++)
+      {
+      Point3d pi;
+      double hi;
+      msf >> pi.X() >> pi.Y() >> pi.Z();
+      msf >> hi;
+      lochfunc->SetH (pi, hi);
+      }
+      }
+      }
+    */
+    //  lochfunc -> Convexify();
+    //  lochfunc -> PrintMemInfo (cout);
+  }
+
+
+  void Mesh :: CalcLocalHFromPointDistances(double grading)
+  {
+    PrintMessage (3, "Calculating local h from point distances");
+
+    if (!lochfunc)
+      {
+        Point3d pmin, pmax;
+        GetBox (pmin, pmax);
+
+        // SetLocalH (pmin, pmax, mparam.grading);
+	SetLocalH (pmin, pmax, grading);
+      }
+
+    PointIndex i,j;
+    double hl;
+
+
+    for (i = PointIndex::BASE; 
+         i < GetNP()+PointIndex::BASE; i++)
+      {
+        for(j=i+1; j<GetNP()+PointIndex::BASE; j++)
+          {
+            const Point3d & p1 = points[i];
+            const Point3d & p2 = points[j];
+            hl = Dist(p1,p2);
+            RestrictLocalH(p1,hl);
+            RestrictLocalH(p2,hl);
+            //cout << "restricted h at " << p1 << " and " << p2 << " to " << hl << endl;
+          }
+      }
+
+
+  }
+
+
+  void Mesh :: CalcLocalHFromSurfaceCurvature (double grading, double elperr) 
+  {
+    PrintMessage (3, "Calculating local h from surface curvature");
+
+    if (!lochfunc)
+      {
+        Point3d pmin, pmax;
+        GetBox (pmin, pmax);
+
+        // SetLocalH (pmin, pmax, mparam.grading);
+	SetLocalH (pmin, pmax, grading);
+      }
+
+
+    INDEX_2_HASHTABLE<int> edges(3 * GetNP() + 2);
+    INDEX_2_HASHTABLE<int> bedges(GetNSeg() + 2);
+    int i, j;
+
+    for (i = 1; i <= GetNSeg(); i++)
+      {
+        const Segment & seg = LineSegment(i);
+        INDEX_2 i2(seg[0], seg[1]);
+        i2.Sort();
+        bedges.Set (i2, 1);
+      }
+    for (i = 1; i <= GetNSE(); i++)
+      {
+        const Element2d & sel = SurfaceElement(i);
+        if (!sel.PNum(1))
+          continue;
+        for (j = 1; j <= 3; j++)
+          {
+            INDEX_2 i2(sel.PNumMod(j), sel.PNumMod(j+1));
+            i2.Sort();
+            if (bedges.Used(i2)) continue;
+
+            if (edges.Used(i2))
+              {
+                int other = edges.Get(i2);
+
+                const Element2d & elother = SurfaceElement(other);
+
+                int pi3 = 1;
+                while ( (sel.PNum(pi3) == i2.I1()) || 
+                        (sel.PNum(pi3) == i2.I2()))
+                  pi3++;
+                pi3 = sel.PNum(pi3);
+
+                int pi4 = 1;
+                while ( (elother.PNum(pi4) == i2.I1()) || 
+                        (elother.PNum(pi4) == i2.I2()))
+                  pi4++;
+                pi4 = elother.PNum(pi4);
+
+                double rad = ComputeCylinderRadius (Point (i2.I1()),
+                                                    Point (i2.I2()),
+                                                    Point (pi3), 
+                                                    Point (pi4));
+
+                RestrictLocalHLine (Point(i2.I1()), Point(i2.I2()), rad/elperr);
+
+
+                /*	      
+                  (*testout) << "pi1,2, 3, 4 = " << i2.I1() << ", " << i2.I2() << ", " << pi3 << ", " << pi4
+                  << " p1 = " << Point(i2.I1()) 
+                  << ", p2 = " << Point(i2.I2()) 
+                  //			 << ", p3 = " << Point(pi3) 
+                  //			 << ", p4 = " << Point(pi4) 
+                  << ", rad = " << rad << endl;
+                */
+              }
+            else
+              edges.Set (i2, i);
+          }
+      }
+
+
+    // Restrict h due to line segments
+
+    for (i = 1; i <= GetNSeg(); i++)
+      {
+        const Segment & seg = LineSegment(i);
+        const Point3d & p1 = Point(seg[0]);
+        const Point3d & p2 = Point(seg[1]);
+        RestrictLocalH (Center (p1, p2),  Dist (p1, p2));
+      }
+
+
+
+    /*
+
+
+    int i, j;
+    int np = GetNP();
+    int nseg = GetNSeg();
+    int nse = GetNSE();
+
+    Array<Vec3d> normals(np);
+    BitArray linepoint(np);
+
+    linepoint.Clear();
+    for (i = 1; i <= nseg; i++)
+    {
+    linepoint.Set (LineSegment(i)[0]);
+    linepoint.Set (LineSegment(i)[1]);
+    }
+
+    for (i = 1; i <= np; i++)
+    normals.Elem(i) = Vec3d(0,0,0);
+
+    for (i = 1; i <= nse; i++)
+    {
+    Element2d & el = SurfaceElement(i);
+    Vec3d nf = Cross (Vec3d (Point (el.PNum(1)), Point(el.PNum(2))),
+    Vec3d (Point (el.PNum(1)), Point(el.PNum(3))));
+    for (j = 1; j <= 3; j++)
+    normals.Elem(el.PNum(j)) += nf;
+    }
+
+    for (i = 1; i <= np; i++)
+    normals.Elem(i) /= (1e-12 + normals.Elem(i).Length());
+
+    for (i = 1; i <= nse; i++)
+    {
+    Element2d & el = SurfaceElement(i);
+    Vec3d nf = Cross (Vec3d (Point (el.PNum(1)), Point(el.PNum(2))),
+    Vec3d (Point (el.PNum(1)), Point(el.PNum(3))));
+    nf /= nf.Length();
+    Point3d c = Center (Point(el.PNum(1)),
+    Point(el.PNum(2)),
+    Point(el.PNum(3)));
+
+    for (j = 1; j <= 3; j++)
+    {
+    if (!linepoint.Test (el.PNum(j)))
+    {
+    double dist = Dist (c, Point(el.PNum(j)));
+    double dn = (nf - normals.Get(el.PNum(j))).Length();
+
+    RestrictLocalH (Point(el.PNum(j)), dist / (dn+1e-12) /elperr);
+    }
+    }
+    }
+    */
+  }
+
+
+  void Mesh :: RestrictLocalH (resthtype rht, int nr, double loch)
+  {
+    int i;
+    switch (rht)
+      {
+      case RESTRICTH_FACE:
+        {
+          for (i = 1; i <= GetNSE(); i++)
+            {
+              const Element2d & sel = SurfaceElement(i);
+              if (sel.GetIndex() == nr)
+                RestrictLocalH (RESTRICTH_SURFACEELEMENT, i, loch);
+            }
+          break;
+        }
+      case RESTRICTH_EDGE:
+        {
+          for (i = 1; i <= GetNSeg(); i++)
+            {
+              const Segment & seg = LineSegment(i);
+              if (seg.edgenr == nr)
+                RestrictLocalH (RESTRICTH_SEGMENT, i, loch);
+            }
+          break;
+        }
+      case RESTRICTH_POINT:
+        {
+          RestrictLocalH (Point (nr), loch);
+          break;
+        }
+
+      case RESTRICTH_SURFACEELEMENT:
+        {
+          const Element2d & sel = SurfaceElement(nr);
+          Point3d p = Center (Point(sel.PNum(1)),
+                              Point(sel.PNum(2)),
+                              Point(sel.PNum(3)));
+          RestrictLocalH (p, loch);
+          break;
+        }
+      case RESTRICTH_SEGMENT:
+        {
+          const Segment & seg = LineSegment(nr);
+          RestrictLocalHLine (Point (seg[0]), Point(seg[1]), loch);
+          break;
+        }
+      }
+  }
+
+
+  void Mesh :: LoadLocalMeshSize (const char * meshsizefilename)
+  {
+    // Philippose - 10/03/2009
+    // Improve error checking when loading and reading
+    // the local mesh size file
+
+    if (!meshsizefilename) return;
+
+    ifstream msf(meshsizefilename);
+
+    // Philippose - 09/03/2009
+    // Adding print message information in case the specified 
+    // does not exist, or does not load successfully due to 
+    // other reasons such as access rights, etc...
+    if (!msf) 
+      {
+        PrintMessage(3, "Error loading mesh size file: ", meshsizefilename, "....","Skipping!");
+        return;
+      }
+
+    PrintMessage (3, "Load local mesh-size file: ", meshsizefilename);
+
+    int nmsp = 0;
+    int nmsl = 0;
+
+    msf >> nmsp;
+    if(!msf.good())
+      throw NgException ("Mesh-size file error: No points found\n");
+
+    if(nmsp > 0)
+      PrintMessage (4, "Number of mesh-size restriction points: ", nmsp);
+
+    for (int i = 0; i < nmsp; i++)
+      {
+        Point3d pi;
+        double hi;
+        msf >> pi.X() >> pi.Y() >> pi.Z();
+        msf >> hi;
+        if (!msf.good())
+          throw NgException ("Mesh-size file error: Number of points don't match specified list size\n");
+        RestrictLocalH (pi, hi);
+      }
+
+    msf >> nmsl;
+    if(!msf.good())
+      throw NgException ("Mesh-size file error: No line definitions found\n");
+
+    if(nmsl > 0)
+      PrintMessage (4, "Number of mesh-size restriction lines: ", nmsl);
+
+    for (int i = 0; i < nmsl; i++)
+      {
+        Point3d p1, p2;
+        double hi;
+        msf >> p1.X() >> p1.Y() >> p1.Z();
+        msf >> p2.X() >> p2.Y() >> p2.Z();
+        msf >> hi;
+        if (!msf.good())
+          throw NgException ("Mesh-size file error: Number of line definitions don't match specified list size\n");
+        RestrictLocalHLine (p1, p2, hi);
+      }
+
+    msf.close();
+  }
+
+
+
+  void Mesh :: GetBox (Point3d & pmin, Point3d & pmax, int dom) const
+  {
+    if (points.Size() == 0)
+      {
+        pmin = pmax = Point3d(0,0,0);
+        return;
+      }
+
+    if (dom <= 0)
+      {
+        pmin = Point3d (1e10, 1e10, 1e10);
+        pmax = Point3d (-1e10, -1e10, -1e10); 
+
+        for (PointIndex pi = PointIndex::BASE; 
+             pi < GetNP()+PointIndex::BASE; pi++)
+          {
+            pmin.SetToMin ( (*this) [pi] );
+            pmax.SetToMax ( (*this) [pi] );
+          }
+      }
+    else
+      {
+        int j, nse = GetNSE();
+        SurfaceElementIndex sei;
+
+        pmin = Point3d (1e10, 1e10, 1e10);
+        pmax = Point3d (-1e10, -1e10, -1e10); 
+        for (sei = 0; sei < nse; sei++)
+          {
+            const Element2d & el = (*this)[sei];
+            if (el.IsDeleted() ) continue;
+
+            if (dom == -1 || el.GetIndex() == dom)
+              {
+                for (j = 0; j < 3; j++)
+                  {
+                    pmin.SetToMin ( (*this) [el[j]] );
+                    pmax.SetToMax ( (*this) [el[j]] );
+                  }
+              }
+          }
+      }
+
+    if (pmin.X() > 0.5e10)
+      {
+        pmin = pmax = Point3d(0,0,0);
+      }
+  }
+
+
+
+
+  void Mesh :: GetBox (Point3d & pmin, Point3d & pmax, POINTTYPE ptyp) const
+  {
+    if (points.Size() == 0)
+      {
+        pmin = pmax = Point3d(0,0,0);
+        return;
+      }
+
+    pmin = Point3d (1e10, 1e10, 1e10);
+    pmax = Point3d (-1e10, -1e10, -1e10); 
+
+    for (PointIndex pi = PointIndex::BASE; 
+         pi < GetNP()+PointIndex::BASE; pi++)
+      if (points[pi].Type() <= ptyp)
+        {
+          pmin.SetToMin ( (*this) [pi] );
+          pmax.SetToMax ( (*this) [pi] );
+        }
+  }
+
+
+
+
+  double Mesh :: ElementError (int eli, const MeshingParameters & mp) const
+  {
+    const Element & el = volelements.Get(eli);
+    return CalcTetBadness (points.Get(el[0]), points.Get(el[1]),
+                           points.Get(el[2]), points.Get(el[3]), -1, mp);
+  }
+
+  void Mesh :: AddLockedPoint (PointIndex pi)
+  { 
+    lockedpoints.Append (pi); 
+  }
+
+  void Mesh :: ClearLockedPoints ()
+  { 
+    lockedpoints.SetSize (0); 
+  }
+
+
+
+  void Mesh :: Compress ()
+  {
+    int i, j;
+    Array<int,PointIndex::BASE> op2np(GetNP());
+    Array<MeshPoint> hpoints;
+    BitArrayChar<PointIndex::BASE> pused(GetNP());
+
+    /*
+      (*testout) << "volels: " << endl;
+      for (i = 1; i <= volelements.Size(); i++)
+      {
+      for (j = 1; j <= volelements.Get(i).GetNP(); j++)
+      (*testout) << volelements.Get(i).PNum(j) << " ";
+      (*testout) << endl;
+      }
+      (*testout) << "np: " << GetNP() << endl;
+    */
+
+    for (i = 0; i < volelements.Size(); i++)
+      if (volelements[i][0] <= PointIndex::BASE-1 ||
+          volelements[i].IsDeleted())
+        {
+          volelements.Delete(i);
+          i--;
+        }
+
+
+    for (i = 0; i < surfelements.Size(); i++)
+      if (surfelements[i].IsDeleted())
+        {
+          surfelements.Delete(i);
+          i--;
+        }
+
+    for (i = 0; i < segments.Size(); i++)
+      if (segments[i][0] <= PointIndex::BASE-1)
+        {
+          segments.Delete(i);
+          i--;
+        }
+
+    pused.Clear();
+    for (i = 0; i < volelements.Size(); i++)
+      {
+        const Element & el = volelements[i];
+        for (j = 0; j < el.GetNP(); j++)
+          pused.Set (el[j]);
+      }
+
+    for (i = 0; i < surfelements.Size(); i++)
+      {
+        const Element2d & el = surfelements[i];
+        for (j = 0; j < el.GetNP(); j++)
+          pused.Set (el[j]);
+      }
+
+    for (i = 0; i < segments.Size(); i++)
+      {
+        const Segment & seg = segments[i];
+        pused.Set (seg[0]);
+        pused.Set (seg[1]);
+      }
+
+    for (i = 0; i < openelements.Size(); i++)
+      {
+        const Element2d & el = openelements[i];
+        for (j = 0; j < el.GetNP(); j++)
+          pused.Set(el[j]);
+      }
+
+    for (i = 0; i < lockedpoints.Size(); i++)
+      pused.Set (lockedpoints[i]);
+
+
+    /*
+    // compress points doesnt work for identified points !
+    if (identifiedpoints)
+    {
+    for (i = 1; i <= identifiedpoints->GetNBags(); i++)
+    if (identifiedpoints->GetBagSize(i))
+    {
+    pused.Set ();
+    break;
+    }
+    }
+    */
+    //  pused.Set();
+
+
+    int npi = PointIndex::BASE-1;
+
+    for (i = PointIndex::BASE; 
+         i < points.Size()+PointIndex::BASE; i++)
+      if (pused.Test(i))
+        {
+          npi++;
+          op2np[i] = npi;
+          hpoints.Append (points[i]);
+        }
+      else
+        op2np[i] = -1;
+
+
+
+    points.SetSize(0);
+    for (i = 0; i < hpoints.Size(); i++)
+      points.Append (hpoints[i]);
+
+
+    for (i = 1; i <= volelements.Size(); i++)
+      {
+        Element & el = VolumeElement(i);
+        for (j = 0; j < el.GetNP(); j++)
+          el[j] = op2np[el[j]];
+      }
+
+    for (i = 1; i <= surfelements.Size(); i++)
+      {
+        Element2d & el = SurfaceElement(i);
+        for (j = 0; j < el.GetNP(); j++)
+          el[j] = op2np[el[j]];
+      }
+
+    for (i = 0; i < segments.Size(); i++)
+      {
+        Segment & seg = segments[i];
+        seg[0] = op2np[seg[0]];
+        seg[1] = op2np[seg[1]];
+      }
+
+    for (i = 1; i <= openelements.Size(); i++)
+      {
+        Element2d & el = openelements.Elem(i);
+        for (j = 0; j < el.GetNP(); j++)
+          el[j] = op2np[el[j]];
+      }  
+
+
+    for (i = 0; i < lockedpoints.Size(); i++)
+      lockedpoints[i] = op2np[lockedpoints[i]];
+
+    for (int i = 0; i < facedecoding.Size(); i++)
+      facedecoding[i].firstelement = -1;
+    for (int i = surfelements.Size()-1; i >= 0; i--)
+      {
+        int ind = surfelements[i].GetIndex();
+        surfelements[i].next = facedecoding[ind-1].firstelement;
+        facedecoding[ind-1].firstelement = i;
+      }
+
+
+    CalcSurfacesOfNode();
+
+
+    //  FindOpenElements();
+    timestamp = NextTimeStamp();
+
+    /*
+      (*testout) << "compress, done" << endl
+      << "np = " << points.Size()
+      << "ne = " << volelements.Size() << ", type.size = " << eltyps.Size()
+      <<  "volelements = " << volelements << endl;
+    */
+  }
+
+
+  int Mesh :: CheckConsistentBoundary () const
+  {
+    int nf = GetNOpenElements();
+    INDEX_2_HASHTABLE<int> edges(nf+2);
+    INDEX_2 i2, i2s, edge;
+    int err = 0;
+
+    for (int i = 1; i <= nf; i++)
+      {
+        const Element2d & sel = OpenElement(i);
+
+        for (int j = 1; j <= sel.GetNP(); j++)
+          {
+            i2.I1() = sel.PNumMod(j);
+            i2.I2() = sel.PNumMod(j+1);
+
+            int sign = (i2.I2() > i2.I1()) ? 1 : -1;
+            i2.Sort();
+            if (!edges.Used (i2))
+              edges.Set (i2, 0);
+            edges.Set (i2, edges.Get(i2) + sign);
+          }
+      }
+
+    for (int i = 1; i <= edges.GetNBags(); i++)
+      for (int j = 1; j <= edges.GetBagSize(i); j++)
+        {
+          int cnt = 0;
+          edges.GetData (i, j, i2, cnt);
+          if (cnt)
+            {
+              PrintError ("Edge ", i2.I1() , " - ", i2.I2(), " multiple times in surface mesh");
+
+              (*testout) << "Edge " << i2 << " multiple times in surface mesh" << endl;
+              i2s = i2;
+              i2s.Sort();
+              for (int k = 1; k <= nf; k++)
+                {
+                  const Element2d & sel = OpenElement(k);
+                  for (int l = 1; l <= sel.GetNP(); l++)
+                    {
+                      edge.I1() = sel.PNumMod(l);
+                      edge.I2() = sel.PNumMod(l+1);
+                      edge.Sort();
+
+                      if (edge == i2s) 
+                        (*testout) << "edge of element " << sel << endl;
+                    }
+                }
+
+
+              err = 2;
+            }
+        }
+
+    return err;
+  }
+
+
+
+  int Mesh :: CheckOverlappingBoundary () 
+  {
+    int i, j, k;
+
+    Point3d pmin, pmax;
+    GetBox (pmin, pmax);
+    Box3dTree setree(pmin, pmax);
+    Array<int> inters;
+
+    bool overlap = 0;
+    bool incons_layers = 0;
+
+
+    for (i = 1; i <= GetNSE(); i++)
+      SurfaceElement(i).badel = 0;
+
+
+    for (i = 1; i <= GetNSE(); i++)
+      {
+        const Element2d & tri = SurfaceElement(i);
+
+        Point3d tpmin (Point(tri[0]));
+        Point3d tpmax (tpmin);
+
+        for (k = 1; k < tri.GetNP(); k++)
+          {
+            tpmin.SetToMin (Point (tri[k]));
+            tpmax.SetToMax (Point (tri[k]));
+          }
+        Vec3d diag(tpmin, tpmax);
+
+        tpmax = tpmax + 0.1 * diag;
+        tpmin = tpmin - 0.1 * diag;
+
+        setree.Insert (tpmin, tpmax, i);
+      }
+
+    for (i = 1; i <= GetNSE(); i++)
+      {
+        const Element2d & tri = SurfaceElement(i);
+
+        Point3d tpmin (Point(tri[0]));
+        Point3d tpmax (tpmin);
+
+        for (k = 1; k < tri.GetNP(); k++)
+          {
+            tpmin.SetToMin (Point (tri[k]));
+            tpmax.SetToMax (Point (tri[k]));
+          }
+
+        setree.GetIntersecting (tpmin, tpmax, inters);
+
+        for (j = 1; j <= inters.Size(); j++)
+          {
+            const Element2d & tri2 = SurfaceElement(inters.Get(j));	  
+
+            if ( (*this)[tri[0]].GetLayer() != (*this)[tri2[0]].GetLayer())
+              continue;
+
+            if ( (*this)[tri[0]].GetLayer() != (*this)[tri[1]].GetLayer() ||
+                 (*this)[tri[0]].GetLayer() != (*this)[tri[2]].GetLayer())
+              {
+                incons_layers = 1;
+                cout << "inconsistent layers in triangle" << endl;
+              }
+
+
+            const netgen::Point<3> *trip1[3], *trip2[3];	  
+            for (k = 1; k <= 3; k++)
+              {
+                trip1[k-1] = &Point (tri.PNum(k));
+                trip2[k-1] = &Point (tri2.PNum(k));
+              }
+
+            if (IntersectTriangleTriangle (&trip1[0], &trip2[0]))
+              {
+                overlap = 1;
+                PrintWarning ("Intersecting elements " 
+                              ,i, " and ", inters.Get(j));
+
+                (*testout) << "Intersecting: " << endl;
+                (*testout) << "openelement " << i << " with open element " << inters.Get(j) << endl;
+
+                cout << "el1 = " << tri << endl;
+                cout << "el2 = " << tri2 << endl;
+                cout << "layer1 = " <<  (*this)[tri[0]].GetLayer() << endl;
+                cout << "layer2 = " <<  (*this)[tri2[0]].GetLayer() << endl;
+
+
+                for (k = 1; k <= 3; k++)
+                  (*testout) << tri.PNum(k) << "  ";
+                (*testout) << endl;
+                for (k = 1; k <= 3; k++)
+                  (*testout) << tri2.PNum(k) << "  ";
+                (*testout) << endl;
+
+                for (k = 0; k <= 2; k++)
+                  (*testout) << *trip1[k] << "   ";
+                (*testout) << endl;
+                for (k = 0; k <= 2; k++)
+                  (*testout) << *trip2[k] << "   ";
+                (*testout) << endl;
+
+                (*testout) << "Face1 = " << GetFaceDescriptor(tri.GetIndex()) << endl;
+                (*testout) << "Face1 = " << GetFaceDescriptor(tri2.GetIndex()) << endl;
+
+                /*
+                  INDEX_3 i3(tri.PNum(1), tri.PNum(2), tri.PNum(3));
+                  i3.Sort();
+                  for (k = 1; k <= GetNSE(); k++)
+                  {
+                  const Element2d & el2 = SurfaceElement(k);
+                  INDEX_3 i3b(el2.PNum(1), el2.PNum(2), el2.PNum(3));
+                  i3b.Sort();
+                  if (i3 == i3b)
+                  {
+                  SurfaceElement(k).badel = 1;
+                  }
+                  }
+                */
+                SurfaceElement(i).badel = 1;
+                SurfaceElement(inters.Get(j)).badel = 1;
+              }
+          }
+      }
+
+    // bug 'fix'
+    if (incons_layers) overlap = 0;
+
+    return overlap;
+  }
+
+
+  int Mesh :: CheckVolumeMesh () const
+  {
+    PrintMessage (3, "Checking volume mesh");
+
+    int ne = GetNE();
+    DenseMatrix dtrans(3,3);
+    int i, j;
+
+    PrintMessage (5, "elements: ", ne);
+    for (i = 1; i <= ne; i++)
+      {
+        Element & el = (Element&) VolumeElement(i);
+        el.flags.badel = 0;
+        int nip = el.GetNIP();
+        for (j = 1; j <= nip; j++)
+          {
+            el.GetTransformation (j, Points(), dtrans);
+            double det = dtrans.Det();
+            if (det > 0)
+              {
+                PrintError ("Element ", i , " has wrong orientation");
+                el.flags.badel = 1;
+              }
+          }
+      }
+
+    return 0;
+  }
+
+
+  bool Mesh :: LegalTrig (const Element2d & el) const
+  {
+    return 1;
+    if ( /* hp */ 1)  // needed for old, simple hp-refinement
+      { 
+        // trigs with 2 or more segments are illegal
+        int i;
+        int nseg = 0;
+
+        if (!segmentht)
+          {
+            cerr << "no segmentht allocated" << endl;
+            return 0;
+          }
+
+        //      Point3d cp(0.5, 0.5, 0.5);
+        for (i = 1; i <= 3; i++)
+          {
+            INDEX_2 i2(el.PNumMod (i), el.PNumMod (i+1));
+            i2.Sort();
+            if (segmentht -> Used (i2))
+              nseg++;
+          }
+        if (nseg >= 2) 
+          return 0;
+      }
+    return 1;
+  }
+
+
+
+
+  ///
+  bool Mesh :: LegalTet2 (Element & el) const
+  {
+    // static int timer1 = NgProfiler::CreateTimer ("Legaltet2");
+
+    // Test, whether 4 points have a common surface plus
+    // at least 4 edges at the boundary
+
+    if(!boundaryedges)
+      const_cast<Mesh *>(this)->BuildBoundaryEdges();
+
+
+    // non-tets are always legal
+    if (el.GetType() != TET)
+      {
+        el.SetLegal (1);
+        return 1;
+      }
+
+    POINTTYPE pointtype[4];
+    for(int i = 0; i < 4; i++)
+      pointtype[i] = (*this)[el[i]].Type();
+
+
+
+    // element has at least 2 inner points ---> legal
+    int cnti = 0;
+    for (int j = 0; j < 4; j++)
+      if ( pointtype[j] == INNERPOINT)
+        {
+          cnti++;
+          if (cnti >= 2)
+            {
+              el.SetLegal (1);
+              return 1;
+            }
+        }
+
+
+
+    // which faces are boundary faces ?
+    int bface[4];
+    for (int i = 0; i < 4; i++)
+      {
+        bface[i] = surfelementht->Used (INDEX_3::Sort(el[gftetfacesa[i][0]],
+                                                      el[gftetfacesa[i][1]],
+                                                      el[gftetfacesa[i][2]]));
+      }
+
+    int bedge[4][4];
+    int segedge[4][4];
+    static const int pi3map[4][4] = { { -1,  2,  1,  1 },
+                                      {  2, -1,  0,  0 },
+                                      {  1,  0, -1,  0 },
+                                      {  1,  0,  0, -1 } };
+
+    static const int pi4map[4][4] = { { -1,  3,  3,  2 },
+                                      {  3, -1,  3,  2 },
+                                      {  3,  3, -1,  1 },
+                                      {  2,  2,  1, -1 } };
+
+
+    for (int i = 0; i < 4; i++)
+      for (int j = 0; j < i; j++)
+        {
+          bool sege = false, be = false;
+
+          int pos = boundaryedges -> Position(INDEX_2::Sort(el[i], el[j]));
+          if (pos)
+            {
+              be = true;
+              if (boundaryedges -> GetData(pos) == 2)
+                sege = true;
+            }
+
+          segedge[j][i] = segedge[i][j] = sege;
+          bedge[j][i] = bedge[i][j] = be;
+        }
+
+    // two boundary faces and no edge is illegal
+    for (int i = 0; i < 3; i++)
+      for (int j = i+1; j < 4; j++)
+        {
+          if (bface[i] && bface[j])
+            if (!segedge[pi3map[i][j]][pi4map[i][j]])
+              {
+                // 2 boundary faces withoud edge in between
+                el.SetLegal (0);
+                return 0;
+              }
+        }
+
+    // three boundary edges meeting in a Surface point
+    for (int i = 0; i < 4; i++)
+      {
+        if ( pointtype[i] == SURFACEPOINT)
+          {
+            bool alledges = 1;
+            for (int j = 0; j < 4; j++)
+              if (j != i && !bedge[i][j])
+                {
+                  alledges = 0;
+                  break;
+                }
+            if (alledges)
+              {
+                // cout << "tet illegal due to unmarked node" << endl;
+                el.SetLegal (0);
+                return 0;
+              }
+          }
+      }
+
+
+
+    for (int fnr = 0; fnr < 4; fnr++)
+      if (!bface[fnr])
+        for (int i = 0; i < 4; i++)
+          if (i != fnr)
+            {
+              int pi1 = pi3map[i][fnr];
+              int pi2 = pi4map[i][fnr];
+
+              if ( pointtype[i] == SURFACEPOINT)
+                {
+                  // two connected edges on surface, but no face
+                  if (bedge[i][pi1] && bedge[i][pi2])
+                    {
+                      el.SetLegal (0);
+                      return 0;
+                    }
+                }
+
+              if ( pointtype[i] == EDGEPOINT)
+                {
+                  // connected surface edge and edge edge, but no face
+                  if ( (bedge[i][pi1] && segedge[i][pi2]) ||
+                       (bedge[i][pi2] && segedge[i][pi1]) )
+                    {
+                      el.SetLegal (0);
+                      return 0;
+                    }
+                }
+
+            }
+
+
+    el.SetLegal (1);
+    return 1;
+
+  }
+
+
+
+  int Mesh :: GetNDomains() const
+  {
+    int ndom = 0;
+
+    for (int k = 0; k < facedecoding.Size(); k++)
+      {
+        if (facedecoding[k].DomainIn() > ndom)
+          ndom = facedecoding[k].DomainIn();
+        if (facedecoding[k].DomainOut() > ndom)
+          ndom = facedecoding[k].DomainOut();
+      }
+
+    return ndom;
+  }
+
+
+
+  void Mesh :: SurfaceMeshOrientation ()
+  {
+    int i, j;
+    int nse = GetNSE();
+
+    BitArray used(nse);
+    used.Clear();
+    INDEX_2_HASHTABLE<int> edges(nse+1);
+
+    bool haschanged = 0;
+
+
+    const Element2d & tri = SurfaceElement(1);
+    for (j = 1; j <= 3; j++)
+      {
+        INDEX_2 i2(tri.PNumMod(j), tri.PNumMod(j+1));
+        edges.Set (i2, 1);
+      }
+    used.Set(1);
+
+    bool unused;
+    do
+      {
+        bool changed;
+        do
+          {
+            changed = 0;
+            for (i = 1; i <= nse; i++)
+              if (!used.Test(i))
+                {
+                  Element2d & el = surfelements.Elem(i);
+                  int found = 0, foundrev = 0;
+                  for (j = 1; j <= 3; j++)
+                    {
+                      INDEX_2 i2(el.PNumMod(j), el.PNumMod(j+1));
+                      if (edges.Used(i2))
+                        foundrev = 1;
+                      swap (i2.I1(), i2.I2());
+                      if (edges.Used(i2))
+                        found = 1;
+                    }
+
+                  if (found || foundrev)
+                    {
+                      if (foundrev)
+                        swap (el.PNum(2), el.PNum(3));
+
+                      changed = 1;
+                      for (j = 1; j <= 3; j++)
+                        {
+                          INDEX_2 i2(el.PNumMod(j), el.PNumMod(j+1));
+                          edges.Set (i2, 1);
+                        }
+                      used.Set (i);
+                    }
+                }
+            if (changed)
+              haschanged = 1;
+          }
+        while (changed);
+
+
+        unused = 0;
+        for (i = 1; i <= nse; i++)
+          if (!used.Test(i))
+            {
+              unused = 1;
+              const Element2d & tri = SurfaceElement(i);
+              for (j = 1; j <= 3; j++)
+                {
+                  INDEX_2 i2(tri.PNumMod(j), tri.PNumMod(j+1));
+                  edges.Set (i2, 1);
+                }
+              used.Set(i);
+              break;
+            }
+      }
+    while (unused);
+
+    if (haschanged)
+      timestamp = NextTimeStamp();
+  }
+
+
+  void Mesh :: Split2Tets()
+  {
+    PrintMessage (1, "Split To Tets");
+    bool has_prisms = 0;
+
+    int oldne = GetNE(); 
+    for (int i = 1; i <= oldne; i++)
+      {
+        Element el = VolumeElement(i);
+
+        if (el.GetType() == PRISM)
+          {
+            // prism, to 3 tets
+
+            // make minimal node to node 1
+            int minpi=0;
+            PointIndex minpnum;
+            minpnum = GetNP() + 1;
+
+            for (int j = 1; j <= 6; j++)
+              {
+                if (el.PNum(j) < minpnum)
+                  {
+                    minpnum = el.PNum(j);
+                    minpi = j;
+                  }
+              }
+
+            if (minpi >= 4)
+              {
+                for (int j = 1; j <= 3; j++)
+                  swap (el.PNum(j), el.PNum(j+3));
+                minpi -= 3;
+              }
+
+            while (minpi > 1)
+              {
+                int hi = 0;
+                for (int j = 0; j <= 3; j+= 3)
+                  {
+                    hi = el.PNum(1+j);
+                    el.PNum(1+j) = el.PNum(2+j);
+                    el.PNum(2+j) = el.PNum(3+j);
+                    el.PNum(3+j) = hi;
+                  }
+                minpi--;
+              }
+
+            /*
+              version 1: edge from pi2 to pi6,
+              version 2: edge from pi3 to pi5,
+            */
+
+            static const int ntets[2][12] =
+              { { 1, 4, 5, 6, 1, 2, 3, 6, 1, 2, 5, 6 },
+                { 1, 4, 5, 6, 1, 2, 3, 5, 3, 1, 5, 6 } };
+
+            const int * min2pi;
+
+            if (min2 (el.PNum(2), el.PNum(6)) <
+                min2 (el.PNum(3), el.PNum(5)))
+              {
+                min2pi = &ntets[0][0];
+                // (*testout) << "version 1 ";
+              }
+            else
+              {
+                min2pi = &ntets[1][0];
+                // (*testout) << "version 2 ";
+              }
+
+
+            int firsttet = 1;
+            for (int j = 1; j <= 3; j++)
+              {
+                Element nel(TET);
+                for (int k = 1; k <= 4; k++)
+                  nel.PNum(k) = el.PNum(min2pi[4 * j + k - 5]);
+                nel.SetIndex (el.GetIndex());
+
+                int legal = 1;
+                for (int k = 1; k <= 3; k++)
+                  for (int l = k+1; l <= 4; l++)
+                    if (nel.PNum(k) == nel.PNum(l))
+                      legal = 0;
+
+                // (*testout) << nel << " ";
+                if (legal)
+                  {
+                    if (firsttet)
+                      {
+                        VolumeElement(i) = nel;
+                        firsttet = 0;
+                      }
+                    else
+                      {
+                        AddVolumeElement(nel);
+                      }
+                  }
+              }
+            if (firsttet) cout << "no legal";
+            (*testout) << endl;
+          }
+
+
+
+        else if (el.GetType() == HEX)
+          {
+            // hex to A) 2 prisms or B) to 5 tets
+
+            // make minimal node to node 1
+            int minpi=0;
+            PointIndex minpnum;
+            minpnum = GetNP() + 1;
+
+            for (int j = 1; j <= 8; j++)
+              {
+                if (el.PNum(j) < minpnum)
+                  {
+                    minpnum = el.PNum(j);
+                    minpi = j;
+                  }
+              }
+
+            if (minpi >= 5)
+              {
+                for (int j = 1; j <= 4; j++)
+                  swap (el.PNum(j), el.PNum(j+4));
+                minpi -= 4;
+              }
+
+            while (minpi > 1)
+              {
+                int hi = 0;
+                for (int j = 0; j <= 4; j+= 4)
+                  {
+                    hi = el.PNum(1+j);
+                    el.PNum(1+j) = el.PNum(2+j);
+                    el.PNum(2+j) = el.PNum(3+j);
+                    el.PNum(3+j) = el.PNum(4+j);
+                    el.PNum(4+j) = hi;
+                  }
+                minpi--;
+              }
+
+
+
+            static const int to_prisms[3][12] =
+              { { 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7 },
+                { 0, 1, 5, 3, 2, 6, 0, 5, 4, 3, 6, 7 },
+                { 0, 7, 4, 1, 6, 5, 0, 3, 7, 1, 2, 6 },
+              };
+
+            const int * min2pi = 0;
+            if (min2 (el[4], el[6]) < min2 (el[5], el[7]))
+              min2pi = &to_prisms[0][0];
+            else if (min2 (el[3], el[6]) < min2 (el[2], el[7]))
+              min2pi = &to_prisms[1][0];
+            else if (min2 (el[1], el[6]) < min2 (el[2], el[5]))
+              min2pi = &to_prisms[2][0];
+
+            if (min2pi)
+              {
+                has_prisms = 1;
+                for (int j = 0; j < 2; j++)
+                  {
+                    Element nel(PRISM);
+                    for (int k = 0; k < 6; k++)
+                      nel[k] = el[min2pi[6*j + k]];
+                    nel.SetIndex (el.GetIndex());
+
+                    if (j == 0)
+                      VolumeElement(i) = nel;
+                    else
+                      AddVolumeElement(nel);
+                  }
+              }
+            else
+              {
+                // split to 5 tets
+
+                static const int to_tets[20] =
+                  {
+                    1, 2, 0, 5,
+                    3, 0, 2, 7,
+                    4, 5, 7, 0,
+                    6, 7, 5, 2,
+                    0, 2, 7, 5
+                  };
+
+                for (int j = 0; j < 5; j++)
+                  {
+                    Element nel(TET);
+                    for (int k = 0; k < 4; k++)
+                      nel[k] = el[to_tets[4*j + k]];
+                    nel.SetIndex (el.GetIndex());
+
+                    if (j == 0)
+                      VolumeElement(i) = nel;
+                    else
+                      AddVolumeElement(nel);
+                  }
+
+              }
+          }
+
+
+
+
+
+        else if (el.GetType() == PYRAMID)
+          {
+            // pyramid, to 2 tets
+
+            // cout << "pyramid: " << el << endl;
+
+            static const int ntets[2][8] =
+              { { 1, 2, 3, 5, 1, 3, 4, 5 },
+                { 1, 2, 4, 5, 4, 2, 3, 5 }};
+
+            const int * min2pi;
+
+            if (min2 (el[0], el[2]) < min2 (el[1], el[3]))
+              min2pi = &ntets[0][0];
+            else
+              min2pi = &ntets[1][0];
+
+            bool firsttet = 1;
+            for (int j = 0; j < 2; j++)
+              {
+                Element nel(TET);
+                for (int k = 0; k < 4; k++)
+                  nel[k] = el[min2pi[4*j + k]-1];
+                nel.SetIndex (el.GetIndex());
+
+                // cout << "pyramid-tet: " << nel << endl;
+
+                bool legal = 1;
+                for (int k = 0; k < 3; k++)
+                  for (int l = k+1; l < 4; l++)
+                    if (nel[k] == nel[l])
+                      legal = 0;
+
+                if (legal)
+                  {
+                    (*testout) << nel << " ";
+                    if (firsttet)
+                      VolumeElement(i) = nel;
+                    else
+                      AddVolumeElement(nel);
+
+                    firsttet = 0;
+                  }
+              }
+            if (firsttet) cout << "no legal";
+            (*testout) << endl;
+          }
+      }
+
+
+    int oldnse = GetNSE(); 
+    for (int i = 1; i <= oldnse; i++)
+      {
+        Element2d el = SurfaceElement(i);
+        if (el.GetNP() == 4)
+          {
+            (*testout) << "split el: " << el << " to ";
+
+            static const int ntris[2][6] =
+              { { 1, 2, 3, 1, 3, 4 },
+                { 1, 2, 4, 4, 2, 3 }};
+
+            const int * min2pi;
+
+            if (min2 (el.PNum(1), el.PNum(3)) <
+                min2 (el.PNum(2), el.PNum(4)))
+              min2pi = &ntris[0][0];
+            else
+              min2pi = &ntris[1][0];
+
+            for (int j = 0; j <6; j++)
+              (*testout) << min2pi[j] << " ";
+
+
+            int firsttri = 1;
+            for (int j = 1; j <= 2; j++)
+              {
+                Element2d nel(3);
+                for (int k = 1; k <= 3; k++)
+                  nel.PNum(k) = el.PNum(min2pi[3 * j + k - 4]);
+                nel.SetIndex (el.GetIndex());
+
+                int legal = 1;
+                for (int k = 1; k <= 2; k++)
+                  for (int l = k+1; l <= 3; l++)
+                    if (nel.PNum(k) == nel.PNum(l))
+                      legal = 0;
+
+                if (legal)
+                  {
+                    (*testout) << nel << " ";
+                    if (firsttri)
+                      {
+                        SurfaceElement(i) = nel;
+                        firsttri = 0;
+                      }
+                    else
+                      {
+                        AddSurfaceElement(nel);
+                      }
+                  }
+              }
+            (*testout) << endl;
+
+          }
+      }
+
+
+    if (has_prisms)
+
+      Split2Tets();
+
+    else
+      {
+        for (int i = 1; i <= GetNE(); i++)
+          {
+            Element & el = VolumeElement(i);
+            const Point3d & p1 = Point (el.PNum(1));
+            const Point3d & p2 = Point (el.PNum(2));
+            const Point3d & p3 = Point (el.PNum(3));
+            const Point3d & p4 = Point (el.PNum(4));
+
+            double vol = (Vec3d (p1, p2) * 
+                          Cross (Vec3d (p1, p3), Vec3d(p1, p4)));
+            if (vol > 0)
+              swap (el.PNum(3), el.PNum(4));
+          }
+
+
+
+        UpdateTopology();
+        timestamp = NextTimeStamp();
+      }
+  }
+
+  void Mesh :: BuildElementSearchTree ()
+  {
+    if (elementsearchtreets == GetTimeStamp())
+      return;
+
+    NgLock lock(mutex);
+    lock.Lock();
+
+    PrintMessage (4, "Rebuild element searchtree");
+
+    delete elementsearchtree;
+    elementsearchtree = NULL;
+
+    Box3d box;
+    int ne = (dimension == 2) ? GetNSE() : GetNE();
+    if (!ne) 
+      {
+        lock.UnLock();
+        return;
+      }
+
+    if (dimension == 2)
+      {
+	box.SetPoint (Point (SurfaceElement(1).PNum(1)));
+	for (int i = 1; i <= ne; i++)
+	  {
+	    const Element2d & el = SurfaceElement(i);
+	    for (int j = 1; j <= el.GetNP(); j++)
+	      box.AddPoint (Point (el.PNum(j)));
+	  }
+	
+	box.Increase (1.01 * box.CalcDiam());
+	elementsearchtree = new Box3dTree (box.PMin(), box.PMax());
+	
+	for (int i = 1; i <= ne; i++)
+	  {
+	    const Element2d & el = SurfaceElement(i);
+	    box.SetPoint (Point (el.PNum(1)));
+	    for (int j = 1; j <= el.GetNP(); j++)
+	      box.AddPoint (Point (el.PNum(j)));
+
+	    elementsearchtree -> Insert (box.PMin(), box.PMax(), i);
+	  }
+      }
+    else
+      {
+	box.SetPoint (Point (VolumeElement(1).PNum(1)));
+	for (int i = 1; i <= ne; i++)
+	  {
+	    const Element & el = VolumeElement(i);
+	    for (int j = 1; j <= el.GetNP(); j++)
+	      box.AddPoint (Point (el.PNum(j)));
+	  }
+	
+	box.Increase (1.01 * box.CalcDiam());
+	elementsearchtree = new Box3dTree (box.PMin(), box.PMax());
+	
+	for (int i = 1; i <= ne; i++)
+	  {
+	    const Element & el = VolumeElement(i);
+	    box.SetPoint (Point (el.PNum(1)));
+	    for (int j = 1; j <= el.GetNP(); j++)
+	      box.AddPoint (Point (el.PNum(j)));
+
+	    elementsearchtree -> Insert (box.PMin(), box.PMax(), i);
+	  }
+      }
+
+    elementsearchtreets = GetTimeStamp();
+
+    lock.UnLock();
+  }
+
+
+
+  bool Mesh :: PointContainedIn2DElement(const Point3d & p,
+                                         double lami[3],
+                                         const int element,
+                                         bool consider3D) const
+  {
+    Vec3d col1, col2, col3;
+    Vec3d rhs, sol;
+    const double eps = 1e-6;
+
+    Array<Element2d> loctrigs;
+
+
+    //SZ 
+    if(SurfaceElement(element).GetType()==QUAD)
+      {
+        const Element2d & el = SurfaceElement(element); 
+
+        const Point3d & p1 = Point(el.PNum(1)); 
+        const Point3d & p2 = Point(el.PNum(2));
+        const Point3d & p3 = Point(el.PNum(3));
+        const Point3d & p4 = Point(el.PNum(4)); 
+
+        // Coefficients of Bilinear Mapping from Ref-Elem to global Elem
+        // X = a + b x + c y + d x y 
+        Vec3d a = p1; 
+        Vec3d b = p2 - a; 
+        Vec3d c = p4 - a; 
+        Vec3d d = p3 - a - b - c; 
+
+        double dxb = d.X()*b.Y()-d.Y()*b.X();
+        double dxc = d.X()*c.Y()-d.Y()*c.X(); 
+        double dxa = d.X()*a.Y()-d.Y()*a.X(); 
+        double dxp = d.X()*p.Y()-d.Y()*p.X(); 
+
+        double c0,c1,c2; // ,rt; 
+        lami[2]=0.; 
+        double eps = 1.E-12; 
+
+        if(fabs(d.X()) <= eps && fabs(d.Y())<= eps)
+          {
+            //Solve Linear System
+            lami[0]=(c.Y()*(p.X()-a.X())-c.X()*(p.Y()-a.Y()))/
+              (b.X()*c.Y() -b.Y()*c.X()); 
+            lami[1]=(-b.Y()*(p.X()-a.X())+b.X()*(p.Y()-a.Y()))/
+              (b.X()*c.Y() -b.Y()*c.X()); 
+          } 
+        else
+          if(fabs(dxb) <= eps) 
+            {
+              lami[1] = (dxp-dxa)/dxc;
+              if(fabs(b.X()-d.X()*lami[1])>=eps)
+                lami[0] = (p.X()-a.X() - c.X()*lami[1])/(b.X()+d.X()*lami[1]); 
+              else
+                lami[0] = (p.Y()-a.Y() - c.Y()*lami[1])/(b.Y()+d.Y()*lami[1]); 
+            }
+          else
+            if(fabs(dxc) <= eps)
+              {
+                lami[0] = (dxp-dxa)/dxb;
+                if(fabs(c.X()-d.X()*lami[0])>=eps)
+                  lami[1] = (p.X()-a.X() - b.X()*lami[0])/(c.X()+d.X()*lami[0]); 
+                else
+                  lami[1] = (p.Y()-a.Y() - b.Y()*lami[0])/(c.Y()+d.Y()*lami[0]); 
+              }
+            else //Solve quadratic equation
+              {
+                if(fabs(d.X()) >= eps)
+                  {
+                    c2 = d.X()*dxc;
+                    c1 = d.X()*dxc - c.X()*dxb - d.X()*(dxp-dxa);
+                    c0 = -b.X()*(dxp -dxa) - (a.X()-p.X())*dxb;
+                  }
+                else 
+                  {
+                    c2 = d.Y()*dxc;
+                    c1 = d.Y()*dxc - c.Y()*dxb - d.Y()*(dxp-dxa);
+                    c0 = -b.Y()*(dxp -dxa) - (a.Y()-p.Y())*dxb;
+                  }
+
+                double rt =  c1*c1 - 4*c2*c0;
+                if (rt < 0.) return false; 
+                lami[1] = (-c1 + sqrt(rt))/2/c2;
+                if(lami[1]<=1. && lami[1]>=0.)
+                  {
+                    lami[0] = (dxp - dxa -dxc*lami[1])/dxb;
+                    if(lami[0]<=1. && lami[0]>=0.)
+                      return true;
+                  }
+
+                lami[1] = (-c1 - sqrt(rt))/2/c2;
+                lami[0] = (dxp - dxa -dxc*lami[1])/dxb;
+              }
+
+        if( lami[0] <= 1.+eps  && lami[0] >= -eps && lami[1]<=1.+eps && lami[1]>=-eps)
+          {
+            if(consider3D)
+              {
+                Vec3d n = Cross(b,c);
+                lami[2] = 0;
+                for(int i=1; i<=3; i++)
+                  lami[2] +=(p.X(i)-a.X(i)-lami[0]*b.X(i)-lami[1]*c.X(i)) * n.X(i);
+                if(lami[2] >= -eps && lami[2] <= eps)
+                  return true;
+              }
+            else
+              return true;
+          }
+
+        return false;
+
+      }
+    else
+      {
+        //	  SurfaceElement(element).GetTets (loctets);
+        loctrigs.SetSize(1);
+        loctrigs.Elem(1) = SurfaceElement(element);
+
+
+
+        for (int j = 1; j <= loctrigs.Size(); j++)
+          {
+            const Element2d & el = loctrigs.Get(j);
+
+
+            const Point3d & p1 = Point(el.PNum(1));
+            const Point3d & p2 = Point(el.PNum(2));
+            const Point3d & p3 = Point(el.PNum(3));
+            /*
+              Box3d box;
+              box.SetPoint (p1);
+              box.AddPoint (p2);
+              box.AddPoint (p3);
+              box.AddPoint (p4);
+              if (!box.IsIn (p))
+              continue;
+            */
+            col1 = p2-p1;
+            col2 = p3-p1;
+            col3 = Cross(col1,col2);
+            //col3 = Vec3d(0, 0, 1);
+            rhs = p - p1;
+
+            // int retval = 
+            SolveLinearSystem (col1, col2, col3, rhs, sol);
+
+            //(*testout) << "retval " << retval << endl;
+
+            //(*testout) << "col1 " << col1 << " col2 " << col2 << " col3 " << col3 << " rhs " << rhs << endl;
+            //(*testout) << "sol " << sol << endl;
+
+            if (sol.X() >= -eps && sol.Y() >= -eps && 
+                sol.X() + sol.Y() <= 1+eps)
+              {
+                if(!consider3D || (sol.Z() >= -eps && sol.Z() <= eps))
+                  {
+                    lami[0] = sol.X();
+                    lami[1] = sol.Y();
+                    lami[2] = sol.Z();
+
+                    return true;
+                  }
+              }
+          }
+      }
+
+    return false;
+
+  }
+
+
+
+
+  bool Mesh :: PointContainedIn3DElement(const Point3d & p,
+                                         double lami[3],
+                                         const int element) const
+  {
+    //bool oldresult = PointContainedIn3DElementOld(p,lami,element);
+    //(*testout) << "old result: " << oldresult
+    //       << " lam " << lami[0] << " " << lami[1] << " " << lami[2] << endl;
+
+    //if(!curvedelems->IsElementCurved(element-1))
+    //  return PointContainedIn3DElementOld(p,lami,element);
+
+
+    const double eps = 1.e-4;
+    const Element & el = VolumeElement(element);
+
+    netgen::Point<3> lam = 0.0;
+
+    if (el.GetType() == TET)
+      {
+        lam = 0.25;
+      }
+    else if (el.GetType() == PRISM)
+      {
+        lam(0) = 0.33; lam(1) = 0.33; lam(2) = 0.5;
+      }
+    else if (el.GetType() == PYRAMID)
+      {
+        lam(0) = 0.4; lam(1) = 0.4; lam(2) = 0.2;
+      }
+    else if (el.GetType() == HEX)
+      {
+        lam = 0.5;
+      }
+
+
+    Vec<3> deltalam,rhs;
+    netgen::Point<3> x;
+    Mat<3,3> Jac,Jact;
+
+    double delta=1;
+
+    bool retval;
+
+    int i = 0;
+
+    const int maxits = 30;
+
+    while(delta > 1e-16 && i<maxits)
+      {
+        curvedelems->CalcElementTransformation(lam,element-1,x,Jac);
+
+        rhs = p-x;
+        Jac.Solve(rhs,deltalam);
+
+        lam += deltalam;
+
+        delta = deltalam.Length2();
+
+        i++;
+        //(*testout) << "pcie i " << i << " delta " << delta << " p " << p << " x " << x << " lam " << lam << endl;
+        //<< "Jac " << Jac << endl;
+      }
+
+    if(i==maxits)
+      return false;
+
+
+    for(i=0; i<3; i++)
+      lami[i] = lam(i);
+
+
+
+    if (el.GetType() == TET)
+      {
+        retval = (lam(0) > -eps && 
+                  lam(1) > -eps && 
+                  lam(2) > -eps && 
+                  lam(0) + lam(1) + lam(2) < 1+eps);
+      }
+    else if (el.GetType() == PRISM)
+      {
+        retval = (lam(0) > -eps &&
+                  lam(1) > -eps &&
+                  lam(2) > -eps &&
+                  lam(2) < 1+eps &&
+                  lam(0) + lam(1) < 1+eps);
+      }
+    else if (el.GetType() == PYRAMID)
+      {
+        retval = (lam(0) > -eps &&
+                  lam(1) > -eps &&
+                  lam(2) > -eps &&
+                  lam(0) + lam(2) < 1+eps &&
+                  lam(1) + lam(2) < 1+eps);
+      }
+    else if (el.GetType() == HEX)
+      {
+        retval = (lam(0) > -eps && lam(0) < 1+eps &&
+                  lam(1) > -eps && lam(1) < 1+eps &&
+                  lam(2) > -eps && lam(2) < 1+eps);
+      }
+    else
+      throw NgException("Da haun i wos vagessn");
+
+    return retval;
+  }
+
+
+
+  bool Mesh :: PointContainedIn3DElementOld(const Point3d & p,
+                                            double lami[3],
+                                            const int element) const
+  {
+    Vec3d col1, col2, col3;
+    Vec3d rhs, sol;
+    const double eps = 1.e-4;
+
+    Array<Element> loctets;
+
+    VolumeElement(element).GetTets (loctets);
+
+    for (int j = 1; j <= loctets.Size(); j++)
+      {
+        const Element & el = loctets.Get(j);
+
+        const Point3d & p1 = Point(el.PNum(1));
+        const Point3d & p2 = Point(el.PNum(2));
+        const Point3d & p3 = Point(el.PNum(3));
+        const Point3d & p4 = Point(el.PNum(4));
+
+        Box3d box;
+        box.SetPoint (p1);
+        box.AddPoint (p2);
+        box.AddPoint (p3);
+        box.AddPoint (p4);
+        if (!box.IsIn (p))
+          continue;
+
+        col1 = p2-p1;
+        col2 = p3-p1;
+        col3 = p4-p1;
+        rhs = p - p1;
+
+        SolveLinearSystem (col1, col2, col3, rhs, sol);
+
+        if (sol.X() >= -eps && sol.Y() >= -eps && sol.Z() >= -eps &&
+            sol.X() + sol.Y() + sol.Z() <= 1+eps)
+          {
+            Array<Element> loctetsloc;
+            Array<netgen::Point<3> > pointsloc;
+
+            VolumeElement(element).GetTetsLocal (loctetsloc);
+            VolumeElement(element).GetNodesLocalNew (pointsloc);
+
+            const Element & le = loctetsloc.Get(j);
+
+
+            Point3d pp = 
+              pointsloc.Get(le.PNum(1)) 
+              + sol.X() * Vec3d (pointsloc.Get(le.PNum(1)), pointsloc.Get(le.PNum(2))) 
+              + sol.Y() * Vec3d (pointsloc.Get(le.PNum(1)), pointsloc.Get(le.PNum(3))) 
+              + sol.Z() * Vec3d (pointsloc.Get(le.PNum(1)), pointsloc.Get(le.PNum(4))) ;
+
+            lami[0] = pp.X();
+            lami[1] = pp.Y();
+            lami[2] = pp.Z();
+            return true;
+          }
+      }
+    return false;
+  }
+
+
+  int Mesh :: GetElementOfPoint (const Point3d & p,
+                                 double lami[3],
+                                 bool build_searchtree,
+                                 const int index,
+                                 const bool allowindex) const
+  {
+    if(index != -1) 
+      {
+        Array<int> dummy(1);
+        dummy[0] = index;
+        return GetElementOfPoint(p,lami,&dummy,build_searchtree,allowindex);
+      }
+    else
+      return GetElementOfPoint(p,lami,NULL,build_searchtree,allowindex);
+  }
+
+
+
+
+  int Mesh :: GetElementOfPoint (const Point3d & p,
+                                 double lami[3],
+                                 const Array<int> * const indices,
+                                 bool build_searchtree,
+                                 const bool allowindex) const
+  {
+    if (dimension == 2)
+      {
+        int ne;
+
+
+        if(ps_startelement != 0 && ps_startelement <= GetNSE() && PointContainedIn2DElement(p,lami,ps_startelement))
+          return ps_startelement;
+
+        Array<int> locels;
+        if (elementsearchtree || build_searchtree)
+          {
+            // update if necessary:
+            const_cast<Mesh&>(*this).BuildElementSearchTree (); 
+            elementsearchtree->GetIntersecting (p, p, locels);
+            ne = locels.Size();
+          }
+        else
+          ne = GetNSE();
+
+        for (int i = 1; i <= ne; i++)
+          {
+            int ii;
+
+            if (elementsearchtree)
+              ii = locels.Get(i);
+            else
+              ii = i;
+
+            if(ii == ps_startelement) continue;
+
+            if(indices != NULL && indices->Size() > 0)
+              {
+                bool contained = indices->Contains(SurfaceElement(ii).GetIndex());
+                if((allowindex && !contained) || (!allowindex && contained)) continue;
+              }
+
+            if(PointContainedIn2DElement(p,lami,ii)) return ii;
+
+          }
+        return 0;
+      }
+    else
+
+      {
+        // int i, j;
+        int ne;
+
+        if(ps_startelement != 0 && PointContainedIn3DElement(p,lami,ps_startelement))
+          return ps_startelement;
+
+        Array<int> locels;
+        if (elementsearchtree || build_searchtree)
+          {
+            // update if necessary:
+            const_cast<Mesh&>(*this).BuildElementSearchTree (); 
+            elementsearchtree->GetIntersecting (p, p, locels);
+            ne = locels.Size();
+          }
+        else
+          ne = GetNE();
+
+        for (int i = 1; i <= ne; i++)
+          {
+            int ii;
+
+            if (elementsearchtree)
+              ii = locels.Get(i);
+            else
+              ii = i;
+
+            if(ii == ps_startelement) continue;
+
+            if(indices != NULL && indices->Size() > 0)
+              {
+                bool contained = indices->Contains(VolumeElement(ii).GetIndex());
+                if((allowindex && !contained) || (!allowindex && contained)) continue;
+              }
+
+            if(PointContainedIn3DElement(p,lami,ii)) 
+              {
+                ps_startelement = ii;
+                return ii;
+              }
+          }
+
+        // Not found, try uncurved variant:
+        for (int i = 1; i <= ne; i++)
+          {
+            int ii;
+
+            if (elementsearchtree)
+              ii = locels.Get(i);
+            else
+              ii = i;
+
+            if(indices != NULL && indices->Size() > 0)
+              {
+                bool contained = indices->Contains(VolumeElement(ii).GetIndex());
+                if((allowindex && !contained) || (!allowindex && contained)) continue;
+              }
+
+
+            if(PointContainedIn3DElementOld(p,lami,ii)) 
+              {
+                ps_startelement = ii;
+                (*testout) << "WARNING: found element of point " << p <<" only for uncurved mesh" << endl;
+                return ii;
+              }
+          }
+
+
+        return 0;
+      }
+  }
+
+
+
+  int Mesh :: GetSurfaceElementOfPoint (const Point3d & p,
+                                        double lami[3],
+                                        bool build_searchtree,
+                                        const int index,
+                                        const bool allowindex) const
+  {
+    if(index != -1) 
+      {
+        Array<int> dummy(1);
+        dummy[0] = index;
+        return GetSurfaceElementOfPoint(p,lami,&dummy,build_searchtree,allowindex);
+      }
+    else
+      return GetSurfaceElementOfPoint(p,lami,NULL,build_searchtree,allowindex);
+  }
+
+
+
+
+  int Mesh :: GetSurfaceElementOfPoint (const Point3d & p,
+                                        double lami[3],
+                                        const Array<int> * const indices,
+                                        bool build_searchtree,
+                                        const bool allowindex) const
+  {
+    if (dimension == 2)
+      {
+        throw NgException("GetSurfaceElementOfPoint not yet implemented for 2D meshes");
+      }
+    else
+      {
+        double vlam[3];
+        int velement = GetElementOfPoint(p,vlam,NULL,build_searchtree,allowindex);
+
+        //(*testout) << "p " << p << endl;
+        //(*testout) << "velement " << velement << endl;
+
+        Array<int> faces;
+        topology->GetElementFaces(velement,faces);
+
+        //(*testout) << "faces " << faces << endl;
+
+        for(int i=0; i<faces.Size(); i++)
+          faces[i] = topology->GetFace2SurfaceElement(faces[i]);
+
+        //(*testout) << "surfel " << faces << endl;
+
+        for(int i=0; i<faces.Size(); i++)
+          {
+            if(faces[i] == 0)
+              continue;
+
+            if(indices && indices->Size() != 0)
+              {
+                if(indices->Contains(SurfaceElement(faces[i]).GetIndex()) &&
+                   PointContainedIn2DElement(p,lami,faces[i],true))
+                  return faces[i];
+              }
+            else
+              {
+                if(PointContainedIn2DElement(p,lami,faces[i],true))
+                  {
+                    //(*testout) << "found point " << p << " in sel " << faces[i]
+                    //	       << ", lam " << lami[0] << ", " << lami[1] << ", " << lami[2] << endl;
+                    return faces[i];
+                  }
+              }
+          }
+
+      }
+
+    return 0;
+  }
+
+
+  void Mesh::GetIntersectingVolEls(const Point3d& p1, const Point3d& p2, 
+                                   Array<int> & locels) const
+  {
+    elementsearchtree->GetIntersecting (p1, p2, locels);
+  }
+
+  void Mesh :: SplitIntoParts()
+  {
+    int i, j, dom;
+    int ne = GetNE();
+    int np = GetNP();
+    int nse = GetNSE();
+
+    BitArray surfused(nse);
+    BitArray pused (np);
+
+    surfused.Clear();
+
+    dom = 0;
+
+    while (1)
+      {
+        int cntd = 1;
+
+        dom++;
+
+        pused.Clear();
+
+        int found = 0;
+        for (i = 1; i <= nse; i++)
+          if (!surfused.Test(i))
+            {
+              SurfaceElement(i).SetIndex (dom);
+              for (j = 1; j <= 3; j++)
+                pused.Set (SurfaceElement(i).PNum(j));
+              found = 1;
+              cntd = 1;
+              surfused.Set(i);
+              break;
+            }
+
+        if (!found)
+          break;
+
+        int change;
+        do
+          {
+            change = 0;
+            for (i = 1; i <= nse; i++)
+              {
+                int is = 0, isnot = 0;
+                for (j = 1; j <= 3; j++)
+                  if (pused.Test(SurfaceElement(i).PNum(j)))
+                    is = 1;
+                  else
+                    isnot = 1;
+
+                if (is && isnot)
+                  {
+                    change = 1;
+                    for (j = 1; j <= 3; j++)
+                      pused.Set (SurfaceElement(i).PNum(j));
+                  }
+
+                if (is) 
+                  {
+                    if (!surfused.Test(i))
+                      {
+                        surfused.Set(i);
+                        SurfaceElement(i).SetIndex (dom);
+                        cntd++;
+                      }
+                  }
+              }
+
+
+            for (i = 1; i <= ne; i++)
+              {
+                int is = 0, isnot = 0;
+                for (j = 1; j <= 4; j++)
+                  if (pused.Test(VolumeElement(i).PNum(j)))
+                    is = 1;
+                  else
+                    isnot = 1;
+
+                if (is && isnot)
+                  {
+                    change = 1;
+                    for (j = 1; j <= 4; j++)
+                      pused.Set (VolumeElement(i).PNum(j));
+                  }
+
+                if (is)
+                  {
+                    VolumeElement(i).SetIndex (dom);
+                  }
+              }
+          }
+        while (change);
+
+        PrintMessage (3, "domain ", dom, " has ", cntd, " surfaceelements");
+      }
+
+    /*
+      facedecoding.SetSize (dom);
+      for (i = 1; i <= dom; i++)
+      {
+      facedecoding.Elem(i).surfnr = 0;
+      facedecoding.Elem(i).domin = i;
+      facedecoding.Elem(i).domout = 0;
+      }
+    */
+    ClearFaceDescriptors();
+    for (i = 1; i <= dom; i++)
+      AddFaceDescriptor (FaceDescriptor (0, i, 0, 0));
+    CalcSurfacesOfNode();
+    timestamp = NextTimeStamp();
+  }
+
+  void Mesh :: SplitSeparatedFaces ()
+  {
+    PrintMessage (3, "SplitSeparateFaces");
+    int fdi;
+    int np = GetNP();
+
+    BitArray usedp(np);
+    Array<SurfaceElementIndex> els_of_face;
+
+    fdi = 1;
+    while (fdi <= GetNFD())
+      {
+        GetSurfaceElementsOfFace (fdi, els_of_face);
+
+        if (els_of_face.Size() == 0) continue;
+
+        SurfaceElementIndex firstel = els_of_face[0];
+
+        usedp.Clear();
+        for (int j = 1; j <= SurfaceElement(firstel).GetNP(); j++)
+          usedp.Set (SurfaceElement(firstel).PNum(j));
+
+        bool changed;
+        do
+          {
+            changed = false;
+
+            for (int i = 0; i < els_of_face.Size(); i++)
+              {
+                const Element2d & el = SurfaceElement(els_of_face[i]);
+
+                bool has = 0;
+                bool hasno = 0;
+                for (int j = 0; j < el.GetNP(); j++)
+                  {
+                    if (usedp.Test(el[j]))
+                      has = true;
+                    else
+                      hasno = true;
+                  }
+
+                if (has && hasno)
+                  changed = true;
+
+                if (has)
+                  for (int j = 0; j < el.GetNP(); j++)
+                    usedp.Set (el[j]);
+              }
+          }
+        while (changed);
+
+        int nface = 0;
+        for (int i = 0; i < els_of_face.Size(); i++)
+          {
+            Element2d & el = SurfaceElement(els_of_face[i]);
+
+            int hasno = 0;
+            for (int j = 1; j <= el.GetNP(); j++)
+              if (!usedp.Test(el.PNum(j)))
+                hasno = 1;
+
+            if (hasno)
+              {
+                if (!nface)
+                  {
+                    FaceDescriptor nfd = GetFaceDescriptor(fdi);
+                    nface = AddFaceDescriptor (nfd);
+                  }
+
+                el.SetIndex (nface);
+              }
+          }
+
+        // reconnect list
+        if (nface)
+          {
+            facedecoding[nface-1].firstelement = -1;
+            facedecoding[fdi-1].firstelement = -1;
+
+            for (int i = 0; i < els_of_face.Size(); i++)
+              {
+                int ind = SurfaceElement(els_of_face[i]).GetIndex();
+                SurfaceElement(els_of_face[i]).next = facedecoding[ind-1].firstelement;
+                facedecoding[ind-1].firstelement = els_of_face[i];
+              }
+          }
+
+        fdi++;
+      }
+
+
+    /*
+      fdi = 1;
+      while (fdi <= GetNFD())
+      {
+      int firstel = 0;
+      for (int i = 1; i <= GetNSE(); i++)
+      if (SurfaceElement(i).GetIndex() == fdi)
+      {
+      firstel = i;
+      break;
+      }
+      if (!firstel) continue;
+
+      usedp.Clear();
+      for (int j = 1; j <= SurfaceElement(firstel).GetNP(); j++)
+      usedp.Set (SurfaceElement(firstel).PNum(j));
+
+      int changed;
+      do
+      {
+      changed = 0;
+      for (int i = 1; i <= GetNSE(); i++)
+      {
+      const Element2d & el = SurfaceElement(i);
+      if (el.GetIndex() != fdi)
+      continue;
+
+      int has = 0;
+      int hasno = 0;
+      for (int j = 1; j <= el.GetNP(); j++)
+      {
+      if (usedp.Test(el.PNum(j)))
+      has = 1;
+      else
+      hasno = 1;
+      }
+      if (has && hasno)
+      changed = 1;
+
+      if (has)
+      for (int j = 1; j <= el.GetNP(); j++)
+      usedp.Set (el.PNum(j));
+      }
+      }
+      while (changed);
+
+      int nface = 0;
+      for (int i = 1; i <= GetNSE(); i++)
+      {
+      Element2d & el = SurfaceElement(i);
+      if (el.GetIndex() != fdi)
+      continue;	  
+
+      int hasno = 0;
+      for (int j = 1; j <= el.GetNP(); j++)
+      {
+      if (!usedp.Test(el.PNum(j)))
+      hasno = 1;
+      }
+
+      if (hasno)
+      {
+      if (!nface)
+      {
+      FaceDescriptor nfd = GetFaceDescriptor(fdi);
+      nface = AddFaceDescriptor (nfd);
+      }
+
+      el.SetIndex (nface);
+      }
+      }
+      fdi++;
+      }
+    */
+  }
+
+
+
+  void Mesh :: RebuildSurfaceElementLists ()
+  {
+    for (int i = 0; i < facedecoding.Size(); i++)
+      facedecoding[i].firstelement = -1;
+    for (int i = surfelements.Size()-1; i >= 0; i--)
+      {
+        int ind = surfelements[i].GetIndex();
+        surfelements[i].next = facedecoding[ind-1].firstelement;
+        facedecoding[ind-1].firstelement = i;
+      }
+  }
+
+  void Mesh :: GetSurfaceElementsOfFace (int facenr, Array<SurfaceElementIndex> & sei) const
+  {
+    static int timer = NgProfiler::CreateTimer ("GetSurfaceElementsOfFace");
+    NgProfiler::RegionTimer reg (timer);
+
+
+     /*
+     sei.SetSize (0);
+     for (SurfaceElementIndex i = 0; i < GetNSE(); i++)
+     {
+        if ( (*this)[i].GetIndex () == facenr && (*this)[i][0] >= PointIndex::BASE &&
+           !(*this)[i].IsDeleted() )
+        {
+           sei.Append (i);
+        }
+     }
+     */
+
+     /* Philippose - 01/10/2009
+     Commented out the following lines, and activated the originally 
+     commented out lines above because of a bug which causes corruption 
+     of the variable "facedecoding" when a mesh is converted to second order
+     */
+
+     //      int size1 = sei.Size();
+     sei.SetSize(0);
+
+     SurfaceElementIndex si = facedecoding[facenr-1].firstelement;
+     while (si != -1)
+     {
+        if ( (*this)[si].GetIndex () == facenr && (*this)[si][0] >= PointIndex::BASE &&
+             !(*this)[si].IsDeleted() )
+        {
+           sei.Append (si);
+        }
+
+        si = (*this)[si].next;
+     }
+     
+     /*
+     // *testout << "with list = " << endl << sei << endl;
+
+     if (size1 != sei.Size()) 
+     {
+        cout << "size mismatch" << endl;
+        exit(1);
+     }
+     */
+  }
+
+
+
+
+  void Mesh :: CalcMinMaxAngle (double badellimit, double * retvalues) 
+  {
+    int i, j;
+    int lpi1, lpi2, lpi3, lpi4;
+    double phimax = 0, phimin = 10;
+    double facephimax = 0, facephimin = 10;
+    int illegaltets = 0, negativetets = 0, badtets = 0;
+
+    for (i = 1; i <= GetNE(); i++)
+      {
+        int badel = 0;
+
+        Element & el = VolumeElement(i);
+
+        if (el.GetType() != TET)
+          {
+            VolumeElement(i).flags.badel = 0;
+            continue;
+          }
+
+        if (el.Volume(Points()) < 0)
+          {
+            badel = 1;
+            negativetets++;
+          }
+
+
+        if (!LegalTet (el)) 
+          {
+            badel = 1;
+            illegaltets++;
+            (*testout) << "illegal tet: " << i << " ";
+            for (j = 1; j <= el.GetNP(); j++)
+              (*testout) << el.PNum(j) << " ";
+            (*testout) << endl;
+          }
+
+
+        // angles between faces
+        for (lpi1 = 1; lpi1 <= 3; lpi1++)
+          for (lpi2 = lpi1+1; lpi2 <= 4; lpi2++)
+            {
+              lpi3 = 1;
+              while (lpi3 == lpi1 || lpi3 == lpi2)
+                lpi3++;
+              lpi4 = 10 - lpi1 - lpi2 - lpi3;
+
+              const Point3d & p1 = Point (el.PNum(lpi1));
+              const Point3d & p2 = Point (el.PNum(lpi2));
+              const Point3d & p3 = Point (el.PNum(lpi3));
+              const Point3d & p4 = Point (el.PNum(lpi4));
+
+              Vec3d n(p1, p2);
+              n /= n.Length();
+              Vec3d v1(p1, p3);
+              Vec3d v2(p1, p4);
+
+              v1 -= (n * v1) * n;
+              v2 -= (n * v2) * n;
+
+              double cosphi = (v1 * v2) / (v1.Length() * v2.Length());
+              double phi = acos (cosphi);
+              if (phi > phimax) phimax = phi;
+              if (phi < phimin) phimin = phi;
+
+              if ((180/M_PI) * phi > badellimit)
+                badel = 1;
+            }
+
+
+        // angles in faces
+        for (j = 1; j <= 4; j++)
+          {
+            Element2d face;
+            el.GetFace (j, face);
+            for (lpi1 = 1; lpi1 <= 3; lpi1++)
+              {
+                lpi2 = lpi1 % 3 + 1;
+                lpi3 = lpi2 % 3 + 1;
+
+                const Point3d & p1 = Point (el.PNum(lpi1));
+                const Point3d & p2 = Point (el.PNum(lpi2));
+                const Point3d & p3 = Point (el.PNum(lpi3));
+
+                Vec3d v1(p1, p2);
+                Vec3d v2(p1, p3);
+                double cosphi = (v1 * v2) / (v1.Length() * v2.Length());
+                double phi = acos (cosphi);
+                if (phi > facephimax) facephimax = phi;
+                if (phi < facephimin) facephimin = phi;
+
+                if ((180/M_PI) * phi > badellimit)
+                  badel = 1;
+
+              }
+          }
+
+
+        VolumeElement(i).flags.badel = badel;
+        if (badel) badtets++;
+      }
+
+    if (!GetNE())
+      {
+        phimin = phimax = facephimin = facephimax = 0;
+      }
+
+    if (!retvalues)
+      {
+        PrintMessage (1, "");
+        PrintMessage (1, "between planes:  phimin = ", (180/M_PI) * phimin,
+                      " phimax = ", (180/M_PI) *phimax);
+        PrintMessage (1, "inside planes:   phimin = ", (180/M_PI) * facephimin,
+                      " phimax = ", (180/M_PI) * facephimax);
+        PrintMessage (1, "");      
+      }
+    else
+      {
+        retvalues[0] = (180/M_PI) * facephimin;
+        retvalues[1] = (180/M_PI) * facephimax;
+        retvalues[2] = (180/M_PI) * phimin;
+        retvalues[3] = (180/M_PI) * phimax;
+      }
+    PrintMessage (3, "negative tets: ", negativetets);
+    PrintMessage (3, "illegal tets:  ", illegaltets);
+    PrintMessage (3, "bad tets:      ", badtets);
+  }
+
+
+  int Mesh :: MarkIllegalElements ()
+  {
+    int cnt = 0;
+    int i;
+
+    for (i = 1; i <= GetNE(); i++)
+      {
+        LegalTet (VolumeElement(i));
+
+        /*
+          Element & el = VolumeElement(i);
+          int leg1 = LegalTet (el);
+          el.flags.illegal_valid = 0;
+          int leg2 = LegalTet (el);
+
+          if (leg1 != leg2) 
+          {
+          cerr << "legal differs!!" << endl;
+          (*testout) << "legal differs" << endl;
+          (*testout) << "elnr = " << i << ", el = " << el
+          << " leg1 = " << leg1 << ", leg2 = " << leg2 << endl;
+          }
+
+          //      el.flags.illegal = !LegalTet (el);
+          */
+        cnt += VolumeElement(i).Illegal();
+      }
+    return cnt;
+  }
+
+  // #ifdef NONE
+  //   void Mesh :: AddIdentification (int pi1, int pi2, int identnr)
+  //   {
+  //     INDEX_2 pair(pi1, pi2);
+  //     //  pair.Sort();
+  //     identifiedpoints->Set (pair, identnr);
+  //     if (identnr > maxidentnr)
+  //       maxidentnr = identnr;
+  //     timestamp = NextTimeStamp();
+  //   }
+
+  //   int Mesh :: GetIdentification (int pi1, int pi2) const
+  //   {
+  //     INDEX_2 pair(pi1, pi2);
+  //     if (identifiedpoints->Used (pair))
+  //       return identifiedpoints->Get(pair);
+  //     else
+  //       return 0;
+  //   }
+
+  //   int Mesh :: GetIdentificationSym (int pi1, int pi2) const
+  //   {
+  //     INDEX_2 pair(pi1, pi2);
+  //     if (identifiedpoints->Used (pair))
+  //       return identifiedpoints->Get(pair);
+
+  //     pair = INDEX_2 (pi2, pi1);
+  //     if (identifiedpoints->Used (pair))
+  //       return identifiedpoints->Get(pair);
+
+  //     return 0;
+  //   }
+
+
+  //   void Mesh :: GetIdentificationMap (int identnr, Array<int> & identmap) const
+  //   {
+  //     int i, j;
+
+  //     identmap.SetSize (GetNP());
+  //     for (i = 1; i <= identmap.Size(); i++)
+  //       identmap.Elem(i) = 0;
+
+  //     for (i = 1; i <= identifiedpoints->GetNBags(); i++)
+  //       for (j = 1; j <= identifiedpoints->GetBagSize(i); j++)
+  // 	{
+  // 	  INDEX_2 i2;
+  // 	  int nr;
+  // 	  identifiedpoints->GetData (i, j, i2, nr);
+
+  // 	  if (nr == identnr)
+  // 	    {
+  // 	      identmap.Elem(i2.I1()) = i2.I2();
+  // 	    }
+  // 	}
+  //   }
+
+
+  //   void Mesh :: GetIdentificationPairs (int identnr, Array<INDEX_2> & identpairs) const
+  //   {
+  //     int i, j;
+
+  //     identpairs.SetSize(0);
+
+  //     for (i = 1; i <= identifiedpoints->GetNBags(); i++)
+  //       for (j = 1; j <= identifiedpoints->GetBagSize(i); j++)
+  // 	{
+  // 	  INDEX_2 i2;
+  // 	  int nr;
+  // 	  identifiedpoints->GetData (i, j, i2, nr);
+
+  // 	  if (identnr == 0 || nr == identnr)
+  // 	    identpairs.Append (i2);
+  // 	}
+  //   }
+  // #endif
+
+
+
+  void Mesh :: InitPointCurve(double red, double green, double blue) const
+  {
+    pointcurves_startpoint.Append(pointcurves.Size());
+    pointcurves_red.Append(red);
+    pointcurves_green.Append(green);
+    pointcurves_blue.Append(blue);
+  }
+  void Mesh :: AddPointCurvePoint(const Point3d & pt) const
+  {
+    pointcurves.Append(pt);
+  }
+  int Mesh :: GetNumPointCurves(void) const
+  {
+    return pointcurves_startpoint.Size();
+  }
+  int Mesh :: GetNumPointsOfPointCurve(int curve) const
+  {
+    if(curve == pointcurves_startpoint.Size()-1)
+      return (pointcurves.Size() - pointcurves_startpoint.Last());
+    else
+      return (pointcurves_startpoint[curve+1]-pointcurves_startpoint[curve]);
+  }
+
+  Point3d & Mesh :: GetPointCurvePoint(int curve, int n) const
+  {
+    return pointcurves[pointcurves_startpoint[curve]+n];
+  }
+
+  void Mesh :: GetPointCurveColor(int curve, double & red, double & green, double & blue) const
+  {
+    red = pointcurves_red[curve];
+    green = pointcurves_green[curve];
+    blue = pointcurves_blue[curve];
+  }
+
+
+  void Mesh :: ComputeNVertices ()
+  {
+    int i, j, nv;
+    int ne = GetNE();
+    int nse = GetNSE();
+
+    numvertices = 0;
+    for (i = 1; i <= ne; i++)
+      {
+        const Element & el = VolumeElement(i);
+        nv = el.GetNV();
+        for (j = 0; j < nv; j++)
+          if (el[j] > numvertices)
+            numvertices = el[j];
+      }
+    for (i = 1; i <= nse; i++)
+      {
+        const Element2d & el = SurfaceElement(i);
+        nv = el.GetNV();
+        for (j = 1; j <= nv; j++)
+          if (el.PNum(j) > numvertices)
+            numvertices = el.PNum(j);
+      } 
+
+    numvertices += 1- PointIndex::BASE;
+  }
+
+  int Mesh :: GetNV () const
+  {
+    if (numvertices < 0)
+      return GetNP();
+    else
+      return numvertices;
+  }
+
+  void Mesh :: SetNP (int np)
+  {
+    points.SetSize(np);
+    //  ptyps.SetSize(np);
+
+    int mlold = mlbetweennodes.Size();
+    mlbetweennodes.SetSize(np);
+    if (np > mlold)
+      for (int i = mlold+PointIndex::BASE; 
+           i < np+PointIndex::BASE; i++)
+        {
+          mlbetweennodes[i].I1() = PointIndex::BASE-1;
+          mlbetweennodes[i].I2() = PointIndex::BASE-1;
+        }
+
+    GetIdentifications().SetMaxPointNr (np + PointIndex::BASE-1);
+  }
+
+
+  /*
+    void Mesh :: BuildConnectedNodes ()
+    {
+    if (PureTetMesh())
+    {
+    connectedtonode.SetSize(0);
+    return;
+    }
+
+
+    int i, j, k;
+    int np = GetNP();
+    int ne = GetNE();
+    TABLE<int> conto(np);
+    for (i = 1; i <= ne; i++)
+    {
+    const Element & el = VolumeElement(i);
+
+    if (el.GetType() == PRISM)
+    {
+    for (j = 1; j <= 6; j++)
+    {
+    int n1 = el.PNum (j);
+    int n2 = el.PNum ((j+2)%6+1);
+    //	    if (n1 != n2)
+    {
+    int found = 0;
+    for (k = 1; k <= conto.EntrySize(n1); k++)
+    if (conto.Get(n1, k) == n2)
+    {
+    found = 1;
+    break;
+    }
+    if (!found)
+    conto.Add (n1, n2);
+    }
+    }
+    }
+    else if (el.GetType() == PYRAMID)
+    {
+    for (j = 1; j <= 4; j++)
+    {
+    int n1, n2;
+    switch (j)
+    {
+    case 1: n1 = 1; n2 = 4; break;
+    case 2: n1 = 4; n2 = 1; break;
+    case 3: n1 = 2; n2 = 3; break;
+    case 4: n1 = 3; n2 = 2; break;
+    }
+
+    int found = 0;
+    for (k = 1; k <= conto.EntrySize(n1); k++)
+    if (conto.Get(n1, k) == n2)
+    {
+    found = 1;
+    break;
+    }
+    if (!found)
+    conto.Add (n1, n2);
+    }
+    }
+    }
+
+    connectedtonode.SetSize(np);
+    for (i = 1; i <= np; i++)
+    connectedtonode.Elem(i) = 0;
+
+    for (i = 1; i <= np; i++)
+    if (connectedtonode.Elem(i) == 0)
+    {
+    connectedtonode.Elem(i) = i;
+    ConnectToNodeRec (i, i, conto);
+    }
+
+
+
+    }
+
+    void Mesh :: ConnectToNodeRec (int node, int tonode, 
+    const TABLE<int> & conto)
+    {
+    int i, n2;
+    //  (*testout) << "connect " << node << " to " << tonode << endl;
+    for (i = 1; i <= conto.EntrySize(node); i++)
+    {
+    n2 = conto.Get(node, i);
+    if (!connectedtonode.Get(n2))
+    {
+    connectedtonode.Elem(n2) = tonode;
+    ConnectToNodeRec (n2, tonode, conto);
+    }
+    }
+    }
+  */
+
+
+  bool Mesh :: PureTrigMesh (int faceindex) const
+  {
+    // if (!faceindex) return !mparam.quad;
+    
+    if (!faceindex)
+      {
+	for (int i = 1; i <= GetNSE(); i++)
+	  if (SurfaceElement(i).GetNP() != 3)
+	    return false;
+	return true;
+      }
+
+    for (int i = 1; i <= GetNSE(); i++)
+      if (SurfaceElement(i).GetIndex() == faceindex &&
+          SurfaceElement(i).GetNP() != 3)
+        return false;
+    return true;
+  }
+
+  bool Mesh :: PureTetMesh () const
+  {
+    for (ElementIndex ei = 0; ei < GetNE(); ei++)
+      if (VolumeElement(ei).GetNP() != 4)
+        return 0;
+    return 1;
+  }
+
+  void Mesh :: UpdateTopology()
+  {
+    topology->Update();
+    clusters->Update();
+  }
+
+
+  void Mesh :: SetMaterial (int domnr, const char * mat)
+  {
+    if (domnr > materials.Size())
+      {
+        int olds = materials.Size();
+        materials.SetSize (domnr);
+        for (int i = olds; i < domnr; i++)
+          materials[i] = 0;
+      }
+    materials.Elem(domnr) = new char[strlen(mat)+1];
+    strcpy (materials.Elem(domnr), mat);
+  }
+
+  const char * Mesh :: GetMaterial (int domnr) const
+  {
+    if (domnr <= materials.Size())
+      return materials.Get(domnr);
+    return 0;
+  }
+
+  void Mesh ::SetNBCNames ( int nbcn )
+  {
+    if ( bcnames.Size() )
+      for ( int i = 0; i < bcnames.Size(); i++)
+        if ( bcnames[i] ) delete bcnames[i];
+    bcnames.SetSize(nbcn);
+    bcnames = 0;
+  }
+
+  void Mesh ::SetBCName ( int bcnr, const string & abcname )
+  {
+    if ( bcnames[bcnr] ) delete bcnames[bcnr];
+    if ( abcname != "default" )
+      bcnames[bcnr] = new string ( abcname );
+    else
+      bcnames[bcnr] = 0;
+  }
+
+  string Mesh ::GetBCName ( int bcnr ) const
+  {
+    if ( !bcnames.Size() )
+      return "default";
+    if ( bcnames[bcnr] )
+      return *bcnames[bcnr];
+    else
+      return "default";
+  }
+
+  void Mesh :: SetUserData(const char * id, Array<int> & data)
+  {
+    if(userdata_int.Used(id))
+      delete userdata_int.Get(id);
+
+    Array<int> * newdata = new Array<int>(data);
+
+    userdata_int.Set(id,newdata);      
+  }
+  bool Mesh :: GetUserData(const char * id, Array<int> & data, int shift) const
+  {
+    if(userdata_int.Used(id))
+      {
+        if(data.Size() < (*userdata_int.Get(id)).Size()+shift)
+          data.SetSize((*userdata_int.Get(id)).Size()+shift);
+        for(int i=0; i<(*userdata_int.Get(id)).Size(); i++)
+          data[i+shift] = (*userdata_int.Get(id))[i];
+        return true;
+      }
+    else
+      {
+        data.SetSize(0);
+        return false;
+      }
+  }
+  void Mesh :: SetUserData(const char * id, Array<double> & data)
+  {
+    if(userdata_double.Used(id))
+      delete userdata_double.Get(id);
+
+    Array<double> * newdata = new Array<double>(data);
+
+    userdata_double.Set(id,newdata);      
+  }
+  bool Mesh :: GetUserData(const char * id, Array<double> & data, int shift) const
+  {
+    if(userdata_double.Used(id))
+      {
+        if(data.Size() < (*userdata_double.Get(id)).Size()+shift)
+          data.SetSize((*userdata_double.Get(id)).Size()+shift);
+        for(int i=0; i<(*userdata_double.Get(id)).Size(); i++)
+          data[i+shift] = (*userdata_double.Get(id))[i];
+        return true;
+      }
+    else
+      {
+        data.SetSize(0);
+        return false;
+      }
+  }
+
+
+
+  void Mesh :: PrintMemInfo (ostream & ost) const
+  {
+    ost << "Mesh Mem:" << endl;
+
+    ost << GetNP() << " Points, of size " 
+        << sizeof (Point3d) << " + " << sizeof(POINTTYPE) << " = "
+        << GetNP() * (sizeof (Point3d) + sizeof(POINTTYPE)) << endl;
+
+    ost << GetNSE() << " Surface elements, of size " 
+        << sizeof (Element2d) << " = " 
+        << GetNSE() * sizeof(Element2d) << endl;
+
+    ost << GetNE() << " Volume elements, of size " 
+        << sizeof (Element) << " = " 
+        << GetNE() * sizeof(Element) << endl;
+
+    ost << "surfs on node:";
+    surfacesonnode.PrintMemInfo (cout);
+
+    ost << "boundaryedges: ";
+    if (boundaryedges)
+      boundaryedges->PrintMemInfo (cout);
+
+    ost << "surfelementht: ";
+    if (surfelementht)
+      surfelementht->PrintMemInfo (cout);
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/meshclass.hpp b/contrib/Netgen/libsrc/meshing/meshclass.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1f45d1f0ec4ffb07d9525b04cf8b9da6d61cfc63
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshclass.hpp
@@ -0,0 +1,770 @@
+#ifndef MESHCLASS
+#define MESHCLASS
+
+/**************************************************************************/
+/* File:   meshclass.hpp                                                  */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   20. Nov. 99                                                    */
+/**************************************************************************/
+
+/*
+  The mesh class
+*/
+
+namespace netgen
+{
+
+  enum resthtype { RESTRICTH_FACE, RESTRICTH_EDGE, 
+		   RESTRICTH_SURFACEELEMENT, RESTRICTH_POINT, RESTRICTH_SEGMENT };
+
+  class HPRefElement;
+
+
+  /// 2d/3d mesh
+  class Mesh
+  {
+  public:
+    typedef ::netgen::T_POINTS T_POINTS;
+
+    // typedef MoveableArray<MeshPoint,PointIndex::BASE> T_POINTS;
+    // typedef MoveableArray<Element> T_VOLELEMENTS;
+    // typedef MoveableArray<Element2d> T_SURFELEMENTS;
+
+    // typedef Array<MeshPoint,PointIndex::BASE> T_POINTS;
+    typedef Array<Element> T_VOLELEMENTS;
+    typedef Array<Element2d> T_SURFELEMENTS;
+
+
+  private:
+    /// point coordinates
+    T_POINTS points;
+
+    /// line-segments at edges
+    Array<Segment> segments;
+    /// surface elements, 2d-inner elements
+    T_SURFELEMENTS surfelements;
+    /// volume elements
+    T_VOLELEMENTS volelements;
+    /// points will be fixed forever
+    Array<PointIndex> lockedpoints;
+
+
+    /// surface indices at boundary nodes
+    TABLE<int,PointIndex::BASE> surfacesonnode;
+    /// boundary edges  (1..normal bedge, 2..segment)
+    INDEX_2_CLOSED_HASHTABLE<int> * boundaryedges;
+    ///
+    INDEX_2_CLOSED_HASHTABLE<int> * segmentht;
+    ///
+    INDEX_3_CLOSED_HASHTABLE<int> * surfelementht;
+
+    /// faces of rest-solid
+    Array<Element2d> openelements;
+    /// open segmenets for surface meshing  
+    Array<Segment> opensegments;
+
+
+
+    /**
+       Representation of local mesh-size h
+    */
+    LocalH * lochfunc;
+    ///
+    double hglob;
+    ///
+    double hmin;
+    ///
+    Array<double> maxhdomain;
+  
+    /**
+       the face-index of the surface element maps into
+       this table.
+    */
+    Array<FaceDescriptor> facedecoding;
+
+  
+    /**
+       the edge-index of the line element maps into
+       this table.
+    */
+    Array<EdgeDescriptor> edgedecoding;
+
+    /// sub-domain materials 
+    Array<char*> materials;
+
+    /// labels for boundary conditions
+    Array<string*> bcnames;
+
+    /// Periodic surface, close surface, etc. identifications
+    Identifications * ident;
+
+
+    /// number of vertices (if < 0, use np)
+    int numvertices;
+
+    /// geometric search tree for interval intersection search
+    Box3dTree * elementsearchtree;
+    /// time stamp for tree
+    int elementsearchtreets;
+
+    /// element -> face, element -> edge etc ...
+    class MeshTopology * topology;
+    /// methods for high order elements
+    class CurvedElements * curvedelems;
+
+    /// nodes identified by close points 
+    class AnisotropicClusters * clusters;
+
+    /// space dimension (2 or 3)
+    int dimension;
+  
+    /// changed by every minor modification (addpoint, ...)
+    int timestamp;
+    /// changed after finishing global algorithm (improve, ...)
+    int majortimestamp;
+
+    /// mesh access semaphors.
+    NgMutex mutex;
+    /// mesh access semaphors.
+    NgMutex majormutex;
+
+    SYMBOLTABLE< Array<int>* > userdata_int;
+    SYMBOLTABLE< Array<double>* > userdata_double; 
+
+
+    mutable Array< Point3d > pointcurves;
+    mutable Array<int> pointcurves_startpoint;
+    mutable Array<double> pointcurves_red,pointcurves_green,pointcurves_blue;
+
+
+    /// start element for point search (GetElementOfPoint)
+    mutable int ps_startelement;
+
+
+#ifdef PARALLEL
+    /// connection to parallel meshes
+    class ParallelMeshTopology * paralleltop;
+
+#endif
+
+
+  private:
+    void BuildBoundaryEdges(void);
+
+  public:
+    bool PointContainedIn2DElement(const Point3d & p,
+				   double lami[3],
+				   const int element,
+				   bool consider3D = false) const;
+    bool PointContainedIn3DElement(const Point3d & p,
+				   double lami[3],
+				   const int element) const;
+    bool PointContainedIn3DElementOld(const Point3d & p,
+				      double lami[3],
+				      const int element) const;
+
+  public:
+
+    // store coarse mesh before hp-refinement
+    Array<HPRefElement> * hpelements;
+    Mesh * coarsemesh;
+  
+  
+    /// number of refinement levels
+    int mglevels;
+    /// refinement hierarchy
+    Array<INDEX_2,PointIndex::BASE> mlbetweennodes;
+    /// parent element of volume element
+    Array<int> mlparentelement;
+    /// parent element of surface element
+    Array<int> mlparentsurfaceelement;
+
+
+
+    ///
+    DLL_HEADER Mesh();
+    ///
+    DLL_HEADER ~Mesh();
+
+    Mesh & operator= (const Mesh & mesh2);
+  
+    ///
+    DLL_HEADER void DeleteMesh();
+  
+    ///
+    void ClearSurfaceElements();
+
+    ///
+    void ClearVolumeElements()
+    {
+      volelements.SetSize(0); 
+      timestamp = NextTimeStamp();
+    }
+
+    ///
+    void ClearSegments()
+    { 
+      segments.SetSize(0); 
+      timestamp = NextTimeStamp();
+    }
+  
+    ///
+    bool TestOk () const;
+
+    void SetAllocSize(int nnodes, int nsegs, int nsel, int nel);
+
+
+    DLL_HEADER PointIndex AddPoint (const Point3d & p, int layer = 1);
+    DLL_HEADER PointIndex AddPoint (const Point3d & p, int layer, POINTTYPE type);
+#ifdef PARALLEL
+    PointIndex AddPoint (const Point3d & p, bool aisghost, int layer = 1);
+    PointIndex AddPoint (const Point3d & p, bool aisghost, int layer, POINTTYPE type);
+#endif
+    int GetNP () const { return points.Size(); }
+
+    MeshPoint & Point(int i) { return points.Elem(i); }
+    MeshPoint & Point(PointIndex pi) { return points[pi]; }
+    const MeshPoint & Point(int i) const { return points.Get(i); }
+    const MeshPoint & Point(PointIndex pi) const { return points[pi]; }
+
+    const MeshPoint & operator[] (PointIndex pi) const { return points[pi]; }
+    MeshPoint & operator[] (PointIndex pi) { return points[pi]; }
+
+    const T_POINTS & Points() const { return points; }
+    T_POINTS & Points() { return points; }
+
+
+    DLL_HEADER SegmentIndex AddSegment (const Segment & s);
+    void DeleteSegment (int segnr)
+    {
+      segments.Elem(segnr)[0] = PointIndex::BASE-1;
+      segments.Elem(segnr)[1] = PointIndex::BASE-1;
+    }
+    void FullDeleteSegment (int segnr)  // von wem ist das ???
+    {
+      segments.Delete(segnr-PointIndex::BASE);
+    }
+
+    int GetNSeg () const { return segments.Size(); }
+    Segment & LineSegment(int i) { return segments.Elem(i); }
+    const Segment & LineSegment(int i) const { return segments.Get(i); }
+
+    Segment & LineSegment(SegmentIndex si) { return segments[si]; }
+    const Segment & LineSegment(SegmentIndex si) const { return segments[si]; }
+    const Segment & operator[] (SegmentIndex si) const { return segments[si]; }
+    Segment & operator[] (SegmentIndex si) { return segments[si]; }
+
+
+
+
+    DLL_HEADER SurfaceElementIndex AddSurfaceElement (const Element2d & el);
+    void DeleteSurfaceElement (int eli)
+    { 
+      surfelements.Elem(eli).Delete();
+      surfelements.Elem(eli).PNum(1) = -1; 
+      surfelements.Elem(eli).PNum(2) = -1; 
+      surfelements.Elem(eli).PNum(3) = -1; 
+      timestamp = NextTimeStamp();
+    }
+
+    void DeleteSurfaceElement (SurfaceElementIndex eli)
+    {
+      DeleteSurfaceElement (int(eli)+1);
+    }
+
+    int GetNSE () const { return surfelements.Size(); }
+    Element2d & SurfaceElement(int i) { return surfelements.Elem(i); }
+    const Element2d & SurfaceElement(int i) const { return surfelements.Get(i); }
+    Element2d & SurfaceElement(SurfaceElementIndex i) { return surfelements[i]; }
+    const Element2d & SurfaceElement(SurfaceElementIndex i) const { return surfelements[i]; }
+
+    const Element2d & operator[] (SurfaceElementIndex ei) const
+    { return surfelements[ei]; }
+    Element2d & operator[] (SurfaceElementIndex ei)
+    { return surfelements[ei]; }
+
+  
+    DLL_HEADER void RebuildSurfaceElementLists ();
+    DLL_HEADER void GetSurfaceElementsOfFace (int facenr, Array<SurfaceElementIndex> & sei) const;
+
+    DLL_HEADER ElementIndex AddVolumeElement (const Element & el);
+
+    int GetNE () const { return volelements.Size(); }
+
+    Element & VolumeElement(int i) { return volelements.Elem(i); }
+    const Element & VolumeElement(int i) const { return volelements.Get(i); }
+    Element & VolumeElement(ElementIndex i) { return volelements[i]; }
+    const Element & VolumeElement(ElementIndex i) const { return volelements[i]; }
+
+    const Element & operator[] (ElementIndex ei) const 
+    { return volelements[ei]; }
+    Element & operator[] (ElementIndex ei)
+    { return volelements[ei]; }
+
+
+
+    ELEMENTTYPE ElementType (ElementIndex i) const 
+    { return (volelements[i].flags.fixed) ? FIXEDELEMENT : FREEELEMENT; }
+
+    const T_VOLELEMENTS & VolumeElements() const { return volelements; }
+    T_VOLELEMENTS & VolumeElements() { return volelements; }
+
+
+    ///
+    DLL_HEADER double ElementError (int eli, const MeshingParameters & mp) const;
+
+    /// 
+    DLL_HEADER void AddLockedPoint (PointIndex pi);
+    ///
+    void ClearLockedPoints ();
+
+    const Array<PointIndex> & LockedPoints() const
+    { return lockedpoints; }
+
+    /// Returns number of domains
+    int GetNDomains() const;
+
+    ///
+    int GetDimension() const 
+    { return dimension; }
+    void SetDimension(int dim)
+    { dimension = dim; }
+
+    /// sets internal tables
+    void CalcSurfacesOfNode ();
+
+    /// additional (temporarily) fix points 
+    void FixPoints (const BitArray & fixpoints);
+
+    /**
+       finds elements without neighbour and
+       boundary elements without inner element.
+       Results are stored in openelements.
+       if dom == 0, all sub-domains, else subdomain dom */
+    DLL_HEADER void FindOpenElements (int dom = 0);
+
+  
+    /**
+       finds segments without surface element,
+       and surface elements without neighbours.
+       store in opensegmentsy
+    */
+    DLL_HEADER void FindOpenSegments (int surfnr = 0);
+    /**
+       remove one layer of surface elements
+    */
+    DLL_HEADER void RemoveOneLayerSurfaceElements ();
+
+
+    int GetNOpenSegments () { return opensegments.Size(); }
+    const Segment & GetOpenSegment (int nr) { return opensegments.Get(nr); }
+  
+    /**
+       Checks overlap of boundary
+       return == 1, iff overlap
+    */
+    DLL_HEADER int CheckOverlappingBoundary ();
+    /**
+       Checks consistent boundary
+       return == 0, everything ok
+    */
+    DLL_HEADER int CheckConsistentBoundary () const;
+
+    /*
+      checks element orientation
+    */
+    DLL_HEADER int CheckVolumeMesh () const;
+
+
+    /**
+       finds average h of surface surfnr if surfnr > 0,
+       else of all surfaces.
+    */
+    DLL_HEADER double AverageH (int surfnr = 0) const;
+    /// Calculates localh 
+    DLL_HEADER void CalcLocalH (double grading);
+    ///
+    DLL_HEADER void SetLocalH (const Point3d & pmin, const Point3d & pmax, double grading);
+    ///
+    DLL_HEADER void RestrictLocalH (const Point3d & p, double hloc);
+    ///
+    DLL_HEADER void RestrictLocalHLine (const Point3d & p1, const Point3d & p2, 
+			     double hloc);
+    /// number of elements per radius
+    DLL_HEADER void CalcLocalHFromSurfaceCurvature(double grading, double elperr);
+    ///
+    DLL_HEADER void CalcLocalHFromPointDistances(double grading);
+    ///
+    DLL_HEADER void RestrictLocalH (resthtype rht, int nr, double loch);
+    ///
+    DLL_HEADER void LoadLocalMeshSize (const char * meshsizefilename);
+    ///
+    DLL_HEADER void SetGlobalH (double h);
+    ///
+    void SetMinimalH (double h);
+    ///
+    double MaxHDomain (int dom) const;
+    ///
+    void SetMaxHDomain (const Array<double> & mhd);
+    ///
+    double GetH (const Point3d & p) const;
+    ///
+    double GetMinH (const Point3d & pmin, const Point3d & pmax);
+    ///
+    LocalH & LocalHFunction () { return * lochfunc; }
+    ///
+    bool LocalHFunctionGenerated(void) const { return (lochfunc != NULL); }
+
+    /// Find bounding box
+    DLL_HEADER void GetBox (Point3d & pmin, Point3d & pmax, int dom = -1) const;
+
+    /// Find bounding box of points of typ ptyp or less
+    DLL_HEADER void GetBox (Point3d & pmin, Point3d & pmax, POINTTYPE ptyp ) const;
+
+    ///
+    int GetNOpenElements() const
+    { return openelements.Size(); }
+    ///
+    const Element2d & OpenElement(int i) const
+    { return openelements.Get(i); }
+
+
+    /// are also quads open elements
+    bool HasOpenQuads () const;
+
+    /// split into connected pieces
+    void SplitIntoParts ();
+
+    /// 
+    void SplitSeparatedFaces ();
+
+    /// Refines mesh and projects points to true surface
+    // void Refine (int levels, const CSGeometry * geom);
+  
+
+    bool BoundaryEdge (PointIndex pi1, PointIndex pi2) const
+    {
+      if(!boundaryedges)
+	const_cast<Mesh *>(this)->BuildBoundaryEdges();
+
+      INDEX_2 i2 (pi1, pi2);
+      i2.Sort();
+      return boundaryedges->Used (i2);
+    }
+
+    bool IsSegment (PointIndex pi1, PointIndex pi2) const
+    {
+      INDEX_2 i2 (pi1, pi2);
+      i2.Sort();
+      return segmentht->Used (i2);
+    }
+
+    SegmentIndex SegmentNr (PointIndex pi1, PointIndex pi2) const
+    {
+      INDEX_2 i2 (pi1, pi2);
+      i2.Sort();
+      return segmentht->Get (i2);
+    }
+
+
+    /**
+       Remove unused points. etc.
+    */
+    DLL_HEADER void Compress ();
+
+    ///
+    void Save (ostream & outfile) const;
+    ///
+    void Load (istream & infile);
+    ///
+    void Merge (istream & infile, const int surfindex_offset = 0);
+    ///
+    void Save (const string & filename) const;
+    ///
+    void Load (const string & filename);
+    ///
+    void Merge (const string & filename, const int surfindex_offset = 0);
+
+
+    ///
+    void ImproveMesh (const MeshingParameters & mp, OPTIMIZEGOAL goal = OPT_QUALITY);
+
+    ///
+    void ImproveMeshJacobian (const MeshingParameters & mp, OPTIMIZEGOAL goal = OPT_QUALITY, const BitArray * usepoint = NULL);
+    ///
+    void ImproveMeshJacobianOnSurface (const MeshingParameters & mp,
+				       const BitArray & usepoint, 
+				       const Array< Vec<3>* > & nv,
+				       OPTIMIZEGOAL goal = OPT_QUALITY,
+				       const Array< Array<int,PointIndex::BASE>* > * idmaps = NULL);
+    /**
+       free nodes in environment of openelements 
+       for optimiztion
+    */
+    void FreeOpenElementsEnvironment (int layers);
+
+    ///
+    bool LegalTet (Element & el) const
+    {
+      if (el.IllegalValid())
+	return !el.Illegal();
+      return LegalTet2 (el);
+    }
+    ///
+    bool LegalTet2 (Element & el) const;
+
+
+    ///
+    bool LegalTrig (const Element2d & el) const;
+    /**
+       if values non-null, return values in 4-double array:
+       triangle angles min/max, tetangles min/max
+       if null, output results on cout
+    */
+    void CalcMinMaxAngle (double badellimit, double * retvalues = NULL);
+
+    /*
+      Marks elements which are dangerous to refine
+      return: number of illegal elements
+    */
+    int MarkIllegalElements ();
+
+    /// orient surface mesh, for one sub-domain only
+    void SurfaceMeshOrientation ();
+
+    /// convert mixed element mesh to tet-mesh
+    void Split2Tets();
+
+
+    /// build box-search tree
+    DLL_HEADER void BuildElementSearchTree ();
+
+    void SetPointSearchStartElement(const int el) const {ps_startelement = el;}
+
+    /// gives element of point, barycentric coordinates
+    int GetElementOfPoint (const Point3d & p,
+			   double * lami,
+			   bool build_searchtree = 0,
+			   const int index = -1,
+			   const bool allowindex = true) const;
+    int GetElementOfPoint (const Point3d & p,
+			   double * lami,
+			   const Array<int> * const indices,
+			   bool build_searchtree = 0,
+			   const bool allowindex = true) const;
+    int GetSurfaceElementOfPoint (const Point3d & p,
+				  double * lami,
+				  bool build_searchtree = 0,
+				  const int index = -1,
+				  const bool allowindex = true) const;
+    int GetSurfaceElementOfPoint (const Point3d & p,
+				  double * lami,
+				  const Array<int> * const indices,
+				  bool build_searchtree = 0,
+				  const bool allowindex = true) const;
+
+    /// give list of vol elements which are int the box(p1,p2)
+    void GetIntersectingVolEls(const Point3d& p1, const Point3d& p2, 
+			       Array<int> & locels) const;
+
+    ///
+    int AddFaceDescriptor(const FaceDescriptor& fd)
+    { return facedecoding.Append(fd); }
+
+    int AddEdgeDescriptor(const EdgeDescriptor & fd)
+    { return edgedecoding.Append(fd) - 1; }
+
+    ///
+    DLL_HEADER void SetMaterial (int domnr, const char * mat);
+    ///
+    const char * GetMaterial (int domnr) const;
+    
+    DLL_HEADER void SetNBCNames ( int nbcn );
+
+    DLL_HEADER void SetBCName ( int bcnr, const string & abcname );
+
+    string GetBCName ( int bcnr ) const;
+
+    string * GetBCNamePtr ( int bcnr )
+    { return bcnames[bcnr]; }
+
+    ///
+    void ClearFaceDescriptors()
+    { facedecoding.SetSize(0); }
+
+    ///
+    int GetNFD () const
+    { return facedecoding.Size(); }
+
+    const FaceDescriptor & GetFaceDescriptor (int i) const
+    { return facedecoding.Get(i); }
+
+    const EdgeDescriptor & GetEdgeDescriptor (int i) const
+    { return edgedecoding[i]; }
+
+
+    ///
+    FaceDescriptor & GetFaceDescriptor (int i) 
+    { return facedecoding.Elem(i); }
+
+    // #ifdef NONE
+    //   /*
+    //     Identify points pi1 and pi2, due to
+    //     identification nr identnr
+    //   */
+    //   void AddIdentification (int pi1, int pi2, int identnr);
+
+    //   int GetIdentification (int pi1, int pi2) const;
+    //   int GetIdentificationSym (int pi1, int pi2) const;
+    //   ///
+    //   INDEX_2_HASHTABLE<int> & GetIdentifiedPoints () 
+    //   { 
+    //     return *identifiedpoints; 
+    //   }
+
+    //   ///
+    //   void GetIdentificationMap (int identnr, Array<int> & identmap) const;
+    //   ///
+    //   void GetIdentificationPairs (int identnr, Array<INDEX_2> & identpairs) const;
+    //   ///
+    //   int GetMaxIdentificationNr () const
+    //   { 
+    //     return maxidentnr; 
+    //   }
+    // #endif
+
+    /// return periodic, close surface etc. identifications
+    Identifications & GetIdentifications () { return *ident; }
+    /// return periodic, close surface etc. identifications
+    const Identifications & GetIdentifications () const { return *ident; }
+
+
+    void InitPointCurve(double red = 1, double green = 0, double blue = 0) const;
+    void AddPointCurvePoint(const Point3d & pt) const;
+    int GetNumPointCurves(void) const;
+    int GetNumPointsOfPointCurve(int curve) const;
+    Point3d & GetPointCurvePoint(int curve, int n) const;
+    void GetPointCurveColor(int curve, double & red, double & green, double & blue) const;
+
+
+
+
+    /// find number of vertices
+    void ComputeNVertices ();
+    /// number of vertices (no edge-midpoints)
+    int GetNV () const;
+    /// remove edge points
+    void SetNP (int np);
+
+  
+
+
+    /*
+   /// build connected nodes along prism stack
+   void BuildConnectedNodes ();
+   void ConnectToNodeRec (int node, int tonode, 
+   const TABLE<int> & conto);
+    */
+
+    bool PureTrigMesh (int faceindex = 0) const;
+    bool PureTetMesh () const;
+
+
+    const class MeshTopology & GetTopology () const
+    { return *topology; }
+
+    void UpdateTopology();
+  
+    class CurvedElements & GetCurvedElements () const
+    { return *curvedelems; }
+
+    const class AnisotropicClusters & GetClusters () const
+    { return *clusters; }
+
+    int GetTimeStamp() const { return timestamp; }
+    void SetNextTimeStamp() 
+    { timestamp = NextTimeStamp(); }
+
+    int GetMajorTimeStamp() const { return majortimestamp; }
+    void SetNextMajorTimeStamp() 
+    { majortimestamp = timestamp = NextTimeStamp(); }
+
+
+    /// return mutex
+    NgMutex & Mutex ()   { return mutex; }
+    NgMutex & MajorMutex ()   { return majormutex; }
+
+
+    ///
+    void SetUserData(const char * id, Array<int> & data);
+    ///
+    bool GetUserData(const char * id, Array<int> & data, int shift = 0) const;
+    ///
+    void SetUserData(const char * id, Array<double> & data);
+    ///
+    bool GetUserData(const char * id, Array<double> & data, int shift = 0) const;
+
+    ///
+    friend void OptimizeRestart (Mesh & mesh3d);
+    ///
+    void PrintMemInfo (ostream & ost) const;
+    /// 
+    friend class Meshing3;
+
+
+    enum GEOM_TYPE { NO_GEOM = 0, GEOM_2D = 1, GEOM_CSG = 10, GEOM_STL = 11, GEOM_OCC = 12, GEOM_ACIS = 13 };
+    GEOM_TYPE geomtype;
+  
+
+
+#ifdef PARALLEL
+    /// returns parallel topology
+    class ParallelMeshTopology & GetParallelTopology () const
+    { return *paralleltop; }
+
+
+    /// distributes the master-mesh to local meshes
+    void Distribute ();
+
+
+    /// find connection to parallel meshes
+    //   void FindExchangePoints () ;
+
+    //   void FindExchangeEdges ();
+    //   void FindExchangeFaces ();
+
+    /// use metis to decompose master mesh 
+    void ParallelMetis (); //  Array<int> & neloc );
+    void PartHybridMesh (); //  Array<int> & neloc );
+    void PartDualHybridMesh (); //  Array<int> & neloc );
+    void PartDualHybridMesh2D ();  // ( Array<int> & neloc );
+
+
+    /// send mesh from master to local procs
+    void SendRecvMesh ();
+
+    /// send mesh to parallel machine, keep global mesh at master 
+    void SendMesh ( ) const;   // Mesh * mastermesh, Array<int> & neloc) const;
+    /// loads a mesh sent from master processor
+    void ReceiveParallelMesh ();
+
+
+    void UpdateOverlap ();
+ 
+#endif
+
+
+  };
+
+  inline ostream& operator<<(ostream& ost, const Mesh& mesh)
+  {
+    ost << "mesh: " << endl;
+    mesh.Save(ost);
+    return ost;
+  }
+
+}
+
+#endif
+
+
diff --git a/contrib/Netgen/libsrc/meshing/meshfunc.cpp b/contrib/Netgen/libsrc/meshing/meshfunc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9dbc8a8ca99d264989f57f3b7bcc90ba5634b108
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshfunc.cpp
@@ -0,0 +1,717 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+  extern const char * tetrules[];
+  // extern const char * tetrules2[];
+  extern const char * prismrules2[];
+  extern const char * pyramidrules[];
+  extern const char * pyramidrules2[];
+
+
+  // extern double teterrpow; 
+  MESHING3_RESULT MeshVolume (MeshingParameters & mp, Mesh& mesh3d)
+  {
+     int oldne;
+     int meshed;
+
+     Array<INDEX_2> connectednodes;
+
+     if (&mesh3d.LocalHFunction() == NULL) mesh3d.CalcLocalH(mp.grading);
+
+     mesh3d.Compress();
+
+     //  mesh3d.PrintMemInfo (cout);
+
+     if (mp.checkoverlappingboundary)
+        if (mesh3d.CheckOverlappingBoundary())
+           throw NgException ("Stop meshing since boundary mesh is overlapping");
+
+     int nonconsist = 0;
+     for (int k = 1; k <= mesh3d.GetNDomains(); k++)
+     {
+        PrintMessage (3, "Check subdomain ", k, " / ", mesh3d.GetNDomains());
+
+        mesh3d.FindOpenElements(k);
+
+        /*
+        bool res = mesh3d.CheckOverlappingBoundary();
+        if (res)
+        {
+        PrintError ("Surface is overlapping !!");
+        nonconsist = 1;
+        }
+        */
+
+        bool res = (mesh3d.CheckConsistentBoundary() != 0);
+        if (res)
+        {
+           PrintError ("Surface mesh not consistent");
+           nonconsist = 1;
+        }
+     }
+
+     if (nonconsist)
+     {
+        PrintError ("Stop meshing since surface mesh not consistent");
+        throw NgException ("Stop meshing since surface mesh not consistent");
+     }
+
+     double globmaxh = mp.maxh;
+
+     for (int k = 1; k <= mesh3d.GetNDomains(); k++)
+       {
+	 if (multithread.terminate)
+           break;
+	 
+	 PrintMessage (2, "");
+	 PrintMessage (1, "Meshing subdomain ", k, " of ", mesh3d.GetNDomains());
+	 (*testout) << "Meshing subdomain " << k << endl;
+	 
+	 mp.maxh = min2 (globmaxh, mesh3d.MaxHDomain(k));
+	 
+	 mesh3d.CalcSurfacesOfNode();
+	 mesh3d.FindOpenElements(k);
+	 
+	 if (!mesh3d.GetNOpenElements())
+           continue;
+	 
+	 
+
+	 Box<3> domain_bbox( Box<3>::EMPTY_BOX ); 
+	 
+	 for (SurfaceElementIndex sei = 0; sei < mesh3d.GetNSE(); sei++)
+	   {
+	     const Element2d & el = mesh3d[sei];
+	     if (el.IsDeleted() ) continue;
+	     
+	     if (mesh3d.GetFaceDescriptor(el.GetIndex()).DomainIn() == k ||
+		 mesh3d.GetFaceDescriptor(el.GetIndex()).DomainOut() == k)
+	       
+	       for (int j = 0; j < el.GetNP(); j++)
+		 domain_bbox.Add (mesh3d[el[j]]);
+	   }
+	 domain_bbox.Increase (0.01 * domain_bbox.Diam());
+	 
+	
+        for (int qstep = 1; qstep <= 3; qstep++)
+	  {
+	    // cout << "openquads = " << mesh3d.HasOpenQuads() << endl;
+	    if (mesh3d.HasOpenQuads())
+	      {
+		string rulefile = ngdir;
+		
+		const char ** rulep = NULL;
+		switch (qstep)
+		  {
+		  case 1:
+		    rulefile += "/rules/prisms2.rls";
+		    rulep = prismrules2;
+		    break;
+		  case 2: // connect pyramid to triangle
+		    rulefile += "/rules/pyramids2.rls";
+		    rulep = pyramidrules2;
+		    break;
+		  case 3: // connect to vis-a-vis point
+		    rulefile += "/rules/pyramids.rls";
+		    rulep = pyramidrules;
+		    break;
+		  }
+		
+		//		Meshing3 meshing(rulefile);
+		Meshing3 meshing(rulep); 
+		
+		MeshingParameters mpquad = mp;
+		
+		mpquad.giveuptol = 15;
+		mpquad.baseelnp = 4;
+		mpquad.starshapeclass = 1000;
+		mpquad.check_impossible = qstep == 1;   // for prisms only (air domain in trafo)
+		
+		
+		for (PointIndex pi = PointIndex::BASE; 
+		     pi < mesh3d.GetNP()+PointIndex::BASE; pi++)
+		  meshing.AddPoint (mesh3d[pi], pi);
+		
+		mesh3d.GetIdentifications().GetPairs (0, connectednodes);
+		for (int i = 1; i <= connectednodes.Size(); i++)
+		  meshing.AddConnectedPair (connectednodes.Get(i));
+		
+		for (int i = 1; i <= mesh3d.GetNOpenElements(); i++)
+		  {
+		    Element2d hel = mesh3d.OpenElement(i);
+		    meshing.AddBoundaryElement (hel);
+		  }
+		
+		oldne = mesh3d.GetNE();
+		
+		meshing.GenerateMesh (mesh3d, mpquad);
+		
+		for (int i = oldne + 1; i <= mesh3d.GetNE(); i++)
+		  mesh3d.VolumeElement(i).SetIndex (k);
+		
+		(*testout) 
+		  << "mesh has " << mesh3d.GetNE() << " prism/pyramid elements" << endl;
+		
+		mesh3d.FindOpenElements(k);
+	      }
+	  }
+	
+
+        if (mesh3d.HasOpenQuads())
+        {
+           PrintSysError ("mesh has still open quads");
+           throw NgException ("Stop meshing since too many attempts");
+           // return MESHING3_GIVEUP;
+        }
+
+
+        if (mp.delaunay && mesh3d.GetNOpenElements())
+        {
+           Meshing3 meshing((const char**)NULL);
+
+           mesh3d.FindOpenElements(k);
+
+
+           for (PointIndex pi = PointIndex::BASE; 
+              pi < mesh3d.GetNP()+PointIndex::BASE; pi++)
+              meshing.AddPoint (mesh3d[pi], pi);
+
+
+           for (int i = 1; i <= mesh3d.GetNOpenElements(); i++)
+              meshing.AddBoundaryElement (mesh3d.OpenElement(i));
+
+           oldne = mesh3d.GetNE();
+
+           meshing.Delaunay (mesh3d, k, mp);
+
+           for (int i = oldne + 1; i <= mesh3d.GetNE(); i++)
+              mesh3d.VolumeElement(i).SetIndex (k);
+
+           PrintMessage (3, mesh3d.GetNP(), " points, ",
+              mesh3d.GetNE(), " elements");
+        }
+
+
+        int cntsteps = 0;
+        if (mesh3d.GetNOpenElements())
+           do
+           {
+              if (multithread.terminate)
+                 break;
+
+              mesh3d.FindOpenElements(k);
+              PrintMessage (5, mesh3d.GetNOpenElements(), " open faces");
+              cntsteps++;
+
+              if (cntsteps > mp.maxoutersteps) 
+                 throw NgException ("Stop meshing since too many attempts");
+
+              string rulefile = ngdir + "/tetra.rls";
+              PrintMessage (1, "start tetmeshing");
+
+              //	  Meshing3 meshing(rulefile);
+              Meshing3 meshing(tetrules);
+
+              Array<int, PointIndex::BASE> glob2loc(mesh3d.GetNP());
+              glob2loc = -1;
+
+              for (PointIndex pi = PointIndex::BASE; 
+                 pi < mesh3d.GetNP()+PointIndex::BASE; pi++)
+
+                 if (domain_bbox.IsIn (mesh3d[pi]))
+                    glob2loc[pi] = 
+                    meshing.AddPoint (mesh3d[pi], pi);
+
+              for (int i = 1; i <= mesh3d.GetNOpenElements(); i++)
+              {
+                 Element2d hel = mesh3d.OpenElement(i);
+                 for (int j = 0; j < hel.GetNP(); j++)
+                    hel[j] = glob2loc[hel[j]];
+                 meshing.AddBoundaryElement (hel);
+                 // meshing.AddBoundaryElement (mesh3d.OpenElement(i));
+              }
+
+              oldne = mesh3d.GetNE();
+
+              mp.giveuptol = 15 + 10 * cntsteps; 
+              mp.sloppy = 5;
+              meshing.GenerateMesh (mesh3d, mp);
+
+              for (ElementIndex ei = oldne; ei < mesh3d.GetNE(); ei++)
+                 mesh3d[ei].SetIndex (k);
+
+
+              mesh3d.CalcSurfacesOfNode();
+              mesh3d.FindOpenElements(k);
+
+              // teterrpow = 2;
+              if (mesh3d.GetNOpenElements() != 0)
+              {
+                 meshed = 0;
+                 PrintMessage (5, mesh3d.GetNOpenElements(), " open faces found");
+
+                 MeshOptimize3d optmesh(mp);
+
+                 const char * optstr = "mcmstmcmstmcmstmcm";
+                 for (size_t j = 1; j <= strlen(optstr); j++)
+                 {
+                    mesh3d.CalcSurfacesOfNode();
+                    mesh3d.FreeOpenElementsEnvironment(2);
+                    mesh3d.CalcSurfacesOfNode();
+
+                    switch (optstr[j-1])
+                    {
+                    case 'c': optmesh.CombineImprove(mesh3d, OPT_REST); break;
+                    case 'd': optmesh.SplitImprove(mesh3d, OPT_REST); break;
+                    case 's': optmesh.SwapImprove(mesh3d, OPT_REST); break;
+                    case 't': optmesh.SwapImprove2(mesh3d, OPT_REST); break;
+                    case 'm': mesh3d.ImproveMesh(mp, OPT_REST); break;
+                    }	  
+
+                 }
+
+                 mesh3d.FindOpenElements(k);	      
+                 PrintMessage (3, "Call remove problem");
+                 RemoveProblem (mesh3d, k);
+                 mesh3d.FindOpenElements(k);
+              }
+              else
+              {
+                 meshed = 1;
+                 PrintMessage (1, "Success !");
+              }
+           }
+           while (!meshed);
+
+           PrintMessage (1, mesh3d.GetNP(), " points, ",
+              mesh3d.GetNE(), " elements");
+     }
+
+     mp.maxh = globmaxh;
+
+     MeshQuality3d (mesh3d);
+
+     return MESHING3_OK;
+  }  
+
+
+
+
+  /*
+
+
+  MESHING3_RESULT MeshVolumeOld (MeshingParameters & mp, Mesh& mesh3d)
+  {
+  int i, k, oldne;
+
+
+  int meshed;
+  int cntsteps; 
+
+
+  PlotStatistics3d * pstat;
+  if (globflags.GetNumFlag("silentflag", 1) <= 2)
+  pstat = new XPlotStatistics3d;
+  else
+  pstat = new TerminalPlotStatistics3d;
+
+  cntsteps = 0;
+  do
+  {
+  cntsteps++;
+  if (cntsteps > mp.maxoutersteps) 
+  {
+  return MESHING3_OUTERSTEPSEXCEEDED;
+  }
+
+
+  int noldp = mesh3d.GetNP();
+      
+      
+  if ( (cntsteps == 1) && globflags.GetDefineFlag ("delaunay"))
+  {
+  cntsteps ++;
+
+  mesh3d.CalcSurfacesOfNode();
+
+
+  for (k = 1; k <= mesh3d.GetNDomains(); k++)
+  {
+  Meshing3 meshing(NULL, pstat);
+
+  mesh3d.FindOpenElements(k);
+	      
+  for (i = 1; i <= noldp; i++)
+  meshing.AddPoint (mesh3d.Point(i), i);
+	      
+  for (i = 1; i <= mesh3d.GetNOpenElements(); i++)
+  {
+  if (mesh3d.OpenElement(i).GetIndex() == k)
+  meshing.AddBoundaryElement (mesh3d.OpenElement(i));
+  }
+	      
+  oldne = mesh3d.GetNE();
+  if (globflags.GetDefineFlag ("blockfill"))
+  {
+  if (!globflags.GetDefineFlag ("localh"))
+  meshing.BlockFill 
+  (mesh3d, mp.h * globflags.GetNumFlag ("relblockfillh", 1));
+  else
+  meshing.BlockFillLocalH (mesh3d);
+  }
+	      
+  MeshingParameters mpd;
+  meshing.Delaunay (mesh3d, mpd);
+
+  for (i = oldne + 1; i <= mesh3d.GetNE(); i++)
+  mesh3d.VolumeElement(i).SetIndex (k);
+  }
+  }
+
+  noldp = mesh3d.GetNP();
+
+  mesh3d.CalcSurfacesOfNode();
+  mesh3d.FindOpenElements();
+  for (k = 1; k <= mesh3d.GetNDomains(); k++)
+  {
+  Meshing3 meshing(globflags.GetStringFlag ("rules3d", NULL), pstat);
+      
+  Point3d pmin, pmax;
+  mesh3d.GetBox (pmin, pmax, k);
+	  
+  rot.SetCenter (Center (pmin, pmax));
+
+  for (i = 1; i <= noldp; i++)
+  meshing.AddPoint (mesh3d.Point(i), i);
+
+  for (i = 1; i <= mesh3d.GetNOpenElements(); i++)
+  {
+  if (mesh3d.OpenElement(i).GetIndex() == k)
+  meshing.AddBoundaryElement (mesh3d.OpenElement(i));
+  }
+
+  oldne = mesh3d.GetNE();
+
+
+  if ( (cntsteps == 1) && globflags.GetDefineFlag ("blockfill"))
+  {
+  if (!globflags.GetDefineFlag ("localh"))
+  {
+  meshing.BlockFill 
+  (mesh3d, 
+  mp.h * globflags.GetNumFlag ("relblockfillh", 1));
+  }
+  else
+  {
+  meshing.BlockFillLocalH (mesh3d);
+  }
+  }
+
+
+  mp.giveuptol = int(globflags.GetNumFlag ("giveuptol", 15));
+
+  meshing.GenerateMesh (mesh3d, mp);
+
+  for (i = oldne + 1; i <= mesh3d.GetNE(); i++)
+  mesh3d.VolumeElement(i).SetIndex (k);
+  }
+
+
+
+  mesh3d.CalcSurfacesOfNode();
+  mesh3d.FindOpenElements();
+      
+  teterrpow = 2;
+  if (mesh3d.GetNOpenElements() != 0)
+  {
+  meshed = 0;
+  (*mycout) << "Open elements found, old" << endl;
+  const char * optstr = "mcmcmcmcm";
+  int j;
+  for (j = 1; j <= strlen(optstr); j++)
+  switch (optstr[j-1])
+  {
+  case 'c': mesh3d.CombineImprove(); break;
+  case 'd': mesh3d.SplitImprove(); break;
+  case 's': mesh3d.SwapImprove(); break;
+  case 'm': mesh3d.ImproveMesh(2); break;
+  }	  
+	  
+  (*mycout) << "Call remove" << endl;
+  RemoveProblem (mesh3d);
+  (*mycout) << "Problem removed" << endl;
+  }
+  else
+  meshed = 1;
+  }
+  while (!meshed);
+
+  MeshQuality3d (mesh3d);
+
+  return MESHING3_OK;
+  }  
+
+  */
+
+
+
+
+  /*
+  MESHING3_RESULT MeshMixedVolume(MeshingParameters & mp, Mesh& mesh3d)
+  {
+    int i, j;
+    MESHING3_RESULT res;
+    Point3d pmin, pmax;
+
+    mp.giveuptol = 10;
+    mp.baseelnp = 4;
+    mp.starshapeclass = 100;
+
+    //  TerminalPlotStatistics3d pstat;
+  
+    Meshing3 meshing1("pyramids.rls");
+    for (i = 1; i <= mesh3d.GetNP(); i++)
+      meshing1.AddPoint (mesh3d.Point(i), i);
+
+    mesh3d.FindOpenElements();
+    for (i = 1; i <= mesh3d.GetNOpenElements(); i++)
+      if (mesh3d.OpenElement(i).GetIndex() == 1)
+	meshing1.AddBoundaryElement (mesh3d.OpenElement(i));
+
+    res = meshing1.GenerateMesh (mesh3d, mp);
+
+    mesh3d.GetBox (pmin, pmax);
+    PrintMessage (1, "Mesh pyramids, res = ", res);
+    if (res)
+      exit (1);
+
+
+    for (i = 1; i <= mesh3d.GetNE(); i++)
+      mesh3d.VolumeElement(i).SetIndex (1);
+
+    // do delaunay
+  
+    mp.baseelnp = 0;
+    mp.starshapeclass = 5;
+
+    Meshing3 meshing2(NULL);
+    for (i = 1; i <= mesh3d.GetNP(); i++)
+      meshing2.AddPoint (mesh3d.Point(i), i);
+    
+    mesh3d.FindOpenElements();
+    for (i = 1; i <= mesh3d.GetNOpenElements(); i++)
+      if (mesh3d.OpenElement(i).GetIndex() == 1)
+	meshing2.AddBoundaryElement (mesh3d.OpenElement(i));
+
+    MeshingParameters mpd;
+    meshing2.Delaunay (mesh3d, mpd);
+
+    for (i = 1; i <= mesh3d.GetNE(); i++)
+      mesh3d.VolumeElement(i).SetIndex (1);
+
+
+    mp.baseelnp = 0;
+    mp.giveuptol = 10;
+
+    for (int trials = 1; trials <= 50; trials++)
+      {
+	if (multithread.terminate)
+	  return MESHING3_TERMINATE;
+
+	Meshing3 meshing3("tetra.rls");
+	for (i = 1; i <= mesh3d.GetNP(); i++)
+	  meshing3.AddPoint (mesh3d.Point(i), i);
+      
+	mesh3d.FindOpenElements();
+	for (i = 1; i <= mesh3d.GetNOpenElements(); i++)
+	  if (mesh3d.OpenElement(i).GetIndex() == 1)
+	    meshing3.AddBoundaryElement (mesh3d.OpenElement(i));
+      
+	if (trials > 1)
+	  CheckSurfaceMesh2 (mesh3d);
+	res = meshing3.GenerateMesh (mesh3d, mp);
+      
+	for (i = 1; i <= mesh3d.GetNE(); i++)
+	  mesh3d.VolumeElement(i).SetIndex (1);
+
+	if (res == 0) break;
+
+
+
+	for (i = 1; i <= mesh3d.GetNE(); i++)
+	  {
+	    const Element & el = mesh3d.VolumeElement(i);
+	    if (el.GetNP() != 4)
+	      {
+		for (j = 1; j <= el.GetNP(); j++)
+		  mesh3d.AddLockedPoint (el.PNum(j));
+	      }
+	  }
+
+	mesh3d.CalcSurfacesOfNode();
+	mesh3d.FindOpenElements();
+
+	MeshOptimize3d optmesh;
+
+	teterrpow = 2;
+	const char * optstr = "mcmcmcmcm";
+	for (j = 1; j <= strlen(optstr); j++)
+	  switch (optstr[j-1])
+	    {
+	    case 'c': optmesh.CombineImprove(mesh3d, OPT_REST); break;
+	    case 'd': optmesh.SplitImprove(mesh3d); break;
+	    case 's': optmesh.SwapImprove(mesh3d); break;
+	    case 'm': mesh3d.ImproveMesh(); break;
+	    }	  
+	        
+	RemoveProblem (mesh3d);
+      }
+
+
+    PrintMessage (1, "Meshing tets, res = ", res);
+    if (res)
+      {
+	mesh3d.FindOpenElements();
+	PrintSysError (1, "Open elemetns: ", mesh3d.GetNOpenElements());
+	exit (1);
+      }
+
+
+  
+    for (i = 1; i <= mesh3d.GetNE(); i++)
+      {
+	const Element & el = mesh3d.VolumeElement(i);
+	if (el.GetNP() != 4)
+	  {
+	    for (j = 1; j <= el.GetNP(); j++)
+	      mesh3d.AddLockedPoint (el.PNum(j));
+	  }
+      }
+  
+    mesh3d.CalcSurfacesOfNode();
+    mesh3d.FindOpenElements();
+  
+    MeshOptimize3d optmesh;
+
+    teterrpow = 2;
+    const char * optstr = "mcmcmcmcm";
+    for (j = 1; j <= strlen(optstr); j++)
+      switch (optstr[j-1])
+	{
+	case 'c': optmesh.CombineImprove(mesh3d, OPT_REST); break;
+	case 'd': optmesh.SplitImprove(mesh3d); break;
+	case 's': optmesh.SwapImprove(mesh3d); break;
+	case 'm': mesh3d.ImproveMesh(); break;
+	}	  
+
+
+    return MESHING3_OK;
+  }
+*/
+
+
+
+
+
+
+  MESHING3_RESULT OptimizeVolume (MeshingParameters & mp, 
+				  Mesh & mesh3d)
+    //				  const CSGeometry * geometry)
+  {
+    int i;
+
+    PrintMessage (1, "Volume Optimization");
+
+    /*
+      if (!mesh3d.PureTetMesh())
+      return MESHING3_OK;
+    */
+
+    // (*mycout) << "optstring = " << mp.optimize3d << endl;
+    /*
+      const char * optstr = globflags.GetStringFlag ("optimize3d", "cmh");
+      int optsteps = int (globflags.GetNumFlag ("optsteps3d", 2));
+    */
+
+    mesh3d.CalcSurfacesOfNode();
+    for (i = 1; i <= mp.optsteps3d; i++)
+      {
+	if (multithread.terminate)
+	  break;
+
+	MeshOptimize3d optmesh(mp);
+
+	// teterrpow = mp.opterrpow;
+	for (size_t j = 1; j <= strlen(mp.optimize3d); j++)
+	  {
+	    if (multithread.terminate)
+	      break;
+
+	    switch (mp.optimize3d[j-1])
+	      {
+	      case 'c': optmesh.CombineImprove(mesh3d, OPT_REST); break;
+	      case 'd': optmesh.SplitImprove(mesh3d); break;
+	      case 's': optmesh.SwapImprove(mesh3d); break;
+                // case 'u': optmesh.SwapImproveSurface(mesh3d); break;
+	      case 't': optmesh.SwapImprove2(mesh3d); break;
+#ifdef SOLIDGEOM
+	      case 'm': mesh3d.ImproveMesh(*geometry); break;
+	      case 'M': mesh3d.ImproveMesh(*geometry); break;
+#else
+	      case 'm': mesh3d.ImproveMesh(mp); break;
+	      case 'M': mesh3d.ImproveMesh(mp); break;
+#endif
+	      case 'j': mesh3d.ImproveMeshJacobian(mp); break;
+	      }
+	  }
+	mesh3d.mglevels = 1;
+	MeshQuality3d (mesh3d);
+      }
+  
+    return MESHING3_OK;
+  }
+
+
+
+
+  void RemoveIllegalElements (Mesh & mesh3d)
+  {
+    int it = 10;
+    int nillegal, oldn;
+
+    PrintMessage (1, "Remove Illegal Elements");
+    // return, if non-pure tet-mesh
+    /*
+      if (!mesh3d.PureTetMesh())
+      return;
+    */
+    mesh3d.CalcSurfacesOfNode();
+
+    nillegal = mesh3d.MarkIllegalElements();
+
+    MeshingParameters dummymp;
+    MeshOptimize3d optmesh(dummymp);
+    while (nillegal && (it--) > 0)
+      {
+	if (multithread.terminate)
+	  break;
+
+	PrintMessage (5, nillegal, " illegal tets");
+	optmesh.SplitImprove (mesh3d, OPT_LEGAL);
+
+	mesh3d.MarkIllegalElements();  // test
+	optmesh.SwapImprove (mesh3d, OPT_LEGAL);
+	mesh3d.MarkIllegalElements();  // test
+	optmesh.SwapImprove2 (mesh3d, OPT_LEGAL);
+
+	oldn = nillegal;
+	nillegal = mesh3d.MarkIllegalElements();
+
+	if (oldn != nillegal)
+	  it = 10;
+      }
+    PrintMessage (5, nillegal, " illegal tets");
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/meshfunc.hpp b/contrib/Netgen/libsrc/meshing/meshfunc.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f39c0a8fcbe6f98a322d113b7fc3c2acc8d0e15c
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshfunc.hpp
@@ -0,0 +1,41 @@
+#ifndef FILE_MESHFUNC
+#define FILE_MESHFUNC
+
+/**************************************************************************/
+/* File:   meshfunc.hpp                                                   */
+/* Author: Johannes Gerstmayr, Joachim Schoeberl                          */
+/* Date:   26. Jan. 98                                                    */
+/**************************************************************************/
+
+
+/*
+  Functions for mesh-generations strategies
+ */
+
+class Mesh;
+// class CSGeometry;
+
+/// Build tet-mesh
+MESHING3_RESULT MeshVolume (MeshingParameters & mp, Mesh& mesh3d);
+
+/// Build mixed-element mesh
+// MESHING3_RESULT MeshMixedVolume (MeshingParameters & mp, Mesh& mesh3d);
+
+/// Optimize tet-mesh
+MESHING3_RESULT OptimizeVolume (MeshingParameters & mp, Mesh& mesh3d);
+//			       const CSGeometry * geometry = NULL);
+
+void RemoveIllegalElements (Mesh & mesh3d);
+
+
+enum MESHING_STEP { 
+  MESHCONST_ANALYSE = 1,
+  MESHCONST_MESHEDGES = 2,
+  MESHCONST_MESHSURFACE = 3,
+  MESHCONST_OPTSURFACE = 4,
+  MESHCONST_MESHVOLUME = 5,
+  MESHCONST_OPTVOLUME = 6
+};
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/meshfunc2d.cpp b/contrib/Netgen/libsrc/meshing/meshfunc2d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..495f8777602b32eda595a3f83a45ccf9ebc889ae
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshfunc2d.cpp
@@ -0,0 +1,61 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+  DLL_HEADER void Optimize2d (Mesh & mesh, MeshingParameters & mp)
+  {
+    int i;
+
+    //double h = mp.maxh;
+  
+    mesh.CalcSurfacesOfNode();
+
+    const char * optstr = mp.optimize2d;
+    int optsteps = mp.optsteps2d;
+
+    //  cout << "optstr = " << optstr << endl;
+
+    for (i = 1; i <= optsteps; i++)
+      for (size_t j = 1; j <= strlen(optstr); j++)
+	{
+	  if (multithread.terminate) break;
+	  switch (optstr[j-1])
+	    {
+	    case 's': 
+	      {  // topological swap
+		MeshOptimize2d meshopt;
+		meshopt.SetMetricWeight (0);
+		meshopt.EdgeSwapping (mesh, 0);
+		break;
+	      }
+	    case 'S': 
+	      {  // metric swap
+		MeshOptimize2d meshopt;
+		meshopt.SetMetricWeight (0);
+		meshopt.EdgeSwapping (mesh, 1);
+		break;
+	      }
+	    case 'm': 
+	      {
+		MeshOptimize2d meshopt;
+		meshopt.SetMetricWeight (1);
+		meshopt.ImproveMesh(mesh, mp);
+		break;
+	      }
+	    
+	    case 'c': 
+	      {
+		MeshOptimize2d meshopt;
+		meshopt.SetMetricWeight (0.2);
+		meshopt.CombineImprove(mesh);
+		break;
+	      }
+	    default:
+	      cerr << "Optimization code " << optstr[j-1] << " not defined" << endl;
+	    }  
+	}
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/meshing.hpp b/contrib/Netgen/libsrc/meshing/meshing.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7fd9c196327cdecdcc4c6ad3246560a19f0b9571
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing.hpp
@@ -0,0 +1,71 @@
+#ifndef FILE_MESHING
+#define FILE_MESHING
+
+
+
+#include "../include/myadt.hpp"
+#include "../include/gprim.hpp"
+#include "../include/linalg.hpp"
+#include "../include/opti.hpp"
+
+
+
+namespace netgen
+{
+  // extern int printmessage_importance;
+
+  class CSGeometry;
+  class NetgenGeometry;
+}
+  
+  
+#include "msghandler.hpp"
+#include "meshtype.hpp"
+#include "localh.hpp"
+#include "meshclass.hpp"
+#include "global.hpp"
+
+
+namespace netgen
+{
+#include "meshtool.hpp"
+#include "ruler2.hpp"
+#include "adfront2.hpp"
+#include "meshing2.hpp"
+#include "improve2.hpp"
+
+
+#include "geomsearch.hpp"
+#include "adfront3.hpp"
+#include "ruler3.hpp"
+
+#define _INCLUDE_MORE
+
+
+#include "meshing3.hpp"
+#include "improve3.hpp"
+
+#include "findip.hpp"
+#include "findip2.hpp"
+
+#include "topology.hpp"
+#include "curvedelems.hpp"
+#include "clusters.hpp"
+
+#include "meshfunc.hpp"
+
+#include "bisect.hpp"
+#include "hprefinement.hpp"
+#include "boundarylayer.hpp"
+#include "specials.hpp"
+}
+
+#include "validate.hpp"
+#include "basegeom.hpp"
+
+#ifdef PARALLEL
+#include "paralleltop.hpp"
+#endif
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/meshing2.cpp b/contrib/Netgen/libsrc/meshing/meshing2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d31c73e1d5ae02248cc6b39d19c341e68bbe946e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing2.cpp
@@ -0,0 +1,1957 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+  static void glrender (int wait);
+
+
+  // global variable for visualization
+//   static Array<Point3d> locpoints;
+//   static Array<int> legalpoints;
+//   static Array<Point2d> plainpoints;
+//   static Array<int> plainzones;
+//   static Array<INDEX_2> loclines;
+//   // static int geomtrig;
+//   //static const char * rname;
+//   static int cntelem, trials, nfaces;
+//   static int oldnl;
+//   static int qualclass;
+
+
+  Meshing2 :: Meshing2 (const MeshingParameters & mp, const Box<3> & aboundingbox)
+  {
+    boundingbox = aboundingbox;
+    
+    LoadRules (NULL, mp.quad);
+    // LoadRules ("rules/quad.rls");
+    // LoadRules ("rules/triangle.rls");
+
+    adfront = new AdFront2(boundingbox);
+    starttime = GetTime();
+
+    maxarea = -1;
+  }
+
+
+  Meshing2 :: ~Meshing2 ()
+  {
+    delete adfront;
+    for (int i = 0; i < rules.Size(); i++)
+      delete rules[i];
+  }
+
+  void Meshing2 :: AddPoint (const Point3d & p, PointIndex globind, 
+			     MultiPointGeomInfo * mgi,
+			     bool pointonsurface)
+  {
+    //(*testout) << "add point " << globind << endl;
+    adfront ->AddPoint (p, globind, mgi, pointonsurface);
+  }
+
+  void Meshing2 :: AddBoundaryElement (int i1, int i2,
+				       const PointGeomInfo & gi1, const PointGeomInfo & gi2)
+  {
+    //    (*testout) << "add line " << i1 << " - " << i2 << endl;
+    if (!gi1.trignum || !gi2.trignum)
+      {
+	PrintSysError ("addboundaryelement: illegal geominfo");
+      }
+    adfront -> AddLine (i1-1, i2-1, gi1, gi2);
+  }
+
+
+
+  void Meshing2 :: StartMesh ()
+  {
+    foundmap.SetSize (rules.Size());
+    canuse.SetSize (rules.Size());
+    ruleused.SetSize (rules.Size());
+
+    foundmap = 0;
+    canuse = 0;
+    ruleused = 0;
+
+    // cntelem = 0;
+    // trials = 0;
+  }
+
+  void Meshing2 :: EndMesh ()
+  {
+    for (int i = 0; i < ruleused.Size(); i++)
+      (*testout) << setw(4) << ruleused[i]
+		 << " times used rule " << rules[i] -> Name() << endl;
+  }
+
+  void Meshing2 :: SetStartTime (double astarttime)
+  {
+    starttime = astarttime;
+  }
+
+  
+  void Meshing2 :: SetMaxArea (double amaxarea)
+  {
+    maxarea = amaxarea;
+  }
+
+
+  double Meshing2 :: CalcLocalH (const Point3d & /* p */, double gh) const
+  {
+    return gh;
+  }
+
+  // should be class variables !!(?)
+  // static Vec3d ex, ey;
+  // static Point3d globp1;
+
+  void Meshing2 :: DefineTransformation (const Point3d & p1, const Point3d & p2,
+					 const PointGeomInfo * geominfo1,
+					 const PointGeomInfo * geominfo2)
+  {
+    globp1 = p1;
+    ex = p2 - p1;
+    ex /= ex.Length();
+    ey.X() = -ex.Y();
+    ey.Y() =  ex.X();
+    ey.Z() = 0;
+  }
+
+  void Meshing2 :: TransformToPlain (const Point3d & locpoint, 
+				     const MultiPointGeomInfo & geominf,
+				     Point2d & plainpoint, double h, int & zone)
+  {
+    Vec3d p1p (globp1, locpoint);
+
+    //    p1p = locpoint - globp1;
+    p1p /= h;
+    plainpoint.X() = p1p * ex;
+    plainpoint.Y() = p1p * ey;
+    zone = 0;
+  }
+
+  int Meshing2 :: TransformFromPlain (Point2d & plainpoint,
+				      Point3d & locpoint, 
+				      PointGeomInfo & gi, 
+				      double h)
+  {
+    Vec3d p1p;
+    gi.trignum = 1;
+
+    p1p = plainpoint.X() * ex + plainpoint.Y() * ey;
+    p1p *= h;
+    locpoint = globp1 + p1p;
+    return 0;
+  }
+
+
+  int Meshing2 :: BelongsToActiveChart (const Point3d & p, 
+					const PointGeomInfo & gi)
+  {
+    return 1;
+  }
+
+
+  int Meshing2 :: ComputePointGeomInfo (const Point3d & p, PointGeomInfo & gi)
+  {
+    gi.trignum = 1;
+    return 0;
+  }
+
+
+  int Meshing2 :: ChooseChartPointGeomInfo (const MultiPointGeomInfo & mpgi, 
+					    PointGeomInfo & pgi)
+  {
+    pgi = mpgi.GetPGI(1);
+    return 0;
+  }
+
+
+
+  int Meshing2 :: 
+  IsLineVertexOnChart (const Point3d & p1, const Point3d & p2,
+		       int endpoint, const PointGeomInfo & geominfo)
+  {
+    return 1;
+  }
+
+  void Meshing2 ::
+  GetChartBoundary (Array<Point2d> & points, 
+		    Array<Point3d> & points3d, 
+		    Array<INDEX_2> & lines, double h) const
+  {
+    points.SetSize (0);
+    points3d.SetSize (0);
+    lines.SetSize (0);
+  }
+
+  double Meshing2 :: Area () const
+  {
+    return -1;
+  }
+
+
+
+
+
+  MESHING2_RESULT Meshing2 :: GenerateMesh (Mesh & mesh, const MeshingParameters & mp, double gh, int facenr)
+  {
+    static int timer = NgProfiler::CreateTimer ("surface meshing");
+
+    static int timer1 = NgProfiler::CreateTimer ("surface meshing1");
+    static int timer2 = NgProfiler::CreateTimer ("surface meshing2");
+    static int timer3 = NgProfiler::CreateTimer ("surface meshing3");
+    NgProfiler::RegionTimer reg (timer);
+
+
+    Array<int> pindex, lindex;
+    Array<int> delpoints, dellines;
+
+    Array<PointGeomInfo> upgeominfo;  // unique info
+    Array<MultiPointGeomInfo> mpgeominfo;  // multiple info
+
+    Array<Element2d> locelements;
+
+    int z1, z2, oldnp(-1);
+    bool found;
+    int rulenr(-1);
+    int globind;
+    Point<3> p1, p2;
+
+    const PointGeomInfo * blgeominfo1;
+    const PointGeomInfo * blgeominfo2;
+
+    bool morerisc;
+    bool debugflag;
+
+    double h, his, hshould;
+
+
+    Array<Point3d> locpoints;
+    Array<int> legalpoints;
+    Array<Point2d> plainpoints;
+    Array<int> plainzones;
+    Array<INDEX_2> loclines;
+    int cntelem = 0, trials = 0, nfaces = 0;
+    int oldnl = 0;
+    int qualclass;
+
+
+
+    // test for 3d overlaps
+    Box3dTree surfeltree (boundingbox.PMin(),
+			  boundingbox.PMax());
+
+    Array<int> intersecttrias;
+    Array<Point3d> critpoints;
+
+    // test for doubled edges
+    //INDEX_2_HASHTABLE<int> doubleedge(300000);
+
+
+    testmode = 0;
+
+    StartMesh();
+
+    Array<Point2d> chartboundpoints;
+    Array<Point3d> chartboundpoints3d;
+    Array<INDEX_2> chartboundlines;
+
+    // illegal points: points with more then 50 elements per node
+    int maxlegalpoint(-1), maxlegalline(-1);
+    Array<int,PointIndex::BASE> trigsonnode;
+    Array<int,PointIndex::BASE> illegalpoint;
+
+    trigsonnode.SetSize (mesh.GetNP());
+    illegalpoint.SetSize (mesh.GetNP());
+
+    trigsonnode = 0;
+    illegalpoint = 0;
+  
+
+    double totalarea = Area ();
+    double meshedarea = 0;
+
+
+    // search tree for surface elements:
+    /*
+    for (sei = 0; sei < mesh.GetNSE(); sei++)
+      {
+	const Element2d & sel = mesh[sei];
+
+	if (sel.IsDeleted()) continue;
+
+	if (sel.GetIndex() == facenr)
+	  {
+	    Box<3> box;
+	    box.Set ( mesh[sel[0]] );
+	    box.Add ( mesh[sel[1]] );
+	    box.Add ( mesh[sel[2]] );
+	    surfeltree.Insert (box, sei);
+	  }
+      }
+    */
+    Array<SurfaceElementIndex> seia;
+    mesh.GetSurfaceElementsOfFace (facenr, seia);
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & sel = mesh[seia[i]];
+
+	if (sel.IsDeleted()) continue;
+
+	Box<3> box;
+	box.Set ( mesh[sel[0]] );
+	box.Add ( mesh[sel[1]] );
+	box.Add ( mesh[sel[2]] );
+	surfeltree.Insert (box, i);
+      }
+
+
+
+
+    if (totalarea > 0 || maxarea > 0)
+      for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++)
+	{
+	  const Element2d & sel = mesh[sei];
+	  if (sel.IsDeleted()) continue;
+	
+	  double trigarea = Cross ( mesh[sel[1]]-mesh[sel[0]],
+				    mesh[sel[2]]-mesh[sel[0]] ).Length() / 2;
+	  
+	  
+	  if (sel.GetNP() == 4)
+	    trigarea += Cross (Vec3d (mesh.Point (sel.PNum(1)),
+				      mesh.Point (sel.PNum(3))),
+			       Vec3d (mesh.Point (sel.PNum(1)),
+				      mesh.Point (sel.PNum(4)))).Length() / 2;;
+	  meshedarea += trigarea;
+	}
+
+
+
+
+    const char * savetask = multithread.task;
+    multithread.task = "Surface meshing";
+
+    adfront ->SetStartFront ();
+
+
+    int plotnexttrial = 999;
+
+    double meshedarea_before = meshedarea;
+
+
+    while (!adfront ->Empty() && !multithread.terminate)
+      {
+	NgProfiler::RegionTimer reg1 (timer1);
+
+	if (multithread.terminate)
+	  throw NgException ("Meshing stopped");
+
+	// known for STL meshing
+	if (totalarea > 0)
+	  multithread.percent = 100 * meshedarea / totalarea;
+	/*
+	  else
+	  multithread.percent = 0;
+	*/
+
+	locpoints.SetSize(0);
+	loclines.SetSize(0);
+	pindex.SetSize(0);
+	lindex.SetSize(0);
+	delpoints.SetSize(0);
+	dellines.SetSize(0);
+	locelements.SetSize(0);
+
+
+
+	// plot statistics
+	if (trials > plotnexttrial)
+	  {
+	    PrintMessage (5, 
+			  "faces = ", nfaces,
+			  " trials = ", trials,
+			  " elements = ", mesh.GetNSE(),
+			  " els/sec = ",
+			  (mesh.GetNSE() / (GetTime() - starttime + 0.0001)));
+	    plotnexttrial += 1000;
+	  }
+
+
+	// unique-pgi, multi-pgi
+	upgeominfo.SetSize(0);
+	mpgeominfo.SetSize(0);
+
+
+	nfaces = adfront->GetNFL();
+	trials ++;
+    
+
+	if (trials % 1000 == 0)
+	  {
+	    (*testout) << "\n";
+	    for (int i = 1; i <= canuse.Size(); i++)
+	      {
+		(*testout) << foundmap.Get(i) << "/" 
+			   << canuse.Get(i) << "/"
+			   << ruleused.Get(i) << " map/can/use rule " << rules.Get(i)->Name() << "\n";
+	      }
+	    (*testout) << "\n";
+	  }
+
+
+	int baselineindex = adfront -> SelectBaseLine (p1, p2, blgeominfo1, blgeominfo2, qualclass);
+
+
+	found = 1;
+
+	his = Dist (p1, p2);
+
+	Point3d pmid = Center (p1, p2);
+	hshould = CalcLocalH (pmid, mesh.GetH (pmid));
+	if (gh < hshould) hshould = gh;
+
+	mesh.RestrictLocalH (pmid, hshould);
+
+	h = hshould;
+
+	double hinner = (3 + qualclass) * max2 (his, hshould);
+
+	adfront ->GetLocals (baselineindex, locpoints, mpgeominfo, loclines, 
+			     pindex, lindex, 2*hinner);
+
+
+	NgProfiler::RegionTimer reg2 (timer2);
+
+	//(*testout) << "h for locals: " << 2*hinner << endl;
+	
+
+	//(*testout) << "locpoints " << locpoints << endl;
+
+	if (qualclass > mp.giveuptol2d)
+	  {
+	    PrintMessage (3, "give up with qualclass ", qualclass);
+	    PrintMessage (3, "number of frontlines = ", adfront->GetNFL());
+	    // throw NgException ("Give up 2d meshing");
+	    break;
+	  }
+
+	/*
+	if (found && qualclass > 60)
+	  {
+	    found = 0;
+	  }
+	*/
+	//      morerisc = ((qualclass > 20) && (qualclass % 2 == 1));
+	//      morerisc = 1;
+	morerisc = 0;
+
+
+	PointIndex gpi1 = adfront -> GetGlobalIndex (pindex.Get(loclines[0].I1()));
+	PointIndex gpi2 = adfront -> GetGlobalIndex (pindex.Get(loclines[0].I2()));
+
+
+	debugflag = 
+	  ( 
+	   debugparam.haltsegment &&
+	   ( ((debugparam.haltsegmentp1 == gpi1) && (debugparam.haltsegmentp2 == gpi2)) || 
+	     ((debugparam.haltsegmentp1 == gpi2) && (debugparam.haltsegmentp2 == gpi1))) 
+	   ) 
+	  ||
+	  (
+	   debugparam.haltnode &&
+	   ( (debugparam.haltsegmentp1 == gpi1) || (debugparam.haltsegmentp2 == gpi1))
+	   );
+	
+      
+	if (debugparam.haltface && debugparam.haltfacenr == facenr)
+	  {
+	    debugflag = 1;
+	    cout << "set debugflag" << endl;
+	  }
+	
+	if (debugparam.haltlargequalclass && qualclass > 50)
+	  debugflag = 1;
+
+	// problem recognition !
+	if (found && 
+	    (gpi1 < illegalpoint.Size()+PointIndex::BASE) && 
+	    (gpi2 < illegalpoint.Size()+PointIndex::BASE) )
+	  {
+	    if (illegalpoint[gpi1] || illegalpoint[gpi2])
+	      found = 0;
+	  }
+
+
+	Point2d p12d, p22d;
+
+	if (found)
+	  {
+	    oldnp = locpoints.Size();
+	    oldnl = loclines.Size();
+	  
+	    if (debugflag)
+	      (*testout) << "define new transformation" << endl;
+
+	    DefineTransformation (p1, p2, blgeominfo1, blgeominfo2);
+	  
+	    plainpoints.SetSize (locpoints.Size());
+	    plainzones.SetSize (locpoints.Size());
+
+	    // (*testout) << endl;
+
+	    if (debugflag)
+	      {
+		*testout << "3d->2d transformation" << endl;
+		*testout << "3d points: " << endl << locpoints << endl;
+	      }
+
+	    for (int i = 1; i <= locpoints.Size(); i++)
+	      {
+		// (*testout) << "pindex(i) = " << pindex[i-1] << endl;
+		TransformToPlain (locpoints.Get(i), 
+				  mpgeominfo.Get(i),
+				  plainpoints.Elem(i), h, plainzones.Elem(i));
+		//		(*testout) << mpgeominfo.Get(i).GetPGI(1).u << " " << mpgeominfo.Get(i).GetPGI(1).v << " ";
+		//		(*testout) << plainpoints.Get(i).X() << " " << plainpoints.Get(i).Y() << endl;
+		//(*testout) << "transform " << locpoints.Get(i) << " to " << plainpoints.Get(i).X() << " " << plainpoints.Get(i).Y() << endl;
+	      }
+	    //	    (*testout) << endl << endl << endl;
+
+
+	    if (debugflag)
+	      *testout << "2d points: " << endl << plainpoints << endl;
+
+
+	    p12d = plainpoints.Get(1);
+	    p22d = plainpoints.Get(2);
+
+	    /*
+	    // last idea on friday
+	    plainzones.Elem(1) = 0;
+	    plainzones.Elem(2) = 0;
+	    */
+
+
+	    /*
+	    // old netgen:
+	    for (i = 2; i <= loclines.Size(); i++)  // don't remove first line
+	    {
+	    z1 = plainzones.Get(loclines.Get(i).I1());
+	    z2 = plainzones.Get(loclines.Get(i).I2());
+	      
+	    if (z1 && z2 && (z1 != z2) || (z1 == -1) || (z2 == -1) )
+	    {
+	    loclines.DeleteElement(i);
+	    lindex.DeleteElement(i);
+	    oldnl--;
+	    i--;
+	    }
+	    }
+
+	    // 	  for (i = 1; i <= plainpoints.Size(); i++)
+	    // 	    if (plainzones.Elem(i) == -1)
+	    // 	      plainpoints.Elem(i) = Point2d (1e4, 1e4);
+	    */
+	  
+
+	  
+	    for (int i = 2; i <= loclines.Size(); i++)  // don't remove first line
+	      {
+		// (*testout) << "loclines(i) = " << loclines.Get(i).I1() << " - " << loclines.Get(i).I2() << endl;
+		z1 = plainzones.Get(loclines.Get(i).I1());
+		z2 = plainzones.Get(loclines.Get(i).I2());
+	      
+	      
+		// one inner point, one outer
+		if ( (z1 >= 0) != (z2 >= 0))
+		  {
+		    int innerp = (z1 >= 0) ? 1 : 2;
+		    if (IsLineVertexOnChart (locpoints.Get(loclines.Get(i).I1()),
+					     locpoints.Get(loclines.Get(i).I2()),
+					     innerp,
+					     adfront->GetLineGeomInfo (lindex.Get(i), innerp)))
+		      // pgeominfo.Get(loclines.Get(i).I(innerp))))
+		      {		
+
+			if (!morerisc)
+			  {
+			    // use one end of line
+			    int pini, pouti;
+			    Vec2d v;
+			  
+			    pini = loclines.Get(i).I(innerp);
+			    pouti = loclines.Get(i).I(3-innerp);
+			  
+			    Point2d pin (plainpoints.Get(pini));
+			    Point2d pout (plainpoints.Get(pouti));
+			    v = pout - pin;
+			    double len = v.Length();
+			    if (len <= 1e-6)
+			      (*testout) << "WARNING(js): inner-outer: short vector" << endl;
+			    else
+			      v /= len;
+			  
+			    /*
+			    // don't elongate line towards base-line !!
+			    if (Vec2d (pin, p12d) * v > 0 && 
+			    Vec2d (pin, p22d) * v > 0)
+			    v *= -1;  
+			    */
+
+			    Point2d newpout = pin + 1000 * v;
+			    newpout = pout;
+
+			  
+			    plainpoints.Append (newpout);
+			    Point3d pout3d = locpoints.Get(pouti);
+			    locpoints.Append (pout3d);
+
+			    plainzones.Append (0);
+			    pindex.Append (-1);
+			    oldnp++;
+			    loclines.Elem(i).I(3-innerp) = oldnp;
+			  }
+			else
+			  plainzones.Elem(loclines.Get(i).I(3-innerp)) = 0;
+			
+
+			//		  (*testout) << "inner - outer correction" << endl;
+		      }
+		    else
+		      {
+			// remove line
+			loclines.DeleteElement(i);
+			lindex.DeleteElement(i);
+			oldnl--;
+			i--;
+		      }			
+		  }
+	      
+		else if ( (z1 > 0 && z2 > 0 && (z1 != z2)) || ((z1 < 0) && (z2 < 0)) )
+		  {
+		    loclines.DeleteElement(i);
+		    lindex.DeleteElement(i);
+		    oldnl--;
+		    i--;
+		  }
+	      }
+	  
+
+
+
+
+	    legalpoints.SetSize(plainpoints.Size());
+	    for (int i = 1; i <= legalpoints.Size(); i++)
+	      legalpoints.Elem(i) = 1;
+
+	    double avy = 0;
+	    for (int i = 1; i <= plainpoints.Size(); i++)
+	      avy += plainpoints.Elem(i).Y();
+	    avy *= 1./plainpoints.Size();
+		
+
+	    for (int i = 1; i <= plainpoints.Size(); i++)
+	      {
+		if (plainzones.Elem(i) < 0)
+		  {
+		    plainpoints.Elem(i) = Point2d (1e4, 1e4);
+		    legalpoints.Elem(i) = 0;
+		  }
+		if (pindex.Elem(i) == -1)
+		  {
+		    legalpoints.Elem(i) = 0;
+		  }
+		    
+
+		if (plainpoints.Elem(i).Y() < -1e-10*avy) // changed
+		  {
+		    legalpoints.Elem(i) = 0;
+		  }
+	      }
+	    /*
+	      for (i = 3; i <= plainpoints.Size(); i++)
+	      if (sqr (plainpoints.Get(i).X()) + sqr (plainpoints.Get(i).Y())
+	      > sqr (2 + 0.2 * qualclass))
+	      legalpoints.Elem(i) = 0;
+	    */  
+
+	    /*	  
+		 int clp = 0;
+		 for (i = 1; i <= plainpoints.Size(); i++)
+		 if (legalpoints.Get(i))
+		 clp++;
+		 (*testout) << "legalpts: " << clp << "/" << plainpoints.Size() << endl; 
+
+		 // sort legal/illegal lines
+		 int lastleg = 2;
+		 int firstilleg = oldnl;
+
+		 while (lastleg < firstilleg)
+		 {
+		 while (legalpoints.Get(loclines.Get(lastleg).I1()) &&
+		 legalpoints.Get(loclines.Get(lastleg).I2()) &&
+		 lastleg < firstilleg)
+		 lastleg++;
+		 while ( ( !legalpoints.Get(loclines.Get(firstilleg).I1()) ||
+		 !legalpoints.Get(loclines.Get(firstilleg).I2())) &&
+		 lastleg < firstilleg)
+		 firstilleg--;
+	      
+		 if (lastleg < firstilleg)
+		 {
+		 swap (loclines.Elem(lastleg), loclines.Elem(firstilleg));
+		 swap (lindex.Elem(lastleg), lindex.Elem(firstilleg));
+		 }
+		 }
+
+		 (*testout) << "leglines " << lastleg << "/" << oldnl << endl;
+	    */
+	
+
+	    GetChartBoundary (chartboundpoints, 
+			      chartboundpoints3d,
+			      chartboundlines, h);
+
+	    oldnp = plainpoints.Size();
+
+	    maxlegalpoint = locpoints.Size();
+	    maxlegalline = loclines.Size();
+
+
+
+	    if (mp.checkchartboundary)
+	      {
+		for (int i = 1; i <= chartboundpoints.Size(); i++)
+		  {
+		    plainpoints.Append (chartboundpoints.Get(i));
+		    locpoints.Append (chartboundpoints3d.Get(i));
+		    legalpoints.Append (0);
+		  }
+	      
+
+		for (int i = 1; i <= chartboundlines.Size(); i++)
+		  {
+		    INDEX_2 line (chartboundlines.Get(i).I1()+oldnp,
+				  chartboundlines.Get(i).I2()+oldnp);
+		    loclines.Append (line);
+		    //	      (*testout) << "line: " << line.I1() << "-" << line.I2() << endl;
+		  }
+	      }
+
+	    oldnl = loclines.Size();
+	    oldnp = plainpoints.Size();
+	  }
+
+
+	/*
+	  if (qualclass > 100)
+	  {
+	  multithread.drawing = 1;
+	  glrender(1);
+	  cout << "qualclass 100, nfl = " << adfront->GetNFL() << endl;
+	  }
+	*/
+
+	if (found)
+	  {
+	    rulenr = ApplyRules (plainpoints, legalpoints, maxlegalpoint,
+				 loclines, maxlegalline, locelements,
+				 dellines, qualclass, mp);
+
+	    //	    (*testout) << "Rule Nr = " << rulenr << endl;
+	    if (!rulenr)
+	      {
+		found = 0;
+		if ( debugflag || debugparam.haltnosuccess )
+		  PrintWarning ("no rule found");
+	      }
+	  }
+      
+	NgProfiler::RegionTimer reg3 (timer3);
+
+
+	for (int i = 1; i <= locelements.Size() && found; i++)
+	  {
+	    const Element2d & el = locelements.Get(i);
+
+	    for (int j = 1; j <= el.GetNP(); j++)
+	      if (el.PNum(j) <= oldnp && pindex.Get(el.PNum(j)) == -1)
+		{
+		  found = 0;
+		  PrintSysError ("meshing2, index missing");
+		}
+	  }
+
+
+	if (found)
+	  {
+	    locpoints.SetSize (plainpoints.Size());
+	    upgeominfo.SetSize(locpoints.Size());
+
+	    for (int i = oldnp+1; i <= plainpoints.Size(); i++)
+	      {
+		int err =
+		  TransformFromPlain (plainpoints.Elem(i), locpoints.Elem(i), 
+				      upgeominfo.Elem(i), h);
+
+		if (err)
+		  {
+		    found = 0;
+
+		    if ( debugflag || debugparam.haltnosuccess )
+		      PrintSysError ("meshing2, Backtransformation failed");
+
+		    break;
+		  }
+	      }
+	  }
+	  
+
+	//      for (i = 1; i <= oldnl; i++)
+	//        adfront -> ResetClass (lindex[i]);
+
+
+	/*
+	  double violateminh;
+	  if (qualclass <= 10)
+	  violateminh = 3;
+	  else
+	  violateminh = 3 * qualclass;
+
+	  if (uselocalh && found) //  && qualclass <= 10)
+	  {
+	  for (i = 1; i <= locelements.Size(); i++)
+	  {
+	  Point3d pmin = locpoints.Get(locelements.Get(i).PNum(1));
+	  Point3d pmax = pmin;
+	  for (j = 2; j <= 3; j++)
+	  {
+	  const Point3d & hp = 
+	  locpoints.Get(locelements.Get(i).PNum(j));
+	  pmin.SetToMin (hp);
+	  pmax.SetToMax (hp);
+	  }
+	  double minh = mesh.GetMinH (pmin, pmax);
+	  if (h > violateminh * minh)
+	  {
+	  found = 0;
+	  loclines.SetSize (oldnl);
+	  locpoints.SetSize (oldnp);
+	  }
+	  }
+	  }
+	*/
+
+
+	if (found) 
+	  {
+	    double violateminh = 3 + 0.1 * sqr (qualclass);
+	    double minh = 1e8;
+	    double newedgemaxh = 0;
+	    for (int i = oldnl+1; i <= loclines.Size(); i++)
+	      {
+		double eh = Dist (locpoints.Get(loclines.Get(i).I1()),
+				  locpoints.Get(loclines.Get(i).I2()));
+
+		// Markus (brute force method to avoid bad elements on geometries like \_/ )
+		//if(eh > 4.*mesh.GetH(locpoints.Get(loclines.Get(i).I1()))) found = 0;
+		//if(eh > 4.*mesh.GetH(locpoints.Get(loclines.Get(i).I2()))) found = 0;
+		// Markus end
+
+		if (eh > newedgemaxh)
+		  newedgemaxh = eh;
+	      }
+
+	    for (int i = 1; i <= locelements.Size(); i++)
+	      {
+		Point3d pmin = locpoints.Get(locelements.Get(i).PNum(1));
+		Point3d pmax = pmin;
+		for (int j = 2; j <= locelements.Get(i).GetNP(); j++)
+		  {
+		    const Point3d & hp = 
+		      locpoints.Get(locelements.Get(i).PNum(j));
+		    pmin.SetToMin (hp);
+		    pmax.SetToMax (hp);
+		  }
+		double eh = mesh.GetMinH (pmin, pmax);
+		if (eh < minh)
+		  minh = eh;
+	      }
+
+	    for (int i = 1; i <= locelements.Size(); i++)
+	      for (int j = 1; j <= locelements.Get(i).GetNP(); j++)
+		if (Dist2 (locpoints.Get(locelements.Get(i).PNum(j)), pmid) > hinner*hinner)
+		  found = 0;
+
+	    //	  cout << "violate = " << newedgemaxh / minh << endl;
+	    static double maxviolate = 0;
+	    if (newedgemaxh / minh > maxviolate)
+	      {
+		maxviolate = newedgemaxh / minh;
+		//	      cout << "max minhviolate = " << maxviolate << endl;
+	      }
+
+
+	    if (newedgemaxh > violateminh * minh)
+	      {
+		found = 0;
+		loclines.SetSize (oldnl);
+		locpoints.SetSize (oldnp);
+
+		if ( debugflag || debugparam.haltnosuccess )
+		  PrintSysError ("meshing2, maxh too large");
+
+
+	      }
+	  }
+
+
+
+	/*
+	// test good ComputeLineGeoInfo
+	if (found)
+	{
+	// is line on chart ?
+	for (i = oldnl+1; i <= loclines.Size(); i++)
+	{
+	int gisize;
+	void *geominfo;
+
+	if (ComputeLineGeoInfo (locpoints.Get(loclines.Get(i).I1()),
+	locpoints.Get(loclines.Get(i).I2()),
+	gisize, geominfo))
+	found = 0;
+	}
+	}
+	*/
+
+
+	// changed for OCC meshing
+	if (found)
+	  {
+	    // take geominfo from dellines
+	    // upgeominfo.SetSize(locpoints.Size());
+
+	    /*
+	      for (i = 1; i <= dellines.Size(); i++)
+	      for (j = 1; j <= 2; j++)
+	      {
+	      upgeominfo.Elem(loclines.Get(dellines.Get(i)).I(j)) =
+	      adfront -> GetLineGeomInfo (lindex.Get(dellines.Get(i)), j);
+	      }
+	    */
+
+
+	    for (int i = 1; i <= locelements.Size(); i++)
+	      for (int j = 1; j <= locelements.Get(i).GetNP(); j++)
+		{
+		  int pi = locelements.Get(i).PNum(j);
+		  if (pi <= oldnp)
+		    {
+		    
+		      if (ChooseChartPointGeomInfo (mpgeominfo.Get(pi), upgeominfo.Elem(pi)))
+			{
+			  // cannot select, compute new one
+			  PrintWarning ("calc point geominfo instead of using");
+			  if (ComputePointGeomInfo (locpoints.Get(pi), upgeominfo.Elem(pi)))
+			    {
+			      found = 0;
+			      PrintSysError ("meshing2d, geominfo failed");
+			    }
+			}
+		    }
+		}
+
+	    /*
+	    // use upgeominfo from ProjectFromPlane
+	    for (i = oldnp+1; i <= locpoints.Size(); i++)
+	    {
+	    if (ComputePointGeomInfo (locpoints.Get(i), upgeominfo.Elem(i)))
+	    {
+	    found = 0;
+	    if ( debugflag || debugparam.haltnosuccess )
+	    PrintSysError ("meshing2d, compute geominfo failed");
+	    }
+	    }
+	    */
+	  }
+
+
+	if (found && mp.checkoverlap)
+	  {
+	    // cout << "checkoverlap" << endl;
+	    // test for overlaps
+	  
+	    Point3d hullmin(1e10, 1e10, 1e10);
+	    Point3d hullmax(-1e10, -1e10, -1e10);
+	  
+	    for (int i = 1; i <= locelements.Size(); i++)
+	      for (int j = 1; j <= locelements.Get(i).GetNP(); j++)
+		{
+		  const Point3d & p = locpoints.Get(locelements.Get(i).PNum(j));
+		  hullmin.SetToMin (p);
+		  hullmax.SetToMax (p);
+		}
+	    hullmin += Vec3d (-his, -his, -his);
+	    hullmax += Vec3d ( his,  his,  his);
+
+	    surfeltree.GetIntersecting (hullmin, hullmax, intersecttrias);
+
+	    critpoints.SetSize (0);
+	    for (int i = oldnp+1; i <= locpoints.Size(); i++)
+	      critpoints.Append (locpoints.Get(i));
+
+	    for (int i = 1; i <= locelements.Size(); i++)
+	      {
+		const Element2d & tri = locelements.Get(i);
+		if (tri.GetNP() == 3)
+		  {
+		    const Point3d & tp1 = locpoints.Get(tri.PNum(1));
+		    const Point3d & tp2 = locpoints.Get(tri.PNum(2));
+		    const Point3d & tp3 = locpoints.Get(tri.PNum(3));
+		  
+		    Vec3d tv1 (tp1, tp2);
+		    Vec3d tv2 (tp1, tp3);
+		  
+		    double lam1, lam2;
+		    for (lam1 = 0.2; lam1 <= 0.8; lam1 += 0.2)
+		      for (lam2 = 0.2; lam2 + lam1 <= 0.8; lam2 += 0.2)
+			{
+			  Point3d hp = tp1 + lam1 * tv1 + lam2 * tv2;
+			  critpoints.Append (hp);
+			}
+		  }
+		else if (tri.GetNP() == 4)
+		  {
+		    const Point3d & tp1 = locpoints.Get(tri.PNum(1));
+		    const Point3d & tp2 = locpoints.Get(tri.PNum(2));
+		    const Point3d & tp3 = locpoints.Get(tri.PNum(3));
+		    const Point3d & tp4 = locpoints.Get(tri.PNum(4));
+		  
+		    double l1, l2;
+		    for (l1 = 0.1; l1 <= 0.9; l1 += 0.1)
+		      for (l2 = 0.1; l2 <= 0.9; l2 += 0.1)
+			{
+			  Point3d hp;
+			  hp.X() = 
+			    (1-l1)*(1-l2) * tp1.X() +
+			    l1*(1-l2) * tp2.X() +
+			    l1*l2 * tp3.X() +
+			    (1-l1)*l2 * tp4.X();
+			  hp.Y() = 
+			    (1-l1)*(1-l2) * tp1.Y() +
+			    l1*(1-l2) * tp2.Y() +
+			    l1*l2 * tp3.Y() +
+			    (1-l1)*l2 * tp4.Y();
+			  hp.Z() = 
+			    (1-l1)*(1-l2) * tp1.Z() +
+			    l1*(1-l2) * tp2.Z() +
+			    l1*l2 * tp3.Z() +
+			    (1-l1)*l2 * tp4.Z();
+
+
+			  critpoints.Append (hp);
+			}
+		  }
+	      }
+	    /*
+	      for (i = oldnl+1; i <= loclines.Size(); i++)
+	      {
+	      Point3d hp = locpoints.Get(loclines.Get(i).I1());
+	      Vec3d hv(hp, locpoints.Get(loclines.Get(i).I2()));
+	      int ncp = 2;
+	      for (j = 1; j <= ncp; j++)
+	      critpoints.Append ( hp + (double(j)/(ncp+1)) * hv);
+	      }
+	    */
+
+
+	    /*
+	      for (i = oldnp+1; i <= locpoints.Size(); i++)
+	      {
+	      const Point3d & p = locpoints.Get(i);
+	    */
+
+
+	    for (int i = 1; i <= critpoints.Size(); i++)
+	      {
+		const Point3d & p = critpoints.Get(i);
+		 
+
+		/*
+		  for (j = 1; j <= mesh.GetNSE(); j++)
+		  {
+		*/
+		int jj;
+		for (jj = 1; jj <= intersecttrias.Size(); jj++)
+		  {
+		    int j = intersecttrias.Get(jj);
+		    const Element2d & el = mesh.SurfaceElement(j);
+		  
+		    int ntrig = (el.GetNP() == 3) ? 1 : 2;
+
+		    int jl;
+		    for (jl = 1; jl <= ntrig; jl++)
+		      {
+			Point3d tp1, tp2, tp3;
+
+			if (jl == 1)
+			  {
+			    tp1 = mesh.Point(el.PNum(1));
+			    tp2 = mesh.Point(el.PNum(2));
+			    tp3 = mesh.Point(el.PNum(3));
+			  }
+			else
+			  {
+			    tp1 = mesh.Point(el.PNum(1));
+			    tp2 = mesh.Point(el.PNum(3));
+			    tp3 = mesh.Point(el.PNum(4));
+			  }
+
+			int onchart = 0;
+			for (int k = 1; k <= el.GetNP(); k++)
+			  if (BelongsToActiveChart (mesh.Point(el.PNum(k)),
+						    el.GeomInfoPi(k)))
+			    onchart = 1;
+			if (!onchart)
+			  continue;
+		      
+			Vec3d e1(tp1, tp2);
+			Vec3d e2(tp1, tp3);
+			Vec3d n = Cross (e1, e2);
+			n /= n.Length();
+			double lam1, lam2, lam3;
+			lam3 = n * Vec3d (tp1, p);
+			LocalCoordinates (e1, e2, Vec3d (tp1, p), lam1, lam2);
+		      
+			if (fabs (lam3) < 0.1 * hshould && 
+			    lam1 > 0 && lam2 > 0 && (lam1 + lam2) < 1)
+			  {
+#ifdef DEVELOP
+			    cout << "overlap" << endl;
+			    (*testout) << "overlap:" << endl
+				       << "tri = " << tp1 << "-" << tp2 << "-" << tp3 << endl
+				       << "point = " << p << endl
+				       << "lam1, 2 = " << lam1 << ", " << lam2 << endl
+				       << "lam3 = " << lam3 << endl;
+			  
+			    //		      cout << "overlap !!!" << endl;
+#endif
+			    for (int k = 1; k <= 5; k++)
+			      adfront -> IncrementClass (lindex.Get(1));
+
+			    found = 0;
+			  
+			    if ( debugflag || debugparam.haltnosuccess )
+			      PrintWarning ("overlapping");
+			  
+			  
+			    if (debugparam.haltoverlap)
+			      {
+				debugflag = 1;
+			      }
+			  
+			    /*
+			      multithread.drawing = 1;
+			      glrender(1);
+			    */
+			  }
+		      }
+		  }
+	      }
+	  }
+
+
+	if (found)
+	  {
+	    // check, whether new front line already exists
+
+	    for (int i = oldnl+1; i <= loclines.Size(); i++)
+	      {
+		int nlgpi1 = loclines.Get(i).I1();
+		int nlgpi2 = loclines.Get(i).I2();
+		if (nlgpi1 <= pindex.Size() && nlgpi2 <= pindex.Size())
+		  {
+		    nlgpi1 = adfront->GetGlobalIndex (pindex.Get(nlgpi1));
+		    nlgpi2 = adfront->GetGlobalIndex (pindex.Get(nlgpi2));
+
+		    int exval = adfront->ExistsLine (nlgpi1, nlgpi2);
+		    if (exval)
+		      {
+			cout << "ERROR: new line exits, val = " << exval << endl;
+			(*testout) << "ERROR: new line exits, val = " << exval << endl;
+			found = 0;
+
+
+			if (debugparam.haltexistingline)
+			  debugflag = 1;
+
+		      }
+		  }
+	      }
+	  
+	  }
+
+
+	/*
+	  if (found)
+	  {
+	  // check, whether new triangles insert edges twice
+	  for (i = 1; i <= locelements.Size(); i++)
+	  for (j = 1; j <= 3; j++)
+	  {
+	  int tpi1 = locelements.Get(i).PNumMod (j);
+	  int tpi2 = locelements.Get(i).PNumMod (j+1);
+	  if (tpi1 <= pindex.Size() && tpi2 <= pindex.Size())
+	  {
+	  tpi1 = adfront->GetGlobalIndex (pindex.Get(tpi1));
+	  tpi2 = adfront->GetGlobalIndex (pindex.Get(tpi2));
+
+	  if (doubleedge.Used (INDEX_2(tpi1, tpi2)))
+	  {
+	  if (debugparam.haltexistingline)
+	  debugflag = 1;
+	  cerr << "ERROR Insert edge "
+	  << tpi1 << " - " << tpi2 << " twice !!!" << endl;
+	  found = 0;
+	  }
+	  doubleedge.Set (INDEX_2(tpi1, tpi2), 1);
+	  }
+	  }
+	  }
+	*/
+
+
+	if (found)
+	  {
+	    // everything is ok, perform mesh update
+
+	    ruleused.Elem(rulenr)++;
+
+
+	    pindex.SetSize(locpoints.Size());
+	      
+	    for (int i = oldnp+1; i <= locpoints.Size(); i++)
+	      {
+		globind = mesh.AddPoint (locpoints.Get(i));
+		pindex.Elem(i) = adfront -> AddPoint (locpoints.Get(i), globind);
+	      }
+	      
+	    for (int i = oldnl+1; i <= loclines.Size(); i++)
+	      {
+		/*
+		  for (j = 1; j <= locpoints.Size(); j++)
+		  {
+		  (*testout) << j << ": " << locpoints.Get(j) << endl;
+		  }
+		*/
+	      
+		/*
+		  ComputeLineGeoInfo (locpoints.Get(loclines.Get(i).I1()),
+		  locpoints.Get(loclines.Get(i).I2()),
+		  gisize, geominfo);
+		*/		  
+
+		if (pindex.Get(loclines.Get(i).I1()) == -1 || 
+		    pindex.Get(loclines.Get(i).I2()) == -1)
+		  {
+		    (*testout) << "pindex is 0" << endl;
+		  }
+
+		if (!upgeominfo.Get(loclines.Get(i).I1()).trignum || 
+		    !upgeominfo.Get(loclines.Get(i).I2()).trignum)
+		  {
+		    cout << "new el: illegal geominfo" << endl;
+		  }
+
+		adfront -> AddLine (pindex.Get(loclines.Get(i).I1()),
+				    pindex.Get(loclines.Get(i).I2()),
+				    upgeominfo.Get(loclines.Get(i).I1()),
+				    upgeominfo.Get(loclines.Get(i).I2()));
+	      }
+	    for (int i = 1; i <= locelements.Size(); i++)
+	      {
+		Element2d mtri(locelements.Get(i).GetNP());
+		mtri = locelements.Get(i);
+		mtri.SetIndex (facenr);
+
+
+		// compute triangle geominfo:
+		//	      (*testout) << "triggeominfo: ";
+		for (int j = 1; j <= locelements.Get(i).GetNP(); j++)
+		  {
+		    mtri.GeomInfoPi(j) = upgeominfo.Get(locelements.Get(i).PNum(j));
+		    //		  (*testout) << mtri.GeomInfoPi(j).trignum << " ";
+		  }
+		//	      (*testout) << endl;
+
+		for (int j = 1; j <= locelements.Get(i).GetNP(); j++)
+		  {
+		    mtri.PNum(j) = 
+		      locelements.Elem(i).PNum(j) =
+		      adfront -> GetGlobalIndex (pindex.Get(locelements.Get(i).PNum(j)));
+		  }
+	      
+		
+	      
+	      
+		mesh.AddSurfaceElement (mtri);
+		cntelem++;
+		//	      cout << "elements: " << cntelem << endl;
+
+
+	      
+		Box<3> box;
+		box.Set (mesh[mtri[0]]);
+		box.Add (mesh[mtri[1]]);
+		box.Add (mesh[mtri[2]]);
+		surfeltree.Insert (box, mesh.GetNSE());
+
+		const Point3d & sep1 = mesh.Point (mtri.PNum(1));
+		const Point3d & sep2 = mesh.Point (mtri.PNum(2));
+		const Point3d & sep3 = mesh.Point (mtri.PNum(3));
+
+		double trigarea = Cross (Vec3d (sep1, sep2), 
+					 Vec3d (sep1, sep3)).Length() / 2;
+
+		if (mtri.GetNP() == 4)
+		  {
+		    const Point3d & sep4 = mesh.Point (mtri.PNum(4));
+		    trigarea += Cross (Vec3d (sep1, sep3), 
+				       Vec3d (sep1, sep4)).Length() / 2;
+		  }
+
+		meshedarea += trigarea;
+
+		if(maxarea > 0 && meshedarea-meshedarea_before > maxarea)
+		  {
+		    cerr << "meshed area = " << meshedarea-meshedarea_before << endl
+			 << "maximal area = " << maxarea << endl
+			 << "GIVING UP" << endl;
+		    return MESHING2_GIVEUP;
+		  }
+	      
+
+
+		for (int j = 1; j <= locelements.Get(i).GetNP(); j++)
+		  {
+		    int gpi = locelements.Get(i).PNum(j);
+
+		    int oldts = trigsonnode.Size();
+		    if (gpi >= oldts+PointIndex::BASE)
+		      {
+			trigsonnode.SetSize (gpi+1-PointIndex::BASE);
+			illegalpoint.SetSize (gpi+1-PointIndex::BASE);
+			for (int k = oldts+PointIndex::BASE; 
+			     k <= gpi; k++)
+			  {
+			    trigsonnode[k] = 0;
+			    illegalpoint[k] = 0;
+			  }
+		      }
+
+		    trigsonnode[gpi]++;
+		  
+		    if (trigsonnode[gpi] > 20)
+		      {
+			illegalpoint[gpi] = 1;
+			//		      cout << "illegal point: " << gpi << endl;
+			(*testout) << "illegal point: " << gpi << endl;
+		      }
+
+		    static int mtonnode = 0;
+		    if (trigsonnode[gpi] > mtonnode)
+		      mtonnode = trigsonnode[gpi];
+		  }
+		//	      cout << "els = " << cntelem << " trials = " << trials << endl;
+		//	      if (trials > 100)		return;
+	      }
+	      
+	    for (int i = 1; i <= dellines.Size(); i++)
+	      adfront -> DeleteLine (lindex.Get(dellines.Get(i)));
+	      
+	    //	  rname = rules.Get(rulenr)->Name();
+#ifdef MYGRAPH
+	    if (silentflag<3) 
+	      {
+		plotsurf.DrawPnL(locpoints, loclines);
+		plotsurf.Plot(testmode, testmode);
+	      }
+#endif
+
+	    if (morerisc)
+	      {
+		cout << "generated due to morerisc" << endl;
+		//	      multithread.drawing = 1;
+		//	      glrender(1);
+	      }
+
+
+
+	  
+	    if ( debugparam.haltsuccess || debugflag )
+	      {
+		// adfront -> PrintOpenSegments (*testout);
+		cout << "success of rule" << rules.Get(rulenr)->Name() << endl;
+		multithread.drawing = 1;
+		multithread.testmode = 1;
+		multithread.pause = 1;
+
+
+		/*
+		  extern STLGeometry * stlgeometry;
+		  stlgeometry->ClearMarkedSegs();
+		  for (i = 1; i <= loclines.Size(); i++)
+		  {
+		  stlgeometry->AddMarkedSeg(locpoints.Get(loclines.Get(i).I1()),
+		  locpoints.Get(loclines.Get(i).I2()));
+		  }
+		*/
+
+		(*testout) << "success of rule" << rules.Get(rulenr)->Name() << endl;
+		(*testout) << "trials = " << trials << endl;
+
+		(*testout) << "locpoints " << endl;
+		for (int i = 1; i <= pindex.Size(); i++)
+		  (*testout) << adfront->GetGlobalIndex (pindex.Get(i)) << endl;
+
+		(*testout) << "old number of lines = " << oldnl << endl;
+		for (int i = 1; i <= loclines.Size(); i++)
+		  {
+		    (*testout) << "line ";
+		    for (int j = 1; j <= 2; j++)
+		      {
+			int hi = 0;
+			if (loclines.Get(i).I(j) >= 1 &&
+			    loclines.Get(i).I(j) <= pindex.Size())
+			  hi = adfront->GetGlobalIndex (pindex.Get(loclines.Get(i).I(j)));
+
+			(*testout) << hi << " ";
+		      }
+		    (*testout) << " : " 
+			       << plainpoints.Get(loclines.Get(i).I1()) << " - "
+			       << plainpoints.Get(loclines.Get(i).I2()) << " 3d: "
+			       << locpoints.Get(loclines.Get(i).I1()) << " - "
+			       << locpoints.Get(loclines.Get(i).I2()) 
+			       << endl;
+		  }
+
+
+
+		glrender(1);
+	      }
+	  }
+	else
+	  {
+	    adfront -> IncrementClass (lindex.Get(1));
+
+	    if ( debugparam.haltnosuccess || debugflag )
+	      {
+		cout << "Problem with seg " << gpi1 << " - " << gpi2
+		     << ", class = " << qualclass << endl;
+
+		(*testout) << "Problem with seg " << gpi1 << " - " << gpi2
+			   << ", class = " << qualclass << endl;
+
+		multithread.drawing = 1;
+		multithread.testmode = 1;
+		multithread.pause = 1;
+
+
+		/*
+		  extern STLGeometry * stlgeometry;
+		  stlgeometry->ClearMarkedSegs();
+		  for (i = 1; i <= loclines.Size(); i++)
+		  {
+		  stlgeometry->AddMarkedSeg(locpoints.Get(loclines.Get(i).I1()),
+		  locpoints.Get(loclines.Get(i).I2()));
+		  }
+		*/
+
+		for (int i = 1; i <= loclines.Size(); i++)
+		  {
+		    (*testout) << "line ";
+		    for (int j = 1; j <= 2; j++)
+		      {
+			int hi = 0;
+			if (loclines.Get(i).I(j) >= 1 &&
+			    loclines.Get(i).I(j) <= pindex.Size())
+			  hi = adfront->GetGlobalIndex (pindex.Get(loclines.Get(i).I(j)));
+
+			(*testout) << hi << " ";
+		      }
+		    (*testout) << " : " 
+			       << plainpoints.Get(loclines.Get(i).I1()) << " - "
+			       << plainpoints.Get(loclines.Get(i).I2()) << " 3d: "
+			       << locpoints.Get(loclines.Get(i).I1()) << " - "
+			       << locpoints.Get(loclines.Get(i).I2()) 
+			       << endl;
+		  }
+
+
+		/*
+		  cout << "p1gi = " << blgeominfo[0].trignum 
+		  << ", p2gi = " << blgeominfo[1].trignum << endl;
+		*/
+
+		glrender(1);
+	      }
+
+	  
+#ifdef MYGRAPH      
+	    if (silentflag<3)
+	      {
+		if (testmode || trials%2 == 0)
+		  {
+		    plotsurf.DrawPnL(locpoints, loclines);
+		    plotsurf.Plot(testmode, testmode);
+		  }
+	      }
+#endif
+	  }
+
+      }
+
+    PrintMessage (3, "Surface meshing done");
+
+
+    adfront->PrintOpenSegments (*testout);
+
+    multithread.task = savetask;
+
+
+    EndMesh ();
+
+
+    if (!adfront->Empty())
+      return MESHING2_GIVEUP;
+    
+    return MESHING2_OK;
+  }
+
+
+
+
+
+
+
+
+
+}
+
+
+
+
+
+
+// #define OPENGL
+#ifdef OPENGLxx
+
+/* *********************** Draw Surface Meshing **************** */
+
+
+#include <visual.hpp>
+#include <stlgeom.hpp>
+
+namespace netgen 
+{
+
+  extern STLGeometry * stlgeometry;
+  extern Mesh * mesh;
+  VisualSceneSurfaceMeshing vssurfacemeshing;
+
+
+
+  void glrender (int wait)
+  {
+    //  cout << "plot adfront" << endl;
+
+    if (multithread.drawing)
+      {
+	//      vssurfacemeshing.Render();
+	Render ();
+      
+	if (wait || multithread.testmode)
+	  {
+	    multithread.pause = 1;
+	  }
+	while (multithread.pause);
+      }
+  }
+
+
+
+  VisualSceneSurfaceMeshing :: VisualSceneSurfaceMeshing ()
+    : VisualScene()
+  {
+    ;
+  }
+
+  VisualSceneSurfaceMeshing :: ~VisualSceneSurfaceMeshing ()
+  {
+    ;
+  }
+
+  void VisualSceneSurfaceMeshing :: DrawScene ()
+  {
+    int i, j, k;
+
+    if (loclines.Size() != changeval)
+      {
+	center = Point<3>(0,0,-5);
+	rad = 0.1;
+  
+	CalcTransformationMatrices();
+	changeval = loclines.Size();
+      }
+
+  glClearColor(backcolor, backcolor, backcolor, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  SetLight();
+
+  //  glEnable (GL_COLOR_MATERIAL);
+
+  //  glDisable (GL_SHADING);
+  //  glColor3f (0.0f, 1.0f, 1.0f);
+  //  glLineWidth (1.0f);
+  //  glShadeModel (GL_SMOOTH);
+
+  //  glCallList (linelists.Get(1));
+
+  //  SetLight();
+
+  glPushMatrix();
+  glMultMatrixf (transformationmat);
+
+  glShadeModel (GL_SMOOTH);
+  // glDisable (GL_COLOR_MATERIAL);
+  glEnable (GL_COLOR_MATERIAL);
+  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+  glEnable (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  //  glEnable (GL_LIGHTING);
+
+  double shine = vispar.shininess;
+  double transp = vispar.transp;
+
+  glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine);
+  glLogicOp (GL_COPY);
+
+
+
+  /*
+
+  float mat_col[] = { 0.2, 0.2, 0.8, 1 };
+  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+  glPolygonOffset (1, 1);
+  glEnable (GL_POLYGON_OFFSET_FILL);
+
+    float mat_colbl[] = { 0.8, 0.2, 0.2, 1 };
+    float mat_cololdl[] = { 0.2, 0.8, 0.2, 1 };
+    float mat_colnewl[] = { 0.8, 0.8, 0.2, 1 };
+
+
+    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+    glPolygonOffset (1, -1);
+    glLineWidth (3);
+
+    for (i = 1; i <= loclines.Size(); i++)
+      {
+	if (i == 1)
+	  {
+	    glEnable (GL_POLYGON_OFFSET_FILL);
+	    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbl);
+	  }
+	else if (i <= oldnl) 
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_cololdl);
+	else 
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colnewl);
+
+	int pi1 = loclines.Get(i).I1();
+	int pi2 = loclines.Get(i).I2();
+
+	if (pi1 >= 1 && pi2 >= 1)
+	  {
+	    Point3d p1 = locpoints.Get(pi1);
+	    Point3d p2 = locpoints.Get(pi2);
+	  
+	    glBegin (GL_LINES);
+	    glVertex3f (p1.X(), p1.Y(), p1.Z());
+	    glVertex3f (p2.X(), p2.Y(), p2.Z());
+	    glEnd();
+	  }
+
+	glDisable (GL_POLYGON_OFFSET_FILL);
+      }
+  
+
+    glLineWidth (1);
+
+
+    glPointSize (5);
+    float mat_colp[] = { 1, 0, 0, 1 };
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp);
+    glBegin (GL_POINTS);
+    for (i = 1; i <= locpoints.Size(); i++)
+      {
+	Point3d p = locpoints.Get(i);
+	glVertex3f (p.X(), p.Y(), p.Z());
+      }
+    glEnd();
+
+
+    glPopMatrix();
+  */
+
+    float mat_colp[] = { 1, 0, 0, 1 };
+
+    float mat_col2d1[] = { 1, 0.5, 0.5, 1 };
+    float mat_col2d[] = { 1, 1, 1, 1 };
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d);
+  
+    double scalex = 0.1, scaley = 0.1;
+
+    glBegin (GL_LINES);
+    for (i = 1; i <= loclines.Size(); i++)
+      {
+	glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d);
+	if (i == 1)
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d1);
+
+	int pi1 = loclines.Get(i).I1();
+	int pi2 = loclines.Get(i).I2();
+
+	if (pi1 >= 1 && pi2 >= 1)
+	  {
+	    Point2d p1 = plainpoints.Get(pi1);
+	    Point2d p2 = plainpoints.Get(pi2);
+	  
+	    glBegin (GL_LINES);
+	    glVertex3f (scalex * p1.X(), scaley * p1.Y(), -5);
+	    glVertex3f (scalex * p2.X(), scaley * p2.Y(), -5);
+	    glEnd();
+	  }
+      }
+    glEnd ();
+
+
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp);
+    glBegin (GL_POINTS);
+    for (i = 1; i <= plainpoints.Size(); i++)
+      {
+	Point2d p = plainpoints.Get(i);
+	glVertex3f (scalex * p.X(), scaley * p.Y(), -5);
+      }
+    glEnd();
+
+
+
+
+
+
+  glDisable (GL_POLYGON_OFFSET_FILL);
+ 
+  glPopMatrix();
+  DrawCoordinateCross ();
+  DrawNetgenLogo ();
+  glFinish();  
+
+  /*
+    glDisable (GL_POLYGON_OFFSET_FILL);
+
+    //  cout << "draw surfacemeshing" << endl;
+    //
+    //  if (changeval != stlgeometry->GetNT())
+    //      BuildScene();
+    //      changeval = stlgeometry->GetNT();
+    
+
+    glClearColor(backcolor, backcolor, backcolor, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    SetLight();
+
+    glPushMatrix();
+    glLoadMatrixf (transmat);
+    glMultMatrixf (rotmat);
+
+    glShadeModel (GL_SMOOTH);
+    glDisable (GL_COLOR_MATERIAL);
+    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+    glEnable (GL_BLEND);
+    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    float mat_spec_col[] = { 1, 1, 1, 1 };
+    glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_spec_col);
+
+    double shine = vispar.shininess;
+    double transp = vispar.transp;
+
+    glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine);
+    glLogicOp (GL_COPY);
+
+
+    float mat_col[] = { 0.2, 0.2, 0.8, transp };
+    float mat_colrt[] = { 0.2, 0.8, 0.8, transp };
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+    glPolygonOffset (1, 1);
+    glEnable (GL_POLYGON_OFFSET_FILL);
+
+    glColor3f (1.0f, 1.0f, 1.0f);
+
+    glEnable (GL_NORMALIZE);
+    
+    //  glBegin (GL_TRIANGLES);
+    //      for (j = 1; j <= stlgeometry -> GetNT(); j++)
+    //      {
+    //      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+    //      if (j == geomtrig)
+    //      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colrt);
+	
+
+    //      const STLReadTriangle & tria = stlgeometry -> GetReadTriangle(j);
+    //      glNormal3f (tria.normal.X(),
+    //      tria.normal.Y(),
+    //      tria.normal.Z());
+		  
+    //      for (k = 0; k < 3; k++)
+    //      {
+    //      glVertex3f (tria.pts[k].X(),
+    //      tria.pts[k].Y(),
+    //      tria.pts[k].Z());
+    //      }
+    //      }    
+    //      glEnd ();
+    
+
+
+    glDisable (GL_POLYGON_OFFSET_FILL);
+
+    float mat_colbl[] = { 0.8, 0.2, 0.2, 1 };
+    float mat_cololdl[] = { 0.2, 0.8, 0.2, 1 };
+    float mat_colnewl[] = { 0.8, 0.8, 0.2, 1 };
+
+
+    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+    glPolygonOffset (1, -1);
+    glLineWidth (3);
+
+    for (i = 1; i <= loclines.Size(); i++)
+      {
+	if (i == 1)
+	  {
+	    glEnable (GL_POLYGON_OFFSET_FILL);
+	    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbl);
+	  }
+	else if (i <= oldnl) 
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_cololdl);
+	else 
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colnewl);
+
+	int pi1 = loclines.Get(i).I1();
+	int pi2 = loclines.Get(i).I2();
+
+	if (pi1 >= 1 && pi2 >= 1)
+	  {
+	    Point3d p1 = locpoints.Get(pi1);
+	    Point3d p2 = locpoints.Get(pi2);
+	  
+	    glBegin (GL_LINES);
+	    glVertex3f (p1.X(), p1.Y(), p1.Z());
+	    glVertex3f (p2.X(), p2.Y(), p2.Z());
+	    glEnd();
+	  }
+
+	glDisable (GL_POLYGON_OFFSET_FILL);
+      }
+
+
+    glLineWidth (1);
+
+
+    glPointSize (5);
+    float mat_colp[] = { 1, 0, 0, 1 };
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp);
+    glBegin (GL_POINTS);
+    for (i = 1; i <= locpoints.Size(); i++)
+      {
+	Point3d p = locpoints.Get(i);
+	glVertex3f (p.X(), p.Y(), p.Z());
+      }
+    glEnd();
+
+
+    glPopMatrix();
+
+
+    float mat_col2d1[] = { 1, 0.5, 0.5, 1 };
+    float mat_col2d[] = { 1, 1, 1, 1 };
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d);
+  
+    double scalex = 0.1, scaley = 0.1;
+
+    glBegin (GL_LINES);
+    for (i = 1; i <= loclines.Size(); i++)
+      {
+	glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d);
+	if (i == 1)
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d1);
+
+	int pi1 = loclines.Get(i).I1();
+	int pi2 = loclines.Get(i).I2();
+
+	if (pi1 >= 1 && pi2 >= 1)
+	  {
+	    Point2d p1 = plainpoints.Get(pi1);
+	    Point2d p2 = plainpoints.Get(pi2);
+	  
+	    glBegin (GL_LINES);
+	    glVertex3f (scalex * p1.X(), scaley * p1.Y(), -5);
+	    glVertex3f (scalex * p2.X(), scaley * p2.Y(), -5);
+	    glEnd();
+	  }
+      }
+    glEnd ();
+
+
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp);
+    glBegin (GL_POINTS);
+    for (i = 1; i <= plainpoints.Size(); i++)
+      {
+	Point2d p = plainpoints.Get(i);
+	glVertex3f (scalex * p.X(), scaley * p.Y(), -5);
+      }
+    glEnd();
+
+    glFinish();  
+*/
+  }
+
+
+  void VisualSceneSurfaceMeshing :: BuildScene (int zoomall)
+  {
+    int i, j, k;
+    /*
+      center = stlgeometry -> GetBoundingBox().Center();
+      rad = stlgeometry -> GetBoundingBox().Diam() / 2;
+
+      CalcTransformationMatrices();
+    */
+  }
+
+}
+
+
+#else
+namespace netgen
+{
+  void glrender (int wait)
+  { ; }
+}
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/meshing2.hpp b/contrib/Netgen/libsrc/meshing/meshing2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e14596438ac8a3227cb72be26a36901ea78d206
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing2.hpp
@@ -0,0 +1,164 @@
+#ifndef FILE_MESHING2
+#define FILE_MESHING2
+
+/**************************************************************************/
+/* File:   meshing2.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+
+
+enum MESHING2_RESULT
+{
+  MESHING2_OK = 0,
+  MESHING2_GIVEUP = 1
+};
+
+
+/*
+   
+The basis class for 2D mesh generation. 
+Has the method GenerateMesh
+
+For surface mesh generation, or non-Euklidean meshing,
+derive from Meshing2, and replace transformation.
+
+*/
+
+class Meshing2
+{
+  /// the current advancing front
+  AdFront2 * adfront;
+  /// rules for mesh generation
+  Array<netrule*> rules;
+  /// statistics
+  Array<int> ruleused, canuse, foundmap;
+  /// 
+  Box<3> boundingbox;
+  ///
+  double starttime;
+  ///
+  double maxarea;
+
+  Vec3d ex, ey;
+  Point3d globp1;
+
+public:
+  ///
+  DLL_HEADER Meshing2 (const MeshingParameters & mp, const Box<3> & aboundingbox);
+
+  ///
+  DLL_HEADER virtual ~Meshing2 ();
+
+  /// Load rules, either from file, or compiled rules
+  void LoadRules (const char * filename, bool quad);
+
+  /// 
+  DLL_HEADER MESHING2_RESULT GenerateMesh (Mesh & mesh, const MeshingParameters & mp, double gh, int facenr);
+
+  DLL_HEADER void Delaunay (Mesh & mesh, int domainnr, const MeshingParameters & mp);
+  DLL_HEADER void BlockFillLocalH (Mesh & mesh, const MeshingParameters & mp);
+
+
+  ///
+  DLL_HEADER void AddPoint (const Point3d & p, PointIndex globind, MultiPointGeomInfo * mgi = NULL,
+		 bool pointonsurface = true);
+
+  ///
+  DLL_HEADER void AddBoundaryElement (INDEX i1, INDEX i2,
+			   const PointGeomInfo & gi1, const PointGeomInfo & gi2);
+  
+  ///
+  void SetStartTime (double astarttime);
+
+  ///
+  void SetMaxArea (double amaxarea);
+
+protected:
+  ///
+  virtual void StartMesh ();
+  ///
+  virtual void EndMesh ();
+  ///
+  virtual double CalcLocalH (const Point3d & p, double gh) const;
+
+  ///
+  virtual void DefineTransformation (const Point3d & p1, const Point3d & p2,
+				     const PointGeomInfo * geominfo1,
+				     const PointGeomInfo * geominfo2);
+  ///
+  virtual void TransformToPlain (const Point3d & locpoint, const MultiPointGeomInfo &  geominfo,
+				 Point2d & plainpoint, double h, int & zone);
+  /// return 0 .. ok
+  /// return >0 .. cannot transform point to true surface
+  virtual int TransformFromPlain (Point2d & plainpoint,
+				  Point3d & locpoint, 
+				  PointGeomInfo & geominfo, 
+				  double h);
+  
+  /// projects to surface
+  /// return 0 .. ok
+  virtual int BelongsToActiveChart (const Point3d & p, 
+				    const PointGeomInfo & gi);
+
+  /// computes geoinfo data for line with respect to
+  /// selected chart
+  virtual int ComputePointGeomInfo (const Point3d & p, 
+				    PointGeomInfo & gi);
+
+  /// Tries to select unique geominfo on active chart
+  /// return 0: success
+  /// return 1: failed
+  virtual int ChooseChartPointGeomInfo (const MultiPointGeomInfo & mpgi, 
+					PointGeomInfo & pgi);
+
+
+
+  /*
+    tests, whether endpoint (= 1 or 2) of line segment p1-p2
+    is inside of the selected chart. The endpoint must be on the
+    chart
+   */
+  virtual int IsLineVertexOnChart (const Point3d & p1, const Point3d & p2,
+				   int endpoint, const PointGeomInfo & geominfo);
+
+  /*
+    get (projected) boundary of current chart
+   */
+  virtual void GetChartBoundary (Array<Point2d> & points, 
+				 Array<Point3d> & points3d,
+				 Array<INDEX_2> & lines, double p) const;
+
+  virtual double Area () const;
+
+
+/** Applies 2D rules.
+ Tests all 2D rules */
+  int ApplyRules (Array<Point2d> & lpoints, 
+		  Array<int> & legalpoints,
+		  int maxlegalpoint,
+		  Array<INDEX_2> & llines,
+		  int maxlegelline,
+		  Array<Element2d> & elements, Array<INDEX> & dellines,
+		  int tolerance,
+		  const MeshingParameters & mp);
+  
+
+};
+
+
+
+
+
+
+
+
+#endif
+
+
+
+
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/meshing3.cpp b/contrib/Netgen/libsrc/meshing/meshing3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3421c3192c68abfc920c5b2410ed5a8980296d1b
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing3.cpp
@@ -0,0 +1,1264 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+double minother;
+double minwithoutother;
+
+
+
+
+
+MeshingStat3d :: MeshingStat3d ()
+{
+  cntsucc = cnttrials = cntelem = qualclass = 0;
+  vol0 = h = 1;
+  problemindex = 1;
+}  
+  
+
+Meshing3 :: Meshing3 (const string & rulefilename) 
+{
+  tolfak = 1;
+
+  LoadRules (rulefilename.c_str(), NULL);
+  adfront = new AdFront3;
+
+  problems.SetSize (rules.Size());
+  foundmap.SetSize (rules.Size());
+  canuse.SetSize (rules.Size());
+  ruleused.SetSize (rules.Size());
+
+  for (int i = 1; i <= rules.Size(); i++)
+    {
+      problems.Elem(i) = new char[255];
+      foundmap.Elem(i) = 0;
+      canuse.Elem(i) = 0;
+      ruleused.Elem(i) = 0;
+    }
+}
+
+
+Meshing3 :: Meshing3 (const char ** rulep)
+{
+  tolfak = 1;
+
+  LoadRules (NULL, rulep);
+  adfront = new AdFront3;
+
+  problems.SetSize (rules.Size());
+  foundmap.SetSize (rules.Size());
+  canuse.SetSize (rules.Size());
+  ruleused.SetSize (rules.Size());
+
+  for (int i = 0; i < rules.Size(); i++)
+    {
+      problems[i] = new char[255];
+      foundmap[i] = 0;
+      canuse[i]   = 0;
+      ruleused[i] = 0;
+    }
+}
+
+Meshing3 :: ~Meshing3 ()
+{
+  delete adfront;
+  for (int i = 0; i < rules.Size(); i++)
+    {
+      delete [] problems[i];
+      delete rules[i];
+    }
+}
+
+
+
+static double CalcLocH (const Array<Point3d> & locpoints,
+			const Array<MiniElement2d> & locfaces,
+			double h)
+{
+  return h;
+
+  // was war das ????
+  
+  int i, j;
+  double hi, h1, d, dn, sum, weight, wi;
+  Point3d p0, pc;
+  Vec3d n, v1, v2;
+
+  p0.X() = p0.Y() = p0.Z() = 0;
+  for (j = 1; j <= 3; j++)
+    {
+      p0.X() += locpoints.Get(locfaces.Get(1).PNum(j)).X();
+      p0.Y() += locpoints.Get(locfaces.Get(1).PNum(j)).Y();
+      p0.Z() += locpoints.Get(locfaces.Get(1).PNum(j)).Z();
+    }
+  p0.X() /= 3; p0.Y() /= 3; p0.Z() /= 3;
+  
+  v1 = locpoints.Get(locfaces.Get(1).PNum(2)) -
+    locpoints.Get(locfaces.Get(1).PNum(1));
+  v2 = locpoints.Get(locfaces.Get(1).PNum(3)) -
+    locpoints.Get(locfaces.Get(1).PNum(1));
+
+  h1 = v1.Length();
+  n = Cross (v2, v1);
+  n /= n.Length();
+
+  sum = 0;
+  weight = 0;
+
+  for (i = 1; i <= locfaces.Size(); i++)
+    {
+      pc.X() = pc.Y() = pc.Z() = 0;
+      for (j = 1; j <= 3; j++)
+	{
+	  pc.X() += locpoints.Get(locfaces.Get(i).PNum(j)).X();
+	  pc.Y() += locpoints.Get(locfaces.Get(i).PNum(j)).Y();
+	  pc.Z() += locpoints.Get(locfaces.Get(i).PNum(j)).Z();
+	}
+      pc.X() /= 3; pc.Y() /= 3; pc.Z() /= 3;
+
+      d = Dist (p0, pc);
+      dn = n * (pc - p0);
+      hi = Dist (locpoints.Get(locfaces.Get(i).PNum(1)),
+		 locpoints.Get(locfaces.Get(i).PNum(2)));
+		 
+      if (dn > -0.2 * h1)
+	{
+	  wi = 1 / (h1 + d);
+	  wi *= wi;
+	}
+      else
+	wi = 0;
+
+      sum += hi * wi;
+      weight += wi;
+    }
+
+  return sum/weight;
+}
+
+
+PointIndex Meshing3 :: AddPoint (const Point3d & p, PointIndex globind)
+{
+  return adfront -> AddPoint (p, globind);  
+}  
+
+void Meshing3 :: AddBoundaryElement (const Element2d & elem)
+{
+  MiniElement2d mini(elem.GetNP());
+  for (int j = 0; j < elem.GetNP(); j++)
+    mini[j] = elem[j];
+  adfront -> AddFace(mini);
+}  
+
+
+void Meshing3 :: AddBoundaryElement (const MiniElement2d & elem)
+{
+  adfront -> AddFace(elem);
+}
+
+int Meshing3 :: AddConnectedPair (const INDEX_2 & apair)
+{
+  return adfront -> AddConnectedPair (apair);
+}
+
+MESHING3_RESULT Meshing3 :: 
+GenerateMesh (Mesh & mesh, const MeshingParameters & mp)
+{
+  static int meshing3_timer = NgProfiler::CreateTimer ("Meshing3::GenerateMesh");
+  static int meshing3_timer_a = NgProfiler::CreateTimer ("Meshing3::GenerateMesh a");
+  static int meshing3_timer_b = NgProfiler::CreateTimer ("Meshing3::GenerateMesh b");
+  static int meshing3_timer_c = NgProfiler::CreateTimer ("Meshing3::GenerateMesh c");
+  static int meshing3_timer_d = NgProfiler::CreateTimer ("Meshing3::GenerateMesh d");
+  NgProfiler::RegionTimer reg (meshing3_timer);
+
+
+  Array<Point3d > locpoints;      // local points
+  Array<MiniElement2d> locfaces;     // local faces
+  Array<PointIndex> pindex;      // mapping from local to front point numbering
+  Array<int> allowpoint;         // point is allowd ?
+  Array<INDEX> findex;           // mapping from local to front face numbering
+  //INDEX_2_HASHTABLE<int> connectedpairs(100);  // connecgted pairs for prism meshing
+
+  Array<Point3d > plainpoints;       // points in reference coordinates
+  Array<int> delpoints, delfaces;   // points and lines to be deleted
+  Array<Element> locelements;       // new generated elements
+
+  int i, j, oldnp, oldnf;
+  int found;
+  referencetransform trans;
+  int rotind;
+  INDEX globind;
+  Point3d inp;
+  float err;
+
+  INDEX locfacesplit;             //index for faces in outer area
+  
+  bool loktestmode = false;
+
+  int uselocalh = mp.uselocalh;
+
+  // int giveuptol = mp.giveuptol; // 
+  MeshingStat3d stat;      // statistics
+  int plotstat_oldne = -1;
+
+  
+  // for star-shaped domain meshing
+  Array<MeshPoint> grouppoints;      
+  Array<MiniElement2d> groupfaces;
+  Array<PointIndex> grouppindex;
+  Array<INDEX> groupfindex;
+  
+  
+  float minerr;
+  int hasfound;
+  double tetvol;
+  // int giveup = 0;
+
+  
+  Array<Point3d> tempnewpoints;
+  Array<MiniElement2d> tempnewfaces;
+  Array<int> tempdelfaces;
+  Array<Element> templocelements;
+
+
+  stat.h = mp.maxh;
+
+  adfront->SetStartFront (mp.baseelnp);
+
+
+  found = 0;
+  stat.vol0 = adfront -> Volume();
+  tetvol = 0;
+
+  stat.qualclass = 1;
+
+  while (1)
+    {
+      if (multithread.terminate)
+	throw NgException ("Meshing stopped");
+
+      // break if advancing front is empty
+      if (!mp.baseelnp && adfront->Empty())
+	break;
+
+      // break, if advancing front has no elements with
+      // mp.baseelnp nodes  
+      if (mp.baseelnp && adfront->Empty (mp.baseelnp))
+	break;
+
+      locpoints.SetSize(0);
+      locfaces.SetSize(0);
+      locelements.SetSize(0);
+      pindex.SetSize(0);
+      findex.SetSize(0);
+
+      INDEX_2_HASHTABLE<int> connectedpairs(100);  // connected pairs for prism meshing
+      
+      // select base-element (will be locface[1])
+      // and get local environment of radius (safety * h)
+
+
+      int baseelem = adfront -> SelectBaseElement ();
+      if (mp.baseelnp && adfront->GetFace (baseelem).GetNP() != mp.baseelnp)
+	{
+	  adfront->IncrementClass (baseelem);	  
+	  continue;
+	}
+
+      const MiniElement2d & bel = adfront->GetFace (baseelem);
+      const Point3d & p1 = adfront->GetPoint (bel.PNum(1));
+      const Point3d & p2 = adfront->GetPoint (bel.PNum(2));
+      const Point3d & p3 = adfront->GetPoint (bel.PNum(3));
+
+      // (*testout) << endl << "base = " << bel << endl;
+
+
+      Point3d pmid = Center (p1, p2, p3);
+
+      double his = (Dist (p1, p2) + Dist(p1, p3) + Dist(p2, p3)) / 3;
+      double hshould;
+
+      hshould = mesh.GetH (pmid);
+
+      if (adfront->GetFace (baseelem).GetNP() == 4)
+	hshould = max2 (his, hshould);
+
+      double hmax = (his > hshould) ? his : hshould;
+      
+      // qualclass should come from baseelem !!!!!
+      double hinner = hmax * (1 + stat.qualclass);
+      double houter = hmax * (1 + 2 * stat.qualclass);
+
+      NgProfiler::StartTimer (meshing3_timer_a);
+      stat.qualclass =
+        adfront -> GetLocals (baseelem, locpoints, locfaces, 
+			      pindex, findex, connectedpairs,
+			      houter, hinner,
+			      locfacesplit);
+      NgProfiler::StopTimer (meshing3_timer_a);
+
+      // (*testout) << "locfaces = " << endl << locfaces << endl;
+
+      int pi1 = pindex.Get(locfaces[0].PNum(1));
+      int pi2 = pindex.Get(locfaces[0].PNum(2));
+      int pi3 = pindex.Get(locfaces[0].PNum(3));
+
+      //loktestmode = 1;
+      testmode = loktestmode;  //changed 
+      // loktestmode = testmode =  (adfront->GetFace (baseelem).GetNP() == 4) && (rules.Size() == 5);
+
+      loktestmode = stat.qualclass > 5;
+      
+
+      if (loktestmode)
+	{
+	  (*testout) << "baseel = " << baseelem << ", ind = " << findex.Get(1) << endl;
+	  (*testout) << "pi = " << pi1 << ", " << pi2 << ", " << pi3 << endl;
+	}
+
+
+
+
+
+      if (testmode)
+	{
+	  (*testout) << "baseelem = " << baseelem << " qualclass = " << stat.qualclass << endl;
+	  (*testout) << "locpoints = " << endl << locpoints << endl;
+	  (*testout) << "connected = " << endl << connectedpairs << endl;
+	}
+
+
+
+      // loch = CalcLocH (locpoints, locfaces, h);
+      
+      stat.nff = adfront->GetNF();
+      stat.vol = adfront->Volume();
+      if (stat.vol < 0) break;
+
+      oldnp = locpoints.Size();
+      oldnf = locfaces.Size();
+
+
+      allowpoint.SetSize(locpoints.Size());
+      if (uselocalh && stat.qualclass <= 3)
+	for (i = 1; i <= allowpoint.Size(); i++)
+	  {
+	    allowpoint.Elem(i) =
+	      (mesh.GetH (locpoints.Get(i)) > 0.4 * hshould / mp.sloppy) ? 2 : 1;
+	  }
+      else
+	allowpoint = 2;
+
+
+      
+      if (stat.qualclass >= mp.starshapeclass &&
+	  mp.baseelnp != 4)   
+	{
+	  NgProfiler::RegionTimer reg1 (meshing3_timer_b);
+	  // star-shaped domain removing
+
+	  grouppoints.SetSize (0);
+	  groupfaces.SetSize (0);
+	  grouppindex.SetSize (0);
+	  groupfindex.SetSize (0);
+	  
+	  adfront -> GetGroup (findex[0], grouppoints, groupfaces, 
+			       grouppindex, groupfindex);
+
+	  bool onlytri = 1;
+	  for (i = 0; i < groupfaces.Size(); i++)
+	    if (groupfaces[i].GetNP() != 3) 
+	      onlytri = 0;
+	  
+	  if (onlytri && groupfaces.Size() <= 20 + 2*stat.qualclass &&
+	      FindInnerPoint (grouppoints, groupfaces, inp))
+	    {
+	      (*testout) << "inner point found" << endl;
+
+	      for (i = 1; i <= groupfaces.Size(); i++)
+		adfront -> DeleteFace (groupfindex.Get(i));
+	      
+	      for (i = 1; i <= groupfaces.Size(); i++)
+		for (j = 1; j <= locfaces.Size(); j++)
+		  if (findex.Get(j) == groupfindex.Get(i))
+		    delfaces.Append (j);
+	      
+	      
+	      delfaces.SetSize (0);
+	      
+	      INDEX npi;
+	      Element newel;
+	      
+	      npi = mesh.AddPoint (inp);
+	      newel.SetNP(4);
+	      newel.PNum(4) = npi;
+	      
+	      for (i = 1; i <= groupfaces.Size(); i++)
+		{
+		  for (j = 1; j <= 3; j++)
+		    {
+		      newel.PNum(j) = 
+			adfront->GetGlobalIndex 
+			(grouppindex.Get(groupfaces.Get(i).PNum(j)));
+		    }
+		  mesh.AddVolumeElement (newel);
+		}
+	      continue;
+	    }
+	}
+      
+      found = 0;
+      hasfound = 0;
+      minerr = 1e6;
+
+      //      int optother = 0;
+
+      /*
+      for (i = 1; i <= locfaces.Size(); i++)
+	{
+	  (*testout) << "Face " << i << ": ";
+	  for (j = 1; j <= locfaces.Get(i).GetNP(); j++)
+	    (*testout) << pindex.Get(locfaces.Get(i).PNum(j)) << " ";
+	  (*testout) << endl;
+	}
+      for (i = 1; i <= locpoints.Size(); i++)
+	{
+	  (*testout) << "p" << i 
+		     << ", gi = " << pindex.Get(i) 
+		     << " = " << locpoints.Get(i) << endl;
+	}
+	*/
+
+      minother = 1e10;
+      minwithoutother = 1e10;
+
+      bool impossible = 1;
+
+      for (rotind = 1; rotind <= locfaces[0].GetNP(); rotind++)
+	{
+	  // set transformatino to reference coordinates
+
+	  if (locfaces.Get(1).GetNP() == 3)
+	    {
+	      trans.Set (locpoints.Get(locfaces.Get(1).PNumMod(1+rotind)),
+			 locpoints.Get(locfaces.Get(1).PNumMod(2+rotind)),
+			 locpoints.Get(locfaces.Get(1).PNumMod(3+rotind)), hshould);
+	    }
+	  else
+	    {
+	      trans.Set (locpoints.Get(locfaces.Get(1).PNumMod(1+rotind)),
+			 locpoints.Get(locfaces.Get(1).PNumMod(2+rotind)),
+			 locpoints.Get(locfaces.Get(1).PNumMod(4+rotind)), hshould);
+	    }
+
+	  trans.ToPlain (locpoints, plainpoints);
+
+
+	  for (i = 1; i <= allowpoint.Size(); i++)
+	    {
+	      if (plainpoints.Get(i).Z() > 0)
+		{
+		  //if(loktestmode)
+		  //  (*testout) << "plainpoints.Get(i).Z() = " << plainpoints.Get(i).Z() << " > 0" << endl;
+		  allowpoint.Elem(i) = 0;
+		}
+	    }
+
+	  stat.cnttrials++;
+
+
+	  if (stat.cnttrials % 100 == 0)
+	    {
+	      (*testout) << "\n";
+	      for (i = 1; i <= canuse.Size(); i++)
+	      {
+		(*testout) << foundmap.Get(i) << "/" 
+			   << canuse.Get(i) << "/"
+			   << ruleused.Get(i) << " map/can/use rule " << rules.Get(i)->Name() << "\n";
+	      }
+	      (*testout) << endl;
+	    }
+
+	  NgProfiler::StartTimer (meshing3_timer_c);	  
+
+	  found = ApplyRules (plainpoints, allowpoint, 
+			      locfaces, locfacesplit, connectedpairs,
+			      locelements, delfaces, 
+			      stat.qualclass, mp.sloppy, rotind, err);
+
+	  if (found >= 0) impossible = 0;
+	  if (found < 0) found = 0;
+
+
+	  NgProfiler::StopTimer (meshing3_timer_c);	  
+
+	  if (!found) loktestmode = 0;
+
+	  NgProfiler::RegionTimer reg2 (meshing3_timer_d);	  
+	  
+	  if (loktestmode)
+	    {
+	      (*testout) << "plainpoints = " << endl << plainpoints << endl;
+	      (*testout) << "Applyrules found " << found << endl;
+	    }
+
+	  if (found) stat.cntsucc++;
+
+	  locpoints.SetSize (plainpoints.Size());
+	  for (i = oldnp+1; i <= plainpoints.Size(); i++)
+	    trans.FromPlain (plainpoints.Elem(i), locpoints.Elem(i));
+	  
+
+
+	  // avoid meshing from large to small mesh-size
+	  if (uselocalh && found && stat.qualclass <= 3)
+	    {
+	      for (i = 1; i <= locelements.Size(); i++)
+		{
+		  Point3d pmin = locpoints.Get(locelements.Get(i).PNum(1));
+		  Point3d pmax = pmin;
+		  for (j = 2; j <= 4; j++)
+		    {
+		      const Point3d & hp = locpoints.Get(locelements.Get(i).PNum(j));
+		      pmin.SetToMin (hp);
+		      pmax.SetToMax (hp);
+		    }
+
+		  if (mesh.GetMinH (pmin, pmax) < 0.4 * hshould / mp.sloppy)
+		    found = 0;
+		}
+	    }
+	  if (found)
+	    {
+	      for (i = 1; i <= locelements.Size(); i++)
+		for (j = 1; j <= 4; j++)
+		  {
+		    const Point3d & hp = locpoints.Get(locelements.Get(i).PNum(j));
+		    if (Dist (hp, pmid) > hinner)
+		      found = 0;
+		  }
+	    }
+
+
+	  if (found)
+	    ruleused.Elem(found)++;
+
+
+	  // plotstat->Plot(stat);
+	  
+	  if (stat.cntelem != plotstat_oldne)
+	    {
+	      plotstat_oldne = stat.cntelem;
+
+	      PrintMessageCR (5, "El: ", stat.cntelem,
+			      //	    << " trials: " << stat.cnttrials
+			      " faces: ", stat.nff,
+			      " vol = ", float(100 * stat.vol / stat.vol0));
+  
+	      multithread.percent = 100 -  100.0 * stat.vol / stat.vol0;
+	    }
+
+
+	  if (found && (!hasfound || err < minerr) )
+	    {
+	      
+	      if (testmode)
+		{
+		  (*testout) << "found is active, 3" << endl;
+		  for (i = 1; i <= plainpoints.Size(); i++)
+		    {
+		      (*testout) << "p";
+		      if (i <= pindex.Size())
+			(*testout) << pindex.Get(i) << ": ";
+		      else
+			(*testout) << "new: ";
+		      (*testout) << plainpoints.Get(i) << endl;
+		    }
+		}
+	      
+	      
+	      
+	      hasfound = found;
+	      minerr = err;
+	      
+	      tempnewpoints.SetSize (0);
+	      for (i = oldnp+1; i <= locpoints.Size(); i++)
+		tempnewpoints.Append (locpoints.Get(i));
+	      
+	      tempnewfaces.SetSize (0);
+	      for (i = oldnf+1; i <= locfaces.Size(); i++)
+		tempnewfaces.Append (locfaces.Get(i));
+	      
+	      tempdelfaces.SetSize (0);
+	      for (i = 1; i <= delfaces.Size(); i++)
+		tempdelfaces.Append (delfaces.Get(i));
+	      
+	      templocelements.SetSize (0);
+	      for (i = 1; i <= locelements.Size(); i++)
+		templocelements.Append (locelements.Get(i));
+
+	      /*
+	      optother =
+		strcmp (problems[found], "other") == 0;
+	      */
+	    }
+	  
+	  locpoints.SetSize (oldnp);
+	  locfaces.SetSize (oldnf);
+	  delfaces.SetSize (0);
+	  locelements.SetSize (0);
+	}
+      
+      
+
+      if (hasfound)
+	{
+
+	  /*
+	  if (optother)
+	    (*testout) << "Other is optimal" << endl;
+
+	  if (minother < minwithoutother)
+	    {
+	      (*testout) << "Other is better, " << minother << " less " << minwithoutother << endl;
+	    }
+	    */
+
+	  for (i = 1; i <= tempnewpoints.Size(); i++)
+	    locpoints.Append (tempnewpoints.Get(i));
+	  for (i = 1; i <= tempnewfaces.Size(); i++)
+	    locfaces.Append (tempnewfaces.Get(i));
+	  for (i = 1; i <= tempdelfaces.Size(); i++)
+	    delfaces.Append (tempdelfaces.Get(i));
+	  for (i = 1; i <= templocelements.Size(); i++)
+	    locelements.Append (templocelements.Get(i));
+
+
+	  if (loktestmode)
+	    {
+	      (*testout) << "apply rule" << endl;
+	      for (i = 1; i <= locpoints.Size(); i++)
+		{
+		  (*testout) << "p";
+		  if (i <= pindex.Size())
+		    (*testout) << pindex.Get(i) << ": ";
+		  else
+		    (*testout) << "new: ";
+		  (*testout) << locpoints.Get(i) << endl;
+		}
+	    }
+
+
+
+	  pindex.SetSize(locpoints.Size());
+
+	  for (i = oldnp+1; i <= locpoints.Size(); i++)
+	    {
+	      globind = mesh.AddPoint (locpoints.Get(i));
+	      pindex.Elem(i) = adfront -> AddPoint (locpoints.Get(i), globind);
+	    }
+
+	  for (i = 1; i <= locelements.Size(); i++)
+	    {
+	      Point3d * hp1, * hp2, * hp3, * hp4;
+	      hp1 = &locpoints.Elem(locelements.Get(i).PNum(1));
+	      hp2 = &locpoints.Elem(locelements.Get(i).PNum(2));
+	      hp3 = &locpoints.Elem(locelements.Get(i).PNum(3));
+	      hp4 = &locpoints.Elem(locelements.Get(i).PNum(4));
+	      
+	      tetvol += (1.0 / 6.0) * ( Cross ( *hp2 - *hp1, *hp3 - *hp1) * (*hp4 - *hp1) );
+
+	      for (j = 1; j <= locelements.Get(i).NP(); j++)
+		locelements.Elem(i).PNum(j) =
+		  adfront -> GetGlobalIndex (pindex.Get(locelements.Get(i).PNum(j)));
+
+	      mesh.AddVolumeElement (locelements.Get(i));
+	      stat.cntelem++;
+	    }
+
+	  for (i = oldnf+1; i <= locfaces.Size(); i++)
+	    {
+	      for (j = 1; j <= locfaces.Get(i).GetNP(); j++)
+		locfaces.Elem(i).PNum(j) = 
+		  pindex.Get(locfaces.Get(i).PNum(j));
+	      // (*testout) << "add face " << locfaces.Get(i) << endl;
+	      adfront->AddFace (locfaces.Get(i));
+	    }
+	  
+	  for (i = 1; i <= delfaces.Size(); i++)
+	    adfront->DeleteFace (findex.Get(delfaces.Get(i)));
+	}
+      else
+	{
+	  adfront->IncrementClass (findex.Get(1));
+	  if (impossible && mp.check_impossible)
+	    {
+	      (*testout) << "skip face since it is impossible" << endl;
+	      for (j = 0; j < 100; j++)
+		adfront->IncrementClass (findex.Get(1));
+	    }
+	}
+
+      locelements.SetSize (0);
+      delpoints.SetSize(0);
+      delfaces.SetSize(0);
+
+      if (stat.qualclass >= mp.giveuptol)
+	break;
+    }
+  
+  PrintMessage (5, "");  // line feed after statistics
+
+  for (i = 1; i <= ruleused.Size(); i++)
+    (*testout) << setw(4) << ruleused.Get(i)
+	       << " times used rule " << rules.Get(i) -> Name() << endl;
+
+
+  if (!mp.baseelnp && adfront->Empty())
+    return MESHING3_OK;
+
+  if (mp.baseelnp && adfront->Empty (mp.baseelnp))
+    return MESHING3_OK;
+
+  if (stat.vol < -1e-15)
+    return MESHING3_NEGVOL;
+
+  return MESHING3_NEGVOL;
+}
+
+
+
+
+enum blocktyp { BLOCKUNDEF, BLOCKINNER, BLOCKBOUND, BLOCKOUTER };
+
+void Meshing3 :: BlockFill (Mesh & mesh, double gh)
+{
+  PrintMessage (3, "Block-filling called (obsolete) ");
+
+  int i, j(0), i1, i2, i3, j1, j2, j3;
+  int n1, n2, n3, n, min1, min2, min3, max1, max2, max3;
+  int changed, filled;
+  double xmin(0), xmax(0), ymin(0), ymax(0), zmin(0), zmax(0);
+  double xminb, xmaxb, yminb, ymaxb, zminb, zmaxb;
+  //double rad = 0.7 * gh;
+  
+  for (i = 1; i <= adfront->GetNP(); i++)
+    {
+      const Point3d & p = adfront->GetPoint(i);
+      if (i == 1)
+	{
+	  xmin = xmax = p.X();
+	  ymin = ymax = p.Y();
+	  zmin = zmax = p.Z();
+	}
+      else
+	{
+	  if (p.X() < xmin) xmin = p.X();
+	  if (p.X() > xmax) xmax = p.X();
+	  if (p.Y() < ymin) ymin = p.Y();
+	  if (p.Y() > ymax) ymax = p.Y();
+	  if (p.Z() < zmin) zmin = p.Z();
+	  if (p.Z() > zmax) zmax = p.Z();
+	}
+    }
+  
+  xmin -= 5 * gh;
+  ymin -= 5 * gh;
+  zmin -= 5 * gh;
+  
+  n1 = int ((xmax-xmin) / gh + 5);
+  n2 = int ((ymax-ymin) / gh + 5);
+  n3 = int ((zmax-zmin) / gh + 5);
+  n = n1 * n2 * n3;
+  
+  PrintMessage (5, "n1 = ", n1, " n2 = ", n2, " n3 = ", n3);
+
+  Array<blocktyp> inner(n);
+  Array<int> pointnr(n), frontpointnr(n);
+
+
+  // initialize inner to 1
+
+  for (i = 1; i <= n; i++)
+    inner.Elem(i) = BLOCKUNDEF;
+
+
+  // set blocks cutting surfaces to 0
+
+  for (i = 1; i <= adfront->GetNF(); i++)
+    {
+      const MiniElement2d & el = adfront->GetFace(i);
+      xminb = xmax; xmaxb = xmin;
+      yminb = ymax; ymaxb = ymin;
+      zminb = zmax; zmaxb = zmin;
+
+      for (j = 1; j <= 3; j++)
+	{
+	  const Point3d & p = adfront->GetPoint (el.PNum(j));
+	  if (p.X() < xminb) xminb = p.X();
+	  if (p.X() > xmaxb) xmaxb = p.X();
+	  if (p.Y() < yminb) yminb = p.Y();
+	  if (p.Y() > ymaxb) ymaxb = p.Y();
+	  if (p.Z() < zminb) zminb = p.Z();
+	  if (p.Z() > zmaxb) zmaxb = p.Z();
+	}
+
+	
+
+      double filldist = 0.2; // globflags.GetNumFlag ("filldist", 0.4);
+      xminb -= filldist * gh;
+      xmaxb += filldist * gh;
+      yminb -= filldist * gh;
+      ymaxb += filldist * gh;
+      zminb -= filldist * gh;
+      zmaxb += filldist * gh;
+
+      min1 = int ((xminb - xmin) / gh) + 1;
+      max1 = int ((xmaxb - xmin) / gh) + 1;
+      min2 = int ((yminb - ymin) / gh) + 1;
+      max2 = int ((ymaxb - ymin) / gh) + 1;
+      min3 = int ((zminb - zmin) / gh) + 1;
+      max3 = int ((zmaxb - zmin) / gh) + 1;
+
+
+      for (i1 = min1; i1 <= max1; i1++)
+	for (i2 = min2; i2 <= max2; i2++)
+	  for (i3 = min3; i3 <= max3; i3++)
+	    inner.Elem(i3 + (i2-1) * n3 + (i1-1) * n2 * n3) = BLOCKBOUND;      
+    }
+
+  
+
+
+  while (1)
+    {
+      int undefi = 0;
+      Point3d undefp;
+
+      for (i1 = 1; i1 <= n1 && !undefi; i1++)
+	for (i2 = 1; i2 <= n2 && !undefi; i2++)
+	  for (i3 = 1; i3 <= n3 && !undefi; i3++)
+	    {
+	      i = i3 + (i2-1) * n3 + (i1-1) * n2 * n3;
+	      if (inner.Elem(i) == BLOCKUNDEF)
+		{
+		  undefi = i;
+		  undefp.X() = xmin + (i1-0.5) * gh;
+		  undefp.Y() = ymin + (i2-0.5) * gh;
+		  undefp.Z() = zmin + (i3-0.5) * gh;
+		}
+	    }
+	      
+      if (!undefi)
+	break;
+
+      //      PrintMessage (5, "Test point: ", undefp);
+      
+      if (adfront -> Inside (undefp))
+	{
+	  //	  (*mycout) << "inner" << endl;
+	  inner.Elem(undefi) = BLOCKINNER;
+	}
+      else
+	{
+	  //	  (*mycout) << "outer" << endl;
+	  inner.Elem(undefi) = BLOCKOUTER;
+	}
+
+      do
+	{
+	  changed = 0;
+	  for (i1 = 1; i1 <= n1; i1++)
+	    for (i2 = 1; i2 <= n2; i2++)
+	      for (i3 = 1; i3 <= n3; i3++)
+		{
+		  i = i3 + (i2-1) * n3 + (i1-1) * n2 * n3;
+
+		  for (int k = 1; k <= 3; k++)
+		    {
+		      switch (k)
+			{
+			case 1: j = i + n2 * n3; break;
+			case 2: j = i + n3; break;
+			case 3: j = i + 1; break;
+			}
+		  
+		      if (j > n1 * n2 * n3) continue;
+
+		      if (inner.Elem(i) == BLOCKOUTER && inner.Elem(j) == BLOCKUNDEF)
+			{
+			  changed = 1;
+			  inner.Elem(j) = BLOCKOUTER;
+			}
+		      if (inner.Elem(j) == BLOCKOUTER && inner.Elem(i) == BLOCKUNDEF)
+			{
+			  changed = 1;
+			  inner.Elem(i) = BLOCKOUTER;
+			}
+		      if (inner.Elem(i) == BLOCKINNER && inner.Elem(j) == BLOCKUNDEF)
+			{
+			  changed = 1;
+			  inner.Elem(j) = BLOCKINNER;
+			}
+		      if (inner.Elem(j) == BLOCKINNER && inner.Elem(i) == BLOCKUNDEF)
+			{
+			  changed = 1;
+			  inner.Elem(i) = BLOCKINNER;
+			}
+		    }
+		}
+	}
+      while (changed); 
+
+    }
+
+
+
+  filled = 0;
+  for (i = 1; i <= n; i++)
+    if (inner.Elem(i) == BLOCKINNER)
+      {
+	filled++;
+      }
+  PrintMessage (5, "Filled blocks: ", filled);
+
+  for (i = 1; i <= n; i++)
+    {
+      pointnr.Elem(i) = 0;
+      frontpointnr.Elem(i) = 0;
+    }
+  
+  for (i1 = 1; i1 <= n1-1; i1++)
+    for (i2 = 1; i2 <= n2-1; i2++)
+      for (i3 = 1; i3 <= n3-1; i3++)
+	{
+	  i = i3 + (i2-1) * n3 + (i1-1) * n2 * n3;
+	  if (inner.Elem(i) == BLOCKINNER)
+	    {
+	      for (j1 = i1; j1 <= i1+1; j1++)
+		for (j2 = i2; j2 <= i2+1; j2++)
+		  for (j3 = i3; j3 <= i3+1; j3++)
+		    {
+		      j = j3 + (j2-1) * n3 + (j1-1) * n2 * n3;
+		      if (pointnr.Get(j) == 0)
+			{
+			  Point3d hp(xmin + (j1-1) * gh, 
+				     ymin + (j2-1) * gh, 
+				     zmin + (j3-1) * gh);
+			  pointnr.Elem(j) = mesh.AddPoint (hp);
+			  frontpointnr.Elem(j) =
+			    AddPoint (hp, pointnr.Elem(j));
+
+			}
+		    }
+	    }
+	}
+
+
+  for (i1 = 2; i1 <= n1-1; i1++)
+    for (i2 = 2; i2 <= n2-1; i2++)
+      for (i3 = 2; i3 <= n3-1; i3++)
+	{
+	  i = i3 + (i2-1) * n3 + (i1-1) * n2 * n3;
+	  if (inner.Elem(i) == BLOCKINNER)
+	    {
+	      int pn[9];
+	      pn[1] = pointnr.Get(i);
+	      pn[2] = pointnr.Get(i+1);
+	      pn[3] = pointnr.Get(i+n3);
+	      pn[4] = pointnr.Get(i+n3+1);
+	      pn[5] = pointnr.Get(i+n2*n3);
+	      pn[6] = pointnr.Get(i+n2*n3+1);
+	      pn[7] = pointnr.Get(i+n2*n3+n3);
+	      pn[8] = pointnr.Get(i+n2*n3+n3+1);
+	      static int elind[][4] =
+	      {
+		{ 1, 8, 2, 4 },
+		{ 1, 8, 4, 3 },
+		{ 1, 8, 3, 7 },
+		{ 1, 8, 7, 5 },
+		{ 1, 8, 5, 6 },
+		{ 1, 8, 6, 2 }
+	      };
+	      for (j = 1; j <= 6; j++)
+		{
+		  Element el(4);
+		  for (int k = 1; k <= 4;  k++)
+		    el.PNum(k) = pn[elind[j-1][k-1]];
+
+		  mesh.AddVolumeElement (el);
+		}
+	    }
+	}
+
+
+
+  for (i1 = 2; i1 <= n1-1; i1++)
+    for (i2 = 2; i2 <= n2-1; i2++)
+      for (i3 = 2; i3 <= n3-1; i3++)
+	{
+	  i = i3 + (i2-1) * n3 + (i1-1) * n2 * n3;
+	  if (inner.Elem(i) == BLOCKINNER)
+	    {    
+	      int pi1(0), pi2(0), pi3(0), pi4(0);
+
+	      int pn1 = frontpointnr.Get(i);
+	      int pn2 = frontpointnr.Get(i+1);
+	      int pn3 = frontpointnr.Get(i+n3);
+	      int pn4 = frontpointnr.Get(i+n3+1);
+	      int pn5 = frontpointnr.Get(i+n2*n3);
+	      int pn6 = frontpointnr.Get(i+n2*n3+1);
+	      int pn7 = frontpointnr.Get(i+n2*n3+n3);
+	      int pn8 = frontpointnr.Get(i+n2*n3+n3+1);
+
+	      for (int k = 1; k <= 6; k++)
+		{
+		  switch (k)
+		    {
+		    case 1: // j3 = i3+1
+		      j = i + 1;
+		      pi1 = pn2;
+		      pi2 = pn6;
+		      pi3 = pn4;
+		      pi4 = pn8;
+		      break;
+		    case 2: // j3 = i3-1
+		      j = i - 1;
+		      pi1 = pn1;
+		      pi2 = pn3;
+		      pi3 = pn5;
+		      pi4 = pn7;
+		      break;
+		    case 3: // j2 = i2+1
+		      j = i + n3;
+		      pi1 = pn3;
+		      pi2 = pn4;
+		      pi3 = pn7;
+		      pi4 = pn8;
+		      break;
+		    case 4: // j2 = i2-1
+		      j = i - n3;
+		      pi1 = pn1;
+		      pi2 = pn5;
+		      pi3 = pn2;
+		      pi4 = pn6;
+		      break;
+		    case 5: // j1 = i1+1
+		      j = i + n3*n2;
+		      pi1 = pn5;
+		      pi2 = pn7;
+		      pi3 = pn6;
+		      pi4 = pn8;
+		      break;
+		    case 6: // j1 = i1-1
+		      j = i - n3*n2;
+		      pi1 = pn1;
+		      pi2 = pn2;
+		      pi3 = pn3;
+		      pi4 = pn4;
+		      break;
+		    }
+
+		  if (inner.Get(j) == BLOCKBOUND)
+		    {
+		      MiniElement2d face;
+		      face.PNum(1) = pi4;
+		      face.PNum(2) = pi1;
+		      face.PNum(3) = pi3;
+		      AddBoundaryElement (face);
+
+		      face.PNum(1) = pi1;
+		      face.PNum(2) = pi4;
+		      face.PNum(3) = pi2;
+		      AddBoundaryElement (face);
+
+		    }
+		}
+	    }
+	}
+}
+
+
+/*
+static const AdFront3 * locadfront;
+static int TestInner (const Point3d & p)
+{
+  return locadfront->Inside (p);
+}
+static int TestSameSide (const Point3d & p1, const Point3d & p2)
+{
+  return locadfront->SameSide (p1, p2);
+}
+*/
+
+
+
+void Meshing3 :: BlockFillLocalH (Mesh & mesh, 
+				  const MeshingParameters & mp)
+{
+  double filldist = mp.filldist;
+
+  (*testout) << "blockfill local h" << endl;
+  (*testout) << "rel filldist = " << filldist << endl;
+  PrintMessage (3, "blockfill local h");
+
+
+  Array<Point<3> > npoints;
+  
+  adfront -> CreateTrees();
+
+  Box<3> bbox ( Box<3>::EMPTY_BOX );
+  double maxh = 0;
+
+  for (int i = 1; i <= adfront->GetNF(); i++)
+    {
+      const MiniElement2d & el = adfront->GetFace(i);
+      for (int j = 1; j <= 3; j++)
+	{
+	  const Point3d & p1 = adfront->GetPoint (el.PNumMod(j));
+	  const Point3d & p2 = adfront->GetPoint (el.PNumMod(j+1));
+
+	  double hi = Dist (p1, p2);
+	  if (hi > maxh) maxh = hi;
+
+	  bbox.Add (p1);
+	}
+    }
+
+
+  Point3d mpmin = bbox.PMin();
+  Point3d mpmax = bbox.PMax();
+  Point3d mpc = Center (mpmin, mpmax);
+  double d = max3(mpmax.X()-mpmin.X(), 
+		  mpmax.Y()-mpmin.Y(), 
+		  mpmax.Z()-mpmin.Z()) / 2;
+  mpmin = mpc - Vec3d (d, d, d);
+  mpmax = mpc + Vec3d (d, d, d);
+  Box3d meshbox (mpmin, mpmax);
+
+  LocalH loch2 (mpmin, mpmax, 1);
+
+  if (mp.maxh < maxh) maxh = mp.maxh;
+
+  bool changed;
+  do 
+    {
+      mesh.LocalHFunction().ClearFlags();
+
+      for (int i = 1; i <= adfront->GetNF(); i++)
+	{
+	  const MiniElement2d & el = adfront->GetFace(i);
+	  
+	  Box<3> bbox (adfront->GetPoint (el[0]));
+	  bbox.Add (adfront->GetPoint (el[1]));
+	  bbox.Add (adfront->GetPoint (el[2]));
+
+
+	  double filld = filldist * bbox.Diam();
+	  bbox.Increase (filld);
+      
+      	  mesh.LocalHFunction().CutBoundary (bbox); // .PMin(), bbox.PMax());
+	}
+
+      //      locadfront = adfront;
+      mesh.LocalHFunction().FindInnerBoxes (adfront, NULL);
+
+      npoints.SetSize(0);
+      mesh.LocalHFunction().GetInnerPoints (npoints);
+
+      changed = false;
+      for (int i = 1; i <= npoints.Size(); i++)
+	{
+	  if (mesh.LocalHFunction().GetH(npoints.Get(i)) > 1.5 * maxh)
+	    {
+	      mesh.LocalHFunction().SetH (npoints.Get(i), maxh);
+	      changed = true;
+	    }
+	}
+    }
+  while (changed);
+
+  if (debugparam.slowchecks)
+    (*testout) << "Blockfill with points: " << endl;
+  for (int i = 1; i <= npoints.Size(); i++)
+    {
+      if (meshbox.IsIn (npoints.Get(i)))
+	{
+	  int gpnum = mesh.AddPoint (npoints.Get(i));
+	  adfront->AddPoint (npoints.Get(i), gpnum);
+
+	  if (debugparam.slowchecks)
+	    {
+	      (*testout) << npoints.Get(i) << endl;
+	      if (!adfront->Inside(npoints.Get(i)))
+		{
+		  cout << "add outside point" << endl;
+		  (*testout) << "outside" << endl;
+		}
+	    }
+
+	}
+    }
+
+  
+
+  // find outer points
+  
+  loch2.ClearFlags();
+
+  for (int i = 1; i <= adfront->GetNF(); i++)
+    {
+      const MiniElement2d & el = adfront->GetFace(i);
+      Point3d pmin = adfront->GetPoint (el.PNum(1));
+      Point3d pmax = pmin;
+      
+      for (int j = 2; j <= 3; j++)
+	{
+	  const Point3d & p = adfront->GetPoint (el.PNum(j));
+	  pmin.SetToMin (p);
+	  pmax.SetToMax (p);
+	}
+      
+      loch2.SetH (Center (pmin, pmax), Dist (pmin, pmax));
+    }
+
+  for (int i = 1; i <= adfront->GetNF(); i++)
+    {
+      const MiniElement2d & el = adfront->GetFace(i);
+      Point3d pmin = adfront->GetPoint (el.PNum(1));
+      Point3d pmax = pmin;
+      
+      for (int j = 2; j <= 3; j++)
+	{
+	  const Point3d & p = adfront->GetPoint (el.PNum(j));
+	  pmin.SetToMin (p);
+	  pmax.SetToMax (p);
+	}
+      
+      double filld = filldist * Dist (pmin, pmax);
+      pmin = pmin - Vec3d (filld, filld, filld);
+      pmax = pmax + Vec3d (filld, filld, filld);
+      // loch2.CutBoundary (pmin, pmax);
+      loch2.CutBoundary (Box<3> (pmin, pmax)); // pmin, pmax);
+    }
+
+  // locadfront = adfront;
+  loch2.FindInnerBoxes (adfront, NULL);
+
+  npoints.SetSize(0);
+  loch2.GetOuterPoints (npoints);
+  
+  for (int i = 1; i <= npoints.Size(); i++)
+    {
+      if (meshbox.IsIn (npoints.Get(i)))
+	{
+	  int gpnum = mesh.AddPoint (npoints.Get(i));
+	  adfront->AddPoint (npoints.Get(i), gpnum);
+	}
+    }  
+}
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/meshing3.hpp b/contrib/Netgen/libsrc/meshing/meshing3.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..84e9ca6b7168fdc3dfa74c1eaa085cfa49a027ea
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing3.hpp
@@ -0,0 +1,130 @@
+#ifndef FILE_MESHING3
+#define FILE_MESHING3
+
+
+
+
+enum MESHING3_RESULT
+{
+  MESHING3_OK = 0,
+  MESHING3_GIVEUP = 1,
+  MESHING3_NEGVOL = 2,
+  MESHING3_OUTERSTEPSEXCEEDED = 3,
+  MESHING3_TERMINATE = 4,
+  MESHING3_BADSURFACEMESH = 5
+};
+
+
+/// 3d volume mesh generation
+class Meshing3
+{
+  /// current state of front
+  AdFront3 * adfront;
+  /// 3d generation rules
+  Array<vnetrule*> rules;
+  /// counts how often a rule is used
+  Array<int> ruleused, canuse, foundmap;
+  /// describes, why a rule is not applied
+  Array<char*> problems;
+  /// tolerance criterion
+  double tolfak;
+public:
+  /// 
+  Meshing3 (const string & rulefilename); 
+  /// 
+  Meshing3 (const char ** rulep);
+  ///
+  virtual ~Meshing3 ();
+  
+  ///
+  void LoadRules (const char * filename, const char ** prules);
+  ///
+  MESHING3_RESULT GenerateMesh (Mesh & mesh, const MeshingParameters & mp);
+  
+  ///
+  int ApplyRules (Array<Point3d> & lpoints, Array<int> & allowpoint,
+		  Array<MiniElement2d> & lfaces, INDEX lfacesplit,
+		  INDEX_2_HASHTABLE<int> & connectedpairs,
+		  Array<Element> & elements,
+		  Array<INDEX> & delfaces, int tolerance, 
+		  double sloppy, int rotind1,
+		  float & retminerr);
+  
+  ///
+  PointIndex AddPoint (const Point3d & p, PointIndex globind);
+  ///
+  void AddBoundaryElement (const Element2d & elem);
+  ///
+  void AddBoundaryElement (const MiniElement2d & elem);
+  ///
+  int AddConnectedPair (const INDEX_2 & pair);
+  
+  ///
+  void BlockFill (Mesh & mesh, double gh);
+  ///
+  void BlockFillLocalH (Mesh & mesh, const MeshingParameters & mp);
+
+  /// uses points of adfront, and puts new elements into mesh
+  void Delaunay (Mesh & mesh, int domainnr, const MeshingParameters & mp);
+  ///
+  friend class PlotVolMesh;
+  ///
+  friend void TestRules ();
+};
+
+
+
+
+/// status of mesh generation
+class MeshingStat3d
+{
+public:
+  ///
+  MeshingStat3d ();
+  ///
+  int cntsucc;
+  ///
+  int cnttrials;
+  ///
+  int cntelem;
+  ///
+  int nff;
+  ///
+  int qualclass;
+  ///
+  double vol0;
+  ///
+  double vol;
+  ///
+  double h;
+  ///
+  int problemindex;
+};
+
+
+
+
+
+/*
+template <typename POINTArray, typename FACEArray>
+extern int FindInnerPoint (POINTArray & grouppoints,
+			   FACEArray & groupfaces,
+			   Point3d & p);
+
+*/
+
+
+
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/meshtool.cpp b/contrib/Netgen/libsrc/meshing/meshtool.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4a349ac9c47a87e1eef6716b5eafc0959723482a
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshtool.cpp
@@ -0,0 +1,1015 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+#include <csg.hpp>
+#include <geometry2d.hpp>
+
+namespace netgen
+{
+
+  int CheckSurfaceMesh (const Mesh & mesh)
+  {
+    PrintMessage (3, "Check Surface mesh");
+
+    int nf = mesh.GetNSE();
+    INDEX_2_HASHTABLE<int> edges(nf+2);
+    int i, j;
+    INDEX_2 i2;
+    int cnt1 = 0, cnt2 = 0;
+
+    for (i = 1; i <= nf; i++)
+      for (j = 1; j <= 3; j++)
+	{
+	  i2.I1() = mesh.SurfaceElement(i).PNumMod(j);
+	  i2.I2() = mesh.SurfaceElement(i).PNumMod(j+1);
+	  if (edges.Used(i2))
+	    {
+	      int hi;
+	      hi = edges.Get(i2);
+	      if (hi != 1) 
+		PrintSysError ("CheckSurfaceMesh, hi = ", hi);
+	      edges.Set(i2, 2);
+	      cnt2++;
+	    }
+	  else
+	    {
+	      Swap (i2.I1(), i2.I2());
+	      edges.Set(i2, 1);
+	      cnt1++;
+	    }
+	}
+  
+
+    if (cnt1 != cnt2)
+      {
+	PrintUserError ("Surface mesh not consistent");
+	//      MyBeep(2);
+	//      (*mycout) << "cnt1 = " << cnt1 << " cnt2 = " << cnt2 << endl;
+	return 0;
+      }
+    return 1;
+  }
+
+
+
+  int CheckSurfaceMesh2 (const Mesh & mesh)
+  {
+    int i, j, k;
+    const Point<3> *tri1[3], *tri2[3];
+
+    for (i = 1; i <= mesh.GetNOpenElements(); i++)
+      {
+	PrintDot ();
+	for (j = 1; j < i; j++)
+	  {
+	    for (k = 1; k <= 3; k++)
+	      {
+		tri1[k-1] = &mesh.Point (mesh.OpenElement(i).PNum(k));
+		tri2[k-1] = &mesh.Point (mesh.OpenElement(j).PNum(k));
+	      }
+	    if (IntersectTriangleTriangle (&tri1[0], &tri2[0]))
+	      {
+		PrintSysError ("Surface elements are intersecting");
+		(*testout) << "Intersecting: " << endl;
+		for (k = 0; k <= 2; k++)
+		  (*testout) << *tri1[k] << "   ";
+		(*testout) << endl;
+		for (k = 0; k <= 2; k++)
+		  (*testout) << *tri2[k] << "   ";
+		(*testout) << endl;
+	      }
+
+	  }
+      }
+    return 0;
+  }
+
+
+
+
+
+  static double TriangleQualityInst (const Point3d & p1, const Point3d & p2,
+				     const Point3d & p3)
+  {
+    // quality 0 (worst) .. 1 (optimal)
+
+    Vec3d v1, v2, v3;
+    double s1, s2, s3;
+    double an1, an2, an3;
+
+    v1 = p2 - p1;
+    v2 = p3 - p1;
+    v3 = p3 - p2;
+
+    an1 = Angle (v1, v2);
+    v1 *= -1;
+    an2 = Angle (v1, v3);
+    an3 = Angle (v2, v3);
+
+    s1 = sin (an1/2);
+    s2 = sin (an2/2);
+    s3 = sin (an3/2);
+
+    return 8 * s1 * s2 * s3;
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  void MeshQuality2d (const Mesh & mesh)
+  {
+    int ncl = 20, cl;
+    Array<INDEX> incl(ncl);
+    INDEX i;
+    SurfaceElementIndex sei;
+    double qual;
+
+    incl = 0;
+
+    for (sei = 0; sei < mesh.GetNSE(); sei++)
+      {
+	qual = TriangleQualityInst (mesh[mesh[sei][0]],
+				    mesh[mesh[sei][1]],
+				    mesh[mesh[sei][2]]);
+
+	cl = int ( (ncl-1e-3) * qual ) + 1;
+	incl.Elem(cl)++;
+      }
+
+    (*testout) << endl << endl;
+
+    (*testout) << "Points:           " << mesh.GetNP() << endl;
+    (*testout) << "Surface Elements: " << mesh.GetNSE() << endl;
+
+    (*testout) << endl;
+    (*testout) << "Elements in qualityclasses:" << endl;
+    // (*testout).precision(2);
+    (*testout) << setprecision(2);
+    for (i = 1; i <= ncl; i++)
+      {
+	(*testout) << setw(4) << double (i-1)/ncl << " - "
+		   << setw(4) << double (i) / ncl << ": "
+		   << incl.Get(i) << endl;
+      }
+  }
+
+
+  static double TetElementQuality (const Point3d & p1, const Point3d & p2,
+				   const Point3d & p3, const Point3d & p4)
+  {
+    double vol, l, l4, l5, l6;
+
+
+    Vec3d v1 = p2 - p1;
+    Vec3d v2 = p3 - p1;
+    Vec3d v3 = p4 - p1;
+
+    vol = fabs ((Cross (v1, v2) * v3)) / 6;
+    l4 = Dist (p2, p3);
+    l5 = Dist (p2, p4);
+    l6 = Dist (p3, p4);
+
+    l = v1.Length() + v2.Length() + v3.Length() + l4 + l5 + l6;
+
+    if (vol <= 1e-8 * l * l * l) return 1e-10;
+
+    return vol/(l*l*l) * 1832.82;    // 6^4 * sqrt(2)
+  }
+
+
+
+
+  // static double teterrpow = 2;
+
+  double CalcTetBadness (const Point3d & p1, const Point3d & p2,
+			 const Point3d & p3, const Point3d & p4, double h,
+			 const MeshingParameters & mp)
+  {
+    double vol, l, ll, lll, ll1, ll2, ll3, ll4, ll5, ll6;
+    double err;
+
+    Vec3d v1 (p1, p2);
+    Vec3d v2 (p1, p3);
+    Vec3d v3 (p1, p4);
+
+    vol = Determinant (v1, v2, v3)  * (-0.166666666666666);
+
+    ll1 = v1.Length2();
+    ll2 = v2.Length2();
+    ll3 = v3.Length2();
+    ll4 = Dist2 (p2, p3);
+    ll5 = Dist2 (p2, p4);
+    ll6 = Dist2 (p3, p4);
+
+    ll = ll1 + ll2 + ll3 + ll4 + ll5 + ll6;
+    l = sqrt (ll);
+    lll = l * ll;
+
+    if (vol <= 1e-24 * lll)
+      return 1e24;
+
+    err = 0.0080187537 * lll / vol;    // sqrt(216) / (6^4 * sqrt(2))
+
+    if (h > 0)
+      err += ll / (h * h) + 
+	h * h * ( 1 / ll1 + 1 / ll2 + 1 / ll3 + 
+		  1 / ll4 + 1 / ll5 + 1 / ll6 ) - 12;
+    
+    double teterrpow = mp.opterrpow;
+    if(teterrpow < 1) teterrpow = 1;
+    
+    if (teterrpow == 1) return err;
+    if (teterrpow == 2) return err*err;
+    return pow (err, teterrpow);
+  }
+
+
+  double CalcTetBadnessGrad (const Point3d & p1, const Point3d & p2,
+			     const Point3d & p3, const Point3d & p4, double h,
+			     int pi, Vec<3> & grad,
+			     const MeshingParameters & mp)
+  {
+    double vol, l, ll, lll;
+    double err;
+
+    const Point3d *pp1, *pp2, *pp3, *pp4;
+
+    pp1 = &p1;
+    pp2 = &p2;
+    pp3 = &p3;
+    pp4 = &p4;
+  
+    switch (pi)
+      {
+      case 2:
+	{
+	  swap (pp1, pp2);
+	  swap (pp3, pp4);
+	  break;
+	}
+      case 3:
+	{
+	  swap (pp1, pp3);
+	  swap (pp2, pp4);
+	  break;
+	}
+      case 4:
+	{
+	  swap (pp1, pp4);
+	  swap (pp3, pp2);
+	  break;
+	}
+      }
+  
+
+    Vec3d v1 (*pp1, *pp2);
+    Vec3d v2 (*pp1, *pp3);
+    Vec3d v3 (*pp1, *pp4);
+
+    Vec3d v4 (*pp2, *pp3);
+    Vec3d v5 (*pp2, *pp4);
+    Vec3d v6 (*pp3, *pp4);
+
+    vol = Determinant (v1, v2, v3) * (-0.166666666666666);
+
+    Vec3d gradvol;
+    Cross (v5, v4, gradvol);
+    gradvol *= (-1.0/6.0);
+
+
+    double ll1 = v1.Length2();
+    double ll2 = v2.Length2();
+    double ll3 = v3.Length2();
+    double ll4 = v4.Length2();
+    double ll5 = v5.Length2();
+    double ll6 = v6.Length2();
+
+    ll = ll1 + ll2 + ll3 + ll4 + ll5 + ll6;
+    l = sqrt (ll);
+    lll = l * ll;
+
+    if (vol <= 1e-24 * lll)
+      { 
+	grad = Vec3d (0, 0, 0);
+	return 1e24;
+      }
+
+
+
+    Vec3d gradll1 (*pp2, *pp1);
+    Vec3d gradll2 (*pp3, *pp1);
+    Vec3d gradll3 (*pp4, *pp1);
+    gradll1 *= 2;
+    gradll2 *= 2;
+    gradll3 *= 2;
+
+    Vec3d gradll (gradll1);
+    gradll += gradll2;
+    gradll += gradll3;
+
+    /*
+    Vec3d gradll;
+    gradll = v1+v2+v3;
+    gradll *= -2;
+    */
+
+    err = 0.0080187537 * lll / vol; 
+
+
+    gradll *= (0.0080187537 * 1.5 * l / vol);
+    Vec3d graderr(gradll);
+    gradvol *= ( -0.0080187537 * lll / (vol * vol) );
+    graderr += gradvol;
+  
+    if (h > 0)
+      {
+	/*
+	Vec3d gradll1 (*pp2, *pp1);
+	Vec3d gradll2 (*pp3, *pp1);
+	Vec3d gradll3 (*pp4, *pp1);
+	gradll1 *= 2;
+	gradll2 *= 2;
+	gradll3 *= 2;
+	*/
+	err += ll / (h*h) + 
+	  h*h * ( 1 / ll1 + 1 / ll2 + 1 / ll3 + 
+		  1 / ll4 + 1 / ll5 + 1 / ll6 ) - 12;
+
+	graderr += (1/(h*h) - h*h/(ll1*ll1)) * gradll1;
+	graderr += (1/(h*h) - h*h/(ll2*ll2)) * gradll2;
+	graderr += (1/(h*h) - h*h/(ll3*ll3)) * gradll3;
+      }
+
+    double errpow;
+
+    double teterrpow = mp.opterrpow;
+    if(teterrpow < 1) teterrpow = 1;
+
+    if (teterrpow == 1)
+    {
+       errpow = err;
+       grad = graderr;
+    }
+
+    if (teterrpow == 2)
+      {
+        errpow = err*err;   
+        grad = (2 * err) * graderr;
+      }
+
+    if(teterrpow > 2)
+      {
+        errpow = pow (err, teterrpow);
+        grad = (teterrpow * errpow / err) * graderr;
+      }
+    return errpow;
+  }
+  
+
+
+
+
+  /*
+
+  double CalcTetBadness (const Point3d & p1, const Point3d & p2,
+  const Point3d & p3, const Point3d & p4, double h)
+  {
+  double vol, l;
+  double err;
+
+
+  Vec3d v1 (p1, p2);
+  Vec3d v2 (p1, p3);
+  Vec3d v3 (p1, p4);
+
+  vol = -Determinant (v1, v2, v3) / 6;
+
+  double l1 = v1.Length();
+  double l2 = v2.Length();
+  double l3 = v3.Length();
+  double l4 = Dist (p2, p3);
+  double l5 = Dist (p2, p4);
+  double l6 = Dist (p3, p4);
+
+  l = l1 + l2 + l3 + l4 + l5 + l6;
+
+  // just for timing
+  // l += 1e-40 * CalcTetBadnessNew (p1, p2, p3, p4, h);
+
+  if (vol <= 1e-24 * l * l * l)
+  { 
+  return 1e24;
+  }
+
+  err = (l*l*l) / (1832.82 * vol);    // 6^4 * sqrt(2)
+  
+  if (h > 0)
+  err += l / h + 
+  h * (1 / l1 + 1/l2 + 1/l3 + 1/l4 + 1/l5 + 1/l6) - 12;
+
+  return pow (err, teterrpow);
+  }
+
+
+  
+  double CalcTetBadnessGrad (const Point3d & p1, const Point3d & p2,
+  const Point3d & p3, const Point3d & p4, double h,
+  int pi, Vec3d & grad)
+  {
+  double vol, l;
+  double err;
+
+  const Point3d *pp1, *pp2, *pp3, *pp4;
+
+  pp1 = &p1;
+  pp2 = &p2;
+  pp3 = &p3;
+  pp4 = &p4;
+  
+  switch (pi)
+  {
+  case 2:
+  {
+  swap (pp1, pp2);
+  swap (pp3, pp4);
+  break;
+  }
+  case 3:
+  {
+  swap (pp1, pp3);
+  swap (pp2, pp4);
+  break;
+  }
+  case 4:
+  {
+  swap (pp1, pp4);
+  swap (pp3, pp2);
+  break;
+  }
+  }
+  
+
+  Vec3d v1 (*pp1, *pp2);
+  Vec3d v2 (*pp1, *pp3);
+  Vec3d v3 (*pp1, *pp4);
+
+  Vec3d v4 (*pp2, *pp3);
+  Vec3d v5 (*pp2, *pp4);
+  Vec3d v6 (*pp3, *pp4);
+
+
+  //   Vec3d n;
+  //   Cross (v1, v2, n);
+  //   vol = - (n * v3) / 6;
+
+
+  vol = -Determinant (v1, v2, v3) / 6;  
+
+  Vec3d gradvol;
+  Cross (v5, v4, gradvol);
+  gradvol *= (-1.0/6.0);
+
+
+  double l1 = v1.Length();
+  double l2 = v2.Length();
+  double l3 = v3.Length();
+  double l4 = v4.Length();
+  double l5 = v5.Length();
+  double l6 = v6.Length();
+
+  l = l1 + l2 + l3 +l4 + l5 + l6;
+
+  Vec3d gradl1 (*pp2, *pp1);
+  Vec3d gradl2 (*pp3, *pp1);
+  Vec3d gradl3 (*pp4, *pp1);
+  gradl1 /= l1;
+  gradl2 /= l2;
+  gradl3 /= l3;
+
+  Vec3d gradl (gradl1);
+  gradl += gradl2;
+  gradl += gradl3;
+
+
+  if (vol <= 1e-24 * l * l * l)
+  { 
+  grad = Vec3d (0, 0, 0);
+  return 1e24;
+  }
+
+
+  double c1 = 1.0 / 1832.82;      // 6^4 * sqrt(2)
+  err = c1 * (l*l*l) / vol; 
+
+
+  gradl *= (c1 * 3 * l * l / vol);
+  Vec3d graderr(gradl);
+  gradvol *= ( -c1 * l * l * l / (vol * vol) );
+  graderr+= gradvol;
+  
+  if (h > 0)
+  {
+  err += l / h + 
+  h * ( 1 / l1 + 1 / l2 + 1 / l3 + 
+  1 / l4 + 1 / l5 + 1 / l6 ) - 12;
+
+  graderr += (1/h - h/(l1*l1)) * gradl1;
+  graderr += (1/h - h/(l2*l2)) * gradl2;
+  graderr += (1/h - h/(l3*l3)) * gradl3;
+  cout << "?";
+  }
+
+  double errpow = pow (err, teterrpow);
+  grad = (teterrpow * errpow / err) * graderr;
+  
+  return errpow;
+  }
+  
+  */
+
+
+
+
+  
+  /*
+    double CalcVolume (const Array<Point3d> & points,
+    const Element & el)
+    {
+    Vec3d v1 = points.Get(el.PNum(2)) - 
+    points.Get(el.PNum(1));
+    Vec3d v2 = points.Get(el.PNum(3)) - 
+    points.Get(el.PNum(1));
+    Vec3d v3 = points.Get(el.PNum(4)) - 
+    points.Get(el.PNum(1)); 
+         
+    return -(Cross (v1, v2) * v3) / 6;	 
+    }  
+  */
+
+  double CalcVolume (const Array<Point3d> & points, 
+		     const Array<Element> & elements)
+  {
+    double vol;
+    Vec3d v1, v2, v3;
+  
+    vol = 0;
+    for (int i = 0; i < elements.Size(); i++)
+      {
+	v1 = points.Get(elements[i][1]) - points.Get(elements[i][0]);
+	v2 = points.Get(elements[i][2]) - points.Get(elements[i][0]);
+	v3 = points.Get(elements[i][3]) - points.Get(elements[i][0]);
+	vol -= (Cross (v1, v2) * v3) / 6;	 
+      }
+    return vol;
+  }
+
+  
+  
+
+  void MeshQuality3d (const Mesh & mesh, Array<int> * inclass)
+  { 
+    int ncl = 20;
+    signed int cl;
+    Array<INDEX> incl(ncl);
+    INDEX i;
+    double qual;
+    double sum = 0;
+    int nontet  = 0;
+
+    for (i = 1; i <= incl.Size(); i++)
+      incl.Elem(i) = 0;
+
+    for (ElementIndex ei = 0; ei < mesh.GetNE(); ei++)
+      {
+	if (mesh[ei].GetType() != TET)
+	  {
+	    nontet++;
+	    continue;
+	  }
+
+	qual = TetElementQuality (mesh.Point(mesh[ei][0]),
+				  mesh.Point(mesh[ei][1]),
+				  mesh.Point(mesh[ei][2]),
+				  mesh.Point(mesh[ei][3]));
+
+	if (qual > 1) qual = 1;
+	cl = int (ncl * qual ) + 1;
+     
+	if (cl < 1) cl = 1; 
+	if (cl > ncl) cl = ncl;
+
+	incl.Elem(cl)++;
+	if (inclass) (*inclass)[ei] = cl;
+	sum += 1/qual;
+      }
+
+    (*testout) << endl << endl;
+    (*testout) << "Points:           " << mesh.GetNP() << endl;
+    (*testout) << "Volume Elements:  " << mesh.GetNE() << endl;
+    if (nontet)
+      (*testout) << nontet << " non tetrahedral elements" << endl;
+    (*testout) << endl;
+
+    (*testout) << "Volume elements in qualityclasses:" << endl;
+    (*testout) << setprecision(2);
+    for (i = 1; i <= ncl; i++)
+      {
+	(*testout) << setw(4) << double (i-1)/ncl << " - "
+		   << setw(4) << double (i) / ncl << ": "
+		   << incl.Get(i) << endl;
+      }
+    (*testout) << "total error: " << sum << endl;
+  }
+
+
+  void SaveEdges (const Mesh & mesh, const char * geomfile, double h, char * filename)
+  {
+    ofstream of (filename);
+    int i;
+    const Segment * seg;
+  
+    of << "edges" << endl;
+    of << geomfile << endl;
+    of << h << endl;
+
+    of << mesh.GetNP() << endl;
+    for (i = 1; i <= mesh.GetNP(); i++)
+      of << mesh.Point(i)(0) << " "
+	 << mesh.Point(i)(1) << " "
+	 << mesh.Point(i)(2) << "\n";
+    
+    of << 2 * mesh.GetNSeg() << endl;
+    for (i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	seg = &mesh.LineSegment(i);
+
+	of << (*seg)[1] << " " << (*seg)[0] << " " << seg->si << "\n";
+      }
+   
+  }
+
+
+  void SaveSurfaceMesh (const Mesh & mesh,
+			double h,
+			char * filename)
+
+  {
+    INDEX i;
+
+    ofstream outfile(filename);
+
+    outfile << "surfacemesh" << endl;
+    outfile << h << endl;
+
+    outfile << mesh.GetNP() << endl;
+    for (i = 1; i <= mesh.GetNP(); i++)
+      outfile << mesh.Point(i)(0) << " "
+	      << mesh.Point(i)(1) << " "
+	      << mesh.Point(i)(2) << endl;
+
+  
+
+    outfile << mesh.GetNSE() << endl;
+    for (i = 1; i <= mesh.GetNSE(); i++)
+      {
+	const Element2d & el = mesh.SurfaceElement(i);
+
+	if (mesh.GetFaceDescriptor(el.GetIndex()).DomainOut() == 0)
+	  outfile << mesh.SurfaceElement(i).PNum(1) << " "
+		  << mesh.SurfaceElement(i).PNum(2) << " "
+		  << mesh.SurfaceElement(i).PNum(3) << endl;
+	if (mesh.GetFaceDescriptor(el.GetIndex()).DomainIn() == 0)
+	  outfile << mesh.SurfaceElement(i).PNum(1) << " "
+		  << mesh.SurfaceElement(i).PNum(3) << " "
+		  << mesh.SurfaceElement(i).PNum(2) << endl;
+      }
+  }
+
+
+#ifdef OLD
+  void Save2DMesh (
+		   const Mesh & mesh2d,
+		   const Array<SplineSegment *> * splines,
+		   ostream & outfile)
+
+  {
+    int i, j;
+    outfile.precision (6);
+  
+    outfile << "areamesh2" << endl;
+
+
+    outfile << endl;
+    outfile << mesh2d.GetNSeg() << endl;
+    for (i = 1; i <= mesh2d.GetNSeg(); i++)
+      outfile << mesh2d.LineSegment(i).si << "        "
+	      << mesh2d.LineSegment(i)[0] << " "
+	      << mesh2d.LineSegment(i)[1] << "  " << endl;
+  
+
+    outfile << mesh2d.GetNSE() << endl;
+    for (i = 1; i <= mesh2d.GetNSE(); i++)
+      {
+	outfile << mesh2d.SurfaceElement(i).GetIndex() << "         ";
+	outfile << mesh2d.SurfaceElement(i).GetNP() << " ";
+	for (j = 1; j <= mesh2d.SurfaceElement(i).GetNP(); j++)
+	  outfile << mesh2d.SurfaceElement(i).PNum(j) << " ";
+	outfile << endl;
+      }
+
+    outfile << mesh2d.GetNP() << endl;
+    for (i = 1; i <= mesh2d.GetNP(); i++)
+      outfile << mesh2d.Point(i).X() << " "
+	      << mesh2d.Point(i).Y() << endl;
+
+    if (splines)
+      {
+	outfile << splines->Size() << endl;
+	for (i = 1; i <= splines->Size(); i++)
+	  splines->Get(i) -> PrintCoeff (outfile);
+      }
+    else
+      outfile << "0" << endl;
+  }
+#endif
+
+
+
+
+
+
+
+
+  void SaveVolumeMesh (const Mesh & mesh, 
+		       const CSGeometry & geometry,
+		       char * filename)
+  {
+    INDEX i;
+
+    ofstream outfile(filename);
+    outfile << "volumemesh" << endl;
+
+    outfile << mesh.GetNSE() << endl;
+    for (i = 1; i <= mesh.GetNSE(); i++)
+      {
+	if (mesh.SurfaceElement(i).GetIndex())
+	  outfile << mesh.GetFaceDescriptor(mesh.SurfaceElement(i).GetIndex ()).SurfNr()
+		  << "\t";
+	else
+	  outfile << "0" << "\t";
+	outfile << mesh.SurfaceElement(i)[0] << " "
+		<< mesh.SurfaceElement(i)[1] << " "
+		<< mesh.SurfaceElement(i)[2] << endl;
+      }
+    outfile << mesh.GetNE() << endl;
+    for (ElementIndex ei = 0; ei < mesh.GetNE(); ei++)
+      outfile << mesh[ei].GetIndex() << "\t"
+	      << mesh[ei][0] << " " << mesh[ei][1] << " "
+	      << mesh[ei][2] << " " << mesh[ei][3] << endl;
+
+    outfile << mesh.GetNP() << endl;
+    for (i = 1; i <= mesh.GetNP(); i++)
+      outfile << mesh.Point(i)(0) << " "
+	      << mesh.Point(i)(1) << " "
+	      << mesh.Point(i)(2) << endl;
+
+#ifdef SOLIDGEOM
+    outfile << geometry.GetNSurf() << endl;
+    for (i = 1; i <= geometry.GetNSurf(); i++)
+      geometry.GetSurface(i) -> Print (outfile);
+#endif
+  }
+
+
+
+
+  int CheckCode ()
+  {
+    return 1;
+
+    /*
+      char st[100];
+      ifstream ist("pw");
+
+      if (!ist.good()) return 0;
+      ist >> st;
+      if (strcmp (st, "JKULinz") == 0) return 1;
+      return 0;
+    */
+  }
+
+
+
+  /* ******************** CheckMesh ******************************* */
+
+  /// Checks, whether mesh contains a valid 3d mesh
+  int CheckMesh3D (const Mesh & mesh)
+  {
+    INDEX_3_HASHTABLE<int> faceused(mesh.GetNE()/3);
+    INDEX i;
+    int j, k, l;
+    INDEX_3 i3;
+    int ok = 1;
+    ElementIndex ei;
+
+    for (i = 1; i <= mesh.GetNSE(); i++)
+      {
+	const Element2d & el = mesh.SurfaceElement(i);
+      
+	if (mesh.GetFaceDescriptor(el.GetIndex()).DomainIn() == 0 ||
+	    mesh.GetFaceDescriptor(el.GetIndex()).DomainOut() == 0)
+	  {
+	    for (j = 1; j <= 3; j++)
+	      i3.I(j) = el.PNum(j);
+	  
+	    i3.Sort();
+	    faceused.Set (i3, 1);
+	  }
+      }
+  
+    for (ei = 0; ei < mesh.GetNE(); ei++)
+      {
+	const Element & el = mesh[ei];
+
+	for (j = 1; j <= 4; j++)
+	  {
+	    l = 0;
+	    for (k = 1; k <= 4; k++)
+	      {
+		if (j != k)
+		  {
+		    l++;
+		    i3.I(l) = el.PNum(k);
+		  }
+	      }
+
+	    i3.Sort();
+	    if (faceused.Used(i3))
+	      faceused.Set(i3, faceused.Get(i3)+1);
+	    else
+	      faceused.Set (i3, 1);
+	  }
+      }
+
+
+    for (i = 1; i <= mesh.GetNSE(); i++)
+      {
+	const Element2d & el = mesh.SurfaceElement(i);
+
+	for (j = 1; j <= 3; j++)
+	  i3.I(j) = el.PNum(j);
+      
+	i3.Sort();
+	k = faceused.Get (i3);
+	if (k != 2)
+	  {
+	    ok = 0;
+	    (*testout) << "face " << i << " with points " 
+		       << i3.I1() << "-" << i3.I2() << "-" << i3.I3() 
+		       << " has " << k << " elements" << endl;
+	  }
+      }
+  
+    for (ei = 0; ei < mesh.GetNE(); ei++)
+      {
+	const Element & el = mesh[ei];
+
+	for (j = 1; j <= 4; j++)
+	  {
+	    l = 0;
+	    for (k = 1; k <= 4; k++)
+	      {
+		if (j != k)
+		  {
+		    l++;
+		    i3.I(l) = el.PNum(k);
+		  }
+	      }
+
+	    i3.Sort();
+	    k = faceused.Get(i3);
+	    if (k != 2)
+	      {
+		ok = 0;
+		(*testout) << "element " << ei << " with face " 
+			   << i3.I1() << "-" << i3.I2() << "-"
+			   << i3.I3() 
+			   << " has " << k << " elements" << endl;
+	      }
+	  }
+      }
+
+
+
+
+
+    /*
+      for (i = 1; i <= faceused.GetNBags(); i++)
+      for (j = 1; j <= faceused.GetBagSize(i); j++)
+      {
+      faceused.GetData(i, j, i3, k);
+      if (k != 2)
+      {
+      (*testout) << "Face: " << i3.I1() << "-" 
+      << i3.I2() << "-" << i3.I3() << " has " 
+      << k << " Faces " << endl;
+      cerr << "Face Error" << endl;
+      ok = 0;
+      }
+      }
+    */
+
+
+    if (!ok)
+      {
+	(*testout) << "surfelements: " << endl;
+	for (i = 1; i <= mesh.GetNSE(); i++)
+	  {
+	    const Element2d & el = mesh.SurfaceElement(i);
+	    (*testout) << setw(5) << i << ":" 
+		       << setw(6) << el.GetIndex() 
+		       << setw(6) << el.PNum(1) 
+		       << setw(4) << el.PNum(2) 
+		       << setw(4) << el.PNum(3)  << endl;
+	  }
+	(*testout) << "volelements: " << endl;
+	for (ei = 0; ei < mesh.GetNE(); ei++)
+	  {
+	    const Element & el = mesh[ei];
+	    (*testout) << setw(5) << i << ":" 
+		       << setw(6) << el.GetIndex() 
+		       << setw(6) << el[0] << setw(4) << el[1]
+		       << setw(4) << el[2] << setw(4) << el[3] << endl;
+	  }
+      }
+
+
+    return ok;
+  }
+
+
+
+  void RemoveProblem (Mesh & mesh, int domainnr)
+  {
+    int i, j, k;
+  
+    mesh.FindOpenElements(domainnr);
+    int np = mesh.GetNP();
+
+    BitArrayChar<PointIndex::BASE> ppoints(np);
+  
+    // int ndom = mesh.GetNDomains();
+
+    PrintMessage (3, "Elements before Remove: ", mesh.GetNE());
+    // for (k = 1; k <= ndom; k++)
+    k = domainnr;
+      {
+	ppoints.Clear();
+      
+	for (i = 1; i <= mesh.GetNOpenElements(); i++)
+	  {
+	    const Element2d & sel = mesh.OpenElement(i);
+	    if (sel.GetIndex() == k)
+	      {
+		for (j = 1; j <= sel.GetNP(); j++)
+		  ppoints.Set (sel.PNum(j));
+	      }
+	  }
+
+	for (ElementIndex ei = 0; ei < mesh.GetNE(); ei++)
+	  {
+	    const Element & el = mesh[ei];
+	    if (el.GetIndex() == k)
+	      {
+		int todel = 0;
+		for (j = 0; j < el.GetNP(); j++)
+		  if (ppoints.Test (el[j]))
+		    todel = 1;
+	      
+		if (el.GetNP() != 4)
+		  todel = 0;
+	      
+		if (todel)
+		  {
+		    mesh[ei].Delete();
+		    // ei--;
+		  }
+	      }
+	  }
+      }
+  
+    mesh.Compress();
+    PrintMessage (3, "Elements after Remove: ", mesh.GetNE());
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/meshtool.hpp b/contrib/Netgen/libsrc/meshing/meshtool.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b6472ce08042ce8e7f9c80894aa47547c4213c74
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshtool.hpp
@@ -0,0 +1,81 @@
+#ifndef FILE_MESHTOOL
+#define FILE_MESHTOOL
+
+
+///
+extern void MeshQuality2d (const Mesh & mesh);
+
+///
+extern void MeshQuality3d (const Mesh & mesh,
+			   Array<int> * inclass = NULL);
+
+///
+extern void SaveEdges (const Mesh & mesh, 
+		       const char * geomfile, 
+		       double h, 
+		       char * filename);
+
+///
+extern void SaveSurfaceMesh (const Mesh & mesh,
+			     double h,
+			     char * filename);
+/*
+///
+extern void Save2DMesh (
+         const Mesh & mesh2d,
+	 const Array<class SplineSegment*> * splines,
+         ostream & outfile);
+*/
+
+class Surface;
+///
+extern void SaveVolumeMesh (
+         const Array<Point3d> & points,
+         const Array<Element> & elements,
+         const Array<Element> & volelements,
+         const Array<Surface*> & surfaces,
+         char * filename);
+
+///
+void SaveVolumeMesh (const Mesh & mesh, 
+		     const class CSGeometry & geometry,
+		     char * filename);
+
+///
+extern int CheckCode ();
+
+
+///
+extern double CalcTetBadness (const Point3d & p1, const Point3d & p2,
+			      const Point3d & p3, const Point3d & p4, 
+			      double h,
+			      const MeshingParameters & mp);
+///
+extern double CalcTetBadnessGrad (const Point3d & p1, const Point3d & p2,
+				  const Point3d & p3, const Point3d & p4, 
+				  double h, int pi,
+				  Vec<3> & grad,
+				  const MeshingParameters & mp);
+
+
+/** Calculates volume of an element.
+  The volume of the tetrahedron el is computed
+ */
+// extern double CalcVolume (const Array<Point3d> & points,
+//        const Element & el);  
+
+/** The total volume of all elements is computed.
+  This function calculates the volume of the mesh */
+extern double CalcVolume (const Array<Point3d> & points, 
+	const Array<Element> & elements);
+
+///
+extern int CheckSurfaceMesh (const Mesh & mesh);
+
+///
+extern int CheckSurfaceMesh2 (const Mesh & mesh);
+///
+extern int CheckMesh3D (const Mesh & mesh);
+///
+extern void RemoveProblem (Mesh & mesh, int domainnr);
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/meshtype.cpp b/contrib/Netgen/libsrc/meshing/meshtype.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8bdbe8cb38fa920cb013fdc90859054f1482c97f
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshtype.cpp
@@ -0,0 +1,2680 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"  
+
+namespace netgen
+{
+  int MultiPointGeomInfo :: 
+  AddPointGeomInfo (const PointGeomInfo & gi)
+  {
+    for (int k = 0; k < cnt; k++)
+      if (mgi[k].trignum == gi.trignum)
+	return 0;
+  
+    if (cnt < MULTIPOINTGEOMINFO_MAX)
+      {
+	mgi[cnt] = gi;
+	cnt++;
+	return 0;
+      }
+
+    throw NgException ("Please report error: MPGI Size too small\n");
+  }
+  
+
+
+#ifdef PARALLEL
+  MPI_Datatype MeshPoint :: MyGetMPIType ( )
+  { 
+    static MPI_Datatype type = NULL;
+    static MPI_Datatype htype = NULL;
+    if (!type)
+      {
+	MeshPoint hp;
+	int blocklen[] = { 3, 1, 1 };
+	MPI_Aint displ[] = { (char*)&hp.x[0] - (char*)&hp,
+			     (char*)&hp.layer - (char*)&hp,
+			     (char*)&hp.singular - (char*)&hp };
+	MPI_Datatype types[] = { MPI_DOUBLE, MPI_INT, MPI_DOUBLE };
+	*testout << "displ = " << displ[0] << ", " << displ[1] << ", " << displ[2] << endl;
+	*testout << "sizeof = " << sizeof (MeshPoint) << endl;
+	MPI_Type_create_struct (3, blocklen, displ, types, &htype);
+	MPI_Type_commit ( &htype );
+	MPI_Aint lb, ext;
+	MPI_Type_get_extent (htype, &lb, &ext);
+	*testout << "lb = " << lb << endl;
+	*testout << "ext = " << ext << endl;
+	ext = sizeof (MeshPoint);
+	MPI_Type_create_resized (htype, lb, ext, &type);
+	MPI_Type_commit ( &type );
+	
+      }
+    return type;
+  }
+#endif
+
+
+
+
+  Segment :: Segment() 
+  {
+    pnums[0] = -1;
+    pnums[1] = -1; 
+    edgenr = -1;
+
+    singedge_left = 0.;
+    singedge_right = 0.;
+    seginfo = 0;
+
+    si = -1;
+
+    domin = -1;
+    domout = -1;
+    tlosurf = -1;
+
+    surfnr1 = -1;
+    surfnr2 = -1;
+    pnums[2] = -1;
+    meshdocval = 0;
+    /*
+      geominfo[0].trignum=-1; 
+      geominfo[1].trignum=-1; 
+
+      epgeominfo[0].edgenr = 1;
+      epgeominfo[0].dist = 0;
+      epgeominfo[1].edgenr = 1;
+      epgeominfo[1].dist = 0;
+    */
+
+    bcname = 0;
+  }    
+
+  Segment::Segment (const Segment & other)
+    :
+    edgenr(other.edgenr),
+    singedge_left(other.singedge_left),
+    singedge_right(other.singedge_right),
+    seginfo(other.seginfo),
+    si(other.si),
+    domin(other.domin),
+    domout(other.domout),
+    tlosurf(other.tlosurf),
+    geominfo(),
+    surfnr1(other.surfnr1),
+    surfnr2(other.surfnr2),
+    epgeominfo(),
+    meshdocval(other.meshdocval),
+    hp_elnr(other.hp_elnr)
+  {
+    for (int j = 0; j < 3; j++)
+      pnums[j] = other.pnums[j];
+
+    geominfo[0] = other.geominfo[0];
+    geominfo[1] = other.geominfo[1];
+    epgeominfo[0] = other.epgeominfo[0];
+    epgeominfo[1] = other.epgeominfo[1];
+    bcname = other.bcname;
+  }
+
+  Segment& Segment::operator=(const Segment & other)
+  {
+    if (&other != this)
+      {
+	pnums[0] = other[0];
+	pnums[1] = other[1];
+	edgenr = other.edgenr;
+	singedge_left = other.singedge_left;
+	singedge_right = other.singedge_right;
+	seginfo = other.seginfo;
+	si = other.si;
+	domin = other.domin;
+	domout = other.domout;
+	tlosurf = other.tlosurf;
+	geominfo[0] = other.geominfo[0];
+	geominfo[1] = other.geominfo[1];
+	surfnr1 = other.surfnr1;
+	surfnr2 = other.surfnr2;
+	epgeominfo[0] = other.epgeominfo[0];
+	epgeominfo[1] = other.epgeominfo[1];
+	pnums[2] = other.pnums[2];
+	meshdocval = other.meshdocval;
+	hp_elnr = other.hp_elnr;
+	bcname = other.bcname;
+      }
+    
+    return *this;
+  }
+
+
+  ostream & operator<<(ostream  & s, const Segment & seg)
+  {
+    s << seg[0] << "(gi=" << seg.geominfo[0].trignum << ") - "
+      << seg[1] << "(gi=" << seg.geominfo[1].trignum << ")"
+      << " domin = " << seg.domin << ", domout = " << seg.domout 
+      << " si = " << seg.si << ", edgenr = " << seg.edgenr;
+    return s;
+  }
+
+
+  Element2d :: Element2d ()
+  { 
+    for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++)
+      {
+	pnum[i] = 0;
+	geominfo[i].trignum = 0;
+      }
+    np = 3;
+    index = 0;
+    badel = 0;
+    deleted = 0;
+    visible = 1;
+    typ = TRIG;
+    orderx = ordery = 1;
+    refflag = 1;
+    strongrefflag = false;
+#ifdef PARALLEL
+    isghost = 0;
+#endif
+  } 
+
+
+  Element2d :: Element2d (int anp)
+  { 
+    for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++)
+      {
+	pnum[i] = 0;
+	geominfo[i].trignum = 0;
+      }
+    np = anp;
+    index = 0;
+    badel = 0;
+    deleted = 0;
+    visible = 1;
+    switch (np)
+      {
+      case 3: typ = TRIG; break;
+      case 4: typ = QUAD; break;
+      case 6: typ = TRIG6; break;
+      case 8: typ = QUAD8; break;
+      }
+    orderx = ordery = 1;
+    refflag = 1;
+    strongrefflag = false;
+#ifdef PARALLEL
+    isghost = 0;
+#endif
+  } 
+
+  Element2d :: Element2d (ELEMENT_TYPE atyp)
+  { 
+    for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++)
+      {
+	pnum[i] = 0;
+	geominfo[i].trignum = 0;
+      }
+
+    SetType (atyp);
+
+    index = 0;
+    badel = 0;
+    deleted = 0;
+    visible = 1;
+    orderx = ordery = 1;
+    refflag = 1;
+    strongrefflag = false;
+#ifdef PARALLEL
+    isghost = 0;
+#endif
+
+  } 
+
+
+
+  Element2d :: Element2d (int pi1, int pi2, int pi3)
+  {
+    pnum[0] = pi1;
+    pnum[1] = pi2;
+    pnum[2] = pi3;
+    np = 3;
+    typ = TRIG;
+    pnum[3] = 0;
+    pnum[4] = 0;
+    pnum[5] = 0;
+  
+    for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++)
+      geominfo[i].trignum = 0;
+    index = 0;
+    badel = 0;
+    refflag = 1;
+    strongrefflag = false;
+    deleted = 0;
+    visible = 1;
+    orderx = ordery = 1;
+
+#ifdef PARALLEL
+    isghost = 0;
+#endif
+
+  }
+
+  Element2d :: Element2d (int pi1, int pi2, int pi3, int pi4)
+  {
+    pnum[0] = pi1;
+    pnum[1] = pi2;
+    pnum[2] = pi3;
+    pnum[3] = pi4;
+    np = 4;
+    typ = QUAD;
+
+    pnum[4] = 0;
+    pnum[5] = 0;
+  
+    for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++)
+      geominfo[i].trignum = 0;
+    index = 0;
+    badel = 0;
+    refflag = 1;
+    strongrefflag = false;
+    deleted = 0;
+    visible = 1;
+    orderx = ordery = 1;
+
+#ifdef PARALLEL
+    isghost = 0;
+#endif
+  }
+
+
+  /*
+    void Element2d :: SetType (ELEMENT_TYPE atyp)
+    {
+    typ = atyp;
+    switch (typ)
+    {
+    case TRIG: np = 3; break;
+    case QUAD: np = 4; break;
+    case TRIG6: np = 6; break;
+    case QUAD6: np = 6; break;
+    default:
+    PrintSysError ("Element2d::SetType, illegal type ", typ);
+    }
+    }
+  */
+
+
+  void Element2d :: GetBox (const T_POINTS & points, Box3d & box) const
+  {
+    box.SetPoint (points.Get(pnum[0]));
+    for (unsigned i = 1; i < np; i++)
+      box.AddPoint (points.Get(pnum[i]));
+  }
+
+  bool Element2d :: operator==(const Element2d & el2) const
+  {
+    bool retval = (el2.GetNP() == np);
+    for(int i= 0; retval && i<np; i++)
+      retval = (el2[i] == (*this)[i]);
+
+    return retval;
+  }
+
+
+  void Element2d :: Invert2()
+  {
+    switch (typ)
+      {
+      case TRIG:
+        {
+          Swap (pnum[1], pnum[2]);
+          break;
+        }
+      case TRIG6:
+        {
+          Swap (pnum[1], pnum[2]);
+          Swap (pnum[4], pnum[5]);
+          break;
+        }
+      case QUAD:
+        {
+          Swap (pnum[0], pnum[3]);
+          Swap (pnum[1], pnum[2]);
+          break;
+        }
+      default:
+        {
+          cerr << "Element2d::Invert2, illegal element type " << int(typ) << endl;
+        }
+      }
+  }
+
+  int Element2d::HasFace(const Element2d & el) const
+  {
+    //nur für tets!!! hannes
+    for (int i = 1; i <= 3; i++)
+      {
+        if (PNumMod(i)   == el[0] && 
+            PNumMod(i+1) == el[1] && 
+            PNumMod(i+2) == el[2])
+          {
+            return 1;
+          }
+      }
+    return 0;
+  }
+
+  void Element2d :: NormalizeNumbering2 ()
+  {
+    if (GetNP() == 3)
+      {
+        if (PNum(1) < PNum(2) && PNum(1) < PNum(3))
+          return;
+        else
+          {
+            if (PNum(2) < PNum(3))
+              {
+                PointIndex pi1 = PNum(2);
+                PNum(2) = PNum(3);
+                PNum(3) = PNum(1);
+                PNum(1) = pi1;
+              }
+            else
+              {
+                PointIndex pi1 = PNum(3);
+                PNum(3) = PNum(2);
+                PNum(2) = PNum(1);
+                PNum(1) = pi1;
+              }
+          }
+      }
+    else
+      {
+        int mini = 1;
+        for (int i = 2; i <= GetNP(); i++)
+          if (PNum(i) < PNum(mini)) mini = i;
+      
+        Element2d hel = (*this);
+        for (int i = 1; i <= GetNP(); i++)
+          PNum(i) = hel.PNumMod (i+mini-1);
+      }
+  }
+
+
+
+
+  Array<IntegrationPointData*> ipdtrig;
+  Array<IntegrationPointData*> ipdquad;
+
+
+  int Element2d :: GetNIP () const
+  {
+    int nip;
+    switch (np)
+      {
+      case 3: nip = 1; break;
+      case 4: nip = 4; break;
+      default: nip = 0; break;
+      }
+    return nip;
+  }
+
+  void Element2d :: 
+  GetIntegrationPoint (int ip, Point2d & p, double & weight) const
+  {
+    static double eltriqp[1][3] =
+      {
+        { 1.0/3.0, 1.0/3.0, 0.5 }
+      };
+
+    static double elquadqp[4][3] =
+      { 
+        { 0, 0, 0.25 },
+        { 0, 1, 0.25 },
+        { 1, 0, 0.25 },
+        { 1, 1, 0.25 }
+      };
+  
+    double * pp = 0;
+    switch (typ)
+      {
+      case TRIG: pp = &eltriqp[0][0]; break;
+      case QUAD: pp = &elquadqp[ip-1][0]; break;
+      default:
+        PrintSysError ("Element2d::GetIntegrationPoint, illegal type ", typ);
+      }
+
+    p.X() = pp[0];
+    p.Y() = pp[1];
+    weight = pp[2];
+  }
+
+  void Element2d :: 
+  GetTransformation (int ip, const Array<Point2d> & points,
+                     DenseMatrix & trans) const
+  {
+    int np = GetNP();
+    DenseMatrix pmat(2, np), dshape(2, np);
+    pmat.SetSize (2, np);
+    dshape.SetSize (2, np);
+
+    Point2d p;
+    double w;
+
+    GetPointMatrix (points, pmat);
+    GetIntegrationPoint (ip, p, w);
+    GetDShape (p, dshape);
+  
+    CalcABt (pmat, dshape, trans);
+
+    /*
+      (*testout) << "p = " << p  << endl
+      << "pmat = " << pmat << endl
+      << "dshape = " << dshape << endl
+      << "tans = " << trans << endl;
+    */
+  }
+
+  void Element2d :: 
+  GetTransformation (int ip, class DenseMatrix & pmat,
+                     class DenseMatrix & trans) const
+  {
+    //  int np = GetNP();
+
+#ifdef DEBUG
+    if (pmat.Width() != np || pmat.Height() != 2)
+      {
+        (*testout) << "GetTransofrmation: pmat doesn't fit" << endl;
+        return;
+      }
+#endif
+
+    ComputeIntegrationPointData ();
+    DenseMatrix * dshapep = NULL;
+    switch (typ)
+      {
+      case TRIG: dshapep = &ipdtrig.Get(ip)->dshape; break;
+      case QUAD: dshapep = &ipdquad.Get(ip)->dshape; break;
+      default:
+        PrintSysError ("Element2d::GetTransformation, illegal type ", typ);
+      }
+  
+    CalcABt (pmat, *dshapep, trans);
+  }
+
+
+  void Element2d :: GetShape (const Point2d & p, Vector & shape) const
+  {
+    if (shape.Size() != GetNP())
+      {
+        cerr << "Element::GetShape: Length not fitting" << endl;
+        return;
+      }
+
+    switch (typ)
+      {
+      case TRIG:
+        shape(0) = 1 - p.X() - p.Y();
+        shape(1) = p.X();
+        shape(2) = p.Y();
+        break;
+      case QUAD:
+        shape(0) = (1-p.X()) * (1-p.Y());
+        shape(1) = p.X() * (1-p.Y());
+        shape(2) = p.X() * p.Y();
+        shape(3) = (1-p.X()) * p.Y();
+        break;
+      default:
+        PrintSysError ("Element2d::GetShape, illegal type ", typ);
+      }
+  }
+
+
+
+  void Element2d :: GetShapeNew (const Point<2> & p, FlatVector & shape) const
+  {
+    switch (typ)
+      {
+      case TRIG:
+        {
+          shape(0) = p(0);
+          shape(1) = p(1);
+          shape(2) = 1-p(0)-p(1);
+          break;
+        }
+
+      case QUAD:
+        {
+          shape(0) = (1-p(0))*(1-p(1));
+          shape(1) =    p(0) *(1-p(1));
+          shape(2) =    p(0) *   p(1) ;
+          shape(3) = (1-p(0))*   p(1) ;
+          break;
+        }
+      }
+  }
+
+
+
+
+
+
+
+
+
+  void Element2d :: 
+  GetDShape (const Point2d & p, DenseMatrix & dshape) const
+  {
+#ifdef DEBUG
+    if (dshape.Height() != 2 || dshape.Width() != np)
+      {
+        PrintSysError ("Element::DShape: Sizes don't fit");
+        return;
+      }
+#endif
+
+    switch (typ)
+      {
+      case TRIG:
+        dshape.Elem(1, 1) = -1;
+        dshape.Elem(1, 2) = 1;
+        dshape.Elem(1, 3) = 0;
+        dshape.Elem(2, 1) = -1;
+        dshape.Elem(2, 2) = 0;
+        dshape.Elem(2, 3) = 1;
+        break;
+      case QUAD:
+        dshape.Elem(1, 1) = -(1-p.Y());
+        dshape.Elem(1, 2) = (1-p.Y());
+        dshape.Elem(1, 3) = p.Y();
+        dshape.Elem(1, 4) = -p.Y();
+        dshape.Elem(2, 1) = -(1-p.X());
+        dshape.Elem(2, 2) = -p.X();
+        dshape.Elem(2, 3) = p.X();
+        dshape.Elem(2, 4) = (1-p.X());
+        break;
+
+      default:
+        PrintSysError ("Element2d::GetDShape, illegal type ", typ);
+      }
+  }
+
+
+
+  void Element2d :: 
+  GetDShapeNew (const Point<2> & p, MatrixFixWidth<2> & dshape) const
+  {
+    switch (typ)
+      {
+      case TRIG:
+        {
+          dshape = 0;
+          dshape(0,0) = 1;
+          dshape(1,1) = 1;
+          dshape(2,0) = -1;
+          dshape(2,1) = -1;
+          break;
+        }
+      case QUAD:
+        {
+          dshape(0,0) = -(1-p(1));
+          dshape(0,1) = -(1-p(0));
+
+          dshape(1,0) =  (1-p(1));
+          dshape(1,1) =  -p(0);
+
+          dshape(2,0) = p(1);
+          dshape(2,1) = p(0);
+
+          dshape(3,0) = -p(1);
+          dshape(3,1) = (1-p(0));
+          break;
+        }
+      }
+  }
+
+
+
+
+
+  void Element2d :: 
+  GetPointMatrix (const Array<Point2d> & points,
+                  DenseMatrix & pmat) const
+  {
+    int np = GetNP();
+
+#ifdef DEBUG
+    if (pmat.Width() != np || pmat.Height() != 2)
+      {
+        cerr << "Element::GetPointMatrix: sizes don't fit" << endl;
+        return;
+      }
+#endif
+  
+    for (int i = 1; i <= np; i++)
+      {
+        const Point2d & p = points.Get(PNum(i));
+        pmat.Elem(1, i) = p.X();
+        pmat.Elem(2, i) = p.Y();
+      }
+  }
+
+
+
+
+
+  double Element2d :: CalcJacobianBadness (const Array<Point2d> & points) const
+  {
+    int i, j;
+    int nip = GetNIP();
+    DenseMatrix trans(2,2);
+    DenseMatrix pmat;
+  
+    pmat.SetSize (2, GetNP());
+    GetPointMatrix (points, pmat);
+
+    double err = 0;
+    for (i = 1; i <= nip; i++)
+      {
+        GetTransformation (i, pmat, trans);
+
+        // Frobenius norm
+        double frob = 0;
+        for (j = 1; j <= 4; j++)
+          frob += sqr (trans.Get(j));
+        frob = sqrt (frob);
+        frob /= 2;
+
+        double det = trans.Det();
+
+        if (det <= 0)
+          err += 1e12;
+        else
+          err += frob * frob / det;
+      }
+
+    err /= nip;
+    return err;
+  }
+
+
+
+  static const int qip_table[4][4] =
+    { { 0, 1, 0, 3 },
+      { 0, 1, 1, 2 },
+      { 3, 2, 0, 3 },
+      { 3, 2, 1, 2 }
+    };
+
+  double Element2d :: 
+  CalcJacobianBadnessDirDeriv (const Array<Point2d> & points,
+                               int pi, Vec2d & dir, double & dd) const
+  {
+    if (typ == QUAD)
+      {
+        Mat<2,2> trans, dtrans;
+        Mat<2,4> vmat, pmat;
+      
+        for (int j = 0; j < 4; j++)
+          {
+            const Point2d & p = points.Get( (*this)[j] );
+            pmat(0, j) = p.X();
+            pmat(1, j) = p.Y();
+          }
+
+        vmat = 0.0;
+        vmat(0, pi-1) = dir.X();
+        vmat(1, pi-1) = dir.Y();
+      
+        double err = 0;
+        dd = 0;
+
+        for (int i = 0; i < 4; i++)
+          {
+            int ix1 = qip_table[i][0];
+            int ix2 = qip_table[i][1];
+            int iy1 = qip_table[i][2];
+            int iy2 = qip_table[i][3];
+	      
+            trans(0,0) = pmat(0, ix2) - pmat(0,ix1);
+            trans(1,0) = pmat(1, ix2) - pmat(1,ix1);
+            trans(0,1) = pmat(0, iy2) - pmat(0,iy1);
+            trans(1,1) = pmat(1, iy2) - pmat(1,iy1);
+
+            double det = trans(0,0)*trans(1,1)-trans(1,0)*trans(0,1);
+
+            if (det <= 0)
+              {
+                dd = 0;
+                return 1e12;
+              }
+	  
+            dtrans(0,0) = vmat(0, ix2) - vmat(0,ix1);
+            dtrans(1,0) = vmat(1, ix2) - vmat(1,ix1);
+            dtrans(0,1) = vmat(0, iy2) - vmat(0,iy1);
+            dtrans(1,1) = vmat(1, iy2) - vmat(1,iy1);
+
+
+            // Frobenius norm
+            double frob = 0;
+            for (int j = 0; j < 4; j++) 
+              frob += sqr (trans(j));
+            frob = sqrt (frob);
+	  
+            double dfrob = 0;
+            for (int j = 0; j < 4; j++)
+              dfrob += trans(j) * dtrans(j);
+            dfrob = dfrob / frob;
+	  
+            frob /= 2;      
+            dfrob /= 2;
+	  
+	  
+            // ddet = \sum_j det (m_j)   with m_j = trans, except col j = dtrans
+            double ddet 
+              = dtrans(0,0) * trans(1,1) - trans(0,1) * dtrans(1,0)
+              + trans(0,0) * dtrans(1,1) - dtrans(0,1) * trans(1,0);
+	  
+            err += frob * frob / det;
+            dd += (2 * frob * dfrob * det - frob * frob * ddet) / (det * det);
+          }
+      
+        err /= 4;
+        dd /= 4;
+        return err;
+      }
+
+    int nip = GetNIP();
+    DenseMatrix trans(2,2), dtrans(2,2);
+    DenseMatrix pmat, vmat;
+  
+    pmat.SetSize (2, GetNP());
+    vmat.SetSize (2, GetNP());
+
+    GetPointMatrix (points, pmat);
+  
+    vmat = 0.0;
+    vmat.Elem(1, pi) = dir.X();
+    vmat.Elem(2, pi) = dir.Y();
+
+
+    double err = 0;
+    dd = 0;
+
+    for (int i = 1; i <= nip; i++)
+      {
+        GetTransformation (i, pmat, trans);
+        GetTransformation (i, vmat, dtrans);
+
+        // Frobenius norm
+        double frob = 0;
+        for (int j = 1; j <= 4; j++)
+          frob += sqr (trans.Get(j));
+        frob = sqrt (frob);
+      
+        double dfrob = 0;
+        for (int j = 1; j <= 4; j++)
+          dfrob += trans.Get(j) * dtrans.Get(j);
+        dfrob = dfrob / frob;
+      
+        frob /= 2;      
+        dfrob /= 2;
+      
+        double det = trans(0,0)*trans(1,1)-trans(1,0)*trans(0,1);
+
+        // ddet = \sum_j det (m_j)   with m_j = trans, except col j = dtrans
+        double ddet 
+          = dtrans(0,0) * trans(1,1) - trans(0,1) * dtrans(1,0)
+          + trans(0,0) * dtrans(1,1) - dtrans(0,1) * trans(1,0);
+
+        if (det <= 0)
+          err += 1e12;
+        else
+          {
+            err += frob * frob / det;
+            dd += (2 * frob * dfrob * det - frob * frob * ddet) / (det * det);
+          }
+      }
+
+    err /= nip;
+    dd /= nip;
+    return err;
+  }
+
+
+
+  double Element2d :: 
+  CalcJacobianBadness (const T_POINTS & points, const Vec<3> & n) const
+  {
+    int i, j;
+    int nip = GetNIP();
+    DenseMatrix trans(2,2);
+    DenseMatrix pmat;
+  
+    pmat.SetSize (2, GetNP());
+
+    Vec<3> t1, t2;
+    t1 = n.GetNormal();
+    t2 = Cross (n, t1);
+
+    for (i = 1; i <= GetNP(); i++)
+      {
+        Point3d p = points.Get(PNum(i));
+        pmat.Elem(1, i) = p.X() * t1(0) + p.Y() * t1(1) + p.Z() * t1(2);
+        pmat.Elem(2, i) = p.X() * t2(0) + p.Y() * t2(1) + p.Z() * t2(2);
+      }
+
+    double err = 0;
+    for (i = 1; i <= nip; i++)
+      {
+        GetTransformation (i, pmat, trans);
+
+        // Frobenius norm
+        double frob = 0;
+        for (j = 1; j <= 4; j++)
+          frob += sqr (trans.Get(j));
+        frob = sqrt (frob);
+        frob /= 2;
+
+        double det = trans.Det();
+        if (det <= 0)
+          err += 1e12;
+        else
+          err += frob * frob / det;
+      }
+
+    err /= nip;
+    return err;
+  }
+
+
+
+
+  void Element2d :: ComputeIntegrationPointData () const
+  {
+    switch (np)
+      {
+      case 3: if (ipdtrig.Size()) return; break;
+      case 4: if (ipdquad.Size()) return; break;
+      }
+
+    for (int i = 1; i <= GetNIP(); i++)
+      {
+        IntegrationPointData * ipd = new IntegrationPointData;
+        Point2d hp;
+        GetIntegrationPoint (i, hp, ipd->weight);
+        ipd->p(0) = hp.X();
+        ipd->p(1) = hp.Y();
+        ipd->p(2) = 0;
+
+        ipd->shape.SetSize(GetNP());
+        ipd->dshape.SetSize(2, GetNP());
+
+        GetShape (hp, ipd->shape);
+        GetDShape (hp, ipd->dshape);
+
+        switch (np)
+          {
+          case 3: ipdtrig.Append (ipd); break;
+          case 4: ipdquad.Append (ipd); break;
+          }
+      }
+  }
+
+
+
+
+
+
+
+
+
+  ostream & operator<<(ostream  & s, const Element2d & el)
+  {
+    s << "np = " << el.GetNP();
+    for (int j = 1; j <= el.GetNP(); j++)
+      s << " " << el.PNum(j);
+    return s;
+  }
+
+
+  ostream & operator<<(ostream  & s, const Element & el)
+  {
+    s << "np = " << el.GetNP();
+    for (int j = 0; j < el.GetNP(); j++)
+      s << " " << int(el[j]);
+    return s;
+  }
+
+
+  Element :: Element ()
+  {
+    typ = TET;
+    np = 4;
+    for (int i = 0; i < ELEMENT_MAXPOINTS; i++)
+      pnum[i] = 0;
+    index = 0;
+    flags.marked = 1;
+    flags.badel = 0;
+    flags.reverse = 0;
+    flags.illegal = 0;
+    flags.illegal_valid = 0;
+    flags.badness_valid = 0;
+    flags.refflag = 1;
+    flags.strongrefflag = false;
+    flags.deleted = 0;
+    flags.fixed = 0;
+    orderx = ordery = orderz = 1;
+
+#ifdef PARALLEL
+    partitionNumber = -1;
+    isghost = 0;
+#endif
+
+  }
+
+
+  Element :: Element (int anp)
+  {
+    np = anp;
+    int i;
+    for (i = 0; i < ELEMENT_MAXPOINTS; i++)
+      pnum[i] = 0;
+    index = 0;
+    flags.marked = 1;
+    flags.badel = 0;
+    flags.reverse = 0;
+    flags.illegal = 0;
+    flags.illegal_valid = 0;
+    flags.badness_valid = 0;
+    flags.refflag = 1;
+    flags.strongrefflag = false;
+    flags.deleted = 0;
+    flags.fixed = 0;
+
+    switch (np)
+      {
+      case 4: typ = TET; break;
+      case 5: typ = PYRAMID; break;
+      case 6: typ = PRISM; break;
+      case 8: typ = HEX; break;
+      case 10: typ = TET10; break;
+      default: cerr << "Element::Element: unknown element with " << np << " points" << endl;
+      }
+    orderx = ordery = orderz = 1;
+
+#ifdef PARALLEL
+    isghost = 0;
+#endif
+  }
+
+  void Element :: SetOrder (const int aorder) 
+  { 
+    orderx = aorder; 
+    ordery = aorder; 
+    orderz = aorder;
+  }
+
+
+  void Element :: SetOrder (const int ox, const int oy, const int oz) 
+  { 
+    orderx = ox; 
+    ordery = oy;
+    orderz = oz; 
+  }
+
+
+  Element :: Element (ELEMENT_TYPE type)
+  {
+    SetType (type);
+
+    int i;
+    for (i = 0; i < ELEMENT_MAXPOINTS; i++)
+      pnum[i] = 0;
+    index = 0;
+    flags.marked = 1;
+    flags.badel = 0;
+    flags.reverse = 0;
+    flags.illegal = 0;
+    flags.illegal_valid = 0;
+    flags.badness_valid = 0;
+    flags.refflag = 1;
+    flags.strongrefflag = false;
+    flags.deleted = 0;
+    flags.fixed = 0;
+    orderx = ordery = orderz = 1;
+
+#ifdef PARALLEL
+    isghost = 0;
+#endif
+  }
+
+
+
+
+
+  Element & Element :: operator= (const Element & el2)
+  {
+    typ = el2.typ;
+    np = el2.np;
+    for (int i = 0; i < ELEMENT_MAXPOINTS; i++)
+      pnum[i] = el2.pnum[i];
+    index = el2.index;
+    flags = el2.flags;
+    orderx = el2.orderx;
+    ordery = el2.ordery;
+    orderz = el2.orderz;
+    hp_elnr = el2.hp_elnr;
+    flags = el2.flags;
+    return *this;
+  }
+
+
+
+  void Element :: SetNP (int anp)
+  {
+    np = anp; 
+    switch (np)
+      {
+      case 4: typ = TET; break;
+      case 5: typ = PYRAMID; break;
+      case 6: typ = PRISM; break;
+      case 8: typ = HEX; break;
+      case 10: typ = TET10; break;
+        // 
+      default: break;
+        cerr << "Element::SetNP unknown element with " << np << " points" << endl;
+      }
+  }
+
+
+
+  void Element :: SetType (ELEMENT_TYPE atyp)
+  {
+    typ = atyp;
+    switch (atyp)
+      {
+      case TET: np = 4; break;
+      case PYRAMID: np = 5; break;
+      case PRISM: np = 6; break;
+      case HEX: np = 8; break;
+      case TET10: np = 10; break;
+      case PRISM12: np = 12; break;
+
+      default: break;
+        cerr << "Element::SetType unknown type  " << int(typ) << endl;
+      }
+  }
+
+
+
+  void Element :: Invert()
+  {
+    switch (GetNP())
+      {
+      case 4:
+        {
+          Swap (PNum(3), PNum(4));
+          break;
+        }
+      case 5:
+        {
+          Swap (PNum(1), PNum(4));
+          Swap (PNum(2), PNum(3));
+          break;
+        }
+      case 6:
+        {
+          Swap (PNum(1), PNum(4));
+          Swap (PNum(2), PNum(5));
+          Swap (PNum(3), PNum(6));
+          break;
+        }
+      }
+  }
+
+
+  void Element :: Print (ostream & ost) const
+  {
+    ost << np << " Points: ";
+    for (int i = 1; i <= np; i++)
+      ost << pnum[i-1] << " " << endl;
+  }
+
+  void Element :: GetBox (const T_POINTS & points, Box3d & box) const
+  {
+    box.SetPoint (points.Get(PNum(1)));
+    box.AddPoint (points.Get(PNum(2)));
+    box.AddPoint (points.Get(PNum(3)));
+    box.AddPoint (points.Get(PNum(4)));
+  }
+
+  double Element :: Volume (const T_POINTS & points) const
+  {
+    Vec<3> v1 = points.Get(PNum(2)) - points.Get(PNum(1));
+    Vec<3> v2 = points.Get(PNum(3)) - points.Get(PNum(1));
+    Vec<3> v3 = points.Get(PNum(4)) - points.Get(PNum(1)); 
+  
+    return -(Cross (v1, v2) * v3) / 6;	 
+  }  
+
+
+  void Element :: GetFace2 (int i, Element2d & face) const
+  {
+    static const int tetfaces[][5] = 
+      { { 3, 2, 3, 4, 0 },
+        { 3, 3, 1, 4, 0 },
+        { 3, 1, 2, 4, 0 },
+        { 3, 2, 1, 3, 0 } };
+
+    static const int tet10faces[][7] = 
+      { { 3, 2, 3, 4, 10, 9, 8 },
+        { 3, 3, 1, 4, 7, 10, 6 },
+        { 3, 1, 2, 4, 9, 7, 5 },
+        { 3, 2, 1, 3, 6, 8, 5 } };
+
+    static const int pyramidfaces[][5] =
+      { { 4, 1, 4, 3, 2 },
+        { 3, 1, 2, 5, 0 },
+        { 3, 2, 3, 5, 0 },
+        { 3, 3, 4, 5, 0 },
+        { 3, 4, 1, 5, 0 } };
+
+    static const int prismfaces[][5] =
+      {
+        { 3, 1, 3, 2, 0 },
+        { 3, 4, 5, 6, 0 },
+        { 4, 1, 2, 5, 4 },
+        { 4, 2, 3, 6, 5 },
+        { 4, 3, 1, 4, 6 }
+      };
+
+    switch (np)
+      {
+      case 4: // tet
+        {
+          face.SetType(TRIG);
+          for (int j = 1; j <= 3; j++)
+            face.PNum(j) = PNum(tetfaces[i-1][j]);
+          break;
+        }
+
+      case 10: // tet10
+        {
+          face.SetType(TRIG6);
+          for (int j = 1; j <= 6; j++)
+            face.PNum(j) = PNum(tet10faces[i-1][j]);
+          break;
+        }
+
+      case 5: // pyramid
+        {
+          // face.SetNP(pyramidfaces[i-1][0]);
+          face.SetType ( (i == 1) ? QUAD : TRIG);
+          for (int j = 1; j <= face.GetNP(); j++)
+            face.PNum(j) = PNum(pyramidfaces[i-1][j]);
+          break;
+        }
+      case 6: // prism
+        {
+          //	face.SetNP(prismfaces[i-1][0]);
+          face.SetType ( (i >= 3) ? QUAD : TRIG);
+          for (int j = 1; j <= face.GetNP(); j++)
+            face.PNum(j) = PNum(prismfaces[i-1][j]);
+          break;
+        }
+      }
+  }
+
+
+
+  void Element :: GetTets (Array<Element> & locels) const
+  {
+    GetTetsLocal (locels);
+    int i, j;
+    for (i = 1; i <= locels.Size(); i++)
+      for (j = 1; j <= 4; j++)
+        locels.Elem(i).PNum(j) = PNum ( locels.Elem(i).PNum(j) );
+  }
+
+  void Element :: GetTetsLocal (Array<Element> & locels) const
+  {
+    int i, j;
+    locels.SetSize(0);
+    switch (GetType())
+      {
+      case TET:
+        {
+          int linels[1][4] = 
+            { { 1, 2, 3, 4 },
+            };
+          for (i = 0; i < 1; i++)
+            {
+              Element tet(4);
+              for (j = 1; j <= 4; j++)
+                tet.PNum(j) = linels[i][j-1];
+              locels.Append (tet);
+            }
+          break;
+        }
+      case TET10:
+        {
+          int linels[8][4] = 
+            { { 1, 5, 6, 7 },
+              { 5, 2, 8, 9 },
+              { 6, 8, 3, 10 },
+              { 7, 9, 10, 4 },
+              { 5, 6, 7, 9 },
+              { 5, 6, 9, 8 },
+              { 6, 7, 9, 10 },
+              { 6, 8, 10, 9 } };
+          for (i = 0; i < 8; i++)
+            {
+              Element tet(4);
+              for (j = 1; j <= 4; j++)
+                tet.PNum(j) = linels[i][j-1];
+              locels.Append (tet);
+            }
+          break;
+        }
+      case PYRAMID:
+        {
+          int linels[2][4] = 
+            { { 1, 2, 3, 5 },
+              { 1, 3, 4, 5 } };
+          for (i = 0; i < 2; i++)
+            {
+              Element tet(4);
+              for (j = 1; j <= 4; j++)
+                tet.PNum(j) = linels[i][j-1];
+              locels.Append (tet);
+            }
+          break;
+        }
+      case PRISM:
+      case PRISM12:
+        {
+          int linels[3][4] = 
+            { { 1, 2, 3, 4 },
+              { 4, 2, 3, 5 },
+              { 6, 5, 4, 3 }
+            };
+          for (i = 0; i < 3; i++)
+            {
+              Element tet(4);
+              for (j = 0; j < 4; j++)
+                tet[j] = linels[i][j];
+              locels.Append (tet);
+            }
+          break;
+        }
+      case HEX:
+        {
+          int linels[6][4] = 
+            { { 1, 7, 2, 3 },
+              { 1, 7, 3, 4 },
+              { 1, 7, 4, 8 },
+              { 1, 7, 8, 5 },
+              { 1, 7, 5, 6 },
+              { 1, 7, 6, 2 }
+            };
+          for (i = 0; i < 6; i++)
+            {
+              Element tet(4);
+              for (j = 0; j < 4; j++)
+                tet[j] = linels[i][j];
+              locels.Append (tet);
+            }
+          break;
+        }
+      default:
+        {
+          cerr << "GetTetsLocal not implemented for el with " << GetNP() << " nodes" << endl;
+        }
+      }
+  }
+
+  bool Element :: operator==(const Element & el2) const
+  {
+    bool retval = (el2.GetNP() == np);
+    for(int i= 0; retval && i<np; i++)
+      retval = (el2[i] == (*this)[i]);
+
+    return retval;
+  }
+
+
+#ifdef OLD
+  void Element :: GetNodesLocal (Array<Point3d> & points) const
+  {
+    const static double tetpoints[4][3] =
+      { { 0, 0, 0 },
+        { 1, 0, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 }};
+  
+    const static double prismpoints[6][3] =
+      { { 0, 0, 0 },
+        { 1, 0, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 },
+        { 1, 0, 1 },
+        { 0, 1, 1 } };
+  
+    const static double pyramidpoints[6][3] =
+      { { 0, 0, 0 },
+        { 1, 0, 0 },
+        { 1, 1, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 } };
+  
+    const static double tet10points[10][3] =
+      { { 0, 0, 0 },
+        { 1, 0, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 },
+        { 0.5, 0, 0 },
+        { 0, 0.5, 0 },
+        { 0, 0, 0.5 },
+        { 0.5, 0.5, 0 },
+        { 0.5, 0, 0.5 },
+        { 0, 0.5, 0.5 } };
+
+    const static double hexpoints[8][3] =
+      { 
+        { 0, 0, 0 },
+        { 1, 0, 0 },
+        { 1, 1, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 },
+        { 1, 0, 1 },
+        { 1, 1, 1 },
+        { 0, 1, 1 }
+      };
+  
+    int np, i;
+    const double (*pp)[3];
+    switch (GetType())
+      {
+      case TET:
+        {
+          np = 4;
+          pp = tetpoints;
+          break;
+        }
+      case PRISM:
+      case PRISM12:
+        {
+          np = 6;
+          pp = prismpoints;
+          break;
+        }
+      case TET10:
+        {
+          np = 10;
+          pp = tet10points;
+          break;
+        }
+      case PYRAMID:
+        {
+          np = 5;
+          pp = pyramidpoints;
+          break;
+        }
+      case HEX:
+        {
+          np = 8;
+          pp = hexpoints;
+          break;
+        }
+      default:
+        {
+          cout << "GetNodesLocal not impelemented for element " << GetType() << endl;
+          np = 0;
+        }
+      }
+  
+    points.SetSize(0);
+    for (i = 0; i < np; i++)
+      points.Append (Point3d (pp[i][0], pp[i][1], pp[i][2]));
+  }
+#endif
+
+
+
+
+
+
+  void Element :: GetNodesLocalNew (Array<Point<3> > & points) const
+  {
+    const static double tetpoints[4][3] =
+      {      
+        { 1, 0, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 },
+        { 0, 0, 0 }
+      };
+  
+    const static double prismpoints[6][3] =
+      {
+        { 1, 0, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 0 },
+        { 1, 0, 1 },
+        { 0, 1, 1 },
+        { 0, 0, 1 }
+      };
+  
+    const static double pyramidpoints[6][3] =
+      { { 0, 0, 0 },
+        { 1, 0, 0 },
+        { 1, 1, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 } };
+  
+    const static double tet10points[10][3] =
+      { { 0, 0, 0 },
+        { 1, 0, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 },
+        { 0.5, 0, 0 },
+        { 0, 0.5, 0 },
+        { 0, 0, 0.5 },
+        { 0.5, 0.5, 0 },
+        { 0.5, 0, 0.5 },
+        { 0, 0.5, 0.5 } };
+
+    const static double hexpoints[8][3] =
+      { 
+        { 0, 0, 0 },
+        { 1, 0, 0 },
+        { 1, 1, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 },
+        { 1, 0, 1 },
+        { 1, 1, 1 },
+        { 0, 1, 1 }
+      };
+  
+
+  
+    int np, i;
+    const double (*pp)[3];
+    switch (GetType())
+      {
+      case TET:
+        {
+          np = 4;
+          pp = tetpoints;
+          break;
+        }
+      case PRISM:
+      case PRISM12:
+        {
+          np = 6;
+          pp = prismpoints;
+          break;
+        }
+      case TET10:
+        {
+          np = 10;
+          pp = tet10points;
+          break;
+        }
+      case PYRAMID:
+        {
+          np = 5;
+          pp = pyramidpoints;
+          break;
+        }
+      case HEX:
+        {
+          np = 8;
+          pp = hexpoints;
+          break;
+        }
+      default:
+        {
+          cout << "GetNodesLocal not impelemented for element " << GetType() << endl;
+          np = 0;
+	  pp = NULL;
+        }
+      }
+  
+    points.SetSize(0);
+    for (i = 0; i < np; i++)
+      points.Append (Point<3> (pp[i][0], pp[i][1], pp[i][2]));
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  void Element :: GetSurfaceTriangles (Array<Element2d> & surftrigs) const
+  {
+    static int tet4trigs[][3] = 
+      { { 2, 3, 4 },
+        { 3, 1, 4 },
+        { 1, 2, 4 },
+        { 2, 1, 3 } };
+
+    static int tet10trigs[][3] = 
+      { { 2, 8, 9 }, { 3, 10, 8}, { 4, 9, 10 }, { 9, 8, 10 },
+        { 3, 6, 10 }, { 1, 7, 6 }, { 4, 10, 7 }, { 6, 7, 10 },
+        { 1, 5, 7 }, { 2, 9, 5 }, { 4, 7, 9 }, { 5, 9, 7 },
+        { 1, 6, 5 }, { 2, 5, 8 }, { 3, 8, 6 }, { 5, 6, 8 }
+      };
+
+    static int pyramidtrigs[][3] =
+      {
+        { 1, 3, 2 },
+        { 1, 4, 3 },
+        { 1, 2, 5 },
+        { 2, 3, 5 },
+        { 3, 4, 5 },
+        { 4, 1, 5 }
+      };
+
+    static int prismtrigs[][3] =
+      {
+        { 1, 3, 2 },
+        { 4, 5, 6 },
+        { 1, 2, 4 },
+        { 4, 2, 5 },
+        { 2, 3, 5 },
+        { 5, 3, 6 },
+        { 3, 1, 6 },
+        { 6, 1, 4 }
+      };
+  
+    static int hextrigs[][3] = 
+      {
+        { 1, 3, 2 },
+        { 1, 4, 3 }, 
+        { 5, 6, 7 },
+        { 5, 7, 8 },
+        { 1, 2, 6 },
+        { 1, 6, 5 },
+        { 2, 3, 7 },
+        { 2, 7, 6 },
+        { 3, 4, 8 },
+        { 3, 8, 7 },
+        { 4, 1, 8 },
+        { 1, 5, 8 }
+      };
+
+    int j;
+
+    int nf;
+    int (*fp)[3];
+
+    switch (GetType())
+      {
+      case TET:
+        {
+          nf = 4;
+          fp = tet4trigs;
+          break;
+        }
+      case PYRAMID:
+        {
+          nf = 6;
+          fp = pyramidtrigs;
+          break;
+        }
+      case PRISM:
+      case PRISM12:
+        {
+          nf = 8;
+          fp = prismtrigs;
+          break;
+        }
+      case TET10:
+        {
+          nf = 16;
+          fp = tet10trigs;
+          break;
+        }
+      case HEX:
+        {
+          nf = 12;
+          fp = hextrigs;
+          break;
+        }
+      default:
+        {
+          nf = 0;
+          fp = NULL;
+        }
+      }
+
+  
+    surftrigs.SetSize (nf);
+    for (j = 0; j < nf; j++)
+      {
+        surftrigs.Elem(j+1) = Element2d(TRIG);
+        surftrigs.Elem(j+1).PNum(1) = fp[j][0];
+        surftrigs.Elem(j+1).PNum(2) = fp[j][1];
+        surftrigs.Elem(j+1).PNum(3) = fp[j][2];
+      }
+  }
+
+
+
+
+
+  Array< AutoPtr < IntegrationPointData > > ipdtet;
+  Array< AutoPtr < IntegrationPointData > > ipdtet10;
+
+
+
+  int Element :: GetNIP () const
+  {
+    int nip;
+    switch (typ)
+      {
+      case TET: nip = 1; break;
+      case TET10: nip = 8; break;
+      default: nip = 0; break;
+      }
+    return nip;
+  }
+
+  void Element :: 
+  GetIntegrationPoint (int ip, Point<3> & p, double & weight) const
+  {
+    static double eltetqp[1][4] =
+      {
+        { 0.25, 0.25, 0.25, 1.0/6.0 }
+      };
+
+    static double eltet10qp[8][4] =
+      {
+        { 0.585410196624969, 0.138196601125011, 0.138196601125011, 1.0/24.0 },
+        { 0.138196601125011, 0.585410196624969, 0.138196601125011, 1.0/24.0 },
+        { 0.138196601125011, 0.138196601125011, 0.585410196624969, 1.0/24.0 },
+        { 0.138196601125011, 0.138196601125011, 0.138196601125011, 1.0/24.0 },
+        { 1, 0, 0, 1 },
+        { 0, 1, 0, 1 },
+        { 0, 0, 1, 1 },
+        { 0, 0, 0, 1 },
+      };
+  
+    double * pp = NULL;
+    switch (typ)
+      {
+      case TET: pp = &eltetqp[0][0]; break;
+      case TET10: pp = &eltet10qp[ip-1][0]; break;
+      }
+
+    p(0) = pp[0];
+    p(1) = pp[1];
+    p(2) = pp[2];
+    weight = pp[3];
+  }
+
+  void Element :: 
+  GetTransformation (int ip, const T_POINTS & points,
+                     DenseMatrix & trans) const
+  {
+    int np = GetNP();
+    DenseMatrix pmat(3, np), dshape(3, np);
+    pmat.SetSize (3, np);
+    dshape.SetSize (3, np);
+
+    Point<3> p;
+    double w;
+
+    GetPointMatrix (points, pmat);
+    GetIntegrationPoint (ip, p, w);
+    GetDShape (p, dshape);
+  
+    CalcABt (pmat, dshape, trans);
+
+    /*
+      (*testout) << "p = " << p  << endl
+      << "pmat = " << pmat << endl
+      << "dshape = " << dshape << endl
+      << "tans = " << trans << endl;
+    */
+  }
+
+  void Element :: 
+  GetTransformation (int ip, class DenseMatrix & pmat,
+                     class DenseMatrix & trans) const
+  {
+    int np = GetNP();
+
+    if (pmat.Width() != np || pmat.Height() != 3)
+      {
+        (*testout) << "GetTransofrmation: pmat doesn't fit" << endl;
+        return;
+      }
+
+    ComputeIntegrationPointData ();
+    DenseMatrix * dshapep = 0;
+    switch (GetType())
+      {
+      case TET: dshapep = &ipdtet.Get(ip)->dshape; break;
+      case TET10: dshapep = &ipdtet10.Get(ip)->dshape; break;
+      default:
+        PrintSysError ("Element::GetTransformation, illegal type ", int(typ));
+      }
+  
+    CalcABt (pmat, *dshapep, trans);
+  }
+
+
+  void Element :: GetShape (const Point<3> & hp, Vector & shape) const
+  {
+    Point3d p = hp;
+
+    if (shape.Size() != GetNP())
+      {
+        cerr << "Element::GetShape: Length not fitting" << endl;
+        return;
+      }
+
+    switch (typ)
+      {
+      case TET:
+        {
+          shape(0) = 1 - p.X() - p.Y() - p.Z(); 
+          shape(1) = p.X();
+          shape(2) = p.Y();
+          shape(3) = p.Z();
+          break;
+        }
+      case TET10:
+        {
+          double lam1 = 1 - p.X() - p.Y() - p.Z();
+          double lam2 = p.X();
+          double lam3 = p.Y();
+          double lam4 = p.Z();
+	
+          shape(4) = 4 * lam1 * lam2;
+          shape(5) = 4 * lam1 * lam3;
+          shape(6) = 4 * lam1 * lam4;
+          shape(7) = 4 * lam2 * lam3;
+          shape(8) = 4 * lam2 * lam4;
+          shape(9) = 4 * lam3 * lam4;
+	
+          shape(0) = lam1 - 0.5 * (shape(4) + shape(5) + shape(6));
+          shape(1) = lam2 - 0.5 * (shape(4) + shape(7) + shape(8));
+          shape(2) = lam3 - 0.5 * (shape(5) + shape(7) + shape(9));
+          shape(3) = lam4 - 0.5 * (shape(6) + shape(8) + shape(9));
+          break;
+        }
+
+      case PRISM:
+        {
+          Point<3> hp = p; 
+          shape(0) = hp(0) * (1-hp(2));
+          shape(1) = hp(1) * (1-hp(2));
+          shape(2) = (1-hp(0)-hp(1)) * (1-hp(2));
+          shape(3) = hp(0) * hp(2);
+          shape(4) = hp(1) * hp(2);
+          shape(5) = (1-hp(0)-hp(1)) * hp(2);
+          break;
+        }
+      case HEX:
+        {
+          Point<3> hp = p; 
+          shape(0) = (1-hp(0))*(1-hp(1))*(1-hp(2));
+          shape(1) = (  hp(0))*(1-hp(1))*(1-hp(2));
+          shape(2) = (  hp(0))*(  hp(1))*(1-hp(2));
+          shape(3) = (1-hp(0))*(  hp(1))*(1-hp(2));
+          shape(4) = (1-hp(0))*(1-hp(1))*(  hp(2));
+          shape(5) = (  hp(0))*(1-hp(1))*(  hp(2));
+          shape(6) = (  hp(0))*(  hp(1))*(  hp(2));
+          shape(7) = (1-hp(0))*(  hp(1))*(  hp(2));
+          break;
+        }
+      }
+  }
+
+
+
+  void Element :: GetShapeNew (const Point<3> & p, FlatVector & shape) const
+  {
+    /*
+      if (shape.Size() < GetNP())
+      {
+      cerr << "Element::GetShape: Length not fitting" << endl;
+      return;
+      }
+    */
+
+    switch (typ)
+      {
+      case TET:
+        {
+          shape(0) = p(0);
+          shape(1) = p(1);
+          shape(2) = p(2);
+          shape(3) = 1-p(0)-p(1)-p(2);
+          break;
+        }
+
+      case TET10:
+        {
+          double lam1 = p(0);
+          double lam2 = p(1);
+          double lam3 = p(2);
+          double lam4 = 1-p(0)-p(1)-p(2);
+	
+          shape(0) = 2 * lam1 * (lam1-0.5);
+          shape(1) = 2 * lam2 * (lam2-0.5);
+          shape(2) = 2 * lam3 * (lam3-0.5);
+          shape(3) = 2 * lam4 * (lam4-0.5);
+
+          shape(4) = 4 * lam1 * lam2;
+          shape(5) = 4 * lam1 * lam3;
+          shape(6) = 4 * lam1 * lam4;
+          shape(7) = 4 * lam2 * lam3;
+          shape(8) = 4 * lam2 * lam4;
+          shape(9) = 4 * lam3 * lam4;
+	
+          break;
+        }
+
+
+      case PYRAMID:
+        {
+          double noz = 1-p(2);
+          if (noz == 0.0) noz = 1e-10;
+
+          double xi  = p(0) / noz;
+          double eta = p(1) / noz;
+          shape(0) = (1-xi)*(1-eta) * (noz);
+          shape(1) = (  xi)*(1-eta) * (noz);
+          shape(2) = (  xi)*(  eta) * (noz);
+          shape(3) = (1-xi)*(  eta) * (noz);
+          shape(4) = p(2);
+          break;
+        }
+
+      case PRISM:
+        {
+          shape(0) = p(0) * (1-p(2));
+          shape(1) = p(1) * (1-p(2));
+          shape(2) = (1-p(0)-p(1)) * (1-p(2));
+          shape(3) = p(0) * p(2);
+          shape(4) = p(1) * p(2);
+          shape(5) = (1-p(0)-p(1)) * p(2);
+          break;
+        }
+      case HEX:
+        {
+          shape(0) = (1-p(0))*(1-p(1))*(1-p(2));
+          shape(1) = (  p(0))*(1-p(1))*(1-p(2));
+          shape(2) = (  p(0))*(  p(1))*(1-p(2));
+          shape(3) = (1-p(0))*(  p(1))*(1-p(2));
+          shape(4) = (1-p(0))*(1-p(1))*(  p(2));
+          shape(5) = (  p(0))*(1-p(1))*(  p(2));
+          shape(6) = (  p(0))*(  p(1))*(  p(2));
+          shape(7) = (1-p(0))*(  p(1))*(  p(2));
+          break;
+        }
+      }
+  }
+
+
+
+  void Element :: 
+  GetDShape (const Point<3> & hp, DenseMatrix & dshape) const
+  {
+    Point3d p = hp;
+
+    int np = GetNP();
+    if (dshape.Height() != 3 || dshape.Width() != np)
+      {
+        cerr << "Element::DShape: Sizes don't fit" << endl;
+        return;
+      }
+
+    double eps = 1e-6;
+    Vector shaper(np), shapel(np);
+
+    for (int i = 1; i <= 3; i++)
+      {
+        Point3d pr(p), pl(p);
+        pr.X(i) += eps;
+        pl.X(i) -= eps;
+      
+        GetShape (pr, shaper);
+        GetShape (pl, shapel);
+        for (int j = 0; j < np; j++)
+          dshape(i-1, j) = (shaper(j) - shapel(j)) / (2 * eps);
+      }
+  }
+
+
+  void Element :: 
+  GetDShapeNew (const Point<3> & p, MatrixFixWidth<3> & dshape) const
+  {
+    switch (typ)
+      {
+      case TET:
+        {
+          dshape = 0;
+          dshape(0,0) = 1;
+          dshape(1,1) = 1;
+          dshape(2,2) = 1;
+          dshape(3,0) = -1;
+          dshape(3,1) = -1;
+          dshape(3,2) = -1;
+          break;
+        }
+      case PRISM:
+        {
+          dshape = 0;
+          dshape(0,0) = 1-p(2);
+          dshape(0,2) = -p(0);
+          dshape(1,1) = 1-p(2);
+          dshape(1,2) = -p(1);
+          dshape(2,0) = -(1-p(2));
+          dshape(2,1) = -(1-p(2));
+          dshape(2,2) = -(1-p(0)-p(1));
+
+          dshape(3,0) = p(2);
+          dshape(3,2) = p(0);
+          dshape(4,1) = p(2);
+          dshape(4,2) = p(1);
+          dshape(5,0) = -p(2);
+          dshape(5,1) = -p(2);
+          dshape(5,2) = 1-p(0)-p(1);
+          break;
+        }
+
+      default:
+        {
+          int np = GetNP();
+          double eps = 1e-6;
+          Vector shaper(np), shapel(np);
+	
+          for (int i = 1; i <= 3; i++)
+            {
+              Point3d pr(p), pl(p);
+              pr.X(i) += eps;
+              pl.X(i) -= eps;
+	    
+              GetShapeNew (pr, shaper);
+              GetShapeNew (pl, shapel);
+              for (int j = 0; j < np; j++)
+                dshape(j, i-1) = (shaper(j) - shapel(j)) / (2 * eps);
+            }
+        }
+      }
+  }
+
+  void Element :: 
+  GetPointMatrix (const T_POINTS & points,
+                  DenseMatrix & pmat) const
+  {
+    int np = GetNP();
+    for (int i = 1; i <= np; i++)
+      {
+        const Point3d & p = points.Get(PNum(i));
+        pmat.Elem(1, i) = p.X();
+        pmat.Elem(2, i) = p.Y();
+        pmat.Elem(3, i) = p.Z();
+      }
+  }
+
+
+
+
+  double Element :: CalcJacobianBadness (const T_POINTS & points) const
+  {
+    int nip = GetNIP();
+    DenseMatrix trans(3,3);
+    DenseMatrix pmat;
+  
+    pmat.SetSize (3, GetNP());
+    GetPointMatrix (points, pmat);
+
+    double err = 0;
+    for (int i = 1; i <= nip; i++)
+      {
+        GetTransformation (i, pmat, trans);
+
+        // Frobenius norm
+        double frob = 0;
+        for (int j = 1; j <= 9; j++)
+          frob += sqr (trans.Get(j));
+        frob = sqrt (frob);
+        frob /= 3;
+
+        double det = -trans.Det();
+      
+        if (det <= 0)
+          err += 1e12;
+        else
+          err += frob * frob * frob / det;
+      }
+
+    err /= nip;
+    return err;
+  }
+
+  double Element :: 
+  CalcJacobianBadnessDirDeriv (const T_POINTS & points,
+                               int pi, Vec<3> & dir, double & dd) const
+  {
+    int i, j, k;
+    int nip = GetNIP();
+    DenseMatrix trans(3,3), dtrans(3,3), hmat(3,3);
+    DenseMatrix pmat, vmat;
+  
+    pmat.SetSize (3, GetNP());
+    vmat.SetSize (3, GetNP());
+
+    GetPointMatrix (points, pmat);
+  
+    for (i = 1; i <= np; i++)
+      for (j = 1; j <= 3; j++)
+        vmat.Elem(j, i) = 0;
+    for (j = 1; j <= 3; j++)
+      vmat.Elem(j, pi) = dir(j-1);
+
+
+
+    double err = 0;
+    dd = 0;
+
+    for (i = 1; i <= nip; i++)
+      {
+        GetTransformation (i, pmat, trans);
+        GetTransformation (i, vmat, dtrans);
+
+
+        // Frobenius norm
+        double frob = 0;
+        for (j = 1; j <= 9; j++)
+          frob += sqr (trans.Get(j));
+        frob = sqrt (frob);
+      
+        double dfrob = 0;
+        for (j = 1; j <= 9; j++)
+          dfrob += trans.Get(j) * dtrans.Get(j);
+        dfrob = dfrob / frob;
+      
+        frob /= 3;      
+        dfrob /= 3;
+
+      
+        double det = trans.Det();
+        double ddet = 0;
+      
+        for (j = 1; j <= 3; j++)
+          {
+            hmat = trans;
+            for (k = 1; k <= 3; k++)
+              hmat.Elem(k, j) = dtrans.Get(k, j);
+            ddet += hmat.Det();
+          }
+
+
+        det *= -1;
+        ddet *= -1;
+
+      
+        if (det <= 0)
+          err += 1e12;
+        else
+          {
+            err += frob * frob * frob / det;
+            dd += (3 * frob * frob * dfrob * det - frob * frob * frob * ddet) / (det * det);
+          }
+      }
+
+    err /= nip;
+    dd /= nip;
+    return err;
+  }
+
+  double Element :: 
+  CalcJacobianBadnessGradient (const T_POINTS & points,
+                               int pi, Vec<3> & grad) const
+  {
+    int nip = GetNIP();
+    DenseMatrix trans(3,3), dtrans(3,3), hmat(3,3);
+    DenseMatrix pmat, vmat;
+  
+    pmat.SetSize (3, GetNP());
+    vmat.SetSize (3, GetNP());
+
+    GetPointMatrix (points, pmat);
+  
+    for (int i = 1; i <= np; i++)
+      for (int j = 1; j <= 3; j++)
+        vmat.Elem(j, i) = 0;
+    for (int j = 1; j <= 3; j++)
+      vmat.Elem(j, pi) = 1.;
+
+
+    double err = 0;
+
+    double dfrob[3];
+
+    grad = 0;
+
+    for (int i = 1; i <= nip; i++)
+      {
+        GetTransformation (i, pmat, trans);
+        GetTransformation (i, vmat, dtrans);
+ 
+        // Frobenius norm
+        double frob = 0;
+        for (int j = 1; j <= 9; j++)
+          frob += sqr (trans.Get(j));
+        frob = sqrt (frob);
+
+        for(int k = 0; k<3; k++)
+          {
+            dfrob[k] = 0;
+            for (int j = 1; j <= 3; j++)
+              dfrob[k] += trans.Get(k+1,j) * dtrans.Get(k+1,j);
+            dfrob[k] = dfrob[k] / (3.*frob);
+          }
+
+        frob /= 3;      
+
+        double det = trans.Det();
+        double ddet[3]; // = 0;
+      
+        for(int k=1; k<=3; k++)
+          {
+            int km1 = (k > 1) ? (k-1) : 3;
+            int kp1 = (k < 3) ? (k+1) : 1;
+            ddet[k-1] = 0;
+            for(int j=1; j<=3; j++)
+              {
+                int jm1 = (j > 1) ? (j-1) : 3;
+                int jp1 = (j < 3) ? (j+1) : 1;
+	      
+                ddet[k-1] += (-1.)* dtrans.Get(k,j) * ( trans.Get(km1,jm1)*trans.Get(kp1,jp1) - 
+                                                        trans.Get(km1,jp1)*trans.Get(kp1,jm1) );
+              }
+          }
+
+      
+        det *= -1;
+      
+        if (det <= 0)
+          err += 1e12;
+        else
+          {
+            err += frob * frob * frob / det;
+            double fac = (frob * frob)/(det * det);
+            for(int j=0; j<3; j++)
+              grad(j) += fac * (3 * dfrob[j] * det - frob * ddet[j]);
+          }
+      }
+
+    err /= nip;
+    grad *= 1./nip;
+    return err;
+  }
+
+
+
+
+
+  void Element :: ComputeIntegrationPointData () const
+  {
+    switch (GetType())
+      {
+      case TET: if (ipdtet.Size()) return; break;
+      case TET10: if (ipdtet10.Size()) return; break;
+      default:
+        PrintSysError ("Element::ComputeIntegrationPoint, illegal type ", int(typ));
+      }
+
+    switch (GetType())
+      {
+      case TET: ipdtet.SetSize(GetNIP()); break;
+      case TET10: ipdtet10.SetSize(GetNIP()); break;
+      default:
+        PrintSysError ("Element::ComputeIntegrationPoint, illegal type2 ", int(typ));
+      }
+
+
+    for (int i = 1; i <= GetNIP(); i++)
+      {
+        IntegrationPointData * ipd = new IntegrationPointData;
+        GetIntegrationPoint (i, ipd->p, ipd->weight);
+        ipd->shape.SetSize(GetNP());
+        ipd->dshape.SetSize(3, GetNP());
+
+        GetShape (ipd->p, ipd->shape);
+        GetDShape (ipd->p, ipd->dshape);
+
+        switch (GetType())
+          {
+          case TET: ipdtet.Elem(i).Reset(ipd); break;
+          case TET10: ipdtet10.Elem(i).Reset(ipd); break;
+          default:
+            PrintSysError ("Element::ComputeIntegrationPoint(2), illegal type ", int(typ));
+          }
+      }
+  }
+
+
+
+
+
+
+
+  FaceDescriptor ::  FaceDescriptor()
+  { 
+    surfnr = domin = domout  = bcprop = 0; 
+    domin_singular = domout_singular = 0.;
+    // Philippose - 06/07/2009
+    // Initialise surface colour
+    surfcolour = Vec3d(0.0,1.0,0.0);
+    tlosurf = -1; 
+    bcname = 0;
+    firstelement = -1;
+  }
+
+  FaceDescriptor ::  FaceDescriptor(const FaceDescriptor& other)
+    : surfnr(other.surfnr), domin(other.domin), domout(other.domout),
+      tlosurf(other.tlosurf), bcprop(other.bcprop), 
+      surfcolour(other.surfcolour), bcname(other.bcname),
+      domin_singular(other.domin_singular), domout_singular(other.domout_singular)
+  { 
+    firstelement = -1;
+  }
+
+  FaceDescriptor :: 
+  FaceDescriptor(int surfnri, int domini, int domouti, int tlosurfi)
+  { 
+    surfnr = surfnri; 
+    domin = domini; 
+    domout = domouti;
+    // Philippose - 06/07/2009
+    // Initialise surface colour
+    surfcolour = Vec3d(0.0,1.0,0.0);
+    tlosurf = tlosurfi; 
+    bcprop = surfnri;
+    domin_singular = domout_singular = 0.;
+    bcname = 0;
+    firstelement = -1;
+  }
+
+  FaceDescriptor :: FaceDescriptor(const Segment & seg)
+  { 
+    surfnr = seg.si; 
+    domin = seg.domin+1;
+    domout = seg.domout+1;
+    // Philippose - 06/07/2009
+    // Initialise surface colour
+    surfcolour = Vec3d(0.0,1.0,0.0);
+    tlosurf = seg.tlosurf+1;
+    bcprop = 0;
+    domin_singular = domout_singular = 0.;
+    bcname = 0;
+    firstelement = -1;
+  }
+
+  int FaceDescriptor ::  SegmentFits (const Segment & seg)
+  {
+    return
+      surfnr == seg.si &&
+      domin == seg.domin+1 &&
+      domout == seg.domout+1  &&
+      tlosurf == seg.tlosurf+1;
+  }
+
+
+  string FaceDescriptor :: GetBCName () const
+  {
+    if ( bcname )
+      return *bcname;
+    else 
+      return "default";
+  
+  }
+
+  /*
+    void FaceDescriptor :: SetBCName (string * bcn)
+    {
+    bcname = bcn;
+    }
+  */
+
+
+  ostream & operator<<(ostream  & s, const FaceDescriptor & fd)
+  {
+    s << "surfnr = " << fd.SurfNr() 
+      << ", domin = " << fd.DomainIn()
+      << ", domout = " << fd.DomainOut()
+      << ", tlosurf = " << fd.TLOSurface()
+      << ", bcprop = " << fd.BCProperty()
+      << ", domin_sing = " << fd.DomainInSingular()
+      << ", domout_sing = " << fd.DomainOutSingular()
+      << ", colour = " << fd.SurfColour();
+    return s;
+  }
+
+
+
+
+
+
+  Identifications :: Identifications (Mesh & amesh)
+    : mesh(amesh)
+  {
+    identifiedpoints = new INDEX_2_HASHTABLE<int>(100);
+    identifiedpoints_nr = new INDEX_3_HASHTABLE<int>(100);
+    maxidentnr = 0;
+  }
+
+  Identifications :: ~Identifications ()
+  {
+    delete identifiedpoints;
+    delete identifiedpoints_nr;
+  }
+
+  void Identifications :: Delete ()
+  {
+    delete identifiedpoints;
+    identifiedpoints = new INDEX_2_HASHTABLE<int>(100);
+    delete identifiedpoints_nr;
+    identifiedpoints_nr = new INDEX_3_HASHTABLE<int>(100);
+    maxidentnr = 0;
+  }
+
+  void Identifications :: Add (PointIndex pi1, PointIndex pi2, int identnr)
+  {
+    //  (*testout) << "Identification::Add, pi1 = " << pi1 << ", pi2 = " << pi2 << ", identnr = " << identnr << endl;
+    INDEX_2 pair (pi1, pi2);
+    identifiedpoints->Set (pair, identnr);
+
+    INDEX_3 tripl (pi1, pi2, identnr);
+    identifiedpoints_nr->Set (tripl, 1);
+
+    if (identnr > maxidentnr) maxidentnr = identnr;
+
+    if (identnr+1 > idpoints_table.Size())
+      idpoints_table.ChangeSize (identnr+1);
+    idpoints_table.Add (identnr, pair);
+  
+    //  timestamp = NextTimeStamp();
+  }
+
+  int Identifications :: Get (PointIndex pi1, PointIndex pi2) const
+  {
+    INDEX_2 pair(pi1, pi2);
+    if (identifiedpoints->Used (pair))
+      return identifiedpoints->Get(pair);
+    else
+      return 0;
+  }
+
+  bool Identifications :: Get (PointIndex pi1, PointIndex pi2, int nr) const
+  {
+    INDEX_3 tripl(pi1, pi2, nr);
+    if (identifiedpoints_nr->Used (tripl))
+      return 1;
+    else
+      return 0;
+  }
+
+
+
+  int Identifications :: GetSymmetric (PointIndex pi1, PointIndex pi2) const
+  {
+    INDEX_2 pair(pi1, pi2);
+    if (identifiedpoints->Used (pair))
+      return identifiedpoints->Get(pair);
+
+    pair = INDEX_2 (pi2, pi1);
+    if (identifiedpoints->Used (pair))
+      return identifiedpoints->Get(pair);
+
+    return 0;
+  }
+
+
+  void Identifications :: GetMap (int identnr, Array<int,PointIndex::BASE> & identmap, bool symmetric) const
+  {
+    identmap.SetSize (mesh.GetNP());
+    identmap = 0;
+
+    if (identnr)
+      for (int i = 0; i < idpoints_table[identnr].Size(); i++)
+        {
+          INDEX_2 pair = idpoints_table[identnr][i];
+          identmap[pair.I1()] = pair.I2();
+          if(symmetric)
+            identmap[pair.I2()] = pair.I1();
+        }
+
+    else
+      {
+        cout << "getmap, identnr = " << identnr << endl;
+
+        for (int i = 1; i <= identifiedpoints_nr->GetNBags(); i++)
+          for (int j = 1; j <= identifiedpoints_nr->GetBagSize(i); j++)
+            {
+              INDEX_3 i3;
+              int dummy;
+              identifiedpoints_nr->GetData (i, j, i3, dummy);
+	    
+              if (i3.I3() == identnr || !identnr)
+                {
+                  identmap.Elem(i3.I1()) = i3.I2();
+                  if(symmetric)
+                    identmap.Elem(i3.I2()) = i3.I1();
+                }
+            }  
+      }
+
+  }
+
+
+  void Identifications :: GetPairs (int identnr, 
+                                    Array<INDEX_2> & identpairs) const
+  {
+    identpairs.SetSize(0);
+  
+    if (identnr == 0)
+      for (int i = 1; i <= identifiedpoints->GetNBags(); i++)
+        for (int j = 1; j <= identifiedpoints->GetBagSize(i); j++)
+          {
+            INDEX_2 i2;
+            int nr;
+            identifiedpoints->GetData (i, j, i2, nr);
+            identpairs.Append (i2);
+          }  
+    else
+      for (int i = 1; i <= identifiedpoints_nr->GetNBags(); i++)
+        for (int j = 1; j <= identifiedpoints_nr->GetBagSize(i); j++)
+          {
+            INDEX_3 i3;
+            int dummy;
+            identifiedpoints_nr->GetData (i, j, i3 , dummy);
+	  
+            if (i3.I3() == identnr)
+              identpairs.Append (INDEX_2(i3.I1(), i3.I2()));
+          }  
+  }
+
+
+  void Identifications :: SetMaxPointNr (int maxpnum)
+  {
+    for (int i = 1; i <= identifiedpoints->GetNBags(); i++)
+      for (int j = 1; j <= identifiedpoints->GetBagSize(i); j++)
+        {
+          INDEX_2 i2;
+          int nr;
+          identifiedpoints->GetData (i, j, i2, nr);
+	
+          if (i2.I1() > maxpnum || i2.I2() > maxpnum)
+            {
+              i2.I1() = i2.I2() = -1;
+              identifiedpoints->SetData (i, j, i2, -1);	    
+            }
+        }
+  }
+
+
+  void Identifications :: Print (ostream & ost) const
+  {
+    ost << "Identifications:" << endl;
+    ost << "pairs: " << endl << *identifiedpoints << endl;
+    ost << "pairs and nr: " << endl << *identifiedpoints_nr << endl;
+    ost << "table: " << endl << idpoints_table << endl;
+  }
+
+
+  MeshingParameters :: MeshingParameters ()
+  {
+    optimize3d = "cmdmustm";
+    //optimize3d = "cmdmstm";
+    optsteps3d = 3;
+    optimize2d = "smsmsmSmSmSm";
+    optsteps2d = 3;
+    opterrpow = 2;
+    blockfill = 1;
+    filldist = 0.1;
+    safety = 5;
+    relinnersafety = 3;
+    uselocalh = 1;
+    grading = 0.3;
+    delaunay = 1;
+    maxh = 1e10;
+    minh = 0;
+    meshsizefilename = NULL;
+    startinsurface = 0;
+    checkoverlap = 1;
+    checkoverlappingboundary = 1;
+    checkchartboundary = 1;
+    curvaturesafety = 2;
+    segmentsperedge = 1;
+    parthread = 0;
+
+    elsizeweight = 0.2;
+    giveuptol2d = 200;
+    giveuptol = 10;
+    maxoutersteps = 10;
+    starshapeclass = 5;
+    baseelnp = 0;
+    sloppy = 1;
+
+    badellimit = 175;
+    check_impossible = 0;
+    secondorder = 0;
+  }
+
+  void MeshingParameters :: Print (ostream & ost) const
+  {
+    ost << "Meshing parameters: " << endl
+        << "optimize3d = " << optimize3d << endl
+        << "optsteps3d = " << optsteps3d << endl
+        << " optimize2d = " <<  optimize2d << endl
+        << " optsteps2d = " <<  optsteps2d << endl
+        << " opterrpow = " <<  opterrpow << endl
+        << " blockfill = " <<  blockfill << endl
+        << " filldist = " <<  filldist << endl
+        << " safety = " <<  safety << endl
+        << " relinnersafety = " <<  relinnersafety << endl
+        << " uselocalh = " <<  uselocalh << endl
+        << " grading = " <<  grading << endl
+        << " delaunay = " <<  delaunay << endl
+        << " maxh = " <<  maxh << endl;
+    if(meshsizefilename)
+      ost << " meshsizefilename = " <<  meshsizefilename << endl;
+    else
+      ost << " meshsizefilename = NULL" << endl;
+    ost << " startinsurface = " <<  startinsurface << endl
+        << " checkoverlap = " <<  checkoverlap << endl
+        << " checkchartboundary = " <<  checkchartboundary << endl
+        << " curvaturesafety = " <<  curvaturesafety << endl
+        << " segmentsperedge = " <<  segmentsperedge << endl
+        << " parthread = " <<  parthread << endl
+        << " elsizeweight = " <<  elsizeweight << endl
+        << " giveuptol2d = " <<  giveuptol2d << endl
+        << " giveuptol = " <<  giveuptol << endl
+        << " maxoutersteps = " <<  maxoutersteps << endl
+        << " starshapeclass = " <<  starshapeclass << endl
+        << " baseelnp        = " <<  baseelnp        << endl
+        << " sloppy = " <<  sloppy << endl
+        << " badellimit = " <<  badellimit << endl
+        << " secondorder = " <<  secondorder << endl
+        << " elementorder = " <<  elementorder << endl
+        << " quad = " <<  quad << endl
+        << " inverttets = " <<  inverttets << endl
+        << " inverttrigs = " <<  inverttrigs << endl;
+  }
+
+  void MeshingParameters :: CopyFrom(const MeshingParameters & other)
+  {
+    //strcpy(optimize3d,other.optimize3d); 
+    optimize3d = other.optimize3d;
+    optsteps3d = other.optsteps3d;
+    //strcpy(optimize2d,other.optimize2d); 
+    optimize2d = other.optimize2d;
+    optsteps2d = other.optsteps2d;
+    opterrpow = other.opterrpow;
+    blockfill = other.blockfill;
+    filldist = other.filldist;
+    safety = other.safety;
+    relinnersafety = other.relinnersafety;
+    uselocalh = other.uselocalh;
+    grading = other.grading;
+    delaunay = other.delaunay;
+    maxh = other.maxh;
+    //strcpy(const_cast<char*>(meshsizefilename), other.meshsizefilename);
+    //const_cast<char*>(meshsizefilename) = other.meshsizefilename; //???
+    startinsurface = other.startinsurface;
+    checkoverlap = other.checkoverlap;
+    checkoverlappingboundary = other.checkoverlappingboundary;
+    checkchartboundary = other.checkchartboundary;
+    curvaturesafety = other.curvaturesafety;
+    segmentsperedge = other.segmentsperedge;
+    parthread = other.parthread;
+    elsizeweight = other.elsizeweight;
+    giveuptol2d = other.giveuptol2d;
+    giveuptol = other.giveuptol;
+    maxoutersteps = other.maxoutersteps;
+    starshapeclass = other.starshapeclass;
+    baseelnp = other.baseelnp;       
+    sloppy = other.sloppy;
+    badellimit = other.badellimit;
+    secondorder = other.secondorder;
+    elementorder = other.elementorder;
+    quad = other.quad;
+    inverttets = other.inverttets;
+    inverttrigs = other.inverttrigs;
+  }
+
+
+  DebugParameters :: DebugParameters ()
+  {
+    slowchecks = 0;
+    haltsuccess = 0;
+    haltnosuccess = 0;
+    haltlargequalclass = 0;
+    haltsegment = 0;
+    haltsegmentp1 = 0;
+    haltsegmentp2 = 0;
+  };
+
+
+
+}
+
diff --git a/contrib/Netgen/libsrc/meshing/meshtype.hpp b/contrib/Netgen/libsrc/meshing/meshtype.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c7b3609ded1231d5781c04af3a28171b241deee3
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshtype.hpp
@@ -0,0 +1,1306 @@
+#ifndef MESHTYPE
+#define MESHTYPE
+
+
+/**************************************************************************/
+/* File:   meshtype.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+namespace netgen
+{
+
+  /*
+    Classes for NETGEN
+  */
+
+
+
+  enum ELEMENT_TYPE { 
+    SEGMENT = 1, SEGMENT3 = 2,
+    TRIG = 10, QUAD=11, TRIG6 = 12, QUAD6 = 13, QUAD8 = 14,
+    TET = 20, TET10 = 21, 
+    PYRAMID = 22, PRISM = 23, PRISM12 = 24,
+    HEX = 25
+  };
+
+  typedef int ELEMENT_EDGE[2];      // initial point, end point
+  typedef int ELEMENT_FACE[4];      // points, last one is -1 for trig
+
+
+#define ELEMENT_MAXPOINTS 12
+#define ELEMENT2D_MAXPOINTS 8
+
+
+  enum POINTTYPE { FIXEDPOINT = 1, EDGEPOINT = 2, SURFACEPOINT = 3, INNERPOINT = 4 };
+  enum ELEMENTTYPE { FREEELEMENT, FIXEDELEMENT };
+  enum OPTIMIZEGOAL { OPT_QUALITY, OPT_CONFORM, OPT_REST, OPT_WORSTCASE, OPT_LEGAL };
+
+
+
+  extern DLL_HEADER int GetTimeStamp();
+  extern DLL_HEADER int NextTimeStamp();
+
+  class PointGeomInfo
+  {
+  public:
+    int trignum;   // for STL Meshing
+    double u, v;   // for OCC Meshing
+
+    PointGeomInfo () 
+      : trignum(-1), u(0), v(0) { ; }
+  };
+
+  inline ostream & operator<< (ostream & ost, const PointGeomInfo & gi)
+  {
+    return (ost << gi.trignum << " " << gi.u << " " << gi.v);
+  }
+
+  inline istream & operator>> (istream & ist, PointGeomInfo & gi)
+  {
+    return (ist >> gi.trignum >> gi.u >> gi.v);
+  }
+
+
+
+#define MULTIPOINTGEOMINFO_MAX 100
+  class MultiPointGeomInfo
+  {
+    int cnt;
+    PointGeomInfo mgi[MULTIPOINTGEOMINFO_MAX];
+  public:
+    MultiPointGeomInfo () { cnt = 0; }
+    int AddPointGeomInfo (const PointGeomInfo & gi);
+    void Init () { cnt = 0; }
+    void DeleteAll () { cnt = 0; }
+
+    int GetNPGI () const { return cnt; }
+    const PointGeomInfo & GetPGI (int i) const { return mgi[i-1]; }
+  };
+
+
+  class EdgePointGeomInfo
+  {
+  public:
+    int edgenr;
+    int body;    // for ACIS
+    double dist; // for 2d meshing
+    double u, v; // for OCC Meshing
+
+  public:
+    EdgePointGeomInfo ()
+      : edgenr(0), body(0), dist(0.0), u(0.0), v(0.0) { ; }
+
+
+    EdgePointGeomInfo & operator= (const EdgePointGeomInfo & gi2)
+    {
+      edgenr = gi2.edgenr;  
+      body = gi2.body;
+      dist = gi2.dist;
+      u = gi2.u; v = gi2.v;
+      return *this;
+    }
+  };
+
+  inline ostream & operator<< (ostream & ost, const EdgePointGeomInfo & gi)
+  {
+    ost << "epgi: edgnr=" << gi.edgenr << ", dist=" << gi.dist;
+    return ost;
+  }
+
+
+
+
+
+  class PointIndex
+  {
+    int i;
+  public:
+    PointIndex () { ; }
+    PointIndex (int ai) : i(ai) { ; }
+    PointIndex & operator= (const PointIndex &ai) { i = ai.i; return *this; }
+    PointIndex & operator= (int ai) { i = ai; return *this; }
+    operator int () const { return i; }
+    int GetInt () const { return i; }
+    PointIndex operator++ (int) { int hi = i; i++; return PointIndex(hi); }
+    PointIndex operator-- (int) { int hi = i; i--; return PointIndex(hi); }
+
+#ifdef BASE0
+    enum { BASE = 0 };
+#else
+    enum { BASE = 1 };
+#endif  
+  };
+
+  inline istream & operator>> (istream & ist, PointIndex & pi)
+  {
+    int i; ist >> i; pi = i; return ist;
+  }
+
+  inline ostream & operator<< (ostream & ost, const PointIndex & pi)
+  {
+    return (ost << pi.GetInt());
+  }
+
+
+
+
+  class ElementIndex
+  {
+    int i;
+  public:
+    ElementIndex () { ; }
+    ElementIndex (int ai) : i(ai) { ; }
+    ElementIndex & operator= (const ElementIndex & ai) { i = ai.i; return *this; }
+    ElementIndex & operator= (int ai) { i = ai; return *this; }
+    operator int () const { return i; }
+    ElementIndex & operator++ (int) { i++; return *this; }
+    ElementIndex & operator-- (int) { i--; return *this; }
+  };
+
+  inline istream & operator>> (istream & ist, ElementIndex & pi)
+  {
+    int i; ist >> i; pi = i; return ist;
+  }
+
+  inline ostream & operator<< (ostream & ost, const ElementIndex & pi)
+  {
+    return (ost << int(pi));
+  }
+
+
+  class SurfaceElementIndex
+  {
+    int i;
+  public:
+    SurfaceElementIndex () { ; }
+    SurfaceElementIndex (int ai) : i(ai) { ; }
+    SurfaceElementIndex & operator= (const SurfaceElementIndex & ai) 
+    { i = ai.i; return *this; }
+    SurfaceElementIndex & operator= (int ai) { i = ai; return *this; }
+    operator int () const { return i; }
+    SurfaceElementIndex & operator++ (int) { i++; return *this; }
+    SurfaceElementIndex & operator-- (int) { i--; return *this; }
+  };
+
+  inline istream & operator>> (istream & ist, SurfaceElementIndex & pi)
+  {
+    int i; ist >> i; pi = i; return ist;
+  }
+
+  inline ostream & operator<< (ostream & ost, const SurfaceElementIndex & pi)
+  {
+    return (ost << int(pi));
+  }
+
+  class SegmentIndex
+  {
+    int i;
+  public:
+    SegmentIndex () { ; }
+    SegmentIndex (int ai) : i(ai) { ; }
+    SegmentIndex & operator= (const SegmentIndex & ai) 
+    { i = ai.i; return *this; }
+    SegmentIndex & operator= (int ai) { i = ai; return *this; }
+    operator int () const { return i; }
+    SegmentIndex & operator++ (int) { i++; return *this; }
+    SegmentIndex & operator-- (int) { i--; return *this; }
+  };
+
+  inline istream & operator>> (istream & ist, SegmentIndex & pi)
+  {
+    int i; ist >> i; pi = i; return ist;
+  }
+
+  inline ostream & operator<< (ostream & ost, const SegmentIndex & pi)
+  {
+    return (ost << int(pi));
+  }
+
+
+
+
+  /**
+     Point in the mesh.
+     Contains layer (a new feature in 4.3 for overlapping meshes.
+  */
+  class MeshPoint : public Point<3>
+  {
+    int layer;
+    double singular; // singular factor for hp-refinement
+    POINTTYPE type;
+
+#ifdef PARALLEL
+    bool isghost;
+#endif
+
+  public:
+    MeshPoint () 
+    { 
+#ifdef PARALLEL
+      isghost = 0;
+#endif
+      ;
+    }
+
+    MeshPoint (const Point<3> & ap, int alayer = 1, POINTTYPE apt = INNERPOINT)
+      : Point<3> (ap), layer(alayer), singular(0.),type(apt) 
+    { 
+#ifdef PARALLEL
+      isghost = 0;
+#endif  
+      ;
+    }
+  
+    void SetPoint (const Point<3> & ap)
+    { 
+      Point<3>::operator= (ap); 
+      layer = 0; 
+      singular = 0; 
+#ifdef PARALLEL
+      isghost = 0;
+#endif
+    }
+
+    int GetLayer() const { return layer; }
+
+    POINTTYPE Type() const { return type; }
+    void SetType(POINTTYPE at) { type = at; }
+ 
+    double Singularity() const { return singular; }
+    void Singularity(double s) { singular = s; }
+    bool IsSingular() const { return (singular != 0.0); }
+
+#ifdef PARALLEL
+    bool IsGhost () const { return isghost; }
+    void SetGhost ( bool aisghost ) { isghost = aisghost; }
+
+    static MPI_Datatype MyGetMPIType ( );
+#endif
+
+  };
+
+  inline ostream & operator<<(ostream  & s, const MeshPoint & pt)
+  { 
+    return (s << Point<3> (pt)); 
+  }
+
+
+
+
+  typedef Array<MeshPoint, PointIndex::BASE> T_POINTS;
+
+
+
+  /**
+     Triangle element for surface mesh generation.
+  */
+  class Element2d
+  { 
+    /// point numbers
+    PointIndex pnum[ELEMENT2D_MAXPOINTS];
+    /// geom info of points
+    PointGeomInfo geominfo[ELEMENT2D_MAXPOINTS];
+
+    /// surface nr
+    int index:16;
+    ///
+    ELEMENT_TYPE typ:6;
+    /// number of points
+    unsigned int np:4;
+    bool badel:1;
+    bool refflag:1;  // marked for refinement
+    bool strongrefflag:1;
+    bool deleted:1;  // element is deleted
+
+    // Philippose - 08 August 2010
+    // Set a new property for each element, to 
+    // control whether it is visible or not
+    bool visible:1;  // element visible
+
+    /// order for hp-FEM
+    unsigned int orderx:6;
+    unsigned int ordery:6;
+
+#ifdef PARALLEL
+    bool isghost;
+    int partitionNumber; 
+#endif
+
+    /// a linked list for all segments in the same face
+    SurfaceElementIndex next;
+
+  public:
+    ///
+    Element2d ();
+    ///
+    Element2d (int anp);
+    ///
+    DLL_HEADER Element2d (ELEMENT_TYPE type);
+    ///
+    Element2d (int pi1, int pi2, int pi3);
+    ///
+    Element2d (int pi1, int pi2, int pi3, int pi4);
+    ///
+    ELEMENT_TYPE GetType () const { return typ; }
+    /// 
+    void SetType (ELEMENT_TYPE atyp)
+    {
+      typ = atyp;
+      switch (typ)
+	{
+	case TRIG: np = 3; break;
+	case QUAD: np = 4; break;
+	case TRIG6: np = 6; break;
+	case QUAD6: np = 6; break;
+	case QUAD8: np = 8; break;
+	default:
+	  PrintSysError ("Element2d::SetType, illegal type ", typ);
+	}
+    }
+    ///
+    int GetNP() const { return np; }
+    ///
+    int GetNV() const
+    {
+      switch (typ)
+	{
+	case TRIG:
+	case TRIG6: return 3;
+
+	case QUAD:
+	case QUAD8:
+	case QUAD6: return 4;
+	default:
+#ifdef DEBUG
+	  PrintSysError ("element2d::GetNV not implemented for typ", typ)
+#endif
+	    ;
+	}
+      return np;
+    }
+
+    ///
+    PointIndex & operator[] (int i) { return pnum[i]; }
+    ///
+    const PointIndex & operator[] (int i) const { return pnum[i]; }
+
+    ///
+    PointIndex & PNum (int i) { return pnum[i-1]; }
+    ///
+    const PointIndex & PNum (int i) const { return pnum[i-1]; }
+    ///
+    PointIndex & PNumMod (int i) { return pnum[(i-1) % np]; }
+    ///
+    const PointIndex & PNumMod (int i) const { return pnum[(i-1) % np]; }
+    ///
+
+    ///
+    PointGeomInfo & GeomInfoPi (int i) { return geominfo[i-1]; }
+    ///
+    const PointGeomInfo & GeomInfoPi (int i) const { return geominfo[i-1]; }
+    ///
+    PointGeomInfo & GeomInfoPiMod (int i) { return geominfo[(i-1) % np]; }
+    ///
+    const PointGeomInfo & GeomInfoPiMod (int i) const { return geominfo[(i-1) % np]; }
+
+
+    void SetIndex (int si) { index = si; }
+    ///
+    int GetIndex () const { return index; }
+
+    int GetOrder () const { return orderx; }
+    void SetOrder (int aorder) { orderx = ordery = aorder; }
+
+
+    void GetOrder (int & ox, int & oy) const { ox = orderx, oy =ordery;};
+    void GetOrder (int & ox, int & oy, int & oz) const { ox = orderx; oy = ordery; oz=0; }
+    void SetOrder (int ox, int oy, int  /* oz */) { orderx = ox; ordery = oy;}
+    void SetOrder (int ox, int oy) { orderx = ox; ordery = oy;}
+
+
+    ///
+    void GetBox (const T_POINTS & points, Box3d & box) const;
+    /// invert orientation
+    inline void Invert ();
+    ///
+    void Invert2 ();
+    /// first point number is smallest
+    inline void NormalizeNumbering ();
+    ///
+    void NormalizeNumbering2 ();
+
+    bool BadElement() const { return badel; }
+
+    // friend ostream & operator<<(ostream  & s, const Element2d & el);
+    friend class Mesh;
+
+
+    /// get number of 'integration points'
+    int GetNIP () const;
+    void GetIntegrationPoint (int ip, Point2d & p, double & weight) const;
+
+    void GetTransformation (int ip, const Array<Point2d> & points,
+			    class DenseMatrix & trans) const;
+    void GetTransformation (int ip, class DenseMatrix & pmat,
+			    class DenseMatrix & trans) const;
+
+    void GetShape (const Point2d & p, class Vector & shape) const;
+    void GetShapeNew (const Point<2> & p, class FlatVector & shape) const;
+    /// matrix 2 * np
+    void GetDShape (const Point2d & p, class DenseMatrix & dshape) const;
+    void GetDShapeNew (const Point<2> & p, class MatrixFixWidth<2> & dshape) const;
+    /// matrix 2 * np
+    void GetPointMatrix (const Array<Point2d> & points,
+			 class DenseMatrix & pmat) const; 
+
+    void ComputeIntegrationPointData () const;
+  
+
+    double CalcJacobianBadness (const Array<Point2d> & points) const;
+    double CalcJacobianBadness (const T_POINTS & points, 
+				const Vec<3> & n) const;
+    double CalcJacobianBadnessDirDeriv (const Array<Point2d> & points,
+					int pi, Vec2d & dir, double & dd) const;
+
+
+
+    void Delete () { deleted = 1; pnum[0] = pnum[1] = pnum[2] = pnum[3] = PointIndex::BASE-1; }
+    bool IsDeleted () const 
+    {
+#ifdef DEBUG
+      if (pnum[0] < PointIndex::BASE && !deleted)
+	cerr << "Surfelement has illegal pnum, but not marked as deleted" << endl;
+#endif    
+      return deleted; 
+    }
+
+    // Philippose - 08 August 2010
+    // Access functions for the new property: visible
+    void Visible(bool vis = 1) 
+    { visible = vis; }
+    bool IsVisible () const 
+    { return visible; }
+   
+    void SetRefinementFlag (bool rflag = 1) 
+    { refflag = rflag; }
+    bool TestRefinementFlag () const
+    { return refflag; }
+
+    void SetStrongRefinementFlag (bool rflag = 1) 
+    { strongrefflag = rflag; }
+    bool TestStrongRefinementFlag () const
+    { return strongrefflag; }
+
+  
+    SurfaceElementIndex NextElement() { return next; }
+
+    bool operator==(const Element2d & el2) const;
+
+    int HasFace(const Element2d& el) const;
+    ///
+    int meshdocval;
+    ///
+    int hp_elnr;
+
+#ifdef PARALLEL
+    bool IsGhost () const { return isghost; }
+    void SetGhost ( bool aisghost ) { isghost = aisghost; }
+
+    // by JS, only for 2D meshes ????
+    int GetPartition () const { return partitionNumber; }
+    void SetPartition (int nr) { partitionNumber = nr; }; 
+
+#else
+    bool IsGhost () const { return false; }
+#endif
+  };
+
+
+  ostream & operator<<(ostream  & s, const Element2d & el);
+
+
+
+
+
+  class IntegrationPointData
+  {
+  public:
+    Point<3> p;
+    double weight;
+    Vector shape;
+    DenseMatrix dshape;
+  };
+
+
+
+
+
+
+
+
+  /**
+     Volume element
+  */
+  class Element
+  {
+  private:
+    /// point numbers
+    PointIndex pnum[ELEMENT_MAXPOINTS];
+    ///
+    ELEMENT_TYPE typ:6;
+    /// number of points (4..tet, 5..pyramid, 6..prism, 8..hex, 10..quad tet, 12..quad prism)
+    int np:5;
+    ///
+    class flagstruct { 
+    public:
+      bool marked:1;  // marked for refinement
+      bool badel:1;   // angles worse then limit
+      bool reverse:1; // for refinement a la Bey
+      bool illegal:1; // illegal, will be split or swaped 
+      bool illegal_valid:1; // is illegal-flag valid ?
+      bool badness_valid:1; // is badness valid ?
+      bool refflag:1;     // mark element for refinement
+      bool strongrefflag:1;
+      bool deleted:1;   // element is deleted, will be removed from array
+      bool fixed:1;     // don't change element in optimization
+    };
+
+    /// sub-domain index
+    short int index;
+    /// order for hp-FEM
+    unsigned int orderx:6;
+    unsigned int ordery:6;
+    unsigned int orderz:6;
+    /* unsigned int levelx:6;
+       unsigned int levely:6;
+       unsigned int levelz:6; */ 
+    /// stored shape-badness of element
+    float badness;
+  
+#ifdef PARALLEL
+    /// number of partition for parallel computation 
+    int partitionNumber;
+    bool isghost;
+#endif
+
+  public:
+    flagstruct flags;
+
+    ///
+    Element ();
+    ///
+    Element (int anp);
+    ///
+    Element (ELEMENT_TYPE type);
+    ///
+    Element & operator= (const Element & el2);
+  
+    ///
+    void SetNP (int anp);
+    ///
+    void SetType (ELEMENT_TYPE atyp);
+    ///
+    int GetNP () const { return np; }
+    ///
+    int GetNV() const
+    {
+      switch (typ)
+	{
+	case TET: 
+	case TET10: 
+	  return 4;
+	case PRISM12: 
+	case PRISM: 
+	  return 6; 
+	case PYRAMID:
+	  return 5;
+	case HEX:
+	  return 8;
+	default:
+#ifdef DEBUG
+	  PrintSysError ("Element3d::GetNV not implemented for typ ", typ)
+#endif
+	    ;
+	}
+      return np;
+    }
+
+    bool operator==(const Element & el2) const;
+
+    // old style:
+    int NP () const { return np; }
+
+    ///
+    ELEMENT_TYPE GetType () const { return typ; }
+
+    ///
+    PointIndex & operator[] (int i) { return pnum[i]; }
+    ///
+    const PointIndex & operator[] (int i) const { return pnum[i]; }
+
+    ///
+    PointIndex & PNum (int i) { return pnum[i-1]; }
+    ///
+    const PointIndex & PNum (int i) const { return pnum[i-1]; }
+    ///
+    PointIndex & PNumMod (int i) { return pnum[(i-1) % np]; }
+    ///
+    const PointIndex & PNumMod (int i) const { return pnum[(i-1) % np]; }
+  
+    ///
+    void SetIndex (int si) { index = si; }
+    ///
+    int GetIndex () const { return index; }
+
+    int GetOrder () const { return orderx; }
+    void SetOrder (const int aorder) ; 
+
+    void GetOrder (int & ox, int & oy, int & oz) const { ox = orderx; oy = ordery; oz = orderz; }
+    void SetOrder (const int ox, const int oy, const int oz);
+    // void GetLevel (int & ox, int & oy, int & oz) const { ox = levelx; oy = levely; oz = levelz; }
+    // void SetLevel (int ox, int oy, int oz) { levelx = ox; levely = oy; levelz = oz; }
+
+
+    ///
+    void GetBox (const T_POINTS & points, Box3d & box) const;
+    /// Calculates Volume of elemenet
+    double Volume (const T_POINTS & points) const;
+    ///
+    virtual void Print (ostream & ost) const;
+    ///
+    int GetNFaces () const
+    {
+      switch (typ)
+	{
+	case TET: 
+	case TET10: return 4;
+	case PYRAMID: return 5;
+	case PRISM: 
+	case PRISM12: return 5;
+	default:
+#ifdef DEBUG
+	  PrintSysError ("element3d::GetNFaces not implemented for typ", typ)
+#endif
+	    ;
+	}
+      return 0;
+    }
+    ///
+    inline void GetFace (int i, Element2d & face) const;
+    ///
+    void GetFace2 (int i, Element2d & face) const;
+    ///
+    void Invert ();
+
+    /// split into 4 node tets
+    void GetTets (Array<Element> & locels) const;
+    /// split into 4 node tets, local point nrs
+    void GetTetsLocal (Array<Element> & locels) const;
+    /// returns coordinates of nodes
+    // void GetNodesLocal (Array<Point<3> > & points) const;
+    void GetNodesLocalNew (Array<Point<3> > & points) const;
+
+    /// split surface into 3 node trigs
+    void GetSurfaceTriangles (Array<Element2d> & surftrigs) const;
+
+
+    /// get number of 'integration points'
+    int GetNIP () const;
+    void GetIntegrationPoint (int ip, Point<3> & p, double & weight) const;
+
+    void GetTransformation (int ip, const T_POINTS & points,
+			    class DenseMatrix & trans) const;
+    void GetTransformation (int ip, class DenseMatrix & pmat,
+			    class DenseMatrix & trans) const;
+
+    void GetShape (const Point<3> & p, class Vector & shape) const;
+    void GetShapeNew (const Point<3> & p, class FlatVector & shape) const;
+    /// matrix 2 * np
+    void GetDShape (const Point<3> & p, class DenseMatrix & dshape) const;
+    void GetDShapeNew (const Point<3> & p, class MatrixFixWidth<3> & dshape) const;
+    /// matrix 3 * np
+    void GetPointMatrix (const T_POINTS & points,
+			 class DenseMatrix & pmat) const; 
+
+    void ComputeIntegrationPointData () const;
+  
+
+    double CalcJacobianBadness (const T_POINTS & points) const;
+    double CalcJacobianBadnessDirDeriv (const T_POINTS & points,
+					int pi, Vec<3> & dir, double & dd) const;
+    double CalcJacobianBadnessGradient (const T_POINTS & points,
+					int pi, Vec<3> & grad) const;
+
+    ///
+    // friend ostream & operator<<(ostream  & s, const Element & el);
+
+    void SetRefinementFlag (bool rflag = 1) 
+    { flags.refflag = rflag; }
+    int TestRefinementFlag () const
+    { return flags.refflag; }
+
+    void SetStrongRefinementFlag (bool rflag = 1) 
+    { flags.strongrefflag = rflag; }
+    int TestStrongRefinementFlag () const
+    { return flags.strongrefflag; }
+
+    int Illegal () const
+    { return flags.illegal; }
+    int IllegalValid () const
+    { return flags.illegal_valid; }
+    void SetIllegal (int aillegal)
+    {
+      flags.illegal = aillegal ? 1 : 0;
+      flags.illegal_valid = 1;
+    }
+    void SetLegal (int alegal)
+    {
+      flags.illegal = alegal ? 0 : 1;
+      flags.illegal_valid = 1;
+    }
+  
+    void Delete () { flags.deleted = 1; }
+    bool IsDeleted () const 
+    { 
+#ifdef DEBUG
+      if (pnum[0] < PointIndex::BASE && !flags.deleted)
+	cerr << "Volelement has illegal pnum, but not marked as deleted" << endl;
+#endif    
+
+      return flags.deleted; 
+    }
+
+
+
+#ifdef PARALLEL
+    int GetPartition () const { return partitionNumber; }
+    void SetPartition (int nr) { partitionNumber = nr; }; 
+
+    bool IsGhost () const { return isghost; }
+    void SetGhost ( const bool aisghost ) { isghost = aisghost; }
+#else
+    bool IsGhost () const { return false; }
+    int GetPartition () const { return 0; }
+#endif
+
+    int hp_elnr;
+  };
+
+  ostream & operator<<(ostream  & s, const Element & el);
+
+
+
+
+
+
+  /**
+     Edge segment.
+  */
+  class Segment
+  {
+  public:
+    ///
+    DLL_HEADER Segment();
+    DLL_HEADER Segment (const Segment& other);
+
+    ~Segment()
+    { ; }
+
+    // friend ostream & operator<<(ostream  & s, const Segment & seg);
+
+    PointIndex pnums[3];  // p1, p2, pmid
+
+    int edgenr;
+    ///
+    double singedge_left;
+    double singedge_right;
+
+    /// 0.. not first segment of segs, 1..first of class, 2..first of class, inverse
+    unsigned int seginfo:2;
+
+    /// surface decoding index
+    int si;          
+    /// domain number inner side
+    int domin;
+    /// domain number outer side
+    int domout;  
+    /// top-level object number of surface
+    int tlosurf;
+    ///
+    PointGeomInfo geominfo[2];
+
+    /// surfaces describing edge
+    int surfnr1, surfnr2;
+    ///
+    EdgePointGeomInfo epgeominfo[2];
+    ///
+    // int pmid; // for second order
+    ///
+    int meshdocval;
+
+  private:
+    string* bcname;
+
+  public:
+    /*
+      PointIndex operator[] (int i) const
+      { return (i == 0) ? p1 : p2; }
+
+      PointIndex & operator[] (int i) 
+      { return (i == 0) ? p1 : p2; }
+    */
+
+    Segment& operator=(const Segment & other);
+
+  
+    int hp_elnr;
+
+    void SetBCName ( string * abcname )
+    {
+      bcname = abcname;
+    }
+
+    string * BCNamePtr () 
+    { return bcname; }
+
+    const string * BCNamePtr () const 
+    { return bcname; }
+
+    string GetBCName () const
+    {
+      if (! bcname )
+	{
+	  return "default";
+	}
+      return *bcname;
+    }
+
+    int GetNP() const
+    {
+      return (pnums[2] < 0) ? 2 : 3;
+    }
+
+    ELEMENT_TYPE GetType() const
+    {
+      return (pnums[2] < 0) ? SEGMENT : SEGMENT3;
+    }
+  
+    PointIndex & operator[] (int i) { return pnums[i]; }
+    const PointIndex & operator[] (int i) const { return pnums[i]; }
+  };
+
+  ostream & operator<<(ostream  & s, const Segment & seg);
+
+
+
+
+  // class Surface;  
+  // class FaceDescriptor;
+
+  ///
+  class FaceDescriptor
+  {
+    /// which surface, 0 if not available
+    int surfnr;
+    /// domain nr inside
+    int domin;
+    /// domain nr outside
+    int domout;
+    /// top level object number of surface
+    int tlosurf;
+    /// boundary condition property
+    int bcprop;
+    // Philippose - 06/07/2009
+    // Add capability to store surface colours along with 
+    // other face data
+    /// surface colour (Default: R=0.0 ; G=1.0 ; B=0.0)
+    Vec3d surfcolour;
+
+    ///
+    string * bcname;
+    /// root of linked list 
+    SurfaceElementIndex firstelement;
+  
+    double domin_singular;
+    double domout_singular;
+
+  public:
+    DLL_HEADER FaceDescriptor();
+    DLL_HEADER FaceDescriptor(int surfnri, int domini, int domouti, int tlosurfi);
+    DLL_HEADER FaceDescriptor(const Segment & seg);
+    DLL_HEADER FaceDescriptor(const FaceDescriptor& other);
+    DLL_HEADER ~FaceDescriptor()  { ; }
+
+    DLL_HEADER int SegmentFits (const Segment & seg);
+
+    int SurfNr () const { return surfnr; }
+    int DomainIn () const { return domin; }
+    int DomainOut () const { return domout; }
+    int TLOSurface () const { return tlosurf; }
+    int BCProperty () const { return bcprop; }
+
+
+    double DomainInSingular() const { return domin_singular; }
+    double DomainOutSingular() const { return domout_singular; }
+
+    // Philippose - 06/07/2009
+    // Get Surface colour
+    Vec3d SurfColour () const { return surfcolour; }
+    string GetBCName () const; 
+    // string * BCNamePtr () { return bcname; }
+    // const string * BCNamePtr () const  { return bcname; }
+    void SetSurfNr (int sn) { surfnr = sn; }
+    void SetDomainIn (int di) { domin = di; }
+    void SetDomainOut (int dom) { domout = dom; }
+    void SetBCProperty (int bc) { bcprop = bc; }
+    void SetBCName (string * bcn) { bcname = bcn; }
+    // Philippose - 06/07/2009
+    // Set the surface colour
+    void SetSurfColour (Vec3d colour) { surfcolour = colour; }
+
+    void SetDomainInSingular (double v) { domin_singular = v; }
+    void SetDomainOutSingular (double v) { domout_singular = v; }
+
+    SurfaceElementIndex FirstElement() { return firstelement; }
+    // friend ostream & operator<<(ostream  & s, const FaceDescriptor & fd);
+    friend class Mesh;
+  };
+
+  ostream & operator<< (ostream  & s, const FaceDescriptor & fd);
+
+ 
+  class EdgeDescriptor
+  {
+    int tlosurf;
+    int surfnr[2];
+  public:
+    EdgeDescriptor ()
+      : tlosurf(-1)
+    { surfnr[0] = surfnr[1] = -1; }
+
+    int SurfNr (int i) const { return surfnr[i]; }
+    void SetSurfNr (int i, int nr) { surfnr[i] = nr; }
+
+    int TLOSurface() const { return tlosurf; }
+    void SetTLOSurface (int nr) { tlosurf = nr; }
+  };
+
+
+
+  class DLL_HEADER MeshingParameters
+  {
+  public:
+    /**
+       3d optimization strategy:
+       // m .. move nodes
+       // M .. move nodes, cheap functional
+       // s .. swap faces
+       // c .. combine elements
+       // d .. divide elements
+       // p .. plot, no pause
+       // P .. plot, Pause
+       // h .. Histogramm, no pause
+       // H .. Histogramm, pause
+       */
+    const char * optimize3d;
+    /// number of 3d optimization steps
+    int optsteps3d;
+    /**
+       2d optimization strategy:
+       // s .. swap, opt 6 lines/node
+       // S .. swap, optimal elements
+       // m .. move nodes
+       // p .. plot, no pause
+       // P .. plot, pause
+       // c .. combine
+       **/
+    const char * optimize2d;
+    /// number of 2d optimization steps
+    int optsteps2d;
+    /// power of error (to approximate max err optimization)
+    double opterrpow;
+    /// do block filling ?  
+    int blockfill;
+    /// block filling up to distance
+    double filldist;
+    /// radius of local environment (times h)
+    double safety;
+    /// radius of active environment (times h)
+    double relinnersafety;
+    /// use local h ?
+    int uselocalh;
+    /// grading for local h
+    double grading;
+    /// use delaunay meshing
+    int delaunay;
+    /// maximal mesh size
+    double maxh;
+    /// minimal mesh size
+    double minh;
+    /// file for meshsize
+    const char * meshsizefilename;
+    /// start surfacemeshing from everywhere in surface
+    int startinsurface;
+    /// check overlapping surfaces (debug)
+    int checkoverlap;
+    /// check overlapping surface mesh before volume meshing
+    int checkoverlappingboundary;
+    /// check chart boundary (sometimes too restrictive)
+    int checkchartboundary;
+    /// safty factor for curvatures (elemetns per radius)
+    double curvaturesafety;
+    /// minimal number of segments per edge
+    double segmentsperedge;
+    /// use parallel threads
+    int parthread;
+    /// weight of element size w.r.t element shape
+    double elsizeweight;
+    /// init with default values
+
+
+    /// from mp3:
+    /// give up quality class, 2d meshing
+    int giveuptol2d;
+    /// give up quality class, 3d meshing
+    int giveuptol;
+    /// maximal outer steps
+    int maxoutersteps;
+    /// class starting star-shape filling
+    int starshapeclass;
+    /// if non-zero, baseelement must have baseelnp points
+    int baseelnp;        
+    /// quality tolerances are handled less careful
+    int sloppy;
+  
+    /// limit for max element angle (150-180)
+    double badellimit;
+
+    bool check_impossible;
+  
+    ///
+    int secondorder;
+    /// high order element curvature
+    int elementorder;
+    /// quad-dominated surface meshing
+    int quad;
+    ///
+    int inverttets;
+    ///
+    int inverttrigs;
+    ///
+    int autozrefine;
+    ///
+    MeshingParameters ();
+    ///
+    void Print (ostream & ost) const;
+
+    void CopyFrom(const MeshingParameters & other);
+  };
+
+
+
+  class DebugParameters 
+  {
+  public:
+    ///
+    int debugoutput;
+    /// use slow checks
+    int slowchecks;
+    ///
+    int haltsuccess;
+    ///
+    int haltnosuccess;
+    ///
+    int haltlargequalclass;
+    ///
+    int haltsegment;
+    ///
+    int haltnode;
+    ///
+    int haltsegmentp1;
+    ///
+    int haltsegmentp2;
+    ///
+    int haltexistingline;
+    ///
+    int haltoverlap;
+    ///
+    int haltface;
+    ///
+    int haltfacenr;
+    ///
+    DebugParameters ();
+  };
+
+
+
+
+  inline void Element2d :: Invert()
+  {
+    if (typ == TRIG)
+      Swap (PNum(2), PNum(3));
+    else
+      Invert2();
+  }
+
+
+
+
+  inline void Element2d :: NormalizeNumbering ()
+  {
+    if (GetNP() == 3)
+      {
+	if (PNum(1) < PNum(2) && PNum(1) < PNum(3))
+	  return;
+	else
+	  {
+	    if (PNum(2) < PNum(3))
+	      {
+		PointIndex pi1 = PNum(2);
+		PNum(2) = PNum(3);
+		PNum(3) = PNum(1);
+		PNum(1) = pi1;
+	      }
+	    else
+	      {
+		PointIndex pi1 = PNum(3);
+		PNum(3) = PNum(2);
+		PNum(2) = PNum(1);
+		PNum(1) = pi1;
+	      }
+	  }
+      }
+    else
+      NormalizeNumbering2();
+  }
+
+
+
+  static const int gftetfacesa[4][3] = 
+    { { 1, 2, 3 },
+      { 2, 0, 3 },
+      { 0, 1, 3 },
+      { 1, 0, 2 } };
+
+  inline void Element :: GetFace (int i, Element2d & face) const
+  {
+    if (typ == TET)
+      {
+	face.SetType(TRIG);
+	face[0] = pnum[gftetfacesa[i-1][0]];
+	face[1] = pnum[gftetfacesa[i-1][1]];
+	face[2] = pnum[gftetfacesa[i-1][2]];
+      }
+    else
+      GetFace2 (i, face);
+  }
+
+
+
+
+
+
+
+  /**
+     Identification of periodic surfaces, close surfaces, etc. 
+  */
+  class Identifications
+  {
+  public:
+    enum ID_TYPE { UNDEFINED = 1, PERIODIC = 2, CLOSESURFACES = 3, CLOSEEDGES = 4};
+  
+
+  private:
+    class Mesh & mesh;
+
+    /// identify points (thin layers, periodic b.c.)  
+    INDEX_2_HASHTABLE<int> * identifiedpoints;
+  
+    /// the same, with info about the id-nr
+    INDEX_3_HASHTABLE<int> * identifiedpoints_nr;
+
+    /// sorted by identification nr
+    TABLE<INDEX_2> idpoints_table;
+
+    Array<ID_TYPE> type;
+
+    /// number of identifications (or, actually used identifications ?)
+    int maxidentnr;
+
+  public:
+    ///
+    DLL_HEADER Identifications (class Mesh & amesh);
+    ///
+    DLL_HEADER ~Identifications ();
+
+    DLL_HEADER void Delete ();
+
+    /*
+      Identify points pi1 and pi2, due to
+      identification nr identnr
+    */
+    DLL_HEADER void Add (PointIndex pi1, PointIndex pi2, int identnr);
+
+
+    int Get (PointIndex pi1, PointIndex pi2) const;
+    int GetSymmetric (PointIndex pi1, PointIndex pi2) const;
+
+    bool Get (PointIndex pi1, PointIndex pi2, int identnr) const;
+    bool GetSymmetric (PointIndex pi1, PointIndex pi2, int identnr) const;
+
+    ///
+    INDEX_2_HASHTABLE<int> & GetIdentifiedPoints () 
+    { 
+      return *identifiedpoints; 
+    }
+
+    bool Used (PointIndex pi1, PointIndex pi2)
+    {
+      return identifiedpoints->Used (INDEX_2 (pi1, pi2));
+    }
+
+    bool UsedSymmetric (PointIndex pi1, PointIndex pi2)
+    {
+      return 
+	identifiedpoints->Used (INDEX_2 (pi1, pi2)) ||
+	identifiedpoints->Used (INDEX_2 (pi2, pi1));
+    }
+
+    ///
+    void GetMap (int identnr, Array<int,PointIndex::BASE> & identmap, bool symmetric = false) const;
+    ///
+    ID_TYPE GetType(int identnr) const
+    {
+      if(identnr <= type.Size())
+	return type[identnr-1];
+      else
+	return UNDEFINED;
+    }
+    void SetType(int identnr, ID_TYPE t)
+    {
+      while(type.Size() < identnr)
+	type.Append(UNDEFINED);
+      type[identnr-1] = t;
+    }
+    
+    ///
+    void GetPairs (int identnr, Array<INDEX_2> & identpairs) const;
+    ///
+    int GetMaxNr () const { return maxidentnr; }  
+
+    /// remove secondorder
+    void SetMaxPointNr (int maxpnum);
+
+    void Print (ostream & ost) const;
+  };
+
+
+}
+
+
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/meshing/msghandler.cpp b/contrib/Netgen/libsrc/meshing/msghandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2818858a4a9bf6d0c5de2c5e289963f1c4a5133
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/msghandler.cpp
@@ -0,0 +1,227 @@
+//File for handling warnings, errors, messages
+#include <meshing.hpp>
+
+namespace netgen
+{
+int printmessage_importance = 5;
+int printwarnings = 1;
+int printerrors = 1;
+int printdots = 1;
+int printfnstart = 0;
+
+// extern void Ng_PrintDest(const MyStr& s);
+extern void Ng_PrintDest(const char * s);
+
+//the dots for progression of program
+void PrintDot(char ch)
+{
+  if (printdots)
+    {
+      char st[2];
+      st[0] = ch;
+      st[1] = 0;
+      Ng_PrintDest(st);
+    }
+}
+
+void PrintMessage(int importance, 
+		  const MyStr& s1, const MyStr& s2)
+{
+  if (importance <= printmessage_importance)
+    {
+      Ng_PrintDest(MyStr(" ")+s1+s2+MyStr("\n"));
+    }
+}
+
+void PrintMessage(int importance, 
+		  const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4)
+{
+  if (importance <= printmessage_importance)
+    {
+      Ng_PrintDest(MyStr(" ")+s1+s2+s3+s4+MyStr("\n"));
+    }
+}
+
+void PrintMessage(int importance, 
+		  const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+		  const MyStr& s5, const MyStr& s6, const MyStr& s7, const MyStr& s8)
+{
+  if (importance <= printmessage_importance)
+    {
+      Ng_PrintDest(MyStr(" ")+s1+s2+s3+s4+s5+s6+s7+s8+MyStr("\n"));
+    }
+}
+
+void PrintMessageCR(int importance, 
+		    const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+		    const MyStr& s5, const MyStr& s6, const MyStr& s7, const MyStr& s8)
+{
+  if (importance <= printmessage_importance)
+    {
+      Ng_PrintDest(MyStr(" ")+s1+s2+s3+s4+s5+s6+s7+s8+MyStr("\r"));
+    }
+}
+
+void PrintFnStart(const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+		  const MyStr& s5, const MyStr& s6, const MyStr& s7, const MyStr& s8)
+{
+  if (printfnstart)
+    Ng_PrintDest(MyStr(" Start Function: ")+s1+s2+s3+s4+s5+s6+s7+s8+MyStr("\n"));
+}
+
+void PrintWarning(const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+		  const MyStr& s5, const MyStr& s6, const MyStr& s7, const MyStr& s8)
+{
+  if (printwarnings)
+    Ng_PrintDest(MyStr(" WARNING: ")+s1+s2+s3+s4+s5+s6+s7+s8+MyStr("\n"));
+}
+
+void PrintError(const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+		const MyStr& s5, const MyStr& s6, const MyStr& s7, const MyStr& s8)
+{
+  if (printerrors)
+    Ng_PrintDest(MyStr(" ERROR: ")+s1+s2+s3+s4+s5+s6+s7+s8+MyStr("\n"));
+}
+
+void PrintFileError(const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+		    const MyStr& s5, const MyStr& s6, const MyStr& s7, const MyStr& s8)
+{
+  if (printerrors)
+    Ng_PrintDest(MyStr(" FILE ERROR: ")+s1+s2+s3+s4+s5+s6+s7+s8+MyStr("\n"));
+}
+
+void PrintUserError(const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+		const MyStr& s5, const MyStr& s6, const MyStr& s7, const MyStr& s8)
+{
+  Ng_PrintDest(MyStr(" USER ERROR: ")+s1+s2+s3+s4+s5+s6+s7+s8+MyStr("\n"));
+}
+
+void PrintSysError(const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+		const MyStr& s5, const MyStr& s6, const MyStr& s7, const MyStr& s8)
+{
+  if (printerrors)
+    Ng_PrintDest(MyStr(" SYSTEM ERROR: ")+s1+s2+s3+s4+s5+s6+s7+s8+MyStr("\n"));
+}
+
+void PrintTime(const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+	       const MyStr& s5, const MyStr& s6, const MyStr& s7, const MyStr& s8)
+{
+  if (printmessage_importance >= 3)
+    Ng_PrintDest(MyStr(" Time = ")+s1+s2+s3+s4+s5+s6+s7+s8+MyStr("\n"));
+}
+
+
+static Array<MyStr*> msgstatus_stack(0);
+static Array<double> threadpercent_stack(0);
+static MyStr msgstatus = "";
+
+
+
+
+void ResetStatus()
+{
+  SetStatMsg("idle");
+
+  for (int i = 0; i < msgstatus_stack.Size(); i++)
+    delete msgstatus_stack[i];
+  msgstatus_stack.SetSize(0);
+  threadpercent_stack.SetSize(0);
+
+  // multithread.task = "";
+  multithread.percent = 100.;
+}
+
+void PushStatus(const MyStr& s)
+{
+  msgstatus_stack.Append(new MyStr (s));  
+  SetStatMsg(s);
+  threadpercent_stack.Append(0);
+}
+
+void PushStatusF(const MyStr& s)
+{
+  msgstatus_stack.Append(new MyStr (s));
+  SetStatMsg(s);
+  threadpercent_stack.Append(0);
+  PrintFnStart(s);
+}
+
+void PopStatus()
+{
+  if (msgstatus_stack.Size())
+    {
+      if (msgstatus_stack.Size() > 1)
+	SetStatMsg (*msgstatus_stack.Last());
+      else
+	SetStatMsg ("");
+      delete msgstatus_stack.Last();
+      msgstatus_stack.DeleteLast();
+      threadpercent_stack.DeleteLast();
+      if(threadpercent_stack.Size() > 0)
+	multithread.percent = threadpercent_stack.Last();
+      else
+	multithread.percent = 100.;
+    }
+  else
+    {
+      PrintSysError("PopStatus failed");
+    }
+}
+
+
+
+/*
+void SetStatMsgF(const MyStr& s)
+{
+  PrintFnStart(s);
+  SetStatMsg(s);
+}
+*/
+
+void SetStatMsg(const MyStr& s)
+{
+  msgstatus = s;
+  multithread.task = msgstatus.c_str();  
+}
+
+void SetThreadPercent(double percent)
+{
+  multithread.percent = percent;
+  if(threadpercent_stack.Size() > 0)
+    threadpercent_stack.Last() = percent;
+}
+
+
+void GetStatus(MyStr & s, double & percentage)
+{
+  if(threadpercent_stack.Size() > 0)
+    percentage = threadpercent_stack.Last();
+  else
+    percentage = multithread.percent;
+  
+  if ( msgstatus_stack.Size() )
+    s = *msgstatus_stack.Last();
+  else
+    s = "idle";     
+}
+
+/*
+#ifdef SMALLLIB
+#define SMALLLIBORNOTCL
+#endif
+#ifdef NOTCL
+#define SMALLLIBORNOTCL
+#endif
+
+#ifdef SMALLLIBORNOTCL
+void Ng_PrintDest(const char * s){cout << s <<flush;}
+double GetTime(){return 0;}
+void MyError(const char * ch)
+{
+  cerr << ch << endl;
+}
+#endif
+*/
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/msghandler.hpp b/contrib/Netgen/libsrc/meshing/msghandler.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bab5717441da89474cb90dd94caaf1247f5aa4f7
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/msghandler.hpp
@@ -0,0 +1,57 @@
+#ifndef FILE_MSGHANDLER
+#define FILE_MSGHANDLER
+
+/**************************************************************************/
+/* File:   msghandler.hh                                                  */
+/* Author: Johannes Gerstmayr                                             */
+/* Date:   20. Nov. 99                                                    */
+/**************************************************************************/
+
+
+namespace netgen
+{
+
+  extern void PrintDot(char ch = '.');
+
+
+  //Message Pipeline:
+
+  //importance: importance of message: 1=very important, 3=middle, 5=low, 7=unimportant
+  extern DLL_HEADER void PrintMessage(int importance, 
+			   const MyStr& s1, const MyStr& s2=MyStr());
+  extern DLL_HEADER void PrintMessage(int importance, 
+			   const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4=MyStr());
+  extern DLL_HEADER void PrintMessage(int importance, 
+			   const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4, 
+			   const MyStr& s5, const MyStr& s6=MyStr(), const MyStr& s7=MyStr(), const MyStr& s8=MyStr());
+
+  // CR without line-feed
+  extern DLL_HEADER void PrintMessageCR(int importance, 
+			     const MyStr& s1, const MyStr& s2="", const MyStr& s3="", const MyStr& s4="", 
+			     const MyStr& s5="", const MyStr& s6="", const MyStr& s7="", const MyStr& s8="");
+  extern DLL_HEADER void PrintFnStart(const MyStr& s1, const MyStr& s2="", const MyStr& s3="", const MyStr& s4="", 
+			   const MyStr& s5="", const MyStr& s6="", const MyStr& s7="", const MyStr& s8="");
+  extern DLL_HEADER void PrintWarning(const MyStr& s1, const MyStr& s2="", const MyStr& s3="", const MyStr& s4="", 
+			   const MyStr& s5="", const MyStr& s6="", const MyStr& s7="", const MyStr& s8="");
+  extern DLL_HEADER void PrintError(const MyStr& s1, const MyStr& s2="", const MyStr& s3="", const MyStr& s4="", 
+			 const MyStr& s5="", const MyStr& s6="", const MyStr& s7="", const MyStr& s8="");
+  extern DLL_HEADER void PrintFileError(const MyStr& s1, const MyStr& s2="", const MyStr& s3="", const MyStr& s4="", 
+			     const MyStr& s5="", const MyStr& s6="", const MyStr& s7="", const MyStr& s8="");
+  extern DLL_HEADER void PrintSysError(const MyStr& s1, const MyStr& s2="", const MyStr& s3="", const MyStr& s4="", 
+			    const MyStr& s5="", const MyStr& s6="", const MyStr& s7="", const MyStr& s8="");
+  extern DLL_HEADER void PrintUserError(const MyStr& s1, const MyStr& s2="", const MyStr& s3="", const MyStr& s4="", 
+			     const MyStr& s5="", const MyStr& s6="", const MyStr& s7="", const MyStr& s8="");
+  extern DLL_HEADER void PrintTime(const MyStr& s1="", const MyStr& s2="", const MyStr& s3="", const MyStr& s4="", 
+			const MyStr& s5="", const MyStr& s6="", const MyStr& s7="", const MyStr& s8="");
+  extern DLL_HEADER void SetStatMsg(const MyStr& s);
+
+  extern DLL_HEADER void PushStatus(const MyStr& s);
+  extern DLL_HEADER void PushStatusF(const MyStr& s);
+  extern DLL_HEADER void PopStatus();
+  extern DLL_HEADER void SetThreadPercent(double percent);
+  extern DLL_HEADER void GetStatus(MyStr & s, double & percentage);
+}
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/meshing/netrule2.cpp b/contrib/Netgen/libsrc/meshing/netrule2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..015ed6fa7c99889d11757187f0f1544e3bb1cd0e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/netrule2.cpp
@@ -0,0 +1,222 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+netrule :: netrule ()
+{
+  name = new char[1];
+  name[0] = char(0);
+  quality = 0;
+}
+
+netrule ::  ~netrule()
+{
+  delete [] name;
+  for(int i = 0; i < oldutofreearea_i.Size(); i++)
+    delete oldutofreearea_i[i];
+  for(int i = 0; i < freezone_i.Size(); i++)
+    delete freezone_i[i];
+}
+
+
+
+void netrule :: SetFreeZoneTransformation (const Vector & devp, int tolclass)
+{
+  double lam1 = 1.0/tolclass;
+  double lam2 = 1.-lam1;
+
+  double mem1[100], mem2[100], mem3[100];
+
+  int vs = oldutofreearea.Height();
+  FlatVector devfree(vs, mem1);
+
+  int fzs = freezone.Size();
+  transfreezone.SetSize (fzs);
+
+  if (tolclass <= oldutofreearea_i.Size())
+    {
+      oldutofreearea_i[tolclass-1] -> Mult (devp, devfree);
+
+      Array<Point2d> & fzi = *freezone_i[tolclass-1];
+      for (int i = 0; i < fzs; i++)
+	{
+	  transfreezone[i].X() = fzi[i].X() + devfree[2*i];
+	  transfreezone[i].Y() = fzi[i].Y() + devfree[2*i+1];
+	}
+    }
+  else
+    {
+      FlatVector devfree1(vs, mem2);
+      FlatVector devfree2(vs, mem3);
+
+      oldutofreearea.Mult (devp, devfree1);
+      oldutofreearealimit.Mult (devp, devfree2);
+      devfree.Set2 (lam1, devfree1, lam2, devfree2);
+
+      for (int i = 0; i < fzs; i++)
+	{
+	  transfreezone[i].X() = lam1 * freezone[i].X() + lam2 * freezonelimit[i].X() + devfree[2*i];
+	  transfreezone[i].Y() = lam1 * freezone[i].Y() + lam2 * freezonelimit[i].Y() + devfree[2*i+1];
+	}
+    }
+
+
+  if (fzs > 0)
+    {
+      fzmaxx = fzminx = transfreezone[0].X();
+      fzmaxy = fzminy = transfreezone[0].Y();
+    }
+
+  for (int i = 1; i < fzs; i++)
+    {
+      if (transfreezone[i].X() > fzmaxx) fzmaxx = transfreezone[i].X();
+      if (transfreezone[i].X() < fzminx) fzminx = transfreezone[i].X();
+      if (transfreezone[i].Y() > fzmaxy) fzmaxy = transfreezone[i].Y();
+      if (transfreezone[i].Y() < fzminy) fzminy = transfreezone[i].Y();
+    }
+
+  for (int i = 0; i < fzs; i++)
+    {
+      Point2d p1 = transfreezone[i];
+      Point2d p2 = transfreezone[(i+1) % fzs];
+
+      Vec2d vn (p2.Y() - p1.Y(), p1.X() - p2.X());
+
+      double len2 = vn.Length2();
+
+      if (len2 < 1e-10)
+	{
+	  freesetinequ(i, 0) = 0;
+	  freesetinequ(i, 1) = 0;
+	  freesetinequ(i, 2) = -1;
+	}
+      else
+	{
+	  vn /= sqrt (len2);    // scaling necessary ?
+
+	  freesetinequ(i,0) = vn.X(); 
+	  freesetinequ(i,1) = vn.Y(); 
+	  freesetinequ(i,2) = -(p1.X() * vn.X() + p1.Y() * vn.Y());
+	}
+    }
+}
+
+
+/*
+int netrule :: IsInFreeZone2 (const Point2d & p) const
+{
+  for (int i = 0; i < transfreezone.Size(); i++)
+    {
+      if (freesetinequ(i, 0) * p.X() + 
+	  freesetinequ(i, 1) * p.Y() +
+	  freesetinequ(i, 2) > 0) return 0;
+    }
+  return 1;
+}
+*/
+
+int netrule :: IsLineInFreeZone2 (const Point2d & p1, const Point2d & p2) const
+{
+  int left, right, allleft, allright;
+
+  if ( (p1.X() > fzmaxx && p2.X() > fzmaxx) ||
+       (p1.X() < fzminx && p2.X() < fzminx) ||
+       (p1.Y() > fzmaxy && p2.Y() > fzmaxy) ||
+       (p1.Y() < fzminy && p2.Y() < fzminy) ) return 0;
+
+  for (int i = 1; i <= transfreezone.Size(); i++)
+    {
+      if (freesetinequ.Get(i, 1) * p1.X() + freesetinequ.Get(i, 2) * p1.Y() +
+	  freesetinequ.Get(i, 3) > -1e-8 &&    // -1e-6
+	  freesetinequ.Get(i, 1) * p2.X() + freesetinequ.Get(i, 2) * p2.Y() +
+	  freesetinequ.Get(i, 3) > -1e-8       // -1e-6
+	  ) return 0;
+    }
+
+  double nx =  (p2.Y() - p1.Y());
+  double ny = -(p2.X() - p1.X());
+  double nl = sqrt (nx * nx + ny * ny);
+  if (nl > 1e-8)
+    {
+      nx /= nl;
+      ny /= nl;
+      double c = - (p1.X() * nx + p1.Y() * ny);
+
+      allleft = 1;
+      allright = 1;
+
+      for (int i = 1; i <= transfreezone.Size(); i++)
+	{
+	  left  = transfreezone.Get(i).X() * nx + transfreezone.Get(i).Y() + c <  1e-7;
+	  right = transfreezone.Get(i).X() * nx + transfreezone.Get(i).Y() + c > -1e-7;
+
+	  if (!left) allleft = 0;
+	  if (!right) allright = 0;
+	}
+      if (allleft || allright) return 0;
+    }
+
+  return 1;
+}
+
+int netrule :: ConvexFreeZone () const
+{
+  int n = transfreezone.Size();
+  for (int i = 1; i <= n; i++)
+    {
+      const bool counterclockwise = CCW (transfreezone.Get(i), 
+					 transfreezone.Get(i % n + 1),
+					 transfreezone.Get( (i+1) % n + 1 ),
+					 1e-7);
+      //(*testout) << "ccw " << counterclockwise << endl << " p1 " << transfreezone.Get(i) << " p2 " << transfreezone.Get(i % n + 1)
+      //		 << " p3 " << transfreezone.Get( (i+1) % n + 1 ) << endl;
+      if (!counterclockwise )
+	return 0;
+    }
+  return 1;
+}
+
+
+/*
+float netrule :: CalcPointDist (int pi, const Point2d & p) const
+{
+  float dx = p.X() - points.Get(pi).X();
+  float dy = p.Y() - points.Get(pi).Y();
+  const threefloat * tf = &tolerances.Get(pi);
+
+  return tf->f1 * dx * dx + tf->f2 * dx * dy + tf->f3 * dy * dy;
+}
+*/
+
+float netrule :: CalcLineError (int li, const Vec2d & v) const
+{
+  float dx = v.X() - linevecs.Get(li).X();
+  float dy = v.Y() - linevecs.Get(li).Y();
+
+  const threefloat * ltf = &linetolerances.Get(li);
+  return ltf->f1 * dx * dx + ltf->f2 * dx * dy + ltf->f3 * dy * dy;
+}
+
+
+
+
+/*
+int GetNRules ()
+  {
+  return rules.Size();
+  }
+*/
+
+
+
+
+
+
+
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/netrule3.cpp b/contrib/Netgen/libsrc/meshing/netrule3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..de0e35e424912c4d8be89a80603081fdef21747e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/netrule3.cpp
@@ -0,0 +1,1138 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+
+namespace netgen
+{
+
+
+vnetrule :: vnetrule ()
+{
+  name = new char[1];
+  name[0] = char(0);
+  quality = 0;
+}
+
+vnetrule :: ~vnetrule ()
+{
+  // if (strlen(name)) 
+  delete [] name;
+  for (int i = 1; i <= freefaces.Size(); i++)
+    delete freefaces.Elem(i);
+  for (int i = 1; i <= freesets.Size(); i++)
+    delete freesets.Elem(i);
+  for (int i = 1; i <= freeedges.Size(); i++)
+    delete freeedges.Elem(i);
+  for (int i = 1; i <= freefaceinequ.Size(); i++)
+    delete freefaceinequ.Elem(i);
+  delete oldutofreezone;
+  delete oldutofreezonelimit;
+}
+
+int vnetrule :: TestFlag (char flag) const
+{
+  for (int i = 1; i <= flags.Size(); i++)
+    if (flags.Get(i) == flag) return 1;
+  return 0;
+}
+
+
+void vnetrule :: SetFreeZoneTransformation (const Vector & allp, int tolclass)
+{
+  int i, j;
+  // double nx, ny, nz, v1x, v1y, v1z, v2x, v2y, v2z;
+  double nl;
+  const threeint * ti;
+  int fs;
+
+  double lam1 = 1.0/(2 * tolclass - 1);
+  double lam2 = 1-lam1;
+
+  transfreezone.SetSize (freezone.Size());
+  
+  int np = points.Size();
+  int nfp = freezone.Size();
+  Vector vp(np), vfp1(nfp), vfp2(nfp);
+
+
+  for (i = 1; i <= 3; i++)
+    {
+      for (j = 1; j <= np; j++)
+	vp(j-1) = allp(i+3*j-3-1);
+
+      oldutofreezone->Mult (vp, vfp1);
+      oldutofreezonelimit->Mult (vp, vfp2);
+
+      vfp1 *= lam1;
+      vfp1.Add (lam2, vfp2);
+
+      for (j = 1; j <= nfp; j++)
+	transfreezone.Elem(j).X(i) = vfp1(j-1);
+    }
+
+  // MARK(setfz2);
+
+
+  fzbox.SetPoint (transfreezone.Elem(1));
+  for (i = 2; i <= freezone.Size(); i++)
+    fzbox.AddPoint (transfreezone.Elem(i));
+  
+  
+  // MARK(setfz3);
+
+
+  for (fs = 1; fs <= freesets.Size(); fs++)
+    {
+      Array<threeint> & freesetfaces = *freefaces.Get(fs);
+      DenseMatrix & freesetinequ = *freefaceinequ.Get(fs);
+      
+      for (i = 1; i <= freesetfaces.Size(); i++)
+	{
+	  ti = &freesetfaces.Get(i);
+	  const Point3d & p1 = transfreezone.Get(ti->i1);
+	  const Point3d & p2 = transfreezone.Get(ti->i2);
+	  const Point3d & p3 = transfreezone.Get(ti->i3);
+
+	  Vec3d v1(p1, p2);   
+	  Vec3d v2(p1, p3);   
+	  Vec3d n;
+	  Cross (v1, v2, n);
+
+	  nl = n.Length();
+
+	  if (nl < 1e-10)
+	    {
+	      freesetinequ.Set(1, 1, 0);
+	      freesetinequ.Set(1, 2, 0);
+	      freesetinequ.Set(1, 3, 0);
+	      freesetinequ.Set(1, 4, -1);
+	    }
+	  else
+	    {
+	      //	      n /= nl;
+	      
+	      freesetinequ.Set(i, 1, n.X()/nl);
+	      freesetinequ.Set(i, 2, n.Y()/nl);
+	      freesetinequ.Set(i, 3, n.Z()/nl);
+	      freesetinequ.Set(i, 4,
+			       -(p1.X() * n.X() + p1.Y() * n.Y() + p1.Z() * n.Z()) / nl);
+	    }
+	}
+    }
+
+  /*
+  (*testout) << "Transformed freezone: " << endl;
+  for (i = 1; i <= transfreezone.Size(); i++)
+    (*testout) << transfreezone.Get(i) << " ";
+  (*testout) << endl;
+  */
+}
+
+int vnetrule :: ConvexFreeZone () const
+{
+  int i, j, k, fs;
+
+  // (*mycout) << "Convex free zone...\n";
+  
+  int ret1=1;
+  // int ret2=1;
+
+  for (fs = 1; fs <= freesets.Size(); fs++)
+    {
+      const DenseMatrix & freesetinequ = *freefaceinequ.Get(fs);
+
+      // const Array<int> & freeset = *freesets.Get(fs);
+      const Array<twoint> & freesetedges = *freeedges.Get(fs);
+      // const Array<threeint> & freesetfaces = *freefaces.Get(fs);
+      
+      for (i = 1; i <= freesetedges.Size(); i++)
+	{
+	  j = freesetedges.Get(i).i1;    //triangle j with opposite point k
+	  k = freesetedges.Get(i).i2;
+	  
+	  if ( freesetinequ.Get(j, 1) * transfreezone.Get(k).X() +
+	       freesetinequ.Get(j, 2) * transfreezone.Get(k).Y() +
+	       freesetinequ.Get(j, 3) * transfreezone.Get(k).Z() +
+	       freesetinequ.Get(j, 4) > 0 )
+	    {
+	      ret1=0;
+	    }
+	}
+      
+    }
+
+  return ret1;
+}
+
+
+int vnetrule :: IsInFreeZone (const Point3d & p) const
+{
+  int i, fs;
+  char inthis;
+  
+  
+  for (fs = 1; fs <= freesets.Size(); fs++)
+    {
+      inthis = 1;
+      Array<threeint> & freesetfaces = *freefaces.Get(fs);
+      DenseMatrix & freesetinequ = *freefaceinequ.Get(fs);
+      
+      for (i = 1; i <= freesetfaces.Size() && inthis; i++)
+	{
+	  if (freesetinequ.Get(i, 1) * p.X() + freesetinequ.Get(i, 2) * p.Y() +
+	      freesetinequ.Get(i, 3) * p.Z() + freesetinequ.Get(i, 4) > 0)
+	    inthis = 0;
+	}
+      
+      if (inthis) return 1;
+    }
+  
+  return 0;
+}
+
+
+int vnetrule :: IsTriangleInFreeZone (const Point3d & p1, 
+				      const Point3d & p2,
+				      const Point3d & p3, 
+				      const Array<int> & pi, int newone)
+{
+  int fs;
+  int infreeset, cannot = 0;
+
+
+  ArrayMem<int,3> pfi(3), pfi2(3);
+
+  // convert from local index to freeset index
+  int i, j;
+  for (i = 1; i <= 3; i++)
+    {
+      pfi.Elem(i) = 0;
+      if (pi.Get(i))
+	{
+	  for (j = 1; j <= freezonepi.Size(); j++)
+	    if (freezonepi.Get(j) == pi.Get(i))
+	      pfi.Elem(i) = j;
+	}
+    }
+
+  for (fs = 1; fs <= freesets.Size(); fs++)
+    {
+      const Array<int> & freeseti = *freesets.Get(fs);
+      for (i = 1; i <= 3; i++)
+	{
+	  pfi2.Elem(i) = 0;
+	  for (j = 1; j <= freeseti.Size(); j++)
+	    if (pfi.Get(i) == freeseti.Get(j))
+	      pfi2.Elem(i) = pfi.Get(i);
+	}
+
+      infreeset = IsTriangleInFreeSet(p1, p2, p3, fs, pfi2, newone);
+      if (infreeset == 1) return 1;
+      if (infreeset == -1) cannot = -1;
+    }
+  
+  return cannot;
+}
+
+
+
+int vnetrule :: IsTriangleInFreeSet (const Point3d & p1, const Point3d & p2,
+                                     const Point3d & p3, int fs,
+				     const Array<int> & pi, int newone)
+{
+  int i, ii;
+  Vec3d n;
+  int allleft, allright;
+  int hos1, hos2, hos3, os1, os2, os3;
+  double hf, lam1, lam2, f, c1, c2, alpha;
+  double v1n, v2n, h11, h12, h22, dflam1, dflam2;
+  double lam1old, lam2old, fold;
+  double hpx, hpy, hpz, v1x, v1y, v1z, v2x, v2y, v2z;
+  int act1, act2, act3, it;
+  int cntout;
+  Array<int> activefaces;
+  int isin;
+  
+
+  // MARK(triinfz);
+  
+  Array<threeint> & freesetfaces = *freefaces.Get(fs);
+  DenseMatrix & freesetinequ = *freefaceinequ.Get(fs);
+  
+
+  int cnt = 0;
+  for (i = 1; i <= 3; i++)
+    if (pi.Get(i)) cnt++;
+
+  /*
+  (*testout) << "trig in free set : " << p1 << " - " << p2 << " - " << p3 << endl;
+  (*testout) << "common points: " << cnt << endl;
+  */
+  if (!newone)
+    cnt = 0;
+
+  if (cnt == 1)
+    {
+      // MARK(triinfz1);
+
+      int upi = 0, lpiu = 0;
+      for (i = 1; i <= 3; i++)
+	if (pi.Get(i))
+	  {
+	    upi = i;
+	    lpiu = pi.Get(i);
+	  }
+
+      Vec3d v1, v2;
+      switch (upi)
+	{
+	case 1:
+	  {
+	    v1 = p2 - p1;
+	    v2 = p3 - p1;
+	    break;
+	  }
+	case 2:
+	  {
+	    v1 = p3 - p2;
+	    v2 = p1 - p2;
+	    break;
+	  }
+	case 3:
+	  {
+	    v1 = p1 - p3;
+	    v2 = p2 - p3;
+	    break;
+	  }
+	}
+
+      v1 /= v1.Length();
+      v2 /= v2.Length();
+      Cross (v1, v2, n);
+      n /= n.Length();
+
+      //      (*testout) << "Test new: " << endl;
+      for (i = 1; i <= freesetfaces.Size(); i++)
+	{
+	  if ( (freesetfaces.Get(i).i1 == lpiu) || 
+	       (freesetfaces.Get(i).i2 == lpiu) ||
+	       (freesetfaces.Get(i).i3 == lpiu) )
+	    {
+	      // freeface has point
+
+
+	      Vec3d a (freesetinequ.Get(i, 1),
+		       freesetinequ.Get(i, 2),
+		       freesetinequ.Get(i, 3));
+	      
+	      //	      if (1 - fabs (a * n) < 1e-8 ) 
+	      //		continue;
+
+	      Vec3d an;
+	      Cross (a, n, an);
+	      double lan = an.Length();
+	      if (lan < 1e-10)
+		continue;
+
+	      an /= lan;
+	      
+	      int out1 = (a * v1) > 0;
+	      int out2 = (a * v2) > 0;
+	      //	      (*testout) << "out1, out2 = " << out1 << ", " << out2 << endl;
+	      if (out1 && out2)
+		return 0;
+
+	      if (!out1 && !out2) 
+		continue;
+
+
+	      //	      if ( ( (an * v1) < 0) &&  ( (an * v2) < 0) )   // falsch !!!!
+	      //		an *= -1;
+
+	      // solve  an = lam1 v1 + lam2 v2
+	      double vii11 = v1 * v1;
+	      double vii12 = v1 * v2;
+	      double vii22 = v2 * v2;
+	      double det = vii11 * vii22 - vii12 * vii12;
+	      if ( fabs (det) < 1e-10 )
+		continue;
+	      double rs1 = an * v1;
+	      double rs2 = an * v2;
+	      
+	      double lambda1 = rs1 * vii22 - rs2 * vii12;
+	      double lambda2 = rs2 * vii11 - rs1 * vii12;
+
+	      if (fabs (lambda1) > fabs (lambda2))
+		{
+		  if (lambda1 < 0)
+		    an *= -1;
+		}
+	      else
+		{
+		  if (lambda2 < 0)
+		    an *= -1;
+		}
+
+
+	      if (lambda1 * lambda2 < 0 && 0)
+		{
+		  if (fabs (lambda1) > 1e-14 && fabs (lambda2) > 1e-14)
+		    {
+		      //		      (*mycout) << "lambda1 lambda2 < 0" << endl;
+		      (*testout) << "lambdai different" << endl;
+		      (*testout) << "v1 = " << v1 << endl;
+		      (*testout) << "v2 = " << v2 << endl;
+		      (*testout) << "n = " << n << endl;
+		      (*testout) << "a = " << a << endl;
+		      (*testout) << "an = " << an << endl;
+		      (*testout) << "a * v1 = " << (a * v1) << endl;
+		      (*testout) << "a * v2 = " << (a * v2) << endl;
+		      (*testout) << "an * v1 = " << (an * v1) << endl;
+		      (*testout) << "an * v2 = " << (an * v2) << endl;
+		      
+		      (*testout) << "vii = " << vii11 << ", " << vii12 << ", " << vii22 << endl;
+		      (*testout) << "lambdai = " << lambda1 << ", " << lambda2 << endl;
+		      (*testout) << "rs = " << rs1 << ", " << rs2 << endl;
+		      continue;
+		    }
+		}
+
+	      if (out1)
+		v1 = an;
+	      else
+		v2 = an;
+	    }
+	}
+      
+      return 1;
+
+      /*
+      (*testout) << "overlap trig " << p1 << p2 << p3 << endl;
+      (*testout) << "upi = " << upi << endl;
+      (*testout) << "v1 = " << v1 << " v2 = " << v2 << endl;
+      */
+
+      switch (upi)
+	{
+	case 1:
+	  {
+	    v1 = p2 - p1;
+	    v2 = p3 - p1;
+	    break;
+	  }
+	case 2:
+	  {
+	    v1 = p3 - p2;
+	    v2 = p1 - p2;
+	    break;
+	  }
+	case 3:
+	  {
+	    v1 = p1 - p3;
+	    v2 = p2 - p3;
+	    break;
+	  }
+	}
+
+      v1 /= v1.Length();
+      v2 /= v2.Length();
+      Cross (v1, v2, n);
+      n /= n.Length();
+
+      //      (*testout) << "orig v1, v2 = " << v1 << ", " << v2 << endl;
+
+      
+      for (i = 1; i <= freesetfaces.Size(); i++)
+	{
+	  if ( (freesetfaces.Get(i).i1 == lpiu) || 
+	       (freesetfaces.Get(i).i2 == lpiu) ||
+	       (freesetfaces.Get(i).i3 == lpiu) )
+	    {
+	      /*
+	      (*testout) << "v1, v2, now = " << v1 << ", " << v2 << endl;
+
+	      // freeface has point
+	      (*testout) << "freesetface: "
+			 << freesetfaces.Get(i).i1 << " "
+			 << freesetfaces.Get(i).i2 << " "
+			 << freesetfaces.Get(i).i3 << " ";
+	      */
+
+	      Vec3d a (freesetinequ.Get(i, 1),
+		       freesetinequ.Get(i, 2),
+		       freesetinequ.Get(i, 3));
+	      //	      (*testout) << "a = " <<  a << endl;
+
+
+	      Vec3d an;
+	      Cross (a, n, an);
+	      double lan = an.Length();
+	      
+	      //	      (*testout) << "an = " << an << endl;
+
+	      if (lan < 1e-10)
+		continue;
+
+	      an /= lan;
+
+	      //	      (*testout) << "a*v1 = " << (a*v1) << " a*v2 = " << (a*v2) << endl;
+	      
+	      int out1 = (a * v1) > 0;
+	      // int out2 = (a * v2) > 0;
+
+
+	      //	      (*testout) << "out1, 2 = " << out1 << ", " << out2 << endl;
+
+	      
+	      double vii11 = v1 * v1;
+	      double vii12 = v1 * v2;
+	      double vii22 = v2 * v2;
+	      double det = vii11 * vii22 - vii12 * vii12;
+	      if ( fabs (det) < 1e-10 )
+		continue;
+	      double rs1 = an * v1;
+	      double rs2 = an * v2;
+	      
+	      double lambda1 = rs1 * vii22 - rs2 * vii12;
+	      double lambda2 = rs2 * vii11 - rs1 * vii12;
+
+	      //	      (*testout) << "lambda1, lambda2 = " << lambda1 << ", " << lambda2 << endl;
+
+
+	      if (fabs (lambda1) > fabs (lambda2))
+		{
+		  if (lambda1 < 0)
+		    an *= -1;
+		}
+	      else
+		{
+		  if (lambda2 < 0)
+		    an *= -1;
+		}
+
+
+	      if (lambda1 * lambda2 < 0)
+		{
+		  if (fabs (lambda1) > 1e-14 && fabs (lambda2) > 1e-14)
+		    {
+		      //		      (*mycout) << "lambda1 lambda2 < 0" << endl;
+		      (*testout) << "lambdai different" << endl;
+		      (*testout) << "v1 = " << v1 << endl;
+		      (*testout) << "v2 = " << v2 << endl;
+		      (*testout) << "n = " << n << endl;
+		      (*testout) << "a = " << a << endl;
+		      (*testout) << "an = " << an << endl;
+		      (*testout) << "a * v1 = " << (a * v1) << endl;
+		      (*testout) << "a * v2 = " << (a * v2) << endl;
+		      (*testout) << "an * v1 = " << (an * v1) << endl;
+		      (*testout) << "an * v2 = " << (an * v2) << endl;
+		      
+		      (*testout) << "vii = " << vii11 << ", " << vii12 << ", " << vii22 << endl;
+		      (*testout) << "lambdai = " << lambda1 << ", " << lambda2 << endl;
+		      (*testout) << "rs = " << rs1 << ", " << rs2 << endl;
+		      continue;
+		    }
+		}
+
+	      if (out1)
+		v1 = an;
+	      else
+		v2 = an;
+
+
+
+	    }
+	}
+
+      return 1;
+    }
+
+
+
+  if (cnt == 2)
+    {
+      //      (*testout) << "tripoitns: " << p1 << " " << p2 << " " << p3 << endl;
+
+      // MARK(triinfz2);
+
+      int pi1 = 0, pi2 = 0, pi3 = 0;
+      Vec3d a1, a2;  // outer normals
+      Vec3d trivec;  // vector from common edge to third point of triangle
+      for (i = 1; i <= 3; i++)
+	if (pi.Get(i))
+	  {
+	    pi2 = pi1;
+	    pi1 = pi.Get(i);
+	  }
+	else
+	  pi3 = i;
+
+      switch (pi3)
+	{
+	case 1: trivec = (p1 - p2); break;
+	case 2: trivec = (p2 - p3); break;
+	case 3: trivec = (p3 - p2); break;
+	}
+
+      Array<int> lpi(freezonepi.Size());
+      for (i = 1; i <= lpi.Size(); i++)
+	lpi.Elem(i) = 0;
+      lpi.Elem(pi1) = 1;
+      lpi.Elem(pi2) = 1;
+      
+      int ff1 = 0, ff2 = 0;
+      for (i = 1; i <= freesetfaces.Size(); i++)
+	{
+	  if (lpi.Get(freesetfaces.Get(i).i1) + 
+	      lpi.Get(freesetfaces.Get(i).i2) + 
+	      lpi.Get(freesetfaces.Get(i).i3) == 2)
+	    {
+	      ff2 = ff1;
+	      ff1 = i;
+	    }
+	}
+
+      if (ff2 == 0)
+	return 1;
+
+      a1 = Vec3d (freesetinequ.Get(ff1, 1),
+		  freesetinequ.Get(ff1, 2),
+		  freesetinequ.Get(ff1, 3));
+      a2 = Vec3d (freesetinequ.Get(ff2, 1),
+		  freesetinequ.Get(ff2, 2),
+		  freesetinequ.Get(ff2, 3));
+
+      if ( ( (a1 * trivec) > 0) || ( (a2 * trivec) > 0))
+	return 0;
+
+      return 1;
+    }
+
+
+  if (cnt == 3)
+    {
+      // MARK(triinfz3);  
+
+      Array<int> lpi(freezonepi.Size());
+      for (i = 1; i <= lpi.Size(); i++)
+	lpi.Elem(i) = 0;
+
+      for (i = 1; i <= 3; i++)
+	lpi.Elem(pi.Get(i)) = 1;
+      
+      for (i = 1; i <= freesetfaces.Size(); i++)
+	{
+	  if (lpi.Get(freesetfaces.Get(i).i1) + 
+	      lpi.Get(freesetfaces.Get(i).i2) + 
+	      lpi.Get(freesetfaces.Get(i).i3) == 3)
+	    {
+	      return 0;
+	    }
+	}
+      return 1;
+    }
+
+  // MARK(triinfz0);  
+
+  
+  os1 = os2 = os3 = 0;
+  activefaces.SetSize(0);
+
+  // is point inside ?
+
+  for (i = 1; i <= freesetfaces.Size(); i++)
+    {
+      hos1 = freesetinequ.Get(i, 1) * p1.X() +
+	freesetinequ.Get(i, 2) * p1.Y() +
+	freesetinequ.Get(i, 3) * p1.Z() +
+	freesetinequ.Get(i, 4) > -1E-5;
+      
+      hos2 = freesetinequ.Get(i, 1) * p2.X() +
+	freesetinequ.Get(i, 2) * p2.Y() +
+	freesetinequ.Get(i, 3) * p2.Z() +
+	freesetinequ.Get(i, 4) > -1E-5;
+      
+      hos3 = freesetinequ.Get(i, 1) * p3.X() +
+	freesetinequ.Get(i, 2) * p3.Y() +
+	freesetinequ.Get(i, 3) * p3.Z() +
+	freesetinequ.Get(i, 4) > -1E-5;
+      
+      if (hos1 && hos2 && hos3) return 0;
+      
+      if (hos1) os1 = 1;
+      if (hos2) os2 = 1;
+      if (hos3) os3 = 1;
+      
+      if (hos1 || hos2 || hos3) activefaces.Append (i);
+    }
+  
+  if (!os1 || !os2 || !os3) return 1;
+
+  v1x = p2.X() - p1.X();
+  v1y = p2.Y() - p1.Y();
+  v1z = p2.Z() - p1.Z();
+
+  v2x = p3.X() - p1.X();
+  v2y = p3.Y() - p1.Y();
+  v2z = p3.Z() - p1.Z();
+
+  n.X() = v1y * v2z - v1z * v2y;
+  n.Y() = v1z * v2x - v1x * v2z;
+  n.Z() = v1x * v2y - v1y * v2x;
+  n /= n.Length();
+
+  allleft = allright = 1;
+  for (i = 1; i <= transfreezone.Size() && (allleft || allright); i++)
+    {
+      const Point3d & p = transfreezone.Get(i);
+      float scal = (p.X() - p1.X()) * n.X() +
+	(p.Y() - p1.Y()) * n.Y() +
+	(p.Z() - p1.Z()) * n.Z();
+
+      if ( scal >  1E-8 ) allleft = 0;
+      if ( scal < -1E-8 ) allright = 0;
+    }
+
+  if (allleft || allright) return 0;
+
+
+  lam1old = lam2old = lam1 = lam2 = 1.0 / 3.0;
+
+
+  //  testout << endl << endl << "Start minimizing" << endl;
+
+  it = 0;
+  int minit;
+  minit = 1000;
+  fold = 1E10;
+
+  
+
+  while (1)
+    {
+      it++;
+
+      if (it > 1000) return -1;
+
+      if (lam1 < 0) lam1 = 0;
+      if (lam2 < 0) lam2 = 0;
+      if (lam1 + lam2 > 1) lam1 = 1 - lam2;
+
+      if (it > minit)
+	{
+	  (*testout) << "it = " << it << endl;
+	  (*testout) << "lam1/2 = " << lam1 << "  " << lam2 << endl;
+	}
+
+      hpx = p1.X() + lam1 * v1x + lam2 * v2x;
+      hpy = p1.Y() + lam1 * v1y + lam2 * v2y;
+      hpz = p1.Z() + lam1 * v1z + lam2 * v2z;
+
+      f = 0;
+
+      h11 = h12 = h22 = dflam1 = dflam2 = 0;
+      cntout = 0;
+
+      isin = 1;
+
+      for (i = 1; i <= activefaces.Size(); i++)
+	{
+	  ii = activefaces.Get(i);
+
+	  hf = freesetinequ.Get(ii, 1) * hpx +
+	    freesetinequ.Get(ii, 2) * hpy +
+	    freesetinequ.Get(ii, 3) * hpz +
+	    freesetinequ.Get(ii, 4);
+
+	  if (hf > -1E-7) isin = 0;
+
+	  hf += 1E-4;
+	  if (hf > 0)
+	    {
+	      f += hf * hf;
+
+	      v1n = freesetinequ.Get(ii, 1) * v1x +
+		freesetinequ.Get(ii, 2) * v1y +
+		freesetinequ.Get(ii, 3) * v1z;
+	      v2n = freesetinequ.Get(ii, 1) * v2x +
+		freesetinequ.Get(ii, 2) * v2y +
+		freesetinequ.Get(ii, 3) * v2z;
+
+	      h11 += 2 * v1n * v1n;
+	      h12 += 2 * v1n * v2n;
+	      h22 += 2 * v2n * v2n;
+	      dflam1 += 2 * hf * v1n;
+	      dflam2 += 2 * hf * v2n;
+	      cntout++;
+	    }
+	}
+
+      if (isin) return 1;
+
+      if (it > minit)
+	{
+	  (*testout) << "f = " << f
+		     << "  dfdlam = " << dflam1 << "  " << dflam2 << endl;
+	  (*testout) << "h = " << h11 << "  " << h12 << "  " << h22 << endl;
+	  (*testout) << "active: " << cntout << endl;
+	  (*testout) << "lam1-lam1old = " << (lam1 - lam1old) << endl;
+	  (*testout) << "lam2-lam2old = " << (lam2 - lam2old) << endl;
+	}
+
+
+      if (f >= fold)
+	{
+	  lam1 = 0.100000000000000 * lam1 + 0.9000000000000000 * lam1old;
+	  lam2 = 0.100000000000000 * lam2 + 0.9000000000000000 * lam2old;
+	}
+      else
+	{
+	  lam1old = lam1;
+	  lam2old = lam2;
+	  fold = f;
+
+
+	  if (f < 1E-9) return 1;
+
+	  h11 += 1E-10;
+	  h22 += 1E-10;
+	  c1 = - ( h22 * dflam1 - h12 * dflam2) / (h11 * h22 - h12 * h12);
+	  c2 = - (-h12 * dflam1 + h11 * dflam2) / (h11 * h22 - h12 * h12);
+	  alpha = 1;
+
+
+	  if (it > minit)
+	    (*testout) << "c1/2 = " << c1 << "  " << c2 << endl;
+
+	  act1 = lam1 <= 1E-6 && c1 <= 0;
+	  act2 = lam2 <= 1E-6 && c2 <= 0;
+	  act3 = lam1 + lam2 >= 1 - 1E-6 && c1 + c2 >= 0;
+
+	  if (it > minit)
+	    (*testout) << "act1,2,3 = " << act1 << act2 << act3 << endl;
+
+	  if ( (act1 && act2) || (act1 && act3) || (act2 && act3) ) return 0;
+
+	  if (act1)
+	    {
+	      c1 = 0;
+	      c2 = - dflam2 / h22;
+	    }
+
+	  if (act2)
+	    {
+	      c1 = - dflam1 / h11;
+	      c2 = 0;
+	    }
+
+	  if (act3)
+	    {
+	      c1 = - (dflam1 - dflam2) / (h11 + h22 - 2 * h12);
+	      c2 = -c1;
+	    }
+
+	  if (it > minit)
+	    (*testout) << "c1/2 now = " << c1 << "  " << c2 << endl;
+
+
+	  if (f > 100 * sqrt (sqr (c1) + sqr (c2))) return 0;
+
+
+	  if (lam1 + alpha * c1 < 0 && !act1)
+	    alpha = -lam1 / c1;
+	  if (lam2 + alpha * c2 < 0 && !act2)
+	    alpha = -lam2 / c2;
+	  if (lam1 + lam2 + alpha * (c1 + c2) > 1 && !act3)
+	    alpha = (1 - lam1 - lam2) / (c1 + c2);
+
+	  if (it > minit)
+	    (*testout) << "alpha = " << alpha << endl;
+
+	  lam1 += alpha * c1;
+	  lam2 += alpha * c2;
+	}
+    }
+}
+
+
+
+
+int vnetrule :: IsQuadInFreeZone (const Point3d & p1, 
+				  const Point3d & p2,
+				  const Point3d & p3, 
+				  const Point3d & p4, 
+				  const Array<int> & pi, int newone)
+{
+  int fs;
+  int infreeset, cannot = 0;
+
+
+  ArrayMem<int,4> pfi(4), pfi2(4);
+
+  // convert from local index to freeset index
+  int i, j;
+  for (i = 1; i <= 4; i++)
+    {
+      pfi.Elem(i) = 0;
+      if (pi.Get(i))
+	{
+	  for (j = 1; j <= freezonepi.Size(); j++)
+	    if (freezonepi.Get(j) == pi.Get(i))
+	      pfi.Elem(i) = j;
+	}
+    }
+
+  for (fs = 1; fs <= freesets.Size(); fs++)
+    {
+      const Array<int> & freeseti = *freesets.Get(fs);
+      for (i = 1; i <= 4; i++)
+	{
+	  pfi2.Elem(i) = 0;
+	  for (j = 1; j <= freeseti.Size(); j++)
+	    if (pfi.Get(i) == freeseti.Get(j))
+	      pfi2.Elem(i) = pfi.Get(i);
+	}
+
+      infreeset = IsQuadInFreeSet(p1, p2, p3, p4, fs, pfi2, newone);
+      if (infreeset == 1) return 1;
+      if (infreeset == -1) cannot = -1;
+    }
+  
+  return cannot;
+}
+
+
+int vnetrule :: IsQuadInFreeSet (const Point3d & p1, const Point3d & p2,
+				 const Point3d & p3, const Point3d & p4, 
+				 int fs, const Array<int> & pi, int newone)
+{
+  int i;
+  
+  int cnt = 0;
+  for (i = 1; i <= 4; i++)
+    if (pi.Get(i)) cnt++;
+  
+  /*
+  (*testout) << "test quad in freeset: " << p1 << " - " << p2 << " - " << p3 << " - " << p4 << endl;
+  (*testout) << "pi = ";
+  for (i = 1; i <= pi.Size(); i++)
+    (*testout) << pi.Get(i) << " ";
+  (*testout) << endl;
+  (*testout) << "cnt = " << cnt  << endl;
+  */
+  if (cnt == 4)
+    {
+      return 1;
+    }
+
+  if (cnt == 3)
+    {
+      return 1;
+    }
+
+  ArrayMem<int,3> pi3(3);
+  int res;
+
+  pi3.Elem(1) = pi.Get(1);
+  pi3.Elem(2) = pi.Get(2);
+  pi3.Elem(3) = pi.Get(3);
+  res = IsTriangleInFreeSet (p1, p2, p3, fs, pi3, newone);
+  if (res) return res;
+
+
+  pi3.Elem(1) = pi.Get(2);
+  pi3.Elem(2) = pi.Get(3);
+  pi3.Elem(3) = pi.Get(4);
+  res = IsTriangleInFreeSet (p2, p3, p4, fs, pi3, newone);
+  if (res) return res;
+
+  pi3.Elem(1) = pi.Get(3);
+  pi3.Elem(2) = pi.Get(4);
+  pi3.Elem(3) = pi.Get(1);
+  res = IsTriangleInFreeSet (p3, p4, p1, fs, pi3, newone);
+  if (res) return res;
+
+  pi3.Elem(1) = pi.Get(4);
+  pi3.Elem(2) = pi.Get(1);
+  pi3.Elem(3) = pi.Get(2);
+  res = IsTriangleInFreeSet (p4, p1, p2, fs, pi3, newone);
+  return res;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+float vnetrule :: CalcPointDist (int pi, const Point3d & p) const
+{
+  float dx = p.X() - points.Get(pi).X();
+  float dy = p.Y() - points.Get(pi).Y();
+  float dz = p.Z() - points.Get(pi).Z();
+  
+  //  const threefloat * tf = &tolerances.Get(pi);
+  //  return tf->f1 * dx * dx + tf->f2 * dx * dy + tf->f3 * dy * dy;
+  return tolerances.Get(pi) * (dx * dx + dy * dy + dz * dz);
+}
+
+
+int vnetrule :: TestOk () const
+{
+  Array<int> cntpused(points.Size());
+  Array<int> edge1, edge2;
+  Array<int> delf(faces.Size());
+  int i, j, k;
+  int pi1, pi2;
+  int found;
+
+  for (i = 1; i <= cntpused.Size(); i++)
+    cntpused.Elem(i) = 0;
+  for (i = 1; i <= faces.Size(); i++)
+    delf.Elem(i) = 0;
+  for (i = 1; i <= delfaces.Size(); i++)
+    delf.Elem(delfaces.Get(i)) = 1;
+
+
+  for (i = 1; i <= faces.Size(); i++)
+    if (delf.Get(i) || i > noldf)
+      for (j = 1; j <= faces.Get(i).GetNP(); j++)
+        cntpused.Elem(faces.Get(i).PNum(j))++;
+
+  for (i = 1; i <= cntpused.Size(); i++)
+    if (cntpused.Get(i) > 0 && cntpused.Get(i) < 2)
+      {
+	return 0;
+      }
+
+
+  //  (*testout) << endl;
+  for (i = 1; i <= faces.Size(); i++)
+    {
+      //      (*testout) << "face " << i << endl;
+      for (j = 1; j <= faces.Get(i).GetNP(); j++)
+	{
+	  pi1 = 0; pi2 = 0;
+	  if (delf.Get(i))
+	    {
+	      pi1 = faces.Get(i).PNumMod(j);
+	      pi2 = faces.Get(i).PNumMod(j+1);
+	    }
+	  if (i > noldf)
+	    {
+	      pi1 = faces.Get(i).PNumMod(j+1);
+	      pi2 = faces.Get(i).PNumMod(j);
+	    }
+
+	  found = 0;
+	  if (pi1)
+	    {
+	      for (k = 1; k <= edge1.Size(); k++)
+		if (edge1.Get(k) == pi1 && edge2.Get(k) == pi2)
+		  {
+		    found = 1;
+		    edge1.DeleteElement(k);
+		    edge2.DeleteElement(k);
+		    k--;
+		    //		    (*testout) << "Del edge " << pi1 << "-" << pi2 << endl;
+		  }
+	      if (!found)
+		{
+		  edge1.Append (pi2);
+		  edge2.Append (pi1);
+		  //		  (*testout) << "Add edge " << pi1 << "-" << pi2 << endl;
+		}
+	    }
+	}
+    }
+
+
+  if (edge1.Size() > 0)
+    {
+      return 0;
+    }
+
+  /*
+    cntpused.SetSize(freezone.Size());
+    for (i = 1; i <= cntpused.Size(); i++)
+    cntpused[i] = 0;
+
+    for (i = 1; i <= freefaces.Size(); i++)
+    {
+    cntpused[freefaces[i].i1]++;
+    cntpused[freefaces[i].i2]++;
+    cntpused[freefaces[i].i3]++;
+    }
+
+    for (i = 1; i <= cntpused.Size(); i++)
+    if (cntpused[i] < 3)
+    {
+    (*mycout) << "Fall 3" << endl;
+    return 0;
+    }
+
+
+
+    for (i = 1; i <= freefaces.Size(); i++)
+    {
+    for (j = 1; j <= 3; j++)
+    {
+    if (j == 1)
+    {
+    pi1 = freefaces[i].i1;
+    pi2 = freefaces[i].i2;
+    }
+    if (j == 2)
+    {
+    pi1 = freefaces[i].i2;
+    pi2 = freefaces[i].i3;
+    }
+    if (j == 3)
+    {
+    pi1 = freefaces[i].i3;
+    pi2 = freefaces[i].i1;
+    }
+
+    found = 0;
+    for (k = 1; k <= edge1.Size(); k++)
+    if (edge1[k] == pi1 && edge2[k] == pi2)
+    {
+    found = 1;
+    edge1.DeleteElement(k);
+    edge2.DeleteElement(k);
+    k--;
+    }
+
+    if (!found)
+    {
+    edge1.Append (pi2);
+    edge2.Append (pi1);
+    }
+    }
+    }
+
+    if (edge1.Size() > 0)
+    {
+    (*mycout) << "Fall 4" << endl;
+    return 0;
+    }
+    */
+  return 1;
+}
+
+
+int vnetrule :: IsDelFace (int fn) const
+{
+  int i;
+  for (i = 1; i <= GetNDelF(); i++)
+    if (GetDelFace(i) == fn) return 1;
+  return 0;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/parallelmesh.cpp b/contrib/Netgen/libsrc/meshing/parallelmesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..03f436aef9df7728cd70cd00d17667bc5f5ca754
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/parallelmesh.cpp
@@ -0,0 +1,1173 @@
+#ifdef PARALLEL
+
+#include <meshing.hpp>
+#include "paralleltop.hpp"
+
+#define METIS4
+
+
+#ifdef METIS
+namespace metis {
+  extern "C" {
+
+#ifdef METIS4
+#include <metis.h>
+    typedef idxtype idx_t;   
+#else
+#include <metis.h>
+    typedef idx_t idxtype;   
+#endif
+  } 
+}
+
+using namespace metis;
+#endif
+
+
+
+namespace netgen
+{
+
+  template <>
+  inline MPI_Datatype MyGetMPIType<netgen::PointIndex> ( ) 
+  { return MPI_INT; }
+
+
+
+
+
+  void Mesh :: SendRecvMesh ()
+  {
+    if (id == 0)
+      PrintMessage (1, "Send/Receive mesh");
+
+    {
+      // distribute global information
+      int nelglob, nseglob, nvglob;
+      if (id == 0)
+	{
+	  paralleltop -> SetNV (GetNV());
+	  paralleltop -> SetNE (GetNE());
+	  paralleltop -> SetNSegm (GetNSeg());
+	  paralleltop -> SetNSE (GetNSE());
+	  
+	  nelglob = GetNE();
+	  nseglob = GetNSE();
+	  nvglob = GetNV();
+	}
+      
+      MyMPI_Bcast (dimension);
+      MyMPI_Bcast (nelglob);
+      MyMPI_Bcast (nseglob);
+      MyMPI_Bcast (nvglob);
+      
+      if (id > 0)
+	{
+	  paralleltop -> SetNEGlob (nelglob);
+	  paralleltop -> SetNSEGlob (nseglob);
+	  paralleltop -> SetNVGlob (nvglob);
+	}
+    }
+
+
+
+    {
+      // distribute number of local elements
+      if (id == 0)
+	{
+	  Array<int> num_els_on_proc(ntasks);
+	  num_els_on_proc = 0;
+	  for (ElementIndex ei = 0; ei < GetNE(); ei++)
+	    num_els_on_proc[(*this)[ei].GetPartition()]++;
+	  
+	  MPI_Scatter (&num_els_on_proc[0], 1, MPI_INT,
+		       MPI_IN_PLACE, -1, MPI_INT, 0, MPI_COMM_WORLD);
+	}
+      else
+	{
+	  int nelloc;
+	  MPI_Scatter (NULL, 0, MPI_INT,
+		       &nelloc, 1, MPI_INT, 0, MPI_COMM_WORLD);
+	  
+	  paralleltop -> SetNE (nelloc);
+	}
+    }
+
+    if (id == 0)
+      SendMesh ();
+    else
+      ReceiveParallelMesh();
+  }
+
+
+
+
+
+
+
+  void Mesh :: SendMesh () const   
+  {
+    Array<MPI_Request> sendrequests;
+
+    PrintMessage ( 3, "Sending vertices");
+
+
+    Array<int> num_els_on_proc(ntasks);
+    num_els_on_proc = 0;
+    for (ElementIndex ei = 0; ei < GetNE(); ei++)
+      num_els_on_proc[(*this)[ei].GetPartition()]++;
+
+    TABLE<ElementIndex> els_of_proc (num_els_on_proc);
+    for (ElementIndex ei = 0; ei < GetNE(); ei++)
+      els_of_proc.Add ( (*this)[ei].GetPartition(), ei);
+
+
+    Array<int> num_sels_on_proc(ntasks);
+    num_sels_on_proc = 0;
+    for (SurfaceElementIndex ei = 0; ei < GetNSE(); ei++)
+      num_sels_on_proc[(*this)[ei].GetPartition()]++;
+
+    TABLE<SurfaceElementIndex> sels_of_proc (num_sels_on_proc);
+    for (SurfaceElementIndex ei = 0; ei < GetNSE(); ei++)
+      sels_of_proc.Add ( (*this)[ei].GetPartition(), ei);
+
+
+
+
+    Array<int, PointIndex::BASE> vert_flag (GetNV());
+    Array<int, PointIndex::BASE> num_procs_on_vert (GetNV());
+    Array<int> num_verts_on_proc (ntasks);
+
+    num_verts_on_proc = 0;
+    num_procs_on_vert = 0;
+    vert_flag = -1;
+
+    Array<int> nelloc (ntasks);
+    nelloc = 0;
+    Array<int> nselloc (ntasks);
+    nselloc = 0;
+
+
+    for (int dest = 1; dest < ntasks; dest++)
+      {
+	FlatArray<ElementIndex> els = els_of_proc[dest];
+
+	for (int hi = 0; hi < els.Size(); hi++)
+	  {
+	    const Element & el = (*this) [ els[hi] ];
+	    for (int i = 0; i < el.GetNP(); i++)
+	      {
+		PointIndex epi = el[i]; 
+		if (vert_flag[epi] < dest)
+		  {
+		    vert_flag[epi] = dest;
+
+		    num_verts_on_proc[dest]++;
+		    num_procs_on_vert[epi]++;
+
+		    paralleltop -> SetDistantPNum ( dest, epi, num_verts_on_proc[dest]);
+		  }
+	      }
+	    nelloc[dest] ++;
+	    paralleltop -> SetDistantEl ( dest, els[hi]+1, nelloc[dest] );
+	  }
+
+
+	FlatArray<SurfaceElementIndex> sels = sels_of_proc[dest];
+
+	for (int hi = 0; hi < sels.Size(); hi++)
+	  {
+	    const Element2d & el = (*this) [ sels[hi] ];
+	    for (int i = 0; i < el.GetNP(); i++)
+	      {
+		PointIndex epi = el[i]; 
+		if (vert_flag[epi] < dest)
+		  {
+		    vert_flag[epi] = dest;
+
+		    num_verts_on_proc[dest]++;
+		    num_procs_on_vert[epi]++;
+
+		    paralleltop -> SetDistantPNum ( dest, epi, num_verts_on_proc[dest]);
+		  }
+	      }
+	    nselloc[dest] ++;
+	    paralleltop -> SetDistantSurfEl ( dest, sels[hi]+1, nselloc[dest] );
+	  }
+      }
+
+    TABLE<PointIndex> verts_of_proc (num_verts_on_proc);
+    TABLE<int, PointIndex::BASE> procs_of_vert (num_procs_on_vert);
+    TABLE<int, PointIndex::BASE> loc_num_of_vert (num_procs_on_vert);
+
+    vert_flag = -1;
+    for (int dest = 1; dest < ntasks; dest++)
+      {
+	FlatArray<ElementIndex> els = els_of_proc[dest];
+	for (int hi = 0; hi < els.Size(); hi++)
+	  {
+	    const Element & el = (*this) [ els[hi] ];
+	    for (int i = 0; i < el.GetNP(); i++)
+	      {
+		PointIndex epi = el[i];
+		if (vert_flag[epi] < dest)
+		  {
+		    vert_flag[epi] = dest;
+		    procs_of_vert.Add (epi, dest);
+		  }
+	      }
+	  }
+
+	FlatArray<SurfaceElementIndex> sels = sels_of_proc[dest];
+	for (int hi = 0; hi < sels.Size(); hi++)
+	  {
+	    const Element2d & el = (*this) [ sels[hi] ];
+	    for (int i = 0; i < el.GetNP(); i++)
+	      {
+		PointIndex epi = el[i];
+		if (vert_flag[epi] < dest)
+		  {
+		    vert_flag[epi] = dest;
+		    procs_of_vert.Add (epi, dest);
+		  }
+	      }
+	  }
+
+      }
+
+    for (int vert = 1; vert <= GetNP(); vert++ )
+      {
+	FlatArray<int> procs = procs_of_vert[vert];
+	for (int j = 0; j < procs.Size(); j++)
+	  {
+	    int dest = procs[j];
+	    verts_of_proc.Add (dest, vert);
+	    loc_num_of_vert.Add (vert, verts_of_proc[dest].Size());
+	  }
+      }
+
+    for (int dest = 1; dest < ntasks; dest++)
+      {
+	FlatArray<PointIndex> verts = verts_of_proc[dest];
+
+	sendrequests.Append (MyMPI_ISend (verts, dest, MPI_TAG_MESH+1));
+
+	MPI_Datatype mptype = MeshPoint::MyGetMPIType();
+
+	int numv = verts.Size();
+
+	MPI_Datatype newtype;
+	Array<int> blocklen (numv);
+	blocklen = 1;
+	
+	MPI_Type_indexed (numv, &blocklen[0], 
+			  reinterpret_cast<int*> (&verts[0]), 
+			  mptype, &newtype);
+	MPI_Type_commit (&newtype);
+
+	MPI_Request request;
+	MPI_Isend( &points[0], 1, newtype, dest, MPI_TAG_MESH+1, MPI_COMM_WORLD, &request);
+	sendrequests.Append (request);
+      }
+
+
+    Array<int> num_distpnums(ntasks);
+    num_distpnums = 0;
+
+    for (int vert = 1; vert <= GetNP(); vert++)
+      {
+	FlatArray<int> procs = procs_of_vert[vert];
+	for (int j = 0; j < procs.Size(); j++)
+	  num_distpnums[procs[j]] += 3 * (procs.Size()-1);
+      }
+
+    TABLE<int> distpnums (num_distpnums);
+
+    for (int vert = 1; vert <= GetNP(); vert++)
+      {
+	FlatArray<int> procs = procs_of_vert[vert];
+	for (int j = 0; j < procs.Size(); j++)
+	  for (int k = 0; k < procs.Size(); k++)
+	    if (j != k)
+	      {
+		distpnums.Add (procs[j], loc_num_of_vert[vert][j]);
+		distpnums.Add (procs[j], procs_of_vert[vert][k]);
+		distpnums.Add (procs[j], loc_num_of_vert[vert][k]);
+	      }
+      }
+    
+    for ( int dest = 1; dest < ntasks; dest ++ )
+      sendrequests.Append (MyMPI_ISend (distpnums[dest], dest, MPI_TAG_MESH+1));
+
+
+
+    PrintMessage ( 3, "Sending elements" );
+
+    Array<int> elarraysize (ntasks);
+    elarraysize = 0;
+    for ( int ei = 1; ei <= GetNE(); ei++)
+      {
+	const Element & el = VolumeElement (ei);
+	int dest = el.GetPartition();
+	elarraysize[dest] += 3 + el.GetNP();
+      }
+
+    TABLE<int> elementarrays(elarraysize);
+
+    for (int ei = 1; ei <= GetNE(); ei++)
+      {
+	const Element & el = VolumeElement (ei);
+	int dest = el.GetPartition();
+
+	elementarrays.Add (dest, ei);
+	elementarrays.Add (dest, el.GetIndex());
+	elementarrays.Add (dest, el.GetNP());
+	for (int i = 0; i < el.GetNP(); i++)
+	  elementarrays.Add (dest, el[i]);
+      }
+
+    for (int dest = 1; dest < ntasks; dest ++ )
+      sendrequests.Append (MyMPI_ISend (elementarrays[dest], dest, MPI_TAG_MESH+2));
+
+
+    PrintMessage ( 3, "Sending Face Descriptors" );
+
+    Array<double> fddata (6 * GetNFD());
+    for (int fdi = 1; fdi <= GetNFD(); fdi++)
+      {
+	fddata[6*fdi-6] = GetFaceDescriptor(fdi).SurfNr();
+	fddata[6*fdi-5] = GetFaceDescriptor(fdi).DomainIn();	
+	fddata[6*fdi-4] = GetFaceDescriptor(fdi).DomainOut();
+	fddata[6*fdi-3] = GetFaceDescriptor(fdi).BCProperty();
+	fddata[6*fdi-2] = GetFaceDescriptor(fdi).domin_singular;
+	fddata[6*fdi-1] = GetFaceDescriptor(fdi).domout_singular;
+	
+      }
+    for (int dest = 1; dest < ntasks; dest++)
+      sendrequests.Append (MyMPI_ISend (fddata, dest, MPI_TAG_MESH+3));
+
+    PrintMessage ( 3, "Sending Surface elements" );
+  
+    Array <int> nlocsel(ntasks), bufsize(ntasks); 
+    nlocsel = 0;
+    bufsize = 1;
+
+    for (int sei = 1; sei <= GetNSE(); sei++ )
+      {
+	const Element2d & sel = SurfaceElement (sei);
+	int dest = sel.GetPartition();
+	nlocsel[dest] ++;
+	bufsize[dest] += 4 + 2*sel.GetNP();
+      }
+    
+    TABLE<int> selbuf(bufsize);
+
+    for (int dest = 1; dest < ntasks; dest++ )
+      selbuf.Add (dest, nlocsel[dest]);
+
+    for (int sei = 1; sei <= GetNSE(); sei ++ )
+      {
+	const Element2d & sel = SurfaceElement (sei);
+	int dest = sel.GetPartition();
+
+	selbuf.Add (dest, sei);
+	selbuf.Add (dest, sel.GetIndex());
+	selbuf.Add (dest, 0);
+	selbuf.Add (dest, sel.GetNP());
+	
+	for ( int ii = 1; ii <= sel.GetNP(); ii++)
+	  {
+	    selbuf.Add (dest, sel.PNum(ii));
+	    selbuf.Add (dest, sel.GeomInfoPi(ii).trignum);
+	  }
+      }
+
+    for (int dest = 1; dest < ntasks; dest++)
+      sendrequests.Append (MyMPI_ISend(selbuf[dest], dest, MPI_TAG_MESH+4));
+
+    PrintMessage ( 3, "Sending Edge Segments");
+
+    Array <int> nlocseg(ntasks), segi(ntasks);
+    for ( int i = 0; i < ntasks; i++)
+      {
+	nlocseg[i] = 0;
+	bufsize[i] = 0;
+	segi[i] = 0;
+      }
+
+    for (int segi = 1; segi <= GetNSeg(); segi ++ )
+      {
+	Array<int> volels;
+	const MeshTopology & topol = GetTopology();
+	topol . GetSegmentSurfaceElements ( segi, volels );
+        for (int j = 0; j < volels.Size(); j++)
+          {
+            int ei = volels[j];
+            if ( ei > 0 && ei <= GetNSE() )
+              {
+                const Element2d & el = SurfaceElement (ei);
+                int dest = el.GetPartition();
+		nlocseg[dest] ++;
+		bufsize[dest] += 14;
+              }
+	  }
+      }
+
+    TABLE<double> segmbuf(bufsize);
+
+    for ( int ls=1; ls <= GetNSeg(); ls++)
+      {
+	Array<int> volels;
+	GetTopology().GetSegmentSurfaceElements ( ls, volels );
+	const Segment & seg = LineSegment (ls);
+
+        for (int j = 0; j < volels.Size(); j++)
+          {
+            int ei = volels[j];
+            if ( ei > 0 && ei <= GetNSE() )
+              {
+                const Element2d & el = SurfaceElement (ei);
+                int dest = el.GetPartition();
+
+		if ( dest > 0 )
+		  {
+		    segmbuf.Add (dest, ls);
+		    segmbuf.Add (dest, seg.si);
+		    segmbuf.Add (dest, seg.pnums[0]);
+		    segmbuf.Add (dest, seg.pnums[1]);
+		    segmbuf.Add (dest, seg.geominfo[0].trignum);
+		    segmbuf.Add (dest, seg.geominfo[1].trignum);
+		    segmbuf.Add (dest, seg.surfnr1);
+		    segmbuf.Add (dest, seg.surfnr2);
+		    segmbuf.Add (dest, seg.edgenr);
+		    segmbuf.Add (dest, seg.epgeominfo[0].dist);
+		    segmbuf.Add (dest, seg.epgeominfo[1].edgenr);
+		    segmbuf.Add (dest, seg.epgeominfo[1].dist);
+		    segmbuf.Add (dest, seg.singedge_right);
+		    segmbuf.Add (dest, seg.singedge_left);
+		    segi[dest] += 14;
+		  }
+		paralleltop -> SetDistantSegm ( dest, ls, int ( segi[dest] / 14 ) );
+	      }
+	  }
+      }
+
+    for ( int dest = 1; dest < ntasks; dest++)
+      sendrequests.Append (MyMPI_ISend(segmbuf[dest], dest, MPI_TAG_MESH+5));
+
+    MPI_Waitall (sendrequests.Size(), &sendrequests[0], MPI_STATUS_IGNORE);
+
+    MPI_Barrier(MPI_COMM_WORLD);
+  }
+
+
+
+
+
+
+
+
+  // slaves receive the mesh from the master
+  void Mesh :: ReceiveParallelMesh ( )
+  {
+    int timer = NgProfiler::CreateTimer ("ReceiveParallelMesh");
+    int timer_pts = NgProfiler::CreateTimer ("Receive points");
+    int timer_els = NgProfiler::CreateTimer ("Receive elements");
+    int timer_sels = NgProfiler::CreateTimer ("Receive surface elements");
+    NgProfiler::RegionTimer reg(timer);
+
+
+    // string st;
+
+    // receive vertices
+    NgProfiler::StartTimer (timer_pts);
+    
+    Array<int> verts;
+    MyMPI_Recv (verts, 0, MPI_TAG_MESH+1);
+      
+    int numvert = verts.Size();
+    paralleltop -> SetNV (numvert);
+    
+    // INDEX_CLOSED_HASHTABLE<int> glob2loc_vert_ht (3*numvert+1);
+    INDEX_HASHTABLE<int> glob2loc_vert_ht (3*numvert+1);
+
+    for (int vert = 0; vert < numvert; vert++)
+      {
+	int globvert = verts[vert];
+	paralleltop->SetLoc2Glob_Vert ( vert+1, globvert  );
+	glob2loc_vert_ht.Set (globvert, vert+1);
+      }
+    
+    for (int i = 0; i < numvert; i++)
+      AddPoint (netgen::Point<3> (0,0,0));
+    
+    MPI_Datatype mptype = MeshPoint::MyGetMPIType();
+    MPI_Status status;
+    MPI_Recv( &points[1], numvert, mptype, 0, MPI_TAG_MESH+1, MPI_COMM_WORLD, &status);
+    
+    Array<int> dist_pnums;
+    MyMPI_Recv (dist_pnums, 0, MPI_TAG_MESH+1);
+    
+    for (int hi = 0; hi < dist_pnums.Size(); hi += 3)
+      paralleltop ->
+	SetDistantPNum (dist_pnums[hi+1], dist_pnums[hi], dist_pnums[hi+2]);
+    
+    NgProfiler::StopTimer (timer_pts);
+    *testout << "got " << numvert << " vertices" << endl;
+
+    {
+      Element el;
+      
+      Array<int> elarray;
+      MyMPI_Recv (elarray, 0, MPI_TAG_MESH+2);
+      
+      NgProfiler::RegionTimer reg(timer_els);
+
+      for (int ind = 0, elnum = 1; ind < elarray.Size(); elnum++)
+	{
+	  paralleltop->SetLoc2Glob_VolEl ( elnum,  elarray[ind++]);
+	  
+	  el.SetIndex(elarray[ind++]);
+	  el.SetNP(elarray[ind++]);
+	  
+	  for ( int j = 0; j < el.GetNP(); j++)
+	    el[j] = glob2loc_vert_ht.Get (elarray[ind++]); 
+	  
+	  AddVolumeElement (el);
+	}
+    }
+
+    {
+      Array<double> fddata;
+      MyMPI_Recv (fddata, 0, MPI_TAG_MESH+3);
+      for (int i = 0; i < fddata.Size(); i += 6)
+	{
+	  int faceind = AddFaceDescriptor (FaceDescriptor(int(fddata[i]), int(fddata[i+1]), int(fddata[i+2]), 0));
+	  GetFaceDescriptor(faceind).SetBCProperty (int(fddata[i+3]));
+	  GetFaceDescriptor(faceind).domin_singular = fddata[i+4];
+	  GetFaceDescriptor(faceind).domout_singular = fddata[i+5];
+	}
+    }
+
+    {
+      NgProfiler::RegionTimer reg(timer_sels);
+      Array<int> selbuf;
+
+      MyMPI_Recv ( selbuf, 0, MPI_TAG_MESH+4);
+      
+      int ii = 0;
+      int sel = 0;
+
+      int nlocsel = selbuf[ii++];
+      paralleltop -> SetNSE ( nlocsel );
+      
+      while (ii < selbuf.Size()-1)
+	{
+	  int globsel = selbuf[ii++];
+	  int faceind = selbuf[ii++];
+	  bool isghost = selbuf[ii++];
+	  int nep = selbuf[ii++];
+	  Element2d tri(nep);
+	  tri.SetIndex(faceind);
+	  for(int j = 1; j <= nep; j++)
+	    {
+	      tri.PNum(j) = glob2loc_vert_ht.Get (selbuf[ii++]);
+	      tri.GeomInfoPi(j).trignum = selbuf[ii++];
+	    }
+	  
+	  tri.SetGhost(isghost);
+	  
+	  paralleltop->SetLoc2Glob_SurfEl ( sel+1, globsel );
+	  AddSurfaceElement (tri);
+	  sel ++;
+	}
+    }
+    
+
+
+    {
+      Array<double> segmbuf;
+      MyMPI_Recv ( segmbuf, 0, MPI_TAG_MESH+5);
+
+      Segment seg;
+      int globsegi;
+      int ii = 0;
+      int segi = 1;
+      int nsegloc = int ( segmbuf.Size() / 14 ) ;
+      paralleltop -> SetNSegm ( nsegloc );
+      while ( ii < segmbuf.Size() )
+	{
+	  globsegi = int (segmbuf[ii++]);
+	  seg.si = int (segmbuf[ii++]);
+	  
+	  seg.pnums[0] = glob2loc_vert_ht.Get (int(segmbuf[ii++]));
+	  seg.pnums[1] = glob2loc_vert_ht.Get (int(segmbuf[ii++]));
+	  seg.geominfo[0].trignum = int( segmbuf[ii++] );
+	  seg.geominfo[1].trignum = int ( segmbuf[ii++]);
+	  seg.surfnr1 = int ( segmbuf[ii++]);
+	  seg.surfnr2 = int ( segmbuf[ii++]);
+	  seg.edgenr = int ( segmbuf[ii++]);
+	  seg.epgeominfo[0].dist = segmbuf[ii++];
+	  seg.epgeominfo[1].edgenr = int (segmbuf[ii++]);
+	  seg.epgeominfo[1].dist = segmbuf[ii++];
+	  
+	  seg.singedge_left = segmbuf[ii++];
+	  seg.singedge_right = segmbuf[ii++];
+	  
+	  seg.epgeominfo[0].edgenr = seg.epgeominfo[1].edgenr;
+	  
+	  seg.domin = seg.surfnr1;
+	  seg.domout = seg.surfnr2;
+	  if ( seg.pnums[0] >0 && seg.pnums[1] > 0 )
+	    {
+	      paralleltop-> SetLoc2Glob_Segm ( segi,  globsegi );
+	      
+	      AddSegment (seg);
+	      segi++;
+	    }
+	  
+	}
+    }
+    
+    MPI_Barrier(MPI_COMM_WORLD);
+
+    int timerloc = NgProfiler::CreateTimer ("Update local mesh");
+    int timerloc2 = NgProfiler::CreateTimer ("CalcSurfacesOfNode");
+
+    NgProfiler::RegionTimer regloc(timerloc);
+    PrintMessage (2, "Got ", GetNE(), " elements and ", GetNSE(), " surface elements");
+    // PrintMessage (2, "Got ", GetNSE(), " surface elements");
+
+    NgProfiler::StartTimer (timerloc2);
+
+    CalcSurfacesOfNode ();
+
+    NgProfiler::StopTimer (timerloc2);
+
+    topology -> Update();
+    clusters -> Update();
+    
+    SetNextMajorTimeStamp();
+    // paralleltop->Print();
+  }
+  
+
+
+  
+  
+  // distribute the mesh to the slave processors
+  // call it only for the master !
+  void Mesh :: Distribute ()
+  {
+    if (id != 0 || ntasks == 1 ) return;
+
+#ifdef METIS
+    ParallelMetis ();
+#else
+    for (ElementIndex ei = 0; ei < GetNE(); ei++)
+      (*this)[ei].SetPartition(ntasks * ei/GetNE() + 1);
+#endif
+
+    for (ElementIndex ei = 0; ei < GetNE(); ei++)
+      *testout << "el(" << ei << ") is in part " << (*this)[ei].GetPartition() << endl;
+    for (SurfaceElementIndex ei = 0; ei < GetNSE(); ei++)
+      *testout << "sel(" << int(ei) << ") is in part " << (*this)[ei].GetPartition() << endl;
+
+    // send partition
+    MyMPI_SendCmd ("mesh");
+    SendRecvMesh (); 
+
+    paralleltop -> UpdateCoarseGrid();
+    // paralleltop -> Print();
+  }
+  
+
+
+
+
+#ifdef METIS
+  void Mesh :: ParallelMetis ( )  
+  {
+    int timer = NgProfiler::CreateTimer ("Mesh::Partition");
+    NgProfiler::RegionTimer reg(timer);
+
+    PrintMessage (3, "Metis called");
+      
+    if (GetDimension() == 2) 
+      {
+	PartDualHybridMesh2D ( ); // neloc );
+	return;
+      }
+
+
+    idx_t ne = GetNE();
+    idx_t nn = GetNP();
+
+    if (ntasks <= 2 || ne <= 1)
+      {
+        if (ntasks == 1) return;
+        
+        for (int i=1; i<=ne; i++)
+          VolumeElement(i).SetPartition(1);
+
+        for (int i=1; i<=GetNSE(); i++)
+          SurfaceElement(i).SetPartition(1);
+
+        return;
+      }
+
+
+    bool uniform_els = true;
+
+    ELEMENT_TYPE elementtype = TET; 
+    for ( int el = 1; el <= GetNE(); el++ )
+      if ( VolumeElement(el).GetType() != elementtype )
+	{
+	  uniform_els = false;
+	  break;
+	}
+
+
+    if (!uniform_els)
+      {
+	PartHybridMesh ();  
+      }
+    else
+      {
+	
+	// uniform (TET) mesh,  JS
+	int npe = VolumeElement(1).GetNP();
+	Array<idxtype> elmnts(ne*npe);
+	
+	int etype;
+	if (elementtype == TET)
+	  etype = 2;
+	else if (elementtype == HEX)
+	  etype = 3;
+	
+    
+    for (int i=1; i<=ne; i++)
+      for (int j=1; j<=npe; j++)
+	elmnts[(i-1)*npe+(j-1)] = VolumeElement(i).PNum(j)-1;
+    
+    int numflag = 0;
+    int nparts = ntasks-1;
+    int ncommon = 3;
+    int edgecut;
+    Array<idxtype> epart(ne), npart(nn);
+
+//     if ( ntasks == 1 ) 
+//       {
+// 	(*this) = *mastermesh;
+// 	nparts = 4;	   
+// 	metis :: METIS_PartMeshDual (&ne, &nn, elmnts, &etype, &numflag, &nparts,
+// 				     &edgecut, epart, npart);
+// 	cout << "done" << endl;
+	
+// 	cout << "edge-cut: " << edgecut << ", balance: " << metis :: ComputeElementBalance(ne, nparts, epart) << endl;
+	
+// 	for (int i=1; i<=ne; i++)
+// 	  {
+// 	    mastermesh->VolumeElement(i).SetPartition(epart[i-1]);
+// 	  }
+	
+// 	return;
+//       }
+    
+
+
+
+    int timermetis = NgProfiler::CreateTimer ("Metis itself");
+    NgProfiler::StartTimer (timermetis);
+
+#ifdef METIS4
+    cout << "call metis ... " << flush;
+    METIS_PartMeshDual (&ne, &nn, &elmnts[0], &etype, &numflag, &nparts,
+			&edgecut, &epart[0], &npart[0]);
+#else
+    cout << "call metis-5 ... " << endl;
+    idx_t options[METIS_NOPTIONS];
+    
+    Array<idx_t> eptr(ne+1);
+    for (int j = 0; j < ne+1; j++)
+      eptr[j] = 4*j;
+
+    METIS_PartMeshDual (&ne, &nn, &eptr[0], &elmnts[0], NULL, NULL, &ncommon, &nparts,
+			NULL, NULL,
+			&edgecut, &epart[0], &npart[0]);
+#endif
+
+    NgProfiler::StopTimer (timermetis);
+
+    cout << "complete" << endl;
+#ifdef METIS4
+    cout << "edge-cut: " << edgecut << ", balance: " 
+     	 << ComputeElementBalance(ne, nparts, &epart[0]) << endl;
+#endif
+
+    // partition numbering by metis : 0 ...  ntasks - 1
+    // we want:                       1 ...  ntasks
+    for (int i=1; i<=ne; i++)
+      VolumeElement(i).SetPartition(epart[i-1] + 1);
+      }
+
+
+
+    for (int sei = 1; sei <= GetNSE(); sei++ )
+      {
+	int ei1, ei2;
+	GetTopology().GetSurface2VolumeElement (sei, ei1, ei2);
+	Element2d & sel = SurfaceElement (sei);
+
+        for (int j = 0; j < 2; j++)
+          {
+            int ei = (j == 0) ? ei1 : ei2;
+            if ( ei > 0 && ei <= GetNE() )
+              {
+		sel.SetPartition (VolumeElement(ei).GetPartition());
+		break;
+	      }
+	  }	
+      }
+    
+  }
+#endif
+
+
+  void Mesh :: PartHybridMesh () 
+  {
+#ifdef METIS
+    int ne = GetNE();
+    
+    int nn = GetNP();
+    int nedges = topology->GetNEdges();
+
+    idxtype  *xadj, * adjacency, *v_weights = NULL, *e_weights = NULL;
+
+    int weightflag = 0;
+    int numflag = 0;
+    int nparts = ntasks - 1;
+
+    int options[5];
+    options[0] = 0;
+    int edgecut;
+    idxtype * part;
+
+    xadj = new idxtype[nn+1];
+    part = new idxtype[nn];
+
+    Array<int> cnt(nn+1);
+    cnt = 0;
+
+    for ( int edge = 1; edge <= nedges; edge++ )
+      {
+	int v1, v2;
+	topology->GetEdgeVertices ( edge, v1, v2);
+	cnt[v1-1] ++;
+	cnt[v2-1] ++;
+      }
+
+    xadj[0] = 0;
+    for ( int n = 1; n <= nn; n++ )
+      {
+	xadj[n] = idxtype(xadj[n-1] + cnt[n-1]); 
+      }
+
+    adjacency = new idxtype[xadj[nn]];
+    cnt = 0;
+
+    for ( int edge = 1; edge <= nedges; edge++ )
+      {
+	int v1, v2;
+	topology->GetEdgeVertices ( edge, v1, v2);
+	adjacency[ xadj[v1-1] + cnt[v1-1] ] = v2-1;
+	adjacency[ xadj[v2-1] + cnt[v2-1] ] = v1-1;
+	cnt[v1-1]++;
+	cnt[v2-1]++;
+      }
+
+    for ( int vert = 0; vert < nn; vert++ )
+      {
+	FlatArray<int> array ( cnt[vert], &adjacency[ xadj[vert] ] );
+	BubbleSort(array);
+      }
+
+#ifdef METIS4
+    METIS_PartGraphKway ( &nn, xadj, adjacency, v_weights, e_weights, &weightflag, 
+			  &numflag, &nparts, options, &edgecut, part );
+#else
+    cout << "currently not supported (metis5), A" << endl;
+#endif
+
+    Array<int> nodesinpart(ntasks);
+
+    for ( int el = 1; el <= ne; el++ )
+      {
+	Element & volel = VolumeElement(el);
+	nodesinpart = 0;
+
+	//	VolumeElement(el).SetPartition(part[ volel[1] ] + 1);
+	
+	int el_np = volel.GetNP();
+	int partition = 0; 
+	for ( int i = 0; i < el_np; i++ )
+	  nodesinpart[ part[volel[i]-1]+1 ] ++;
+
+	for ( int i = 1; i < ntasks; i++ )
+	  if ( nodesinpart[i] > nodesinpart[partition] ) 
+	    partition = i;
+
+	volel.SetPartition(partition);
+
+      }
+
+    /*
+    for ( int i=1; i<=ne; i++)
+      {
+	neloc[ VolumeElement(i).GetPartition() ] ++;
+      }
+    */
+
+    delete [] xadj;
+    delete [] part;
+    delete [] adjacency;
+#else
+    cout << "parthybridmesh not available" << endl;
+#endif
+  }
+
+
+  void Mesh :: PartDualHybridMesh ( ) // Array<int> & neloc ) 
+  {
+#ifdef METIS
+    int ne = GetNE();
+    
+    // int nn = GetNP();
+    // int nedges = topology->GetNEdges();
+    int nfaces = topology->GetNFaces();
+
+    idxtype  *xadj, * adjacency, *v_weights = NULL, *e_weights = NULL;
+
+    int weightflag = 0;
+    int numflag = 0;
+    int nparts = ntasks - 1;
+
+    int options[5];
+    options[0] = 0;
+    int edgecut;
+    idxtype * part;
+
+    Array<int, 0> facevolels1(nfaces), facevolels2(nfaces);
+    facevolels1 = -1;
+    facevolels2 = -1;
+
+    Array<int, 0> elfaces;
+    xadj = new idxtype[ne+1];
+    part = new idxtype[ne];
+
+    Array<int, 0> cnt(ne+1);
+    cnt = 0;
+
+    for ( int el=1; el <= ne; el++ )
+      {
+	Element volel = VolumeElement(el);
+	topology->GetElementFaces(el, elfaces);
+	for ( int i = 0; i < elfaces.Size(); i++ )
+	  {
+	    if ( facevolels1[elfaces[i]-1] == -1 )
+	      facevolels1[elfaces[i]-1] = el;
+	    else
+	      {
+		facevolels2[elfaces[i]-1] = el;
+		cnt[facevolels1[elfaces[i]-1]-1]++;
+		cnt[facevolels2[elfaces[i]-1]-1]++;
+	      }
+	  }
+      }
+
+    xadj[0] = 0;
+    for ( int n = 1; n <= ne; n++ )
+      {
+	xadj[n] = idxtype(xadj[n-1] + cnt[n-1]); 
+      }
+
+    adjacency = new idxtype[xadj[ne]];
+    cnt = 0;
+
+    for ( int face = 1; face <= nfaces; face++ )
+      {
+	int e1, e2;
+	e1 = facevolels1[face-1];
+	e2 = facevolels2[face-1];
+	if ( e2 == -1 ) continue;
+	adjacency[ xadj[e1-1] + cnt[e1-1] ] = e2-1;
+	adjacency[ xadj[e2-1] + cnt[e2-1] ] = e1-1;
+	cnt[e1-1]++;
+	cnt[e2-1]++;
+      }
+
+    for ( int el = 0; el < ne; el++ )
+      {
+	FlatArray<int> array ( cnt[el], &adjacency[ xadj[el] ] );
+	BubbleSort(array);
+      }
+
+    int timermetis = NgProfiler::CreateTimer ("Metis itself");
+    NgProfiler::StartTimer (timermetis);
+
+#ifdef METIS4
+    METIS_PartGraphKway ( &ne, xadj, adjacency, v_weights, e_weights, &weightflag, 
+			  &numflag, &nparts, options, &edgecut, part );
+#else
+    cout << "currently not supported (metis5), B" << endl;
+#endif
+
+
+    NgProfiler::StopTimer (timermetis);
+
+    Array<int> nodesinpart(ntasks);
+
+    for ( int el = 1; el <= ne; el++ )
+      {
+	// Element & volel = VolumeElement(el);
+	nodesinpart = 0;
+
+	VolumeElement(el).SetPartition(part[el-1 ] + 1);
+	
+      }
+
+    /*    
+    for ( int i=1; i<=ne; i++)
+      {
+	neloc[ VolumeElement(i).GetPartition() ] ++;
+      }
+    */
+
+    delete [] xadj;
+    delete [] part;
+    delete [] adjacency;
+#else
+    cout << "partdualmesh not available" << endl;
+#endif
+
+  }
+
+
+
+
+
+  void Mesh :: PartDualHybridMesh2D ( ) 
+  {
+#ifdef METIS
+    int ne = GetNSE();
+    int nv = GetNV();
+
+    Array<idxtype> xadj(ne+1);
+    Array<idxtype> adjacency(ne*4);
+
+    // first, build the vertex 2 element table:
+    Array<int, PointIndex::BASE> cnt(nv);
+    cnt = 0;
+    for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
+      for (int j = 0; j < (*this)[sei].GetNP(); j++)
+	cnt[ (*this)[sei][j] ] ++;
+    
+    TABLE<SurfaceElementIndex, PointIndex::BASE> vert2els(cnt);
+    for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++)
+      for (int j = 0; j < (*this)[sei].GetNP(); j++)
+	vert2els.Add ((*this)[sei][j], sei);
+    
+
+    // find all neighbour elements
+    int cntnb = 0;
+    Array<int> marks(ne);   // to visit each neighbour just once
+    marks = -1;
+    for (SurfaceElementIndex sei = 0; sei < ne; sei++)
+      {
+	xadj[sei] = cntnb;
+	for (int j = 0; j < (*this)[sei].GetNP(); j++)
+	  {
+	    PointIndex vnr = (*this)[sei][j];
+
+	    // all elements with at least one common vertex
+	    for (int k = 0; k < vert2els[vnr].Size(); k++)   
+	      {
+		SurfaceElementIndex sei2 = vert2els[vnr][k];
+		if (sei == sei2) continue;
+		if (marks[sei2] == sei) continue;
+		
+		// neighbour, if two common vertices
+		int common = 0;
+		for (int m1 = 0; m1 < (*this)[sei].GetNP(); m1++)
+		  for (int m2 = 0; m2 < (*this)[sei2].GetNP(); m2++)
+		    if ( (*this)[sei][m1] == (*this)[sei2][m2])
+		      common++;
+		
+		if (common >= 2)
+		  {
+		    marks[sei2] = sei;     // mark as visited
+		    adjacency[cntnb++] = sei2;
+		  }
+	      }
+	  }
+      }
+    xadj[ne] = cntnb;
+
+    idxtype *v_weights = NULL, *e_weights = NULL;
+
+    int weightflag = 0;
+    int numflag = 0;
+    int nparts = ntasks - 1;
+
+    int options[5];
+    options[0] = 0;
+    int edgecut;
+    Array<idxtype> part(ne);
+
+    for ( int el = 0; el < ne; el++ )
+      BubbleSort (adjacency.Range (xadj[el], xadj[el+1]));
+
+#ifdef METIS4	
+    METIS_PartGraphKway ( &ne, &xadj[0], &adjacency[0], v_weights, e_weights, &weightflag, 
+			  &numflag, &nparts, options, &edgecut, &part[0] );
+#else
+    idx_t ncon = 1;
+    METIS_PartGraphKway ( &ne, &ncon, &xadj[0], &adjacency[0], 
+			  v_weights, NULL, e_weights, 
+			  &nparts, 
+			  NULL, NULL, NULL,
+			  &edgecut, &part[0] );
+#endif
+
+
+    for (SurfaceElementIndex sei = 0; sei < ne; sei++)
+      (*this) [sei].SetPartition (part[sei]+1);
+#else
+    cout << "partdualmesh not available" << endl;
+#endif
+
+  }
+
+
+
+
+
+
+  
+  void Mesh :: UpdateOverlap()
+  {
+    cout << "UpdateOverlap depreciated" << endl;
+  }
+
+
+
+
+
+
+
+
+}
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/paralleltop.cpp b/contrib/Netgen/libsrc/meshing/paralleltop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7467b03b363ae7dfb3f47583e3ed08a5c7815c90
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/paralleltop.cpp
@@ -0,0 +1,896 @@
+#ifdef PARALLEL
+
+
+#include <meshing.hpp>
+#include "paralleltop.hpp"
+
+
+namespace netgen
+{
+
+  static MPI_Group MPI_HIGHORDER_WORLD;
+  static MPI_Comm MPI_HIGHORDER_COMM;
+
+  void MyMPI_ExchangeTable (TABLE<int> & send_verts, 
+			    TABLE<int> & recv_verts, int tag,
+			    MPI_Comm comm = MPI_COMM_WORLD)
+  {
+    int ntasks, rank;
+    MPI_Comm_size(comm, &ntasks);
+    MPI_Comm_rank(comm, &rank);
+
+    Array<MPI_Request> requests;
+    for (int dest = 0; dest < ntasks; dest++)
+      if (dest != rank)
+	requests.Append (MyMPI_ISend (send_verts[dest], dest, tag, comm));
+
+    for (int i = 0; i < ntasks-1; i++)
+      {
+	MPI_Status status;
+	MPI_Probe (MPI_ANY_SOURCE, tag, comm, &status);
+	int size, src = status.MPI_SOURCE;
+	MPI_Get_count (&status, MPI_INT, &size);
+	recv_verts.SetEntrySize (src, size, sizeof(int));
+	requests.Append (MyMPI_IRecv (recv_verts[src], src, tag, comm));
+      }
+    MPI_Waitall (requests.Size(), &requests[0], MPI_STATUS_IGNORE);
+  }
+
+
+
+
+  void ParallelMeshTopology :: Reset ()
+  {
+    *testout << "ParallelMeshTopology::Reset" << endl;
+    
+    if ( ntasks == 1 ) return;
+    
+    int nvold = nv;
+    
+    ne = mesh.GetNE();
+    nv = mesh.GetNV();
+    nseg = mesh.GetNSeg();
+    nsurfel = mesh.GetNSE();
+    
+    ned = mesh.GetTopology().GetNEdges();
+    nfa = mesh.GetTopology().GetNFaces();
+    
+    loc2distedge.ChangeSize (ned);
+    for (int i = 0; i < ned; i++)
+      if (loc2distedge[i].Size() == 0)
+        loc2distedge.Add (i, -1);  // will be the global nr
+
+    loc2distface.ChangeSize (nfa);
+    for (int i = 0; i < nfa; i++)
+      if (loc2distface[i].Size() == 0)
+        loc2distface.Add (i, -1);  // will be the global nr
+
+    if ( nvold == nv ) return;
+
+    SetNV(nv);
+    SetNE(ne);
+  }
+
+
+  ParallelMeshTopology :: ~ParallelMeshTopology ()
+  {
+    ;
+  }
+
+
+
+  ParallelMeshTopology :: ParallelMeshTopology ( const netgen::Mesh & amesh )
+    : mesh(amesh)
+  {
+    ned = 0; 
+    nfa = 0; 
+    nv = 0;
+    ne = 0;
+    np = 0;
+    nseg = 0;
+    nsurfel = 0;
+    neglob = 0;
+    nvglob = 0;
+
+    nparel = 0;
+
+    coarseupdate = 0;
+  }
+
+ 
+
+  void ParallelMeshTopology ::  Print() const
+  {
+
+    (*testout) << endl <<  "TOPOLOGY FOR PARALLEL MESHES" << endl << endl;
+
+    for ( int i = 1; i <= nv; i++ )
+      if ( IsExchangeVert (i) )
+	{
+	  (*testout) << "exchange point  " << i << ":  global " << GetLoc2Glob_Vert(i) << endl;
+	  for ( int dest = 0; dest < ntasks; dest ++)
+	    if ( dest != id )
+ 	      if ( GetDistantPNum( dest, i  ) > 0 )
+ 		(*testout) << "   p" << dest << ": " << GetDistantPNum ( dest, i ) << endl; 
+	}
+
+    for ( int i = 1; i <= ned; i++ )
+      if ( IsExchangeEdge ( i ) )
+	{
+	  int v1, v2;
+	  mesh . GetTopology().GetEdgeVertices(i, v1, v2);
+	  (*testout) << "exchange edge  " << i << ":  global vertices "  << GetLoc2Glob_Vert(v1) << "  " 
+		     << GetLoc2Glob_Vert(v2) << endl;
+	  for ( int dest = 0; dest < ntasks; dest++)
+	    if ( GetDistantEdgeNum ( dest, i ) > 0 )
+	      if ( dest != id )
+		(*testout) << "   p" << dest << ": " << GetDistantEdgeNum ( dest, i ) << endl;
+	}
+
+    for ( int i = 1; i <= nfa; i++ )
+      if ( IsExchangeFace(i) )
+	{
+	  Array<int> facevert;
+	  mesh . GetTopology().GetFaceVertices(i, facevert);
+	    
+	  (*testout) << "exchange face  " << i << ":  global vertices " ;
+	  for ( int fi=0; fi < facevert.Size(); fi++)
+	    (*testout) << GetLoc2Glob_Vert(facevert[fi]) << "  ";
+	  (*testout) << endl; 
+	  for ( int dest = 0; dest < ntasks; dest++)
+	    if ( dest != id )
+	      {
+		if ( GetDistantFaceNum ( dest, i ) >= 0 )
+		  (*testout) << "   p" << dest << ": " << GetDistantFaceNum ( dest, i ) << endl;
+	      }
+	}
+
+
+    /*
+    for ( int i = 1; i < mesh.GetNE(); i++)
+      {
+	if ( !IsExchangeElement(i) ) continue;
+	Array<int> vert;
+	const Element & el = mesh.VolumeElement(i);
+
+	(*testout) << "parallel local element " << i << endl;
+	  
+	(*testout) << "vertices " ;
+	for ( int j = 0; j < el.GetNV(); j++)
+	  (*testout) << el.PNum(j+1)  << "  ";
+	(*testout) << "is ghost " << IsGhostEl(i) << endl;
+	(*testout) << endl;
+      }
+    */
+  }
+
+
+
+
+
+  int  ParallelMeshTopology :: GetDistantPNum ( int proc, int locpnum ) const
+  {
+    if ( proc == 0 )
+      return loc2distvert[locpnum][0];
+
+    for (int i = 1; i < loc2distvert[locpnum].Size(); i += 2)
+      if ( loc2distvert[locpnum][i] == proc )
+	return loc2distvert[locpnum][i+1];
+
+    return -1;
+  } 
+
+  int  ParallelMeshTopology :: GetDistantFaceNum ( int proc, int locfacenum ) const
+  {
+    if ( proc == 0 )
+      return loc2distface[locfacenum-1][0];
+
+    for ( int i = 1; i < loc2distface[locfacenum-1].Size(); i+=2 )
+      if ( loc2distface[locfacenum-1][i] == proc )
+	return loc2distface[locfacenum-1][i+1];
+
+    return -1;
+  } 
+
+  int  ParallelMeshTopology :: GetDistantEdgeNum ( int proc, int locedgenum ) const
+  {
+    if ( proc == 0 )
+      return loc2distedge[locedgenum-1][0];
+
+    for ( int i = 1; i < loc2distedge[locedgenum-1].Size(); i+=2 )
+      if ( loc2distedge[locedgenum-1][i] == proc )
+	return loc2distedge[locedgenum-1][i+1];
+
+    return -1;
+  } 
+
+  int  ParallelMeshTopology :: GetDistantElNum ( int proc, int locelnum ) const
+  {
+    if ( proc == 0 )
+      return loc2distel[locelnum-1][0];
+
+    for ( int i = 1; i < loc2distel[locelnum-1].Size(); i+=2 )
+      if ( loc2distel[locelnum-1][i] == proc )
+	return loc2distel[locelnum-1][i+1];
+
+    return -1;
+  } 
+
+
+
+  // gibt anzahl an distant pnums zurueck
+  // * pnums entspricht Array<int[2] >
+  int  ParallelMeshTopology :: GetDistantPNums ( int locpnum, int * distpnums ) const
+  {
+    distpnums[0] = 0;
+    distpnums[1] = loc2distvert[locpnum][0];
+    for ( int i = 1; i < loc2distvert[locpnum].Size(); i++ )
+      distpnums[i+1] = loc2distvert[locpnum][i];
+
+    int size = loc2distvert[locpnum].Size() / 2 + 1;
+    return size;
+  } 
+
+  int  ParallelMeshTopology :: GetDistantFaceNums ( int locfacenum, int * distfacenums ) const
+  {
+    distfacenums[0] = 0;
+    distfacenums[1] = loc2distface[locfacenum-1][0];
+
+    for ( int i = 1; i < loc2distface[locfacenum-1].Size(); i++ )
+      distfacenums[i+1] = loc2distface[locfacenum-1][i];
+
+    int size = loc2distface[locfacenum-1].Size() / 2 + 1;
+    return size;
+  } 
+
+  int  ParallelMeshTopology :: GetDistantEdgeNums ( int locedgenum, int * distedgenums ) const
+  {
+    distedgenums[0] = 0;
+    distedgenums[1] = loc2distedge[locedgenum-1][0];
+
+    for ( int i = 1; i < loc2distedge[locedgenum-1].Size(); i++ )
+      distedgenums[i+1] = loc2distedge[locedgenum-1][i];
+
+    int size = loc2distedge[locedgenum-1].Size() / 2 + 1;
+    return size;
+  } 
+
+  int  ParallelMeshTopology :: GetDistantElNums ( int locelnum, int * distelnums ) const
+  {
+    distelnums[0] = 0;
+    distelnums[1] = loc2distel[locelnum-1][0];
+
+    for ( int i = 1; i < loc2distel[locelnum-1].Size(); i++ )
+      distelnums[i+1] = loc2distel[locelnum-1][i];
+
+    int size = loc2distel[locelnum-1].Size() / 2 + 1;
+    return size;
+  } 
+
+
+
+
+  void ParallelMeshTopology :: SetDistantFaceNum ( int dest, int locnum, int distnum )
+  {
+    if ( dest == 0 )
+      {
+	loc2distface[locnum-1][0] = distnum;
+	return;
+      }
+
+    for ( int i = 1; i < loc2distface[locnum-1].Size(); i+=2 )
+      if ( loc2distface[locnum-1][i] == dest )
+	{
+	  loc2distface[locnum-1][i+1] = distnum;
+	  return;
+	}
+
+    loc2distface.Add(locnum-1, dest);
+    loc2distface.Add(locnum-1, distnum);
+  }
+
+  void ParallelMeshTopology :: SetDistantPNum ( int dest, int locnum, int distnum )
+  {
+    if ( dest == 0 )
+      {
+	loc2distvert[locnum][0] = distnum;  // HERE
+	return;
+      }
+
+    for ( int i = 1;  i < loc2distvert[locnum].Size(); i+=2 )
+      if ( loc2distvert[locnum][i] == dest )
+	{
+	  loc2distvert[locnum][i+1] = distnum;
+	  return;
+	}
+
+    loc2distvert.Add (locnum, dest);  
+    loc2distvert.Add (locnum, distnum); 
+  }
+
+
+  void ParallelMeshTopology :: SetDistantEdgeNum ( int dest, int locnum, int distnum )
+  {
+    if ( dest == 0 )
+      {
+	loc2distedge[locnum-1][0] = distnum;
+	return;
+      }
+
+    for ( int i = 1; i < loc2distedge[locnum-1].Size(); i+=2 )
+      if ( loc2distedge[locnum-1][i] == dest )
+	{
+	  loc2distedge[locnum-1][i+1] = distnum;
+	  return;
+	}
+
+    loc2distedge.Add (locnum-1, dest);
+    loc2distedge.Add (locnum-1, distnum);
+  }
+
+  void ParallelMeshTopology :: SetDistantEl ( int dest, int locnum, int distnum )
+  {
+    if ( dest == 0 )
+      {
+	loc2distel[locnum-1][0] = distnum;
+	return;
+      }
+
+    for ( int i = 1; i < loc2distel[locnum-1].Size(); i+=2 )
+      if ( loc2distel[locnum-1][i] == dest )
+	{
+	  loc2distel[locnum-1][i+1] = distnum;
+	  return;
+	}
+
+
+    loc2distel.Add (locnum-1, dest);
+    loc2distel.Add (locnum-1, distnum);
+  }
+
+  void ParallelMeshTopology :: SetDistantSurfEl ( int dest, int locnum, int distnum )
+  {
+    if ( dest == 0 )
+      {
+	loc2distsurfel[locnum-1][0] = distnum;
+	return;
+      }
+
+    for ( int i = 1;  i < loc2distsurfel[locnum-1].Size(); i+=2 )
+      if ( loc2distsurfel[locnum-1][i] == dest )
+	{
+	  loc2distsurfel[locnum-1][i+1] = distnum;
+	  return;
+	}
+
+    loc2distsurfel.Add (locnum-1, dest);
+    loc2distsurfel.Add (locnum-1, distnum);
+  }
+
+  void ParallelMeshTopology :: SetDistantSegm ( int dest, int locnum, int distnum )
+  {
+    if ( dest == 0 )
+      {
+	loc2distsegm[locnum-1][0] = distnum;
+	return;
+      }
+
+    for (int i = 1; i < loc2distsegm[locnum-1].Size(); i+=2 )
+      if ( loc2distsegm[locnum-1][i] == dest )
+	{
+	  loc2distsegm[locnum-1][i+1] = distnum;
+	  return;
+	}
+
+    loc2distsegm.Add (locnum-1, dest);
+    loc2distsegm.Add (locnum-1, distnum);
+  }
+
+  void ParallelMeshTopology :: GetVertNeighbours ( int vnum, Array<int> & dests ) const
+  {
+    dests.SetSize(0);
+    int i = 1;
+    while ( i < loc2distvert[vnum].Size() )
+      {
+	dests.Append ( loc2distvert[vnum][i] );
+	i+=2;
+      }
+  }
+
+
+  void ParallelMeshTopology :: Update ()
+  {
+    ne = mesh.GetNE();
+    nv = mesh.GetNV();
+    nseg = mesh.GetNSeg();
+    nsurfel = mesh.GetNSE();
+    
+    ned = mesh.GetTopology().GetNEdges();
+    nfa = mesh.GetTopology().GetNFaces();
+  }
+
+
+
+
+
+
+
+
+  void ParallelMeshTopology :: UpdateRefinement ()
+  {
+    ; 
+  }
+
+
+
+
+  void ParallelMeshTopology :: UpdateCoarseGridGlobal ()
+  {
+    if (id == 0)
+      PrintMessage ( 3, "UPDATE GLOBAL COARSEGRID STARTS" );      // JS
+
+
+    MPI_Group MPI_GROUP_WORLD;
+    int process_ranks[] = { 0 };
+    MPI_Comm_group (MPI_COMM_WORLD, &MPI_GROUP_WORLD);
+    MPI_Group_excl (MPI_GROUP_WORLD, 1, process_ranks, &MPI_HIGHORDER_WORLD);
+    MPI_Comm_create (MPI_COMM_WORLD, MPI_HIGHORDER_WORLD, &MPI_HIGHORDER_COMM);
+
+
+    int timer = NgProfiler::CreateTimer ("UpdateCoarseGridGlobal");
+    NgProfiler::RegionTimer reg(timer);
+
+
+    *testout << "ParallelMeshTopology :: UpdateCoarseGridGlobal" << endl;
+
+    const MeshTopology & topology = mesh.GetTopology();
+  
+    Array<int> sendarray, recvarray;
+
+    nfa = topology . GetNFaces();
+    ned = topology . GetNEdges();
+    np = mesh . GetNP();
+    nv = mesh . GetNV();
+    ne = mesh . GetNE();
+    nseg = mesh.GetNSeg();
+    nsurfel = mesh.GetNSE();
+
+    // low order processor - save mesh partition
+    if ( id == 0 )
+      {
+	for ( int eli = 1; eli <= ne; eli++ )
+	  loc2distel[eli-1][0] = eli;
+      
+	for ( int i = 1; i <= mesh .GetNV(); i++)
+	  loc2distvert[i][0] = i;
+      
+	for ( int i = 0; i < mesh . GetNSeg(); i++)
+	  loc2distsegm[i][0] = i+1;
+      
+	for ( int i = 0; i < mesh . GetNSE(); i++)
+	  loc2distsurfel[i][0] = i+1;
+      
+	for ( int i = 0; i < topology .GetNEdges(); i++)
+	  loc2distedge[i][0] = i+1;
+
+	for ( int i = 0; i < topology .GetNFaces(); i++)
+	  loc2distface[i][0] = i+1;
+      }
+
+
+    
+    if ( id == 0 )
+      {
+	sendarray.Append (nfa);
+	sendarray.Append (ned);
+
+	Array<int> edges, pnums, faces, elpnums;
+	sendarray.Append (ne);
+	for ( int el = 1; el <= ne; el++ )
+	  {
+	    topology.GetElementFaces (el, faces);
+	    topology.GetElementEdges ( el, edges );
+	    const Element & volel = mesh.VolumeElement (el);
+
+	    int globeli = GetLoc2Glob_VolEl(el);
+	    // cout << "el = " << el << ", globeli = " << globeli << endl;
+
+	    sendarray. Append ( globeli );
+	    sendarray. Append ( faces.Size() );
+	    sendarray. Append ( edges.Size() );
+	    sendarray. Append ( volel.GetNP() );
+
+	    for ( int i = 0; i < faces.Size(); i++ )
+	      sendarray. Append(faces[i] );
+	    for ( int i = 0; i < edges.Size(); i++ )
+	      sendarray. Append(edges[i] );
+
+	    for ( int i = 0; i < volel.GetNP(); i++ )
+	      if (id == 0)
+		sendarray. Append(volel[i] );
+	      else
+		sendarray. Append(GetLoc2Glob_Vert (volel[i]));
+	  }
+
+
+	sendarray.Append (nsurfel);
+	for (int el = 1; el <= nsurfel; el++)
+	  {
+	    topology.GetSurfaceElementEdges ( el, edges );
+	    const Element2d & surfel = mesh.SurfaceElement (el);
+
+	    sendarray. Append ( el );
+	    sendarray. Append ( edges.Size() );
+	    sendarray. Append ( surfel.GetNP() );
+
+	    for ( int i = 0; i < edges.Size(); i++ )
+	      sendarray. Append(edges[i] );
+
+	    for ( int i = 0; i < surfel.GetNP(); i++ )
+	      sendarray. Append(surfel[i] );
+	  }
+
+      }
+
+
+    if (id == 0)
+      MyMPI_Bcast ( sendarray );
+    else
+      MyMPI_Bcast ( recvarray );
+
+
+    if (id != 0)
+      {
+	Array<int,1> glob2loc_el;
+
+	glob2loc_el.SetSize (neglob);  
+	glob2loc_el = -1;
+	for ( int locel = 1; locel <= mesh.GetNE(); locel++)
+	  glob2loc_el[GetLoc2Glob_VolEl(locel)] = locel;
+
+	int ii = 0;
+	nfaglob = recvarray[ii++];
+	nedglob = recvarray[ii++];
+
+	Array<int> faces, edges;
+	Array<int> pnums, globalpnums;
+
+	int recv_ne = recvarray[ii++];
+	for (int hi = 0; hi < recv_ne; hi++)
+	  {
+	    int globvolel = recvarray[ii++];
+	    int distnfa = recvarray[ii++];
+	    int distned = recvarray[ii++];
+	    int distnp  = recvarray[ii++];
+
+	    int volel = glob2loc_el[globvolel];
+	    if (volel != -1)
+	      {
+		topology.GetElementFaces( volel, faces);
+		topology.GetElementEdges ( volel, edges);
+		const Element & volelement = mesh.VolumeElement (volel);
+
+		for ( int i = 0; i  < faces.Size(); i++)
+		  SetDistantFaceNum ( 0, faces[i], recvarray[ii++]);
+		
+		for ( int i = 0; i  < edges.Size(); i++)
+		  SetDistantEdgeNum ( 0, edges[i], recvarray[ii++]);
+
+		for ( int i = 0; i  < volelement.GetNP(); i++)
+		  SetDistantPNum ( 0, volelement[i], recvarray[ii++]);
+	      }
+	    else
+	      ii += distnfa + distned + distnp;
+	  }
+
+	
+	Array<int,1> glob2loc_sel;
+
+	int recv_nse = recvarray[ii++];
+	nseglob = recv_nse;
+
+	glob2loc_sel.SetSize (nseglob);  
+	glob2loc_sel = -1;
+	for ( int locel = 1; locel <= mesh.GetNSE(); locel++)
+	  glob2loc_sel[GetLoc2Glob_SurfEl(locel)] = locel;
+
+
+	for (int hi = 0; hi < recv_nse; hi++)
+	  {
+	    int globvolel = recvarray[ii++];
+	    int distned = recvarray[ii++];
+	    int distnp  = recvarray[ii++];
+
+	    int surfel = glob2loc_sel[globvolel];
+	    if (surfel != -1)
+	      {
+		topology.GetSurfaceElementEdges ( surfel, edges);
+		const Element2d & element = mesh.SurfaceElement (surfel);
+		
+		for ( int i = 0; i  < edges.Size(); i++)
+		  SetDistantEdgeNum ( 0, edges[i], recvarray[ii++]);
+
+		for ( int i = 0; i  < element.GetNP(); i++)
+		  SetDistantPNum ( 0, element[i], recvarray[ii++]);
+	      }
+	    else
+	      ii += distned + distnp;
+	  }
+
+
+      }
+    
+    if (id != 0)
+      {
+	*testout << "l2d - vert = " << loc2distvert << endl;
+	*testout << "l2d - edge = " << loc2distedge << endl;
+	*testout << "l2d - el = " << loc2distel << endl;
+	*testout << "l2d - sel = " << loc2distsurfel << endl;
+      }
+
+    coarseupdate = 1;
+  }
+
+
+
+
+
+
+  void ParallelMeshTopology :: UpdateCoarseGrid ()
+  {
+    static int timer = NgProfiler::CreateTimer ("UpdateCoarseGrid");
+    NgProfiler::RegionTimer reg(timer);
+
+
+    (*testout) << "UPDATE COARSE GRID PARALLEL TOPOLOGY " << endl;
+    if (id == 0)
+      PrintMessage (1, "UPDATE COARSE GRID PARALLEL TOPOLOGY ");
+
+
+    // find exchange edges - first send exchangeedges locnum, v1, v2
+    // receive distant distnum, v1, v2
+    // find matching
+    const MeshTopology & topology = mesh.GetTopology();
+
+    UpdateCoarseGridGlobal();
+    
+    MPI_Barrier (MPI_COMM_WORLD);
+
+    if ( id == 0 ) 
+      {
+	return;
+      }
+
+
+    Array<int> sendarray, recvarray;
+
+    nfa = topology . GetNFaces();
+    ned = topology . GetNEdges();
+    np = mesh . GetNP();
+    nv = mesh . GetNV();
+    ne = mesh . GetNE();
+    nseg = mesh.GetNSeg();
+    nsurfel = mesh.GetNSE();
+    
+
+    static int timerv = NgProfiler::CreateTimer ("UpdateCoarseGrid - ex vertices");
+    static int timere = NgProfiler::CreateTimer ("UpdateCoarseGrid - ex edges");
+    static int timerf = NgProfiler::CreateTimer ("UpdateCoarseGrid - ex faces");
+
+
+    Array<int,1> glob2loc;
+    Array<int> cnt_send(ntasks-1);
+
+    NgProfiler::StartTimer (timere);
+
+    // exchange edges
+    int maxedge = 0;
+    for (int edge = 1; edge <= ned; edge++)
+      maxedge = max (maxedge, GetDistantEdgeNum (0, edge));
+
+    glob2loc.SetSize (maxedge);
+    glob2loc = -1;
+    
+    for (int edge = 1; edge <= ned; edge++)
+      glob2loc[GetDistantEdgeNum(0, edge)] = edge;
+
+    cnt_send = 0;
+    int v1, v2;
+    for (int edge = 1; edge <= ned; edge++)
+      {
+	topology.GetEdgeVertices (edge, v1, v2);
+	for (int dest = 1; dest < ntasks; dest++)
+	  if (IsExchangeVert (dest, v1) && 
+	      IsExchangeVert (dest, v2))
+	    {
+	      cnt_send[dest-1]+=2;
+	    }
+      }
+    
+    TABLE<int> send_edges(cnt_send);
+    for (int edge = 1; edge <= ned; edge++)
+      {
+	topology.GetEdgeVertices (edge, v1, v2);
+	for (int dest = 1; dest < ntasks; dest++)
+	  {
+	    if (IsExchangeVert (dest, v1) && 
+		IsExchangeVert (dest, v2))
+	      {
+		send_edges.Add (dest-1, GetDistantEdgeNum(0, edge));
+		send_edges.Add (dest-1, edge);
+	      }
+	  }
+      }
+
+
+    // *testout << "send exchange edges: " << send_edges << endl;
+
+    TABLE<int> recv_edges(ntasks-1);
+    MyMPI_ExchangeTable (send_edges, recv_edges, MPI_TAG_MESH+9, MPI_HIGHORDER_COMM);
+
+    // *testout << "recv exchange edges: " << recv_edges << endl;
+
+    for (int sender = 1; sender < ntasks; sender ++)
+      if (id != sender)
+	{
+	  FlatArray<int> recvarray = recv_edges[sender-1];
+
+	  for (int ii = 0; ii < recvarray.Size(); )
+	    { 
+	      int globe = recvarray[ii++];
+	      int diste = recvarray[ii++];
+
+	      if (globe <= maxedge)
+		{
+		  int loce = glob2loc[globe];
+		  if (loce != -1)
+		    SetDistantEdgeNum (sender, loce, diste);
+		}
+	    }
+	} 
+
+
+ 
+    NgProfiler::StopTimer (timere);
+
+
+    MPI_Barrier (MPI_HIGHORDER_COMM);
+
+    if (mesh.GetDimension() == 3)
+      {
+
+    NgProfiler::StartTimer (timerf);
+
+    glob2loc.SetSize (nfaglob);
+    glob2loc = -1;
+    
+    for (int loc = 1; loc <= nfa; loc++)
+      glob2loc[GetDistantFaceNum(0, loc)] = loc;
+
+    cnt_send = 0;
+    Array<int> verts;
+    for (int face = 1; face <= nfa; face++)
+      {
+	topology.GetFaceVertices (face, verts);
+	for (int dest = 1; dest < ntasks; dest++)
+	  if (IsExchangeVert (dest, verts[0]) && 
+	      IsExchangeVert (dest, verts[1]) &&
+	      IsExchangeVert (dest, verts[2]))
+	    {
+	      cnt_send[dest-1]+=2;
+	    }
+      }
+    
+    TABLE<int> send_faces(cnt_send);
+    for (int face = 1; face <= nfa; face++)
+      {
+	topology.GetFaceVertices (face, verts);
+	for (int dest = 1; dest < ntasks; dest++)
+	  {
+	    if (IsExchangeVert (dest, verts[0]) && 
+		IsExchangeVert (dest, verts[1]) &&
+		IsExchangeVert (dest, verts[2]))
+	      {
+		send_faces.Add (dest-1, GetDistantFaceNum(0, face));
+		send_faces.Add (dest-1, face);
+	      }
+	  }
+      }
+    TABLE<int> recv_faces(ntasks-1);
+    MyMPI_ExchangeTable (send_faces, recv_faces, MPI_TAG_MESH+8, MPI_HIGHORDER_COMM);
+
+    // *testout << "send exchange faces: " << send_faces << endl;
+    // *testout << "recv exchange faces: " << recv_faces << endl;
+
+    for (int sender = 1; sender < ntasks; sender ++)
+      if (id != sender)
+	{
+	  FlatArray<int> recvarray = recv_faces[sender-1];
+
+	  for (int ii = 0; ii < recvarray.Size(); )
+	    { 
+	      int globf = recvarray[ii++];
+	      int distf = recvarray[ii++];
+	      int locf = glob2loc[globf];
+
+	      // *testout << "set distant face, sender = " << sender << ", locf = " << locf << "; distf = " << distf << endl;
+	      if (locf != -1)
+		SetDistantFaceNum (sender, locf, distf);
+	    }
+	} 
+
+
+    NgProfiler::StopTimer (timerf);
+      }
+
+
+    // set which elements are where for the master processor
+
+    coarseupdate = 1;
+  }
+
+
+
+
+
+  void ParallelMeshTopology :: UpdateTopology () 
+  {
+    ;
+  }
+
+
+
+
+
+
+
+  void ParallelMeshTopology :: SetNV ( const int anv )
+  {
+    *testout << "called setnv"  << endl
+             << "old size: " << loc2distvert.Size() << endl
+             << "new size: " << anv << endl;
+
+    loc2distvert.ChangeSize (anv);
+    for (int i = 1; i <= anv; i++)
+      if (loc2distvert.EntrySize(i) == 0)
+        loc2distvert.Add (i, -1);  // will be the global nr
+
+    nv = anv;
+  }
+
+  void ParallelMeshTopology :: SetNE ( const int ane )
+  {
+    loc2distel.ChangeSize (ane);
+    for (int i = 0; i < ane; i++)
+      if (loc2distel[i].Size() == 0)
+	loc2distel.Add (i, -1);   // will be the global nr
+    ne = ane;
+  }
+
+  void ParallelMeshTopology :: SetNSE ( int anse )
+  {
+    loc2distsurfel.ChangeSize (anse);
+    for (int i = 0; i < anse; i++)
+      if (loc2distsurfel[i].Size() == 0)
+        loc2distsurfel.Add (i, -1);  // will be the global nr
+
+    nsurfel = anse;
+  }
+
+  void ParallelMeshTopology :: SetNSegm ( int anseg )
+  {
+    loc2distsegm.ChangeSize (anseg);
+    for (int i = 0; i < anseg; i++)
+      if (loc2distsegm[i].Size() == 0)
+        loc2distsegm.Add (i, -1);  // will be the global nr
+
+    nseg = anseg;
+  }
+
+
+}
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/paralleltop.hpp b/contrib/Netgen/libsrc/meshing/paralleltop.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..52c03c5225fd37a2389d8b492000a4c82b88752d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/paralleltop.hpp
@@ -0,0 +1,156 @@
+#ifndef FILE_PARALLELTOP
+#define FILE_PARALLELTOP
+
+namespace netgen
+{
+
+  //  extern int ntasks;
+
+  class ParallelMeshTopology
+  {
+    const Mesh & mesh;
+
+    // number of local elements, vertices, points (?), edges, faces
+    int ne, nv, np, ned, nfa;
+
+    // number of local segments and surface elements
+    int nseg, nsurfel;
+
+    // number of global elements, vertices, ???,  faces
+    int neglob, nseglob, nvglob;
+    int nparel;
+    int nfaglob;
+    int nedglob;
+
+    /**
+       mapping from local to distant vertex number
+       each row of the table corresponds to one vertex
+       each row contains a list of pairs (procnr, dist_vnum)
+    */
+    TABLE<int,PointIndex::BASE> loc2distvert;
+    TABLE<int,0> loc2distedge, loc2distface;
+    TABLE<int,0> loc2distel, loc2distsegm, loc2distsurfel;
+
+    bool coarseupdate;
+
+  public:
+
+    ParallelMeshTopology (const Mesh & amesh);
+    ~ParallelMeshTopology ();
+
+    /// set number of local vertices, reset sizes of loc2dist_vert, isexchangevert...
+    void SetNV (int anv);
+    void SetNE (int ane);
+    void SetNSE (int anse);
+    void SetNSegm (int anseg);
+
+    void SetNVGlob ( int anvglob )   { nvglob = anvglob; }
+    void SetNEGlob ( int aneglob )   { neglob = aneglob; }
+    void SetNSEGlob ( int anseglob )   { nseglob = anseglob; }
+
+    int GetNVGlob ()  { return nvglob; }
+    int GetNEGlob ()  { return neglob; }
+
+
+    void Reset ();
+
+    void SetLoc2Glob_Vert   ( int locnum, int globnum ) { loc2distvert[locnum][0] = globnum; }
+    void SetLoc2Glob_VolEl  ( int locnum, int globnum ) { loc2distel[locnum-1][0] = globnum; }
+    void SetLoc2Glob_SurfEl ( int locnum, int globnum ) { loc2distsurfel[locnum-1][0] = globnum; }
+    void SetLoc2Glob_Segm   ( int locnum, int globnum ) { loc2distsegm[locnum-1][0] = globnum; }
+
+    int GetLoc2Glob_Vert  ( int locnum ) const { return loc2distvert[locnum][0]; }
+    int GetLoc2Glob_VolEl ( int locnum ) const { return loc2distel[locnum-1][0]; }
+    int GetLoc2Glob_SurfEl ( int locnum ) const { return loc2distsurfel[locnum-1][0]; }
+
+    void GetVertNeighbours ( int vnum, Array<int> & dests ) const;
+
+
+    int GetNDistantPNums ( int locpnum ) const       
+    { return loc2distvert[locpnum].Size() / 2 + 1; } 
+
+    int GetNDistantFaceNums ( int locfacenum ) const 
+    { return loc2distface[locfacenum-1].Size() / 2 + 1; } 
+
+    int GetNDistantEdgeNums ( int locedgenum ) const  
+    { return loc2distedge[locedgenum-1].Size() / 2 + 1; }
+
+    int GetNDistantElNums ( int locelnum ) const      
+    { return loc2distel[locelnum-1].Size() / 2 + 1; }
+
+    int GetDistantPNum ( int proc, int locpnum ) const;
+    int GetDistantEdgeNum ( int proc, int locedgenum ) const;
+    int GetDistantFaceNum ( int proc, int locedgenum ) const;
+    int GetDistantElNum ( int proc, int locelnum ) const;
+
+    int GetDistantPNums ( int locpnum, int * distpnums ) const;
+    int GetDistantEdgeNums ( int locedgenum, int * distedgenums ) const;
+    int GetDistantFaceNums ( int locedgenum, int * distfacenums ) const;
+    int GetDistantElNums ( int locelnum, int * distfacenums ) const;
+
+    void Print() const;
+
+
+    bool IsExchangeVert ( PointIndex vnum ) const  { return loc2distvert[vnum].Size() > 1; }
+    bool IsExchangeEdge ( int ednum ) const  { return loc2distedge[ednum-1].Size() > 1; }
+    bool IsExchangeFace ( int fnum ) const   { return loc2distface[fnum-1].Size() > 1; }
+    bool IsExchangeElement ( int elnum ) const   { return false; }
+
+
+    bool IsExchangeSEl ( int selnum ) const { return loc2distsurfel[selnum-1].Size() > 1; }
+
+
+    bool IsExchangeVert (int dest, int vnum ) const
+    {
+      FlatArray<int> exchange = loc2distvert[vnum];
+      for (int i = 1; i < exchange.Size(); i += 2)
+	if (exchange[i] == dest) return true;
+      return false;
+    }
+
+    bool IsExchangeEdge (int dest, int ednum ) const
+    {
+      FlatArray<int> exchange = loc2distedge[ednum-1];
+      for (int i = 1; i < exchange.Size(); i += 2)
+	if (exchange[i] == dest) return true;
+      return false;
+    }
+
+    bool IsExchangeFace (int dest, int fnum ) const
+    {
+      FlatArray<int> exchange = loc2distface[fnum-1];
+      for (int i = 1; i < exchange.Size(); i += 2)
+	if (exchange[i] == dest) return true;
+      return false;
+    }
+
+    bool IsExchangeElement (int dest, int elnum ) const  { return false; }
+
+    void Update();
+
+    void UpdateCoarseGrid();
+    void UpdateRefinement ();
+    void UpdateTopology ();
+    void UpdateExchangeElements();
+
+    void UpdateCoarseGridGlobal();
+
+    bool DoCoarseUpdate() const { return !coarseupdate; }
+
+    void SetDistantFaceNum ( int dest, int locnum, int distnum );
+    void SetDistantPNum ( int dest, int locnum, int distnum );
+    void SetDistantEdgeNum ( int dest, int locnum, int distnum );
+    void SetDistantEl ( int dest, int locnum, int distnum );
+    void SetDistantSurfEl ( int dest, int locnum, int distnum );
+    void SetDistantSegm ( int dest, int locnum, int distnum );
+
+    bool IsGhostEl ( int elnum ) const   { return mesh.VolumeElement(elnum).IsGhost(); }
+  };
+ 
+
+}
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/parser2.cpp b/contrib/Netgen/libsrc/meshing/parser2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..55bd1a5d6e5aa8450a9f59e497b555ea4e7ca93f
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/parser2.cpp
@@ -0,0 +1,605 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+#ifdef WIN32
+#define COMMASIGN ':'
+#else
+#define COMMASIGN ','
+#endif
+
+
+namespace netgen
+{
+
+
+void LoadMatrixLine (istream & ist, DenseMatrix & m, int line)
+{
+  char ch;
+  int pnum;
+  float f;
+
+  ist >> ch;
+  while (ch != '}')
+    {
+      ist.putback (ch);
+      ist >> f;
+      ist >> ch;
+      ist >> pnum;
+
+      if (ch == 'x' || ch == 'X')
+	m.Elem(line, 2 * pnum - 1) = f;
+      if (ch == 'y' || ch == 'Y')
+	m.Elem(line, 2 * pnum) = f;
+
+      ist >> ch;
+      if (ch == COMMASIGN)
+	ist >> ch;
+    }
+}
+
+
+void netrule :: LoadRule (istream & ist)
+{
+  char buf[256];
+  char ch;
+  Point2d p;
+  INDEX_2 lin;
+  int i, j;
+  DenseMatrix tempoldutonewu(20, 20), tempoldutofreearea(20, 20),
+    tempoldutofreearealimit(20, 20);
+
+  tempoldutonewu = 0;
+  tempoldutofreearea = 0;
+  tempoldutofreearealimit = 0;
+
+  noldp = 0;
+  noldl = 0;
+
+  ist.get (buf, sizeof(buf), '"');
+  ist.get (ch);
+  ist.get (buf, sizeof(buf), '"');
+  ist.get (ch);
+
+  // if(name != NULL) 
+  delete [] name;
+  name = new char[strlen (buf) + 1];
+  strcpy (name, buf);
+  //(*testout) << "name " << name << endl;
+  //  (*mycout) << "Rule " << name << " found." << endl;
+
+  do
+    {
+      ist >> buf;
+
+      //(*testout) << "buf " << buf << endl;
+
+      if (strcmp (buf, "quality") == 0)
+
+	{
+	  ist >> quality;
+	}
+
+      else if (strcmp (buf, "mappoints") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      ist >> p.X();
+	      ist >> ch;    // ','
+	      ist >> p.Y();
+	      ist >> ch;    // ')'
+
+	      points.Append (p);
+	      noldp++;
+
+	      tolerances.SetSize (noldp);
+	      tolerances.Elem(noldp).f1 = 1.0;
+	      tolerances.Elem(noldp).f2 = 0;
+	      tolerances.Elem(noldp).f3 = 1.0;
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  if (ch == '{')
+		    {
+		      ist >> tolerances.Elem(noldp).f1;
+		      ist >> ch;  // ','
+		      ist >> tolerances.Elem(noldp).f2;
+		      ist >> ch;  // ','
+		      ist >> tolerances.Elem(noldp).f3;
+		      ist >> ch;  // '}'
+		    }
+		  else if (ch == 'd')
+		    {
+		      //            delpoints.Append (noldp);
+		      ist >> ch; // 'e'
+		      ist >> ch; // 'l'
+		    }
+
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+
+      else if (strcmp (buf, "maplines") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      ist >> lin.I1();
+	      ist >> ch;    // ','
+	      ist >> lin.I2();
+	      ist >> ch;    // ')'
+
+
+	      //(*testout) << "read line " << lin.I1() << " " << lin.I2() << endl;
+	      lines.Append (lin);
+	      linevecs.Append (points.Get(lin.I2()) - points.Get(lin.I1()));
+	      noldl++;
+	      linetolerances.SetSize (noldl);
+	      linetolerances.Elem(noldl).f1 = 0;
+	      linetolerances.Elem(noldl).f2 = 0;
+	      linetolerances.Elem(noldl).f3 = 0;
+
+	      //(*testout) << "mapl1" << endl; 
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  //(*testout) << "working on character \""<<ch<<"\""<< endl;
+		  if (ch == '{')
+		    {
+		      ist >> linetolerances.Elem(noldl).f1;
+		      ist >> ch;  // ','
+		      ist >> linetolerances.Elem(noldl).f2;
+		      ist >> ch;  // ','
+		      ist >> linetolerances.Elem(noldl).f3;
+		      ist >> ch;  // '}'
+		    }
+		  else if (ch == 'd')
+		    {
+		      dellines.Append (noldl);
+		      ist >> ch; // 'e'
+		      ist >> ch; // 'l'
+		      //(*testout) << "read del" << endl;
+		    }
+
+		  ist >> ch;
+		  //(*testout) << "read character \""<<ch<<"\""<< endl;
+		}
+
+	      ist >> ch;
+	      //(*testout) << "read next character \""<<ch<<"\""<< endl;
+	    }
+	  
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "newpoints") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      ist >> p.X();
+	      ist >> ch;    // ','
+	      ist >> p.Y();
+	      ist >> ch;    // ')'
+
+	      points.Append (p);
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  if (ch == '{')
+		    {
+		      LoadMatrixLine (ist, tempoldutonewu,
+				      2 * (points.Size()-noldp) - 1);
+
+		      ist >> ch; // '{'
+		      LoadMatrixLine (ist, tempoldutonewu,
+				      2 * (points.Size()-noldp));
+		    }
+
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "newlines") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      ist >> lin.I1();
+	      ist >> ch;    // ','
+	      ist >> lin.I2();
+	      ist >> ch;    // ')'
+
+	      lines.Append (lin);
+	      linevecs.Append (points.Get(lin.I2()) - points.Get(lin.I1()));
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "freearea") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      ist >> p.X();
+	      ist >> ch;    // ','
+	      ist >> p.Y();
+	      ist >> ch;    // ')'
+
+	      freezone.Append (p);
+	      freezonelimit.Append (p);
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  if (ch == '{')
+		    {
+		      LoadMatrixLine (ist, tempoldutofreearea,
+				      2 * freezone.Size() - 1);
+
+		      ist >> ch; // '{'
+		      LoadMatrixLine (ist, tempoldutofreearea,
+				      2 * freezone.Size());
+		    }
+
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  for (i = 1; i <= tempoldutofreearealimit.Height(); i++)
+	    for (j = 1; j <= tempoldutofreearealimit.Width(); j++)
+	      tempoldutofreearealimit.Elem(i,j) =
+		tempoldutofreearea.Elem(i,j);
+
+
+	  ist.putback (ch);
+	}    
+      else if (strcmp (buf, "freearea2") == 0)
+	{
+	  ist >> ch;
+	  int freepi = 0;
+	  tempoldutofreearealimit = 0;
+
+	  while (ch == '(')
+	    {
+	      freepi++;
+
+	      ist >> p.X();
+	      ist >> ch;    // ','
+	      ist >> p.Y();
+	      ist >> ch;    // ')'
+
+	      freezonelimit.Elem(freepi) = p;
+	  
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  if (ch == '{')
+		    {
+		      LoadMatrixLine (ist, tempoldutofreearealimit,
+				      2 * freepi - 1);
+
+		      ist >> ch; // '{'
+		      LoadMatrixLine (ist, tempoldutofreearealimit,
+				      2 * freepi);
+		    }
+
+		  ist >> ch;
+		}
+	  
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "elements") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      elements.Append (Element2d());
+
+	      ist >> elements.Last().PNum(1);
+	      ist >> ch;    // ','
+	  
+	      if (ch == COMMASIGN)
+		{
+		  ist >> elements.Last().PNum(2);
+		  ist >> ch;    // ','
+		}
+	      if (ch == COMMASIGN)
+		{
+		  ist >> elements.Last().PNum(3);
+		  ist >> ch;    // ','
+		}
+	      if (ch == COMMASIGN)
+		{
+		  elements.Last().SetType (QUAD);
+		  ist >> elements.Last().PNum(4);
+		  ist >> ch;    // ','
+		  
+		  // const Element2d & el = elements.Last();
+		  /*
+		  orientations.Append (threeint(el.PNum(1), el.PNum(2), el.PNum(3)));
+		  orientations.Append (threeint(el.PNum(2), el.PNum(3), el.PNum(4)));
+		  orientations.Append (threeint(el.PNum(3), el.PNum(4), el.PNum(1)));
+		  orientations.Append (threeint(el.PNum(4), el.PNum(1), el.PNum(2)));
+		  */
+		}
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "orientations") == 0)
+
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      //        threeint a = threeint();
+	      orientations.Append (threeint());
+
+	      ist >> orientations.Last().i1;
+	      ist >> ch;    // ','
+	      ist >> orientations.Last().i2;
+	      ist >> ch;    // ','
+	      ist >> orientations.Last().i3;
+	      ist >> ch;    // ','
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "endrule") != 0)
+	{
+	  PrintSysError ("Parser error, unknown token ", buf);
+	}
+    }
+  while (!ist.eof() && strcmp (buf, "endrule") != 0);
+
+  oldutonewu.SetSize (2 * (points.Size() - noldp), 2 * noldp);
+  oldutofreearea.SetSize (2 * freezone.Size(), 2 * noldp);
+  oldutofreearealimit.SetSize (2 * freezone.Size(), 2 * noldp);
+
+  for (i = 1; i <= oldutonewu.Height(); i++)
+    for (j = 1; j <= oldutonewu.Width(); j++)
+      oldutonewu.Elem(i, j) = tempoldutonewu.Elem(i, j);
+
+  for (i = 1; i <= oldutofreearea.Height(); i++)
+    for (j = 1; j <= oldutofreearea.Width(); j++)
+      oldutofreearea.Elem(i, j) = tempoldutofreearea.Elem(i, j);
+
+  for (i = 1; i <= oldutofreearea.Height(); i++)
+    for (j = 1; j <= oldutofreearea.Width(); j++)
+      oldutofreearealimit.Elem(i, j) = tempoldutofreearealimit.Elem(i, j);
+
+  freesetinequ.SetSize (freezone.Size());
+
+
+  {
+    char ok;
+    int minn;
+    Array<int> pnearness (noldp);
+
+    for (i = 1; i <= pnearness.Size(); i++)
+      pnearness.Elem(i) = 1000;
+
+    for (j = 1; j <= 2; j++)
+      pnearness.Elem(GetPointNr (1, j)) = 0;
+
+    do
+      {
+	ok = 1;
+
+	for (i = 1; i <= noldl; i++)
+	  {
+	    minn = 1000;
+	    for (j = 1; j <= 2; j++)
+	      minn = min2 (minn, pnearness.Get(GetPointNr (i, j)));
+
+	    for (j = 1; j <= 2; j++)
+	      if (pnearness.Get(GetPointNr (i, j)) > minn+1)
+		{
+		  ok = 0;
+		  pnearness.Elem(GetPointNr (i, j)) = minn+1;
+		}
+	  }
+      }
+    while (!ok);
+
+    lnearness.SetSize (noldl);
+
+    for (i = 1; i <= noldl; i++)
+      {
+	lnearness.Elem(i) = 0;
+	for (j = 1; j <= 2; j++)
+	  lnearness.Elem(i) += pnearness.Get(GetPointNr (i, j));
+      }
+  }
+
+  oldutofreearea_i.SetSize (10);
+  freezone_i.SetSize (10);
+
+  for (i = 0; i < oldutofreearea_i.Size(); i++)
+    {
+      double lam1 = 1.0/(i+1);
+
+      oldutofreearea_i[i] = new DenseMatrix (oldutofreearea.Height(), oldutofreearea.Width());
+      DenseMatrix & mati = *oldutofreearea_i[i];
+      for (j = 0; j < oldutofreearea.Height(); j++)
+	for (int k = 0; k < oldutofreearea.Width(); k++)
+	  mati(j,k) = lam1 * oldutofreearea(j,k) + (1 - lam1) * oldutofreearealimit(j,k);
+
+      freezone_i[i] = new Array<Point2d> (freezone.Size());
+      Array<Point2d> & fzi = *freezone_i[i];
+      for (int j = 0; j < freezone.Size(); j++)
+	fzi[j] = freezonelimit[j] + lam1 * (freezone[j] - freezonelimit[j]);
+    }
+}
+
+
+
+
+extern const char * triarules[];
+extern const char * quadrules[];
+
+void Meshing2 :: LoadRules (const char * filename, bool quad)
+{
+  char buf[256];
+  istream * ist;
+  //char *tr1 = NULL;
+  string tr1;
+
+  /*
+  ifstream ist (filename);
+  if (!ist.good())
+    {
+      cerr << "Rule description file " << filename << " not found" << endl;
+      exit (1);
+    }
+  */
+
+
+  if (filename)
+    {
+      //      (*mycout) << "rule-filename = " << filename << endl;
+      ist = new ifstream (filename);
+    }
+  else 
+    {
+      /* connect tetrules to one string */
+      const char ** hcp;
+
+      // if (!mparam.quad)
+      if (!quad)
+	{
+	  hcp = triarules;
+	  PrintMessage (3, "load internal triangle rules");
+	}
+      else
+	{
+	  hcp = quadrules;
+	  PrintMessage (3, "load internal quad rules");
+	  // LoadRules ("rules/quad.rls");
+	}
+
+      size_t len = 0;
+      while (*hcp)
+	{
+	  //	  (*testout) << "POS2 *hcp " << *hcp << endl;
+	  len += strlen (*hcp);
+	  hcp++;
+	}
+      //tr1 = new char[len+1];
+      //tr1[0] = 0;
+      tr1.reserve(len+1);
+
+
+      // if (!mparam.quad)
+      if (!quad)
+	hcp = triarules;
+      else
+	hcp = quadrules;
+
+
+      //char * tt1 = tr1;
+      while (*hcp)
+	{
+	  //strcat (tt1, *hcp);
+	  //tt1 += strlen (*hcp);
+	  tr1.append(*hcp);
+	  hcp++;
+	}
+      
+#ifdef WIN32
+      // VC++ 2005 workaround
+	  for(string::size_type i=0; i<tr1.size(); i++)
+	if(tr1[i] == ',')
+	  tr1[i] = ':';
+#endif
+
+      ist = new istringstream (tr1);
+    }
+
+
+  if (!ist->good())
+    {
+      cerr << "Rule description file " << filename << " not found" << endl;
+      delete ist;
+      exit (1);
+    }
+    
+  while (!ist->eof())
+    {
+      buf[0] = 0;
+      (*ist) >> buf;
+
+      if (strcmp (buf, "rule") == 0)
+	{
+	  //(*testout) << "found rule" << endl;
+	  netrule * rule = new netrule;
+	  //(*testout) << "fr1" << endl;
+	  rule -> LoadRule(*ist);
+	  //(*testout) << "fr2" << endl;
+	  
+	  rules.Append (rule);
+	}
+      //(*testout) << "loop" << endl;
+    }
+  //(*testout) << "POS3" << endl;
+
+  delete ist;
+  //delete [] tr1;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/parser3.cpp b/contrib/Netgen/libsrc/meshing/parser3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c6ccba4a6892226347c3a9d7120c0bf61e3e7d38
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/parser3.cpp
@@ -0,0 +1,1019 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+#ifdef WIN32
+#define COMMASIGN ':'
+#else
+#define COMMASIGN ','
+#endif
+
+
+namespace netgen
+{
+
+extern const char * tetrules[];
+
+void LoadVMatrixLine (istream & ist, DenseMatrix & m, int line)
+{
+  char ch;
+  int pnum;
+  float f;
+  
+  ist >> ch;
+  while (ch != '}')
+    {
+      ist.putback (ch);
+      ist >> f;
+      ist >> ch;
+      ist >> pnum;
+      
+      if (ch == 'x' || ch == 'X')
+	m.Elem(line, 3 * pnum - 2) = f;
+      if (ch == 'y' || ch == 'Y')
+	m.Elem(line, 3 * pnum - 1) = f;
+      if (ch == 'z' || ch == 'Z')
+	m.Elem(line, 3 * pnum    ) = f;
+
+      if (ch == 'p' || ch == 'P')
+	{
+	  m.Elem(line  , 3 * pnum-2) = f;
+	  m.Elem(line+1, 3 * pnum-1) = f;
+	  m.Elem(line+2, 3 * pnum  ) = f;
+	}
+
+      ist >> ch;
+      if (ch == COMMASIGN)
+	ist >> ch;
+    }
+}
+
+
+
+
+
+int vnetrule :: NeighbourTrianglePoint (const threeint & t1, const threeint & t2) const
+{
+  Array<int> tr1(3);
+  Array<int> tr2(3);
+  tr1.Elem(1)=t1.i1;
+  tr1.Elem(2)=t1.i2;
+  tr1.Elem(3)=t1.i3;
+  tr2.Elem(1)=t2.i1;
+  tr2.Elem(2)=t2.i2;
+  tr2.Elem(3)=t2.i3;
+
+
+  int ret=0;
+
+  for (int i=1; i<=3; i++)
+    {
+      for (int j=1; j<=3; j++)
+	{
+	  if ((tr1.Get(i)==tr2.Get(j) && tr1.Get((i%3)+1)==tr2.Get((j%3)+1)) ||
+              (tr1.Get(i)==tr2.Get((j%3)+1) && tr1.Get((i%3)+1)==tr2.Get(j)))
+	    {ret = tr2.Get((j+1)%3+1);}
+	}      
+    }
+
+  return ret;
+
+}
+
+void vnetrule :: LoadRule (istream & ist)
+{
+  char buf[256];
+  char ch, ok;
+  Point3d p;
+  Element2d face;
+  int i, j, i1, i2, i3, fs, ii, ii1, ii2, ii3;
+  twoint edge;
+  DenseMatrix tempoldutonewu(30, 20), 
+    tempoldutofreezone(30, 20),
+    tempoldutofreezonelimit(30, 20),
+    tfz(20, 20),
+    tfzl(20, 20);
+
+  tempoldutonewu = 0;
+  tempoldutofreezone = 0;
+  tfz = 0;
+  tfzl = 0;
+
+
+  noldp = 0;
+  noldf = 0;
+
+  ist.get (buf, sizeof(buf), '"');
+  ist.get (ch);
+  ist.get (buf, sizeof(buf), '"');
+  ist.get (ch);
+
+  delete [] name;
+  name = new char[strlen (buf) + 1];
+  strcpy (name, buf);
+  //  (*mycout) << "Rule " << name << " found." << endl;
+
+  do
+    {
+      ist >> buf;
+
+      if (strcmp (buf, "quality") == 0)
+
+	{
+	  ist >> quality;
+	}
+
+      else if (strcmp (buf, "flags") == 0)
+	{
+	  ist >> ch;
+	  while (ch != ';')
+	    {
+	      flags.Append (ch);
+	      ist >> ch;
+	    }
+	}
+
+      else if (strcmp (buf, "mappoints") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      ist >> p.X();
+	      ist >> ch;    // ','
+	      ist >> p.Y();
+	      ist >> ch;    // ','
+	      ist >> p.Z();
+	      ist >> ch;    // ')'
+
+	      points.Append (p);
+	      noldp++;
+
+	      tolerances.SetSize (noldp);
+	      tolerances.Elem(noldp) = 1;
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  if (ch == '{')
+		    {
+		      ist >> tolerances.Elem(noldp);
+		      ist >> ch;  // '}'
+		    }
+
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+
+      else if (strcmp (buf, "mapfaces") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      face.SetType(TRIG);
+	      ist >> face.PNum(1);
+	      ist >> ch;    // ','
+	      ist >> face.PNum(2);
+	      ist >> ch;    // ','
+	      ist >> face.PNum(3);
+	      ist >> ch;    // ')' or ','
+	      if (ch == COMMASIGN)
+		{
+		  face.SetType(QUAD);
+		  ist >> face.PNum(4);
+		  ist >> ch;    // ')' 
+		}
+	      faces.Append (face);
+	      noldf++;
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  if (ch == 'd')
+		    {
+		      delfaces.Append (noldf);
+		      ist >> ch; // 'e'
+		      ist >> ch; // 'l'
+		    }
+
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "mapedges") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      ist >> edge.i1;
+	      ist >> ch;    // ','
+	      ist >> edge.i2;
+	      ist >> ch;    // ')'
+
+	      edges.Append (edge);
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+
+      else if (strcmp (buf, "newpoints") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      ist >> p.X();
+	      ist >> ch;    // ','
+	      ist >> p.Y();
+	      ist >> ch;    // ','
+	      ist >> p.Z();
+	      ist >> ch;    // ')'
+
+	      points.Append (p);
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  if (ch == '{')
+		    {
+		      LoadVMatrixLine (ist, tempoldutonewu,
+				       3 * (points.Size()-noldp) - 2);
+
+		      ist >> ch; // '{'
+		      LoadVMatrixLine (ist, tempoldutonewu,
+				       3 * (points.Size()-noldp) - 1);
+
+		      ist >> ch; // '{'
+		      LoadVMatrixLine (ist, tempoldutonewu,
+				       3 * (points.Size()-noldp)    );
+		    }
+
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "newfaces") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      face.SetType(TRIG);
+	      ist >> face.PNum(1);
+	      ist >> ch;    // ','
+	      ist >> face.PNum(2);
+	      ist >> ch;    // ','
+	      ist >> face.PNum(3);
+	      ist >> ch;    // ')' or ','
+	      if (ch == COMMASIGN)
+		{
+		  face.SetType(QUAD);
+		  ist >> face.PNum(4);
+		  ist >> ch;    // ')' 
+		}
+	      faces.Append (face);
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "freezone") == 0)
+	{
+	  ist >> ch;
+	
+	  while (ch == '(')
+	    {
+	      ist >> p.X();
+	      ist >> ch;    // ','
+	      ist >> p.Y();
+	      ist >> ch;    // ','
+	      ist >> p.Z();
+	      ist >> ch;    // ')'
+	    
+	      freezone.Append (p);
+	    
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  if (ch == '{')
+		    {
+		      LoadVMatrixLine (ist, tempoldutofreezone,
+				       3 * freezone.Size() - 2);
+		    
+		      ist >> ch; // '{'
+		      LoadVMatrixLine (ist, tempoldutofreezone,
+				       3 * freezone.Size() - 1);
+		    
+		      ist >> ch; // '{'
+		      LoadVMatrixLine (ist, tempoldutofreezone,
+				       3 * freezone.Size()    );
+		    }
+		
+		  ist >> ch;
+		}
+	    
+	      ist >> ch;
+	    }
+	
+	  ist.putback (ch);
+	}
+      else if (strcmp (buf, "freezone2") == 0)
+	{
+	  int k, nfp;
+
+	  nfp = 0;
+	  ist >> ch;
+
+	  DenseMatrix hm1(3, 50), hm2(50, 50), hm3(50, 50);
+	  hm3 = 0;
+
+	  while (ch == '{')
+	    {
+	      hm1 = 0;
+	      nfp++;
+	      LoadVMatrixLine (ist, hm1, 1);
+
+	      for (i = 1; i <= points.Size(); i++)
+		tfz.Elem(nfp, i) = hm1.Get(1, 3*i-2);
+
+
+	      p.X() = p.Y() = p.Z() = 0;
+	      for (i = 1; i <= points.Size(); i++)
+		{
+		  p.X() += hm1.Get(1, 3*i-2) * points.Get(i).X();
+		  p.Y() += hm1.Get(1, 3*i-2) * points.Get(i).Y();
+		  p.Z() += hm1.Get(1, 3*i-2) * points.Get(i).Z();
+		}
+	      freezone.Append (p);
+	      freezonelimit.Append (p);
+	    
+	      hm2 = 0;
+	      for (i = 1; i <= 3 * noldp; i++)
+		hm2.Elem(i, i) = 1;
+	      for (i = 1; i <= 3 * noldp; i++)
+		for (j = 1; j <= 3 * (points.Size() - noldp); j++)
+		  hm2.Elem(j + 3 * noldp, i) = tempoldutonewu.Get(j, i);
+		  
+	      for (i = 1; i <= 3; i++)
+		for (j = 1; j <= 3 * noldp; j++)
+		  {
+		    double sum = 0;
+		    for (k = 1; k <= 3 * points.Size(); k++)
+		      sum += hm1.Get(i, k) * hm2.Get(k, j);
+		  
+		    hm3.Elem(i + 3 * (nfp-1), j) = sum;
+		  }
+
+	      //	    (*testout) << "freepoint: " << p << endl;
+
+	      while (ch != ';')
+		ist >> ch; 
+
+	      ist >> ch;
+	    }
+
+	  tfzl = tfz;
+
+	  tempoldutofreezone = hm3;
+	  tempoldutofreezonelimit = hm3;
+	  ist.putback(ch);
+	}
+
+      else if (strcmp (buf, "freezonelimit") == 0)
+	{
+	  int k, nfp;
+	  nfp = 0;
+	  ist >> ch;
+
+	  DenseMatrix hm1(3, 50), hm2(50, 50), hm3(50, 50);
+	  hm3 = 0;
+
+	  while (ch == '{')
+	    {
+	      hm1 = 0;
+	      nfp++;
+	      LoadVMatrixLine (ist, hm1, 1);
+
+	      for (i = 1; i <= points.Size(); i++)
+		tfzl.Elem(nfp, i) = hm1.Get(1, 3*i-2);
+
+
+	      p.X() = p.Y() = p.Z() = 0;
+	      for (i = 1; i <= points.Size(); i++)
+		{
+		  p.X() += hm1.Get(1, 3*i-2) * points.Get(i).X();
+		  p.Y() += hm1.Get(1, 3*i-2) * points.Get(i).Y();
+		  p.Z() += hm1.Get(1, 3*i-2) * points.Get(i).Z();
+		}
+	      freezonelimit.Elem(nfp) = p;
+	    
+	      hm2 = 0;
+	      for (i = 1; i <= 3 * noldp; i++)
+		hm2.Elem(i, i) = 1;
+	      for (i = 1; i <= 3 * noldp; i++)
+		for (j = 1; j <= 3 * (points.Size() - noldp); j++)
+		  hm2.Elem(j + 3 * noldp, i) = tempoldutonewu.Get(j, i);
+		  
+	      for (i = 1; i <= 3; i++)
+		for (j = 1; j <= 3 * noldp; j++)
+		  {
+		    double sum = 0;
+		    for (k = 1; k <= 3 * points.Size(); k++)
+		      sum += hm1.Get(i, k) * hm2.Get(k, j);
+		  
+		    hm3.Elem(i + 3 * (nfp-1), j) = sum;
+		  }
+
+	      //	    (*testout) << "freepoint: " << p << endl;
+
+	      while (ch != ';')
+		ist >> ch; 
+
+	      ist >> ch;
+	    }
+
+	  tempoldutofreezonelimit = hm3;
+	  ist.putback(ch);
+	}
+
+      else if (strcmp (buf, "freeset") == 0)
+	{
+	  freesets.Append (new Array<int>);
+
+	  ist >> ch;
+
+	  while (ch != ';')
+	    {
+	      ist.putback (ch);
+	      ist >> i;
+	      freesets.Last()->Append(i);
+	      ist >> ch;
+	    }
+	}
+
+      else if (strcmp (buf, "elements") == 0)
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      elements.Append (Element(TET));
+
+	      //	      elements.Last().SetNP(1);
+	      ist >> elements.Last().PNum(1);
+	      ist >> ch;    // ','
+
+	      if (ch == COMMASIGN)
+		{
+		  //		  elements.Last().SetNP(2);
+		  ist >> elements.Last().PNum(2);
+		  ist >> ch;    // ','
+		}
+	      if (ch == COMMASIGN)
+		{
+		  //		  elements.Last().SetNP(3);
+		  ist >> elements.Last().PNum(3);
+		  ist >> ch;    // ','
+		}
+	      if (ch == COMMASIGN)
+		{
+		  //		  elements.Last().SetNP(4);
+		  elements.Last().SetType(TET);
+		  ist >> elements.Last().PNum(4);
+		  ist >> ch;    // ','
+		}
+	      if (ch == COMMASIGN)
+		{
+		  //		  elements.Last().SetNP(5);
+		  elements.Last().SetType(PYRAMID);
+		  ist >> elements.Last().PNum(5);
+		  ist >> ch;    // ','
+		}
+	      if (ch == COMMASIGN)
+		{
+		  //		  elements.Last().SetNP(6);
+		  elements.Last().SetType(PRISM);
+		  ist >> elements.Last().PNum(6);
+		  ist >> ch;    // ','
+		}
+
+	      /*
+	      orientations.Append (fourint());
+	      orientations.Last().i1 = elements.Last().PNum(1);
+	      orientations.Last().i2 = elements.Last().PNum(2);
+	      orientations.Last().i3 = elements.Last().PNum(3);
+	      orientations.Last().i4 = elements.Last().PNum(4);
+	      */
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+      else if (strcmp (buf, "orientations") == 0)
+
+	{
+	  ist >> ch;
+
+	  while (ch == '(')
+	    {
+	      //        fourint a = fourint();
+	      orientations.Append (fourint());
+
+	      ist >> orientations.Last().i1;
+	      ist >> ch;    // ','
+	      ist >> orientations.Last().i2;
+	      ist >> ch;    // ','
+	      ist >> orientations.Last().i3;
+	      ist >> ch;    // ','
+	      ist >> orientations.Last().i4;
+	      ist >> ch;    // ','
+
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  ist >> ch;
+		}
+
+	      ist >> ch;
+	    }
+
+	  ist.putback (ch);
+	}
+
+
+      else if (strcmp (buf, "endrule") != 0)
+	{
+	  PrintSysError ("Parser3d, unknown token " , buf);
+	}
+    }
+  while (!ist.eof() && strcmp (buf, "endrule") != 0);
+
+
+  //  (*testout) << endl;
+  //  (*testout) << Name() << endl;
+  //  (*testout) << "no1 = " << GetNO() << endl;
+
+  oldutonewu.SetSize (3 * (points.Size() - noldp), 3 * noldp);
+  oldutonewu = 0;
+
+  for (i = 1; i <= oldutonewu.Height(); i++)
+    for (j = 1; j <= oldutonewu.Width(); j++)
+      oldutonewu.Elem(i, j) = tempoldutonewu.Elem(i, j);
+
+
+  /*
+    oldutofreezone = new SparseMatrixFlex (3 * freezone.Size(), 3 * noldp);
+    oldutofreezonelimit = new SparseMatrixFlex (3 * freezone.Size(), 3 * noldp);
+
+    oldutofreezone -> SetSymmetric(0);
+    oldutofreezonelimit -> SetSymmetric(0);
+    */
+
+  /*
+    oldutofreezone = new DenseMatrix (3 * freezone.Size(), 3 * noldp);
+    oldutofreezonelimit = new DenseMatrix (3 * freezone.Size(), 3 * noldp);
+  
+    for (i = 1; i <= oldutofreezone->Height(); i++)
+    for (j = 1; j <= oldutofreezone->Width(); j++)
+    //      if (j == 4 || j >= 7)
+    {
+    if (tempoldutofreezone.Elem(i, j))
+    (*oldutofreezone)(i, j) = tempoldutofreezone(i, j);
+    if (tempoldutofreezonelimit.Elem(i, j))
+    (*oldutofreezonelimit)(i, j) = tempoldutofreezonelimit(i, j);
+    }
+    */
+
+
+
+
+  oldutofreezone = new DenseMatrix (freezone.Size(), points.Size());
+  oldutofreezonelimit = new DenseMatrix (freezone.Size(), points.Size());
+  //  oldutofreezone = new SparseMatrixFlex (freezone.Size(), points.Size());
+  //  oldutofreezonelimit = new SparseMatrixFlex (freezone.Size(), points.Size());
+
+  for (i = 1; i <= freezone.Size(); i++)
+    for (j = 1; j <= points.Size(); j++)
+      {
+	if (tfz.Elem(i, j))
+	  (*oldutofreezone).Elem(i, j) = tfz.Elem(i, j);
+	if (tfzl.Elem(i, j))
+	  (*oldutofreezonelimit).Elem(i, j) = tfzl.Elem(i, j);
+      }
+  
+  /*
+  (*testout) << "Rule " << Name() << endl;
+  (*testout) << "oldutofreezone = " << (*oldutofreezone) << endl;
+  (*testout) << "oldutofreezonelimit = " << (*oldutofreezonelimit) << endl;
+  */
+
+  freezonepi.SetSize (freezone.Size());
+  for (i = 1; i <= freezonepi.Size(); i++)
+    freezonepi.Elem(i) = 0;
+  for (i = 1; i <= freezone.Size(); i++)
+    for (j = 1; j <= noldp; j++)
+      if (Dist (freezone.Get(i), points.Get(j)) < 1e-8)
+	freezonepi.Elem(i) = j;
+
+
+
+  
+  for (i = 1; i <= elements.Size(); i++)
+    {
+      if (elements.Elem(i).GetNP() == 4)
+	{
+	  orientations.Append (fourint());
+	  orientations.Last().i1 = elements.Get(i).PNum(1);
+	  orientations.Last().i2 = elements.Get(i).PNum(2);
+	  orientations.Last().i3 = elements.Get(i).PNum(3);
+	  orientations.Last().i4 = elements.Get(i).PNum(4);
+	}
+      if (elements.Elem(i).GetNP() == 5)
+	{
+	  orientations.Append (fourint());
+	  orientations.Last().i1 = elements.Get(i).PNum(1);
+	  orientations.Last().i2 = elements.Get(i).PNum(2);
+	  orientations.Last().i3 = elements.Get(i).PNum(3);
+	  orientations.Last().i4 = elements.Get(i).PNum(5);
+
+	  orientations.Append (fourint());
+	  orientations.Last().i1 = elements.Get(i).PNum(1);
+	  orientations.Last().i2 = elements.Get(i).PNum(3);
+	  orientations.Last().i3 = elements.Get(i).PNum(4);
+	  orientations.Last().i4 = elements.Get(i).PNum(5);
+	}
+    }
+
+
+
+  if (freesets.Size() == 0)
+    {
+      freesets.Append (new Array<int>);
+      for (i = 1; i <= freezone.Size(); i++)
+	freesets.Elem(1)->Append(i);
+    }
+
+
+  //  testout << "Freezone: " << endl;
+
+  //  for (i = 1; i <= freezone.Size(); i++)
+  //    (*testout) << "freepoint: " << freezone.Get(i) << endl;
+  Vector vp(points.Size()), vfp(freezone.Size());
+
+
+  if (quality < 100)
+    {
+      for (int i = 1; i <= 3; i++)
+	{
+	  for (int j = 1; j <= points.Size(); j++)
+	    vp(j-1) = points.Get(j).X(i);
+	  oldutofreezone->Mult(vp, vfp);
+	  for (int j = 1; j <= freezone.Size(); j++)
+	    freezone.Elem(j).X(i) = vfp(j-1);
+	}
+      //      for (i = 1; i <= freezone.Size(); i++)
+      //	(*testout) << "freepoint: " << freezone.Get(i) << endl;
+    }
+
+
+  for (fs = 1; fs <= freesets.Size(); fs++)
+    {
+      freefaces.Append (new Array<threeint>);
+
+      Array<int> & freeset = *freesets.Elem(fs);
+      Array<threeint> & freesetfaces = *freefaces.Last();
+
+      for (ii1 = 1; ii1 <= freeset.Size(); ii1++)
+	for (ii2 = 1; ii2 <= freeset.Size(); ii2++)
+	  for (ii3 = 1; ii3 <= freeset.Size(); ii3++)
+	    if (ii1 < ii2 && ii1 < ii3 && ii2 != ii3)
+	      {
+		i1 = freeset.Get(ii1);
+		i2 = freeset.Get(ii2);
+		i3 = freeset.Get(ii3);
+
+		Vec3d v1, v2, n;
+
+		v1 = freezone.Get(i3) - freezone.Get(i1);
+		v2 = freezone.Get(i2) - freezone.Get(i1);
+		n = Cross (v1, v2);
+		n /= n.Length();
+		//		(*testout) << "i1,2,3 = " << i1 << ", " << i2 << ", " << i3 << endl;
+		//		(*testout) << "v1 = " << v1 << " v2 = " << v2 << " n = " << n << endl;
+		ok = 1;
+		for (ii = 1; ii <= freeset.Size(); ii++)
+		  {
+		    i = freeset.Get(ii);
+		    //		    (*testout) << "i = " << i << endl;
+		    if (i != i1 && i != i2 && i != i3)
+		      if ( (freezone.Get(i) - freezone.Get(i1)) * n < 0 ) ok = 0;
+		  }
+
+		if (ok)
+		  {
+		    freesetfaces.Append (threeint());
+		    freesetfaces.Last().i1 = i1;
+		    freesetfaces.Last().i2 = i2;
+		    freesetfaces.Last().i3 = i3;
+		  }
+	      }
+    }
+
+  for (fs = 1; fs <= freesets.Size(); fs++)
+    {
+      freefaceinequ.Append (new DenseMatrix (freefaces.Get(fs)->Size(), 4));
+    }
+
+
+  {
+    int minn;
+    //    Array<int> pnearness (noldp);
+    pnearness.SetSize (noldp);
+
+    for (i = 1; i <= pnearness.Size(); i++)
+      pnearness.Elem(i) = INT_MAX/10;
+
+    for (j = 1; j <= GetNP(1); j++)
+      pnearness.Elem(GetPointNr (1, j)) = 0;
+
+    do
+      {
+	ok = 1;
+
+	for (i = 1; i <= noldf; i++)
+	  {
+	    minn = INT_MAX/10;
+	    for (j = 1; j <= GetNP(i); j++)
+	      minn = min2 (minn, pnearness.Get(GetPointNr (i, j)));
+
+	    for (j = 1; j <= GetNP(i); j++)
+	      if (pnearness.Get(GetPointNr (i, j)) > minn+1)
+		{
+		  ok = 0;
+		  pnearness.Elem(GetPointNr (i, j)) = minn+1;
+		}
+	  }
+
+	for (i = 1; i <= edges.Size(); i++)
+	  {
+	    int pi1 = edges.Get(i).i1;
+	    int pi2 = edges.Get(i).i2;
+
+	    if (pnearness.Get(pi1) > pnearness.Get(pi2)+1)
+	      {
+		ok = 0;
+		pnearness.Elem(pi1) = pnearness.Get(pi2)+1;
+	      }
+	    if (pnearness.Get(pi2) > pnearness.Get(pi1)+1)
+	      {
+		ok = 0;
+		pnearness.Elem(pi2) = pnearness.Get(pi1)+1;
+	      }
+	  }
+	
+
+	for (i = 1; i <= elements.Size(); i++)
+	  if (elements.Get(i).GetNP() == 6)  // prism rule
+	    {
+	      for (j = 1; j <= 3; j++)
+		{
+		  int pi1 = elements.Get(i).PNum(j);
+		  int pi2 = elements.Get(i).PNum(j+3);
+
+		  if (pnearness.Get(pi1) > pnearness.Get(pi2)+1)
+		    {
+		      ok = 0;
+		      pnearness.Elem(pi1) = pnearness.Get(pi2)+1;
+		    }
+		  if (pnearness.Get(pi2) > pnearness.Get(pi1)+1)
+		    {
+		      ok = 0;
+		      pnearness.Elem(pi2) = pnearness.Get(pi1)+1;
+		    }
+		}
+	    }
+      }
+    while (!ok);
+
+    maxpnearness = 0;
+    for (i = 1; i <= pnearness.Size(); i++)
+      maxpnearness = max2 (maxpnearness, pnearness.Get(i));
+
+
+    fnearness.SetSize (noldf);
+
+    for (i = 1; i <= noldf; i++)
+      {
+	fnearness.Elem(i) = 0;
+	for (j = 1; j <= GetNP(i); j++)
+	  fnearness.Elem(i) += pnearness.Get(GetPointNr (i, j));
+      }
+
+    // (*testout) << "rule " << name << ", pnear = " << pnearness << endl;
+  }
+
+  
+  //Table of edges:
+  for (fs = 1; fs <= freesets.Size(); fs++)
+    {
+      freeedges.Append (new Array<twoint>);
+      
+      //      Array<int> & freeset = *freesets.Get(fs);
+      Array<twoint> & freesetedges = *freeedges.Last();
+      Array<threeint> & freesetfaces = *freefaces.Get(fs);
+      int k,l;
+      INDEX ind;
+      
+      for (k = 1; k <= freesetfaces.Size(); k++)
+	{
+          threeint tr = freesetfaces.Get(k);
+
+	  for (l = k+1; l <= freesetfaces.Size(); l++)
+	    {
+	      ind = NeighbourTrianglePoint(freesetfaces.Get(k), freesetfaces.Get(l));
+	      if (!ind) continue;
+
+	      INDEX_3 f1(freesetfaces.Get(k).i1, 
+			 freesetfaces.Get(k).i2, 
+			 freesetfaces.Get(k).i3);
+	      INDEX_3 f2(freesetfaces.Get(l).i1, 
+			 freesetfaces.Get(l).i2, 
+			 freesetfaces.Get(l).i3);
+	      INDEX_2 ed(0, 0);
+	      for (int f11 = 1; f11 <= 3; f11++)
+		for (int f12 = 1; f12 <= 3; f12++)
+		  if (f11 != f12)
+		    for (int f21 = 1; f21 <= 3; f21++)
+		      for (int f22 = 1; f22 <= 3; f22++)		    
+			if (f1.I(f11) == f2.I(f21) && f1.I(f12) == f2.I(f22))
+			{
+			  ed.I(1) = f1.I(f11);
+			  ed.I(2) = f1.I(f12);
+			}
+	      //	      (*testout) << "ed = " << ed.I(1) << "-" << ed.I(2) << endl;
+	      //	      (*testout) << "ind = " << ind << " ed = " << ed << endl;
+	      for (int eli = 1; eli <= GetNOldF(); eli++)
+		{
+		  if (GetNP(eli) == 4)
+		    {
+		      for (int elr = 1; elr <= 4; elr++)
+			{
+			  if (GetPointNrMod (eli, elr) == ed.I(1) &&
+			      GetPointNrMod (eli, elr+2) == ed.I(2))
+			    {
+			      /*
+			      (*testout) << "ed is diagonal of rectangle" << endl;
+			      (*testout) << "ed = " << ed.I(1) << "-" << ed.I(2) << endl;
+			      (*testout) << "ind = " << ind << endl;
+			      */
+			      ind = 0;
+			    }
+
+			}
+		    }
+		}
+
+	      if (ind)
+		{
+		  /*
+		  (*testout) << "new edge from face " << k 
+			     << " = (" << freesetfaces.Get(k).i1 
+			     << ", " << freesetfaces.Get(k).i2 
+			     << ", " << freesetfaces.Get(k).i3
+			     << "), point " << ind << endl;
+			     */
+		  freesetedges.Append(twoint(k,ind));
+		}
+	    }	
+	}
+    }
+    
+}
+
+
+
+
+
+void Meshing3 :: LoadRules (const char * filename, const char ** prules)
+{
+  char buf[256];
+  istream * ist;
+  char *tr1 = NULL;
+
+  if (filename)
+    {
+      PrintMessage (3, "rule-filename = ", filename);
+      ist = new ifstream (filename);
+    }
+  else 
+    {
+      /* connect tetrules to one string */
+      PrintMessage (3, "Use internal rules");
+      if (!prules) prules = tetrules;
+
+      const char ** hcp = prules; 
+      size_t len = 0;
+      while (*hcp)
+	{
+	  len += strlen (*hcp);
+	  hcp++;
+	}
+      tr1 = new char[len+1];
+      tr1[0] = 0;
+      hcp = prules; //  tetrules;
+
+
+      char * tt1 = tr1;
+      while (*hcp)
+	{
+	  strcat (tt1, *hcp);
+	  tt1 += strlen (*hcp);	  
+	  hcp++;
+	}
+
+
+#ifdef WIN32
+      // VC++ 2005 workaround
+      for(size_t i=0; i<len; i++)
+	if(tr1[i] == ',')
+	  tr1[i] = ':';
+#endif
+
+      ist = new istringstream (tr1);
+    }
+  
+  if (!ist->good())
+    {
+      cerr << "Rule description file " << filename << " not found" << endl;
+      delete ist;
+      exit (1);
+    }
+    
+  while (!ist->eof())
+    {
+      buf[0] = 0;
+      (*ist) >> buf;
+	
+      if (strcmp (buf, "rule") == 0)
+	{
+	  vnetrule * rule = new vnetrule;
+	  rule -> LoadRule(*ist);
+	  rules.Append (rule);
+	  if (!rule->TestOk())
+	    {
+	      PrintSysError ("Parser3d: Rule ", rules.Size(), " not ok");
+	      exit (1);
+	    }
+	}
+      else if (strcmp (buf, "tolfak") == 0)
+	{
+	  (*ist) >> tolfak;
+	}
+    }
+  delete ist;
+  delete [] tr1;
+}
+}
diff --git a/contrib/Netgen/libsrc/meshing/prism2rls.cpp b/contrib/Netgen/libsrc/meshing/prism2rls.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e696554c0fef82fbafcb0633917eddcaaf5de27
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/prism2rls.cpp
@@ -0,0 +1,457 @@
+namespace netgen
+{
+const char * prismrules2[] = {
+"tolfak 0.5\n",\
+"\n",\
+"rule \"prism on quad\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(1, 5, 2) del;\n",\
+"(4, 3, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(5, 2, 3, 6);\n",\
+"(1, 5, 6, 4);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3, 5);\n",\
+"(1, 3, 4, 6);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\
+"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\
+"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5 6 7;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 5 6 8;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on quad, one trig\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(1, 5, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(5, 2, 3, 6);\n",\
+"(1, 5, 6, 4);\n",\
+"(4, 6, 3);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3, 5);\n",\
+"(1, 3, 4, 6);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\
+"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\
+"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5 6 7;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 5 6 8;\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on 2 quad\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 5, 6, 3) del;\n",\
+"(1, 5, 2) del;\n",\
+"(4, 3, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 5, 6, 4);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5 6 7;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 6;\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on 2 quad, one trig\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 5, 6, 3) del;\n",\
+"(1, 5, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 5, 6, 4);\n",\
+"(4, 6, 3);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5 6 7;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 6;\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on 2 quada\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(5, 1, 4, 6) del;\n",\
+"(1, 5, 2) del;\n",\
+"(4, 3, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(5, 2, 3, 6);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5 6 7;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 6;\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"fill prism\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 5, 6, 3) del;\n",\
+"(5, 1, 4, 6) del;\n",\
+"(1, 5, 2) del;\n",\
+"(4, 3, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 6;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on 3 quad, one trig\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 5, 6, 3) del;\n",\
+"(5, 1, 4, 6) del;\n",\
+"(1, 5, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(4, 6, 3);\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 6;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"flat prism\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(0.5, 0.866, 0);\n",\
+"(0, 0, -1);\n",\
+"(1, 0, -1);\n",\
+"(0.5, 0.866, -1);\n",\
+"\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(5, 4, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 4);\n",\
+"(4, 2, 5);\n",\
+"(2, 3, 5);\n",\
+"(5, 3, 6);\n",\
+"(3, 1, 6);\n",\
+"(6, 1, 4);\n",\
+"\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 5, 4, 6);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"endrule\n",\
+"\n",\
+0};
+}
diff --git a/contrib/Netgen/libsrc/meshing/prism2rls_2.cpp b/contrib/Netgen/libsrc/meshing/prism2rls_2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..baf0bef388bcec25d3c21391d602caf1ba44b39a
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/prism2rls_2.cpp
@@ -0,0 +1,446 @@
+namespace netgen
+{
+const char * prismrules2[] = {
+"tolfak 0.5\n",\
+"\n",\
+"rule \"prism on quad\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(1, 5, 2) del;\n",\
+"(4, 3, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(5, 2, 3, 6);\n",\
+"(1, 5, 6, 4);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3, 5);\n",\
+"(1, 3, 4, 6);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\
+"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\
+"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on quad, one trig\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(1, 5, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(5, 2, 3, 6);\n",\
+"(1, 5, 6, 4);\n",\
+"(4, 6, 3);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3, 5);\n",\
+"(1, 3, 4, 6);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\
+"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\
+"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on 2 quad\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 5, 6, 3) del;\n",\
+"(1, 5, 2) del;\n",\
+"(4, 3, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 5, 6, 4);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5 6 7;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 6;\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on 2 quad, one trig\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 5, 6, 3) del;\n",\
+"(1, 5, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 5, 6, 4);\n",\
+"(4, 6, 3);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5 6 7;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 6;\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on 2 quada\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(5, 1, 4, 6) del;\n",\
+"(1, 5, 2) del;\n",\
+"(4, 3, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(5, 2, 3, 6);\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5 6 7;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 6;\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"fill prism\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 5, 6, 3) del;\n",\
+"(5, 1, 4, 6) del;\n",\
+"(1, 5, 2) del;\n",\
+"(4, 3, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 6;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"prism on 3 quad, one trig\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0, -0.86);\n",\
+"(0.5, 1, -0.86);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 5, 6, 3) del;\n",\
+"(5, 1, 4, 6) del;\n",\
+"(1, 5, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(4, 6, 3);\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 5, 2, 4, 6, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5;\n",\
+"\n",\
+"freeset\n",\
+"2 3 4 6;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"flat prism\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(0.5, 0.866, 0);\n",\
+"(0, 0, -1);\n",\
+"(1, 0, -1);\n",\
+"(0.5, 0.866, -1);\n",\
+"\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(5, 4, 6) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 4);\n",\
+"(4, 2, 5);\n",\
+"(2, 3, 5);\n",\
+"(5, 3, 6);\n",\
+"(3, 1, 6);\n",\
+"(6, 1, 4);\n",\
+"\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 5, 4, 6);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"endrule\n",\
+"\n",\
+0};
+}
diff --git a/contrib/Netgen/libsrc/meshing/pyramid2rls.cpp b/contrib/Netgen/libsrc/meshing/pyramid2rls.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a97e7f13e594ee25b0a2308233b15132331d989c
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/pyramid2rls.cpp
@@ -0,0 +1,309 @@
+namespace netgen
+{
+const char * pyramidrules2[] = {
+"tolfak 0.5\n",\
+"\n",\
+"rule \"Pyramid on quad\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.5, -0.5) \n",\
+"	{ 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 } 	\n",\
+"	{ 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 } { };\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 5);\n",\
+"(2, 3, 5);\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1.4 P5, -0.1 P1, -0.1 P2, -0.1 P3, -0.1 P4 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"rule \"small Pyramid on quad\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.5, -0.1 )\n",\
+"	{ 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 } \n",\
+"	{ 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 } { };\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 5);\n",\
+"(2, 3, 5);\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1.4 P5, -0.1 P1, -0.1 P2, -0.1 P3, -0.1 P4 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"connect pyramid\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0.5, -0.5);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 5);\n",\
+"(2, 3, 5);\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"pyramid with one trig\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0.5, -0.5);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 1, 5) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(2, 3, 5);\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 0.34 P2, 0.34 P3, 0.34 P5, -0.02 P1 };\n",\
+"{ 0.34 P3, 0.34 P4, 0.34 P5, -0.02 P1 };\n",\
+"{ 0.34 P1, 0.34 P4, 0.34 P5, -0.02 P2 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 0.333 P2, 0.333 P3, 0.334 P5, 0 P1 };\n",\
+"{ 0.333 P3, 0.333 P4, 0.334 P5, 0 P1 };\n",\
+"{ 0.333 P1, 0.333 P4, 0.334 P5, 0 P2 };\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3, 5);\n",\
+"(1, 3, 4, 5);\n",\
+"\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"freeset\n",\
+"2 3 5 6;\n",\
+"freeset\n",\
+"3 4 5 7;\n",\
+"freeset \n",\
+"1 4 5 8;\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"pyramid with two trig\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0.5, -0.5);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 1, 5) del;\n",\
+"(3, 2, 5) del;\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"pyramid with two trig, left\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0.5, -0.5);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 1, 5) del;\n",\
+"(1, 4, 5) del;\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(3, 4, 5);\n",\
+"(2, 3, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+0};
+}
diff --git a/contrib/Netgen/libsrc/meshing/pyramidrls.cpp b/contrib/Netgen/libsrc/meshing/pyramidrls.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d4e997c1fea470d99cebb3d21bf815b33d02b745
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/pyramidrls.cpp
@@ -0,0 +1,263 @@
+namespace netgen
+{
+const char * pyramidrules[] = {
+"tolfak 0.5\n",\
+"\n",\
+"rule \"Pyramid on quad\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.5, -0.5) \n",\
+"	{ 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 } 	\n",\
+"	{ 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 } { };\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 5);\n",\
+"(2, 3, 5);\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1.4 P5, -0.1 P1, -0.1 P2, -0.1 P3, -0.1 P4 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"rule \"small Pyramid on quad\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.5, -0.1 )\n",\
+"	{ 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 } \n",\
+"	{ 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 } { };\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 5);\n",\
+"(2, 3, 5);\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1.4 P5, -0.1 P1, -0.1 P2, -0.1 P3, -0.1 P4 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"connect pyramid\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0.5, -0.5);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 5);\n",\
+"(2, 3, 5);\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"pyramid with one trig\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0.5, -0.5);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 1, 5) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(2, 3, 5);\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 0.34 P2, 0.34 P3, 0.34 P5, -0.02 P1 };\n",\
+"{ 0.34 P3, 0.34 P4, 0.34 P5, -0.02 P1 };\n",\
+"{ 0.34 P1, 0.34 P4, 0.34 P5, -0.02 P3 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 0.333 P2, 0.333 P3, 0.334 P5, 0 P1 };\n",\
+"{ 0.333 P3, 0.333 P4, 0.334 P5, 0 P1 };\n",\
+"{ 0.333 P1, 0.333 P4, 0.334 P5, 0 P3 };\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3, 5);\n",\
+"(1, 3, 4, 5);\n",\
+"\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"freeset\n",\
+"2 3 5 6;\n",\
+"freeset\n",\
+"3 4 5 7;\n",\
+"freeset \n",\
+"1 4 5 8;\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"pyramid with two trig\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(1, 1, 0);\n",\
+"(0, 1, 0);\n",\
+"(0.5, 0.5, -0.5);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3, 4) del;\n",\
+"(2, 1, 5) del;\n",\
+"(3, 2, 5) del;\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(3, 4, 5);\n",\
+"(4, 1, 5);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+0};
+}
diff --git a/contrib/Netgen/libsrc/meshing/quadrls.cpp b/contrib/Netgen/libsrc/meshing/quadrls.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1c2cd23b778abf62d7f8dcc6a9bf23d53dd14a93
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/quadrls.cpp
@@ -0,0 +1,887 @@
+namespace netgen
+{
+const char * quadrules[] = {
+"rule \"Free Quad (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(1, 1) { 1 X2 } { };\n",\
+"(0, 1) { } { };\n",\
+"\n",\
+"newlines\n",\
+"(3, 2);\n",\
+"(4, 3);\n",\
+"(1, 4);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 1.5) { 1.5 X2 } { };\n",\
+"(-0.5, 1.5) { -0.5 X2 } { };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Free Quad (5)\"\n",\
+"\n",\
+"quality 5\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(1, 1) { 1 X2 } { };\n",\
+"(0, 1) { } { };\n",\
+"\n",\
+"newlines\n",\
+"(3, 2);\n",\
+"(4, 3);\n",\
+"(1, 4);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 1.5) { 1.5 X2 } { };\n",\
+"(-0.5, 1.5) { -0.5 X2 } { };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X2 } { };\n",\
+"(0, 1) { } { };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Quad Right (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0, 1) { } { 1 y3 };\n",\
+"\n",\
+"newlines\n",\
+"(1, 4);\n",\
+"(4, 3);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(-0.5, 1.5) { } { 1.5 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { } { 1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Quad P Right (2)\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0, 1) { -1 X2, 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"newlines\n",\
+"(1, 4);\n",\
+"(4, 3);\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.2, 0.5) { 0.7 X2, 0.5 X3 } { 0.5 Y3 };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(-0.5, 1.5) { -2 X2, 1.5 X3 } { 1.5 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 0.5) { 0.5 X2, 0.5 X3 } { 0.5 Y3 };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { -1 X2, 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"rule \"Quad P Right (150)\"\n",\
+"\n",\
+"quality 150\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0, 1) { 1 X2, -1 X3 } { 1 Y3 };\n",\
+"\n",\
+"newlines\n",\
+"(1, 4)\n;",\
+"(4, 3)\n;",\
+"(3, 2)\n;",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.2, 0.5) { 0.7 X2, 0.5 X3 } { 0.5 Y3 };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(-0.5, 1.5) { -2 X2, 1.5 X3 } { 1.5 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 0.5) { 0.5 X2, 0.5 X3 } { 0.5 Y3 };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X2, -1 X3 } { 1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"rule \"Quad Right PL (2)\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1);\n",\
+"(0, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(1, 4);\n",\
+"(4, 3);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0.5, 1.2) { -0.1 X2, 0.6 X3, 0.6 X4 } { -0.1 Y2, 0.6 Y3, 0.6 Y4 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"(-0.2, 0.5) { -0.1 X2, -0.1 X3, 0.6 X4 } { -0.1 Y2, -0.1 Y3, 0.6 Y4 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0.5, 1) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"(0, 0.5) { 0.5 X4 } { 0.5 Y4 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3);\n",\
+"(1, 3, 4);\n",\
+"(1, 2, 4);\n",\
+"(4, 2, 3);\n",\
+"\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Left Quad (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(0, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"(1, 1) { 1 X2, 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"newlines\n",\
+"(3, 4);\n",\
+"(4, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 1.5) { 1.5 X2, 1.5 X3 } { 1.5 Y3 };\n",\
+"(0, 1) { 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X2, 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 4, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Left P Quad (2)\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(0, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(1, 1) { 1 X2, 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"(3, 4);\n",\
+"(4, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 1.5) { 1.5 X2, 1.5 X3 } { 1.5 Y3 };\n",\
+"(0, 1) { 1 X3 } { 1 Y3 };\n",\
+"(-0.2, 0.6) { -0.2 X2, 0.6 X3 } { 0.6 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X2, 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 0.5) { 0.5 X3 } { 0.5 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 4, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Left P Quad (150)\"\n",\
+"\n",\
+"quality 150\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(0, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(1, 1) { 1 X2, -1 X3 } { 1 Y3 };\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"(3, 4);\n",\
+"(4, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 1.5) { 1.5 X2, 1.5 X3 } { 1.5 Y3 };\n",\
+"(0, 1) { 1 X3 } { 1 Y3 };\n",\
+"(-0.2, 0.6) { -0.2 X2, 0.6 X3 } { 0.6 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X2, -1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 0.5) { 0.5 X3 } { 0.5 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 4, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Left Quad RP (2)\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(0, 1);\n",\
+"(1, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(3, 4);\n",\
+"(4, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.2, 0.5) { 0.6 X2, 0.6 X4, -0.1 X3 } { 0.6 Y2, 0.6 Y4, -0.1 Y3 };\n",\
+"(1, 1) { 1 X4 } { 1 Y4 };\n",\
+"(0.5, 1.2) { -0.1 X2, 0.6 X3, 0.6 X4 } { -0.1 Y2, 0.6 Y3, 0.6 Y4 };\n",\
+"(0, 1) { 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 0.5) { 0.5 X2, 0.5 X4 } { 0.5 Y2, 0.5 Y4 };\n",\
+"(1, 1) { 1 X4 } { 1 Y4 };\n",\
+"(0.5, 1) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 };\n",\
+"(0, 1) { 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 4, 3);\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 4);\n",\
+"(1, 4, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Two left (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1);\n",\
+"(0, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 4) del;\n",\
+"(4, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 0.5) { 0.75 X2, 0.75 X3, -0.25 X4 } { 0.75 Y3, -0.25 Y4 };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 0.5) { 0.5 X2, 0.5 X3 } { 0.5 Y3 };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Two Right (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1);\n",\
+"(0, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"(3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(1, 4);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"(-0.5, 0.5) { -0.25 X2, -0.25 X3, 0.75 X4 } { -0.25 Y3, 0.75 Y4 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"(0, 0.5) { 0.5 X4 } { 0.5 Y4 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Right 120 (1)\"\n",\
+"\n",\
+"quality 1000\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1.5, 0.866);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.866) { 1 X3, -1 X2 } { 1 Y3 };\n",\
+"\n",\
+"newlines\n",\
+"(1, 4);\n",\
+"(4, 3);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(1, 1.732) { -2 X2, 2 X3 } { 2 Y3 };\n",\
+"(0, 1.732) { -3 X2, 2 X3 } { 2 Y3 };\n",\
+"(-0.5, 0.866) { -2 X2, 1 X3 } {1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 4);\n",\
+"(2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Left 120 (1)\"\n",\
+"\n",\
+"quality 1000\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(-0.5, 0.866);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.866) { 1 X3, 1 X2 } { 1 Y3 };\n",\
+"\n",\
+"newlines\n",\
+"(3, 4);\n",\
+"(4, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 0.866) { 2 X2, 1 X3 } { 1 Y3 };\n",\
+"(1, 1.732) { 2 X2, 2 X3 } { 2 Y3 };\n",\
+"(0, 1.732) { -1 X2, 2 X3 } { 2 Y3 };\n",\
+"(-0.5, 0.866) { 1 X3 } {1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 4);\n",\
+"(2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Left Right (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1);\n",\
+"(0, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"(4, 1) del;\n",\
+"\n",\
+"\n",\
+"newlines\n",\
+"(4, 3);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0.5, 1.5) { -0.25 X2, 0.75 X3, 0.75 X4 } { 0.75 Y3, 0.75 Y4 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0.5, 1) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Fill Quad\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1);\n",\
+"(0, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"(3, 4) del;\n",\
+"(4, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { 1 Y2 };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Fill Triangle\"\n",\
+"\n",\
+"quality 10\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(0.5, 0.86);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"(3, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { 1 Y2 };\n",\
+"(0.5, 0.86) { 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Right 60 (1)\"\n",\
+"\n",\
+"quality 10\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0) { 0.5, 0, 1.0 };\n",\
+"(0.5, 0.866) { 0.6, 0, 0.8 };\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(-0.125, 0.6495) { -0.5 X2, 0.75 X3 } { -0.5 Y2, 0.75 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(0.25, 0.433) { 0.5 X3 } { 0.5 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Vis A Vis (2)\"\n",\
+"\n",\
+"quality 2\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1);\n",\
+"(0, 1);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(1, 4);\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 0.5) { 0.75 X2, 0.75 X3, -0.25 X4 } { 0.75 Y3, -0.25 Y4 };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"(-0.5, 0.5) { -0.25 X2, -0.25 X3, 0.75 X4 } { -0.25 Y3, 0.75 Y4 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 0.5) { 0.5 X2, 0.5 X3 } { 0.5 Y3 };\n",\
+"(1, 1) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1) { 1 X4 } { 1 Y4 };\n",\
+"(0, 0.5) { 0.5 X4 } { 0.5 Y4 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"orientations\n",\
+"(1, 3, 4);\n",\
+"(2, 3, 4);\n",\
+"(1, 2, 3);\n",\
+"(1, 2, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Triangle Vis A Vis (200)\"\n",\
+"\n",\
+"quality 200\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(0.5, 0.866);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.2, 0.693) { 0.8 X2, 0.8 X3 } { 0.8 Y2, 0.8 Y3 };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(-0.2, 0.693) { -0.6 X2, 0.8 X3 } { -0.6 Y2, 0.8 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.75, 0.433) { 0.5 X2, 0.5 X3 } { 0.5 Y2, 0.5 Y3 };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(0.25, 0.433) { 0.5 X3 } { 0.5 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"2 h Vis A Vis (1)\"\n",\
+"\n",\
+"quality 3000\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1.732);\n",\
+"(0, 1.732);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.866) { 0.25 X3, 0.25 X4 } { 0.25 Y2, 0.25 Y3, 0.25 Y4 };\n",\
+"\n",\
+"newlines\n",\
+"(1, 5);\n",\
+"(5, 4);\n",\
+"(3, 5);\n",\
+"(5, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { 1 Y2 };\n",\
+"(1.5, 0.866) { 0.75 X2, 0.75 X3, -0.25 X4 } { 0.75 Y2, 0.75 Y3, -0.25 Y4 };\n",\
+"(1, 1.732) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1.732) { 1 X4 } { 1 Y4 };\n",\
+"(-0.5, 0.866) { 0.75 X4, -0.25 X2, -0.25 X3 } { 0.75 Y4, -0.25 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 5);\n",\
+"(3, 4, 5);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+0};
+}
diff --git a/contrib/Netgen/libsrc/meshing/refine.cpp b/contrib/Netgen/libsrc/meshing/refine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..403e45325bf1b2e4d59338ab8c12a8f923498fdc
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/refine.cpp
@@ -0,0 +1,757 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+
+  void Refinement :: Refine (Mesh & mesh) const
+  {
+    const_cast<Refinement&> (*this).Refine(mesh);
+  }
+
+
+  void Refinement :: Refine (Mesh & mesh)
+  {
+    // reduce 2nd order
+    mesh.ComputeNVertices();
+    mesh.SetNP(mesh.GetNV());
+
+
+    INDEX_2_HASHTABLE<int> between(mesh.GetNP() + 5);
+      
+    int oldne, oldns, oldnf;
+
+    // refine edges
+
+    Array<EdgePointGeomInfo,PointIndex::BASE> epgi;
+
+    oldns = mesh.GetNSeg();
+    for (SegmentIndex si = 0; si < oldns; si++)
+      {
+	const Segment & el = mesh.LineSegment(si);
+
+	INDEX_2 i2 = INDEX_2::Sort(el[0], el[1]);
+	PointIndex pinew;
+	EdgePointGeomInfo ngi;
+
+	if (between.Used(i2))
+	  {
+	    pinew = between.Get(i2);
+	    ngi = epgi[pinew]; 
+	  }
+	else
+	  {
+	    Point<3> pnew;
+	    PointBetween (mesh.Point (el[0]),
+			  mesh.Point (el[1]), 0.5,
+			  el.surfnr1, el.surfnr2,
+			  el.epgeominfo[0], el.epgeominfo[1],
+			  pnew, ngi);
+
+	    pinew = mesh.AddPoint (pnew);
+	    between.Set (i2, pinew);
+
+
+	    if (pinew >= epgi.Size()+PointIndex::BASE)
+	      epgi.SetSize (pinew+1-PointIndex::BASE);
+	    epgi[pinew] = ngi;
+	  }
+
+	Segment ns1 = el;
+	Segment ns2 = el;
+	ns1[1] = pinew;
+	ns1.epgeominfo[1] = ngi;
+	ns2[0] = pinew;
+	ns2.epgeominfo[0] = ngi;
+
+	mesh.LineSegment(si) = ns1;
+	mesh.AddSegment (ns2);
+      }
+
+    // refine surface elements
+    Array<PointGeomInfo,PointIndex::BASE> surfgi (8*mesh.GetNP());
+    for (int i = PointIndex::BASE;
+	 i < surfgi.Size()+PointIndex::BASE; i++)
+      surfgi[i].trignum = -1;
+
+
+    oldnf = mesh.GetNSE();
+    for (SurfaceElementIndex sei = 0; sei < oldnf; sei++)
+      {
+	int j, k;
+	const Element2d & el = mesh.SurfaceElement(sei);
+
+	switch (el.GetType())
+	  {
+	  case TRIG:
+	  case TRIG6:
+	    {
+	      ArrayMem<int,6> pnums(6);
+	      ArrayMem<PointGeomInfo,6> pgis(6);
+
+	      static int betw[3][3] =
+		{ { 2, 3, 4 },
+		  { 1, 3, 5 },
+		  { 1, 2, 6 } };
+
+	      for (j = 1; j <= 3; j++)
+		{
+		  pnums.Elem(j) = el.PNum(j);
+		  pgis.Elem(j) = el.GeomInfoPi(j);
+		}
+
+	      for (j = 0; j < 3; j++)
+		{
+		  PointIndex pi1 = pnums.Elem(betw[j][0]);
+		  PointIndex pi2 = pnums.Elem(betw[j][1]);
+
+		  INDEX_2 i2 (pi1, pi2);
+		  i2.Sort();
+
+		  Point<3> pb;
+		  PointGeomInfo pgi;
+		  PointBetween (mesh.Point (pi1),
+				mesh.Point (pi2), 0.5,
+				mesh.GetFaceDescriptor(el.GetIndex ()).SurfNr(),
+				el.GeomInfoPi (betw[j][0]),
+				el.GeomInfoPi (betw[j][1]),
+				pb, pgi);
+
+
+		  pgis.Elem(4+j) = pgi;
+		  if (between.Used(i2))
+		    pnums.Elem(4+j) = between.Get(i2);
+		  else
+		    {
+		      pnums.Elem(4+j) = mesh.AddPoint (pb);
+		      between.Set (i2, pnums.Get(4+j));
+		    }
+
+		  if (surfgi.Size() < pnums.Elem(4+j))
+		    surfgi.SetSize (pnums.Elem(4+j));
+		  surfgi.Elem(pnums.Elem(4+j)) = pgis.Elem(4+j);
+		}
+
+
+	      static int reftab[4][3] =
+		{ { 1, 6, 5 },
+		  { 2, 4, 6 },
+		  { 3, 5, 4 },
+		  { 6, 4, 5 } };
+
+	      int ind = el.GetIndex();
+	      for (j = 0; j < 4; j++)
+		{
+		  Element2d nel(TRIG);
+		  for (k = 1; k <= 3; k++)
+		    {
+		      nel.PNum(k) = pnums.Get(reftab[j][k-1]);
+		      nel.GeomInfoPi(k) = pgis.Get(reftab[j][k-1]);
+		    }
+		  nel.SetIndex(ind);
+
+		  if (j == 0)
+		    mesh.SurfaceElement(sei) = nel;
+		  else
+		    mesh.AddSurfaceElement(nel);
+		}
+	      break;
+	    }
+	  case QUAD:
+	  case QUAD6:
+	  case QUAD8:
+	    {
+	      ArrayMem<int,9> pnums(9);
+	      ArrayMem<PointGeomInfo,9> pgis(9);
+
+	      static int betw[5][3] =
+		{ { 1, 2, 5 },
+		  { 2, 3, 6 },
+		  { 3, 4, 7 },
+		  { 1, 4, 8 },
+		  { 5, 7, 9 } };
+
+	      for (j = 1; j <= 4; j++)
+		{
+		  pnums.Elem(j) = el.PNum(j);
+		  pgis.Elem(j) = el.GeomInfoPi(j);
+		}
+
+	      for (j = 0; j < 5; j++)
+		{
+		  int pi1 = pnums.Elem(betw[j][0]);
+		  int pi2 = pnums.Elem(betw[j][1]);
+
+		  INDEX_2 i2 (pi1, pi2);
+		  i2.Sort();
+
+		  if (between.Used(i2))
+		    {
+		      pnums.Elem(5+j) = between.Get(i2);
+		      pgis.Elem(5+j) = surfgi.Get(pnums.Elem(4+j));
+		    }
+		  else
+		    {
+		      Point<3> pb;
+		      PointBetween (mesh.Point (pi1),
+				    mesh.Point (pi2), 0.5,
+				    mesh.GetFaceDescriptor(el.GetIndex ()).SurfNr(),
+				    el.GeomInfoPi (betw[j][0]),
+				    el.GeomInfoPi (betw[j][1]),
+				    pb, pgis.Elem(5+j));
+
+		      pnums.Elem(5+j) = mesh.AddPoint (pb);
+
+		      between.Set (i2, pnums.Get(5+j));
+		      
+		      if (surfgi.Size() < pnums.Elem(5+j))
+			surfgi.SetSize (pnums.Elem(5+j));
+		      surfgi.Elem(pnums.Elem(5+j)) = pgis.Elem(5+j);
+		    }
+		}
+
+	      static int reftab[4][4] =
+		{
+		  { 1, 5, 9, 8 },
+		  { 5, 2, 6, 9 },
+		  { 8, 9, 7, 4 },
+		  { 9, 6, 3, 7 } };
+
+	      int ind = el.GetIndex();
+	      for (j = 0; j < 4; j++)
+		{
+		  Element2d nel(QUAD);
+		  for (k = 1; k <= 4; k++)
+		    {
+		      nel.PNum(k) = pnums.Get(reftab[j][k-1]);
+		      nel.GeomInfoPi(k) = pgis.Get(reftab[j][k-1]);
+		    }
+		  nel.SetIndex(ind);
+
+		  if (j == 0)
+		    mesh.SurfaceElement(sei) = nel;
+		  else
+		    mesh.AddSurfaceElement(nel);
+		}
+	      break;
+	    }
+	  default:
+	    PrintSysError ("Refine: undefined surface element type ", int(el.GetType()));
+	  }
+      }
+
+    // refine volume elements
+    oldne = mesh.GetNE();
+    for (ElementIndex ei = 0; ei < oldne; ei++)
+      {
+	int j, k;
+
+	const Element & el = mesh.VolumeElement(ei);
+	switch (el.GetType())
+	  {
+	  case TET:
+	  case TET10:
+	    {
+	     ArrayMem<int,10> pnums(10);
+	     static int betw[6][3] =
+	     { { 1, 2, 5 },
+	       { 1, 3, 6 },
+	       { 1, 4, 7 },
+	       { 2, 3, 8 },
+	       { 2, 4, 9 },
+	       { 3, 4, 10 } };
+
+	     int elrev = el.flags.reverse;
+
+	     for (j = 1; j <= 4; j++)
+	     pnums.Elem(j) = el.PNum(j);
+	     if (elrev)
+	     swap (pnums.Elem(3), pnums.Elem(4));
+
+	     for (j = 0; j < 6; j++)
+	     {
+	       INDEX_2 i2;
+	       i2.I1() = pnums.Get(betw[j][0]);
+	       i2.I2() = pnums.Get(betw[j][1]);
+	       i2.Sort();
+
+	       if (between.Used(i2))
+	          pnums.Elem(5+j) = between.Get(i2);
+	       else
+	       {
+		  pnums.Elem(5+j) = mesh.AddPoint
+		  (Center (mesh.Point(i2.I1()),
+			   mesh.Point(i2.I2())));
+		  between.Set (i2, pnums.Elem(5+j));
+	       }
+	    }
+
+	    static int reftab[8][4] =
+	    { { 1, 5, 6, 7 },
+	      { 5, 2, 8, 9 },
+	      { 6, 8, 3, 10 },
+	      { 7, 9, 10, 4 },
+	      { 5, 6, 7, 9 },
+	      { 5, 6, 9, 8 },
+	      { 6, 7, 9, 10 },
+	      { 6, 8, 10, 9 } };
+	/*
+	  { { 1, 5, 6, 7 },
+	  { 5, 2, 8, 9 },
+	  { 6, 8, 3, 10 },
+	  { 7, 9, 10, 4 },
+	  { 5, 6, 7, 9 },
+	  { 5, 6, 8, 9 },
+	  { 6, 7, 9, 10 },
+	  { 6, 8, 9, 10 } };
+	*/
+	   static bool reverse[8] =
+	   {
+	      false, false, false, false, false, true, false, true
+	   };
+
+	   int ind = el.GetIndex();
+	   for (j = 0; j < 8; j++)
+	   {
+	      Element nel;
+	      for (k = 1; k <= 4; k++)
+	        nel.PNum(k) = pnums.Get(reftab[j][k-1]);
+	      nel.SetIndex(ind);
+	      nel.flags.reverse = reverse[j];
+	      if (elrev)
+	      {
+		nel.flags.reverse = !nel.flags.reverse;
+		swap (nel.PNum(3), nel.PNum(4));
+	      }
+
+	      if (j == 0)
+	        mesh.VolumeElement(ei) = nel;
+	      else
+	        mesh.AddVolumeElement (nel);
+	    }
+	    break;
+          }
+          case HEX:
+          {
+	     ArrayMem<int,27> pnums(27);
+	     static int betw[13][3] =
+	     { { 1, 2, 9 },
+	       { 3, 4, 10 },
+	       { 4, 1, 11 },
+               { 2, 3, 12 },
+	       { 5, 6, 13 },
+	       { 7, 8, 14 },
+	       { 8, 5, 15 },
+	       { 6, 7, 16 },
+	       { 1, 5, 17 },
+	       { 2, 6, 18 },
+	       { 3, 7, 19 },
+	       { 4, 8, 20 },
+	       { 2, 8, 21 },
+	       };
+
+             /*
+	     static int fbetw[12][3] =
+	     { { 1, 3, 22 },
+	       { 2, 4, 22 },
+	       { 5, 7, 23 },
+               { 6, 8, 23 },
+	       { 1, 6, 24 },
+	       { 2, 5, 24 },
+	       { 2, 7, 25 },
+	       { 3, 6, 25 },
+	       { 3, 8, 26 },
+	       { 4, 7, 26 },
+	       { 1, 8, 27 },
+	       { 4, 5, 27 },
+	       };
+             */
+             
+             // udpated by anonymous supporter, donations please to Karo W.
+             static int fbetw[12][3] =
+               { { 11, 12, 22 },
+                 { 9, 10, 22 },
+                 { 13, 14, 23 },
+                 { 15, 16, 23 },
+                 { 9, 13, 24 },
+                 { 17, 18, 24 },
+                 { 12, 16, 25 },
+                 { 18, 19, 25 },
+                 { 19, 20, 26 },
+                 { 10, 14, 26 },
+                 { 11, 15, 27 },
+                 { 17, 20, 27 },
+               };
+
+	     pnums = -1;
+
+	     for (j = 1; j <= 8; j++)
+	     pnums.Elem(j) = el.PNum(j);
+
+
+	     for (j = 0; j < 13; j++)
+	     {
+	       INDEX_2 i2;
+	       i2.I1() = pnums.Get(betw[j][0]);
+	       i2.I2() = pnums.Get(betw[j][1]);
+	       i2.Sort();
+
+	       if (between.Used(i2))
+	          pnums.Elem(9+j) = between.Get(i2);
+	       else
+	       {
+		  pnums.Elem(9+j) = mesh.AddPoint
+		  (Center (mesh.Point(i2.I1()),
+			   mesh.Point(i2.I2())));
+		  between.Set (i2, pnums.Elem(9+j));
+	       }
+	    }
+
+	    for (j = 0; j < 6; j++)
+	    {
+	       INDEX_2 i2a, i2b;
+	       i2a.I1() = pnums.Get(fbetw[2*j][0]);
+	       i2a.I2() = pnums.Get(fbetw[2*j][1]);
+	       i2a.Sort();
+	       i2b.I1() = pnums.Get(fbetw[2*j+1][0]);
+	       i2b.I2() = pnums.Get(fbetw[2*j+1][1]);
+	       i2b.Sort();
+
+	       if (between.Used(i2a))
+		 pnums.Elem(22+j) = between.Get(i2a);
+	       else if (between.Used(i2b))
+		 pnums.Elem(22+j) = between.Get(i2b);
+	       else
+		 {
+		   pnums.Elem(22+j) = mesh.AddPoint
+		     (Center (mesh.Point(i2a.I1()),
+			      mesh.Point(i2a.I2())));
+
+		   between.Set (i2a, pnums.Elem(22+j));
+		 }
+	    }
+
+	    static int reftab[8][8] =
+	    { { 1, 9, 22, 11, 17, 24, 21, 27 },
+	      { 9, 2, 12, 22, 24, 18, 25, 21 },
+	      { 11, 22, 10, 4, 27, 21, 26, 20},
+	      { 22, 12, 3, 10, 21, 25, 19, 26},
+	      { 17, 24, 21, 27, 5, 13, 23, 15},
+	      { 24, 18, 25, 21, 13, 6, 16, 23},
+	      { 27, 21, 26, 20, 15, 23, 14, 8},
+	      { 21, 25, 19, 26, 23, 16, 7, 14} };
+
+
+	   int ind = el.GetIndex();
+	   for (j = 0; j < 8; j++)
+	   {
+	      Element nel(HEX);
+	      for (k = 1; k <= 8; k++)
+	        nel.PNum(k) = pnums.Get(reftab[j][k-1]);
+	      nel.SetIndex(ind);
+
+              if (j == 0)
+	        mesh.VolumeElement(ei) = nel;
+	      else
+	        mesh.AddVolumeElement (nel);
+           }
+           break;
+	  }
+	  case PRISM:
+          {
+	     ArrayMem<int,18> pnums(18);
+	     static int betw[9][3] =
+	     { { 3, 1, 7 },
+	       { 1, 2, 8 },
+	       { 3, 2, 9 },
+               { 6, 4, 10 },
+	       { 4, 5, 11 },
+	       { 6, 5, 12 },
+	       { 1, 4, 13 },
+	       { 3, 6, 14 },
+	       { 2, 5, 15 },
+	       };
+
+// he: 15.jul 08, old version is wrong
+//                produces double points ad quad faces and inconsistent mesh
+// 	     static int fbetw[6][3] =
+// 	     { { 1, 6, 16 },
+// 	       { 3, 4, 16 },
+// 	       { 1, 5, 17 },
+//                { 2, 4, 17 },
+// 	       { 2, 6, 18 },
+// 	       { 3, 5, 18 },
+// 	       };
+           
+           static int fbetw[6][3] =
+           { { 7, 10, 16 },
+           { 14, 13, 16 },
+           { 11, 8, 17 },
+           { 13, 15, 17 },
+           { 12, 9, 18 },
+           { 14, 15, 18 },
+           };
+
+	     //int elrev = el.flags.reverse;
+	     pnums = -1;
+
+	     for (j = 1; j <= 6; j++)
+	     pnums.Elem(j) = el.PNum(j);
+	    // if (elrev)
+	    // swap (pnums.Elem(3), pnums.Elem(4));
+
+	     for (j = 0; j < 9; j++)
+	     {
+	       INDEX_2 i2;
+	       i2.I1() = pnums.Get(betw[j][0]);
+	       i2.I2() = pnums.Get(betw[j][1]);
+	       i2.Sort();
+
+	       if (between.Used(i2))
+	          pnums.Elem(7+j) = between.Get(i2);
+	       else
+	       {
+		  pnums.Elem(7+j) = mesh.AddPoint
+		  (Center (mesh.Point(i2.I1()),
+			   mesh.Point(i2.I2())));
+		  between.Set (i2, pnums.Elem(7+j));
+	       }
+	    }
+
+	    for (j = 0; j < 3; j++)
+	    {
+	       INDEX_2 i2a, i2b;
+	       i2a.I1() = pnums.Get(fbetw[2*j][0]);
+	       i2a.I2() = pnums.Get(fbetw[2*j][1]);
+	       i2a.Sort();
+	       i2b.I1() = pnums.Get(fbetw[2*j+1][0]);
+	       i2b.I2() = pnums.Get(fbetw[2*j+1][1]);
+	       i2b.Sort();
+
+	       if (between.Used(i2a))
+		 pnums.Elem(16+j) = between.Get(i2a);
+	       else if (between.Used(i2b))
+		 pnums.Elem(16+j) = between.Get(i2b);
+	       else
+		 {
+		   pnums.Elem(16+j) = mesh.AddPoint
+		     (Center (mesh.Point(i2a.I1()),
+			      mesh.Point(i2a.I2())));
+
+		   between.Set (i2a, pnums.Elem(16+j));
+		 }
+	    }
+
+
+	    static int reftab[8][6] =
+	    { { 1, 8, 7, 13, 17, 16 },
+	      { 7, 8, 9, 16, 17, 18 },
+	      { 7, 9, 3, 16, 18, 14 },
+	      { 8, 2, 9, 17, 15, 18 },
+	      { 13, 17, 16, 4, 11, 10 },
+	      { 16, 17, 18, 10, 11, 12 },
+	      { 16, 18, 14, 10, 12, 6 },
+	      { 17, 15, 18, 11, 5, 12 } };
+
+
+	   int ind = el.GetIndex();
+	   for (j = 0; j < 8; j++)
+	   {
+	      Element nel(PRISM);
+	      for (k = 1; k <= 6; k++)
+	        nel.PNum(k) = pnums.Get(reftab[j][k-1]);
+	      nel.SetIndex(ind);
+
+
+	      //nel.flags.reverse = reverse[j];
+	      //if (elrev)
+	     // {
+		//nel.flags.reverse = 1 - nel.flags.reverse;
+		//swap (nel.PNum(3), nel.PNum(4));
+
+
+	      if (j == 0)
+	        mesh.VolumeElement(ei) = nel;
+	      else
+	        mesh.AddVolumeElement (nel);
+           }
+           break;
+	  }
+	  default:
+	    PrintSysError ("Refine: undefined volume element type ", int(el.GetType()));
+        }
+      }
+
+
+    // update identification tables
+    for (int i = 1; i <= mesh.GetIdentifications().GetMaxNr(); i++)
+      {
+	Array<int,PointIndex::BASE> identmap;
+	mesh.GetIdentifications().GetMap (i, identmap);
+
+	for (int j = 1; j <= between.GetNBags(); j++)
+	  for (int k = 1; k <= between.GetBagSize(j); k++)
+	    {
+	      INDEX_2 i2;
+	      int newpi;
+	      between.GetData (j, k, i2, newpi);
+	      INDEX_2 oi2(identmap.Get(i2.I1()),
+			  identmap.Get(i2.I2()));
+	      oi2.Sort();
+	      if (between.Used (oi2))
+		{
+		  int onewpi = between.Get(oi2);
+		  mesh.GetIdentifications().Add (newpi, onewpi, i);
+		}
+	    }
+
+      }
+
+    mesh.ComputeNVertices();
+    mesh.RebuildSurfaceElementLists();
+    return;
+
+    int cnttrials = 10;
+    int wrongels = 0;
+    for (int i = 1; i <= mesh.GetNE(); i++)
+      if (mesh.VolumeElement(i).Volume(mesh.Points()) < 0)
+	{
+	  wrongels++;
+	  mesh.VolumeElement(i).flags.badel = 1;
+	}
+      else
+	mesh.VolumeElement(i).flags.badel = 0;
+
+    if (wrongels)
+      {
+	cout << "WARNING: " << wrongels << " with wrong orientation found" << endl;
+
+	int np = mesh.GetNP();
+	Array<Point<3> > should(np);
+	Array<Point<3> > can(np);
+	for (int i = 1; i <= np; i++)
+	  {
+	    should.Elem(i) = can.Elem(i) = mesh.Point(i);
+	  }
+	for (int i = 1; i <= between.GetNBags(); i++)
+	  for (int j = 1; j <= between.GetBagSize(i); j++)
+	    {
+	      INDEX_2 parent;
+	      int child;
+	      between.GetData (i, j, parent, child);
+	      can.Elem(child) = Center (can.Elem(parent.I1()),
+					can.Elem(parent.I2()));
+	    }
+
+	BitArray boundp(np);
+	boundp.Clear();
+	for (int i = 1; i <= mesh.GetNSE(); i++)
+	  {
+	    const Element2d & sel = mesh.SurfaceElement(i);
+	    for (int j = 1; j <= sel.GetNP(); j++)
+	      boundp.Set(sel.PNum(j));
+	  }
+
+
+	double lam = 0.5;
+
+	while (lam < 0.9 && cnttrials > 0)
+	  {
+	    lam = 2;
+	    do
+	      {
+		lam *= 0.5;
+		cnttrials--;
+
+		cout << "lam = " << lam << endl;
+
+		for (int i = 1; i <= np; i++)
+		  if (boundp.Test(i))
+		    {
+		      for (int j = 0; j < 3; j++)
+			mesh.Point(i)(j) = 
+			  lam * should.Get(i)(j) +
+			  (1-lam) * can.Get(i)(j);
+		    }
+		  else
+		    mesh.Point(i) = can.Get(i);
+	      
+
+		BitArray free (mesh.GetNP()), fhelp(mesh.GetNP());
+		free.Clear();
+		for (int i = 1; i <= mesh.GetNE(); i++)
+		  {
+		    const Element & el = mesh.VolumeElement(i);
+		    if (el.Volume(mesh.Points()) < 0)
+		      for (int j = 1; j <= el.GetNP(); j++)
+			free.Set (el.PNum(j));
+		  }
+		for (int k = 1; k <= 3; k++)
+		  {
+		    fhelp.Clear();
+		    for (int i = 1; i <= mesh.GetNE(); i++)
+		      {
+			const Element & el = mesh.VolumeElement(i);
+			int freeel = 0;
+			for (int j = 1; j <= el.GetNP(); j++)
+			  if (free.Test(el.PNum(j)))
+			    freeel = 1;
+			if (freeel)
+			  for (int j = 1; j <= el.GetNP(); j++)
+			    fhelp.Set (el.PNum(j));
+		      }
+		    free.Or (fhelp);
+		  }
+
+		(*testout) << "smooth points: " << endl;
+		for (int i = 1; i <= free.Size(); i++)
+		  if (free.Test(i))
+		    (*testout) << "p " << i << endl;
+
+		(*testout) << "surf points: " << endl;
+		for (int i = 1; i <= mesh.GetNSE(); i++)
+		  for (int j = 1; j <= 3; j++)
+		    (*testout) << mesh.SurfaceElement(i).PNum(j) << endl;
+		  
+
+
+		mesh.CalcSurfacesOfNode();
+		free.Invert();
+		mesh.FixPoints (free);
+		MeshingParameters dummymp;
+		mesh.ImproveMesh (dummymp, OPT_REST);
+
+
+		wrongels = 0;
+		for (int i = 1; i <= mesh.GetNE(); i++)
+		  {
+		    if (mesh.VolumeElement(i).Volume(mesh.Points()) < 0)
+		      {
+			wrongels++;
+			mesh.VolumeElement(i).flags.badel = 1;
+			(*testout) << "wrong el: ";
+			for (int j = 1; j <= 4; j++)
+			  (*testout) << mesh.VolumeElement(i).PNum(j) << " ";
+			(*testout) << endl;
+		      }
+		    else
+		      mesh.VolumeElement(i).flags.badel = 0;
+		  }
+		cout << "wrongels = " << wrongels << endl;
+	      }
+	    while (wrongels && cnttrials > 0);
+	  
+	    for (int i = 1; i <= np; i++)
+	      can.Elem(i) = mesh.Point(i);
+	  }
+      }
+
+    if (cnttrials <= 0)
+      {
+	cerr << "ERROR: Sorry, reverted elements" << endl;
+      }
+ 
+    mesh.ComputeNVertices();
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/ruler2.cpp b/contrib/Netgen/libsrc/meshing/ruler2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..62058d2ed232f025bca6bc9ec4ec5d458bf05802
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/ruler2.cpp
@@ -0,0 +1,719 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+
+  static double CalcElementBadness (const Array<Point2d> & points,
+				    const Element2d & elem)
+  {
+    // badness = sqrt(3) /36 * circumference^2 / area - 1 +
+    //           h / li + li / h - 2
+
+    Vec2d v12, v13, v23;
+    double l12, l13, l23, cir, area;
+    static const double c = sqrt(3.0) / 36;
+
+    v12 = points.Get(elem.PNum(2)) - points.Get(elem.PNum(1));
+    v13 = points.Get(elem.PNum(3)) - points.Get(elem.PNum(1));
+    v23 = points.Get(elem.PNum(3)) - points.Get(elem.PNum(2));
+
+    l12 = v12.Length();
+    l13 = v13.Length();
+    l23 = v23.Length();
+
+    cir = l12 + l13 + l23;
+    area = 0.5 * (v12.X() * v13.Y() - v12.Y() * v13.X());
+    if (area < 1e-6)
+      {
+	return 1e8;
+      }
+
+    if (testmode)
+      {
+	(*testout) << "l = " << l12 << " + " << l13 << " + " << l23 << " = " 
+		   << cir << ", area = " << area << endl;
+	(*testout) << "shapeerr = " << 10 * (c * cir * cir / area - 1) << endl
+		   << "sizeerr = " << 1/l12 + l12 + 1/l13 + l13 + 1/l23 + l23 - 6
+		   << endl;
+      }
+
+    return 10 * (c * cir * cir / area - 1)
+      + 1/l12 + l12 + 1/l13 + l13 + 1/l23 + l23 - 6;
+  }
+
+
+
+  int Meshing2 ::ApplyRules (Array<Point2d> & lpoints, 
+			     Array<int> & legalpoints,
+			     int maxlegalpoint,
+			     Array<INDEX_2> & llines1,
+			     int maxlegalline,
+			     Array<Element2d> & elements,
+			     Array<INDEX> & dellines, int tolerance,
+			     const MeshingParameters & mp)
+  {
+    static int timer = NgProfiler::CreateTimer ("meshing2::ApplyRules");
+    NgProfiler::RegionTimer reg (timer);
+
+
+
+    double maxerr = 0.5 + 0.3 * tolerance;
+    double minelerr = 2 + 0.5 * tolerance * tolerance;
+
+    int noldlp = lpoints.Size();
+    int noldll = llines1.Size();
+
+
+    ArrayMem<int,100> pused(maxlegalpoint), lused(maxlegalline);
+    ArrayMem<int,100> pnearness(noldlp), lnearness(llines1.Size());
+
+    ArrayMem<int, 20> pmap, pfixed, lmap;
+  
+    ArrayMem<Point2d,100> tempnewpoints;
+    ArrayMem<INDEX_2,100> tempnewlines;
+    ArrayMem<int,100> tempdellines;
+    ArrayMem<Element2d,100> tempelements;
+
+
+    elements.SetSize (0);
+    dellines.SetSize (0);
+
+    testmode = debugparam.debugoutput;
+
+#ifdef LOCDEBUG
+    int loctestmode = testmode;
+
+    if (loctestmode)
+      {
+	(*testout) << endl << endl << "Check new environment" << endl;
+	(*testout) << "tolerance = " << tolerance << endl;
+	for (int i = 1; i <= lpoints.Size(); i++)
+	  (*testout) << "P" << i << " = " << lpoints.Get(i) << endl;
+	(*testout) << endl;
+	for (int i = 1; i <= llines1.Size(); i++)
+	  (*testout) << "(" << llines1.Get(i).I1() << "-" << llines1.Get(i).I2() << ")" << endl;
+      }
+#endif
+
+    // check every rule
+
+    int found = 0;   // rule number
+
+    pnearness = 1000;
+  
+    for (int j = 0; j < 2; j++)
+      pnearness.Set(llines1[0][j], 0);
+
+
+
+    enum { MAX_NEARNESS = 3 };
+
+    for (int cnt = 0; cnt < MAX_NEARNESS; cnt++)
+      {
+	bool ok = true;
+	for (int i = 0; i < maxlegalline; i++)
+	  {
+	    const INDEX_2 & hline = llines1[i];
+
+	    int minn = min2 (pnearness.Get(hline[0]),  pnearness.Get(hline[1]));
+
+	    for (int j = 0; j < 2; j++)
+	      if (pnearness.Get(hline[j]) > minn+1)
+		{
+		  ok = false;
+		  pnearness.Set(hline[j], minn+1);
+		}
+	  }
+	if (!ok) break;
+      }
+
+
+    for (int i = 0; i < maxlegalline; i++)
+      lnearness[i] = pnearness.Get(llines1[i][0]) + pnearness.Get(llines1[i][1]);
+
+
+    // resort lines after lnearness
+    Array<INDEX_2> llines(llines1.Size());
+    Array<int> sortlines(llines1.Size());
+    int lnearness_class[MAX_NEARNESS];
+
+    for (int j = 0; j < MAX_NEARNESS; j++)
+      lnearness_class[j] = 0;
+    for (int i = 0; i < maxlegalline; i++)
+      if (lnearness[i] < MAX_NEARNESS)
+	lnearness_class[lnearness[i]]++;
+    
+    int cumm = 0;
+    for (int j = 0; j < MAX_NEARNESS; j++)
+      {
+	int hcnt = lnearness_class[j];
+	lnearness_class[j] = cumm;
+	cumm += hcnt;
+      }
+
+    for (int i = 0; i < maxlegalline; i++)
+      if (lnearness[i] < MAX_NEARNESS)
+	{
+	  llines[lnearness_class[lnearness[i]]] = llines1[i];
+	  sortlines[lnearness_class[lnearness[i]]] = i+1;
+	  lnearness_class[lnearness[i]]++;
+	}
+      else
+	{
+	  llines[cumm] = llines1[i];
+	  sortlines[cumm] = i+1;
+	  cumm++;
+	}
+
+    for (int i = maxlegalline; i < llines1.Size(); i++)
+      {
+	llines[cumm] = llines1[i];
+	sortlines[cumm] = i+1;
+	cumm++;
+      }
+
+    for (int i = 0; i < maxlegalline; i++)
+      lnearness[i] = pnearness.Get(llines[i][0]) + pnearness.Get(llines[i][1]);
+
+
+
+
+    static bool firsttime = true;
+    static int timers[100];
+    static int timers2[100];
+    static int timers3[100];
+    if (firsttime)
+      {
+	for (int ri = 0; ri < rules.Size(); ri++)
+	  timers[ri] = NgProfiler::CreateTimer (string("netrule ")+rules[ri]->Name());
+	for (int ri = 0; ri < rules.Size(); ri++)
+	  timers2[ri] = NgProfiler::CreateTimer (string("netrule,mapped ")+rules[ri]->Name());
+	for (int ri = 0; ri < rules.Size(); ri++)
+	  timers3[ri] = NgProfiler::CreateTimer (string("netrule,lines mapped ")+rules[ri]->Name());
+	firsttime = false;
+      }
+
+    lused = 0;
+    pused = 0;
+
+
+    static int timer1 = NgProfiler::CreateTimer ("meshing2::ApplyRules 1");
+    NgProfiler::RegionTimer reg1 (timer1);
+
+
+    for (int ri = 1; ri <= rules.Size(); ri++)
+      {
+	NgProfiler::RegionTimer reg(timers[ri-1]);
+	netrule * rule = rules.Get(ri);
+
+#ifdef LOCDEBUG
+	if (loctestmode)
+	  (*testout) << "Rule " << rule->Name() << endl;
+#endif
+
+	if (rule->GetQuality() > tolerance) continue;
+
+	pmap.SetSize (rule->GetNP());
+	lmap.SetSize (rule->GetNL());
+      
+	pmap = 0;
+	lmap = 0;
+
+	lused[0] = 1; 
+	lmap[0] = 1;  
+
+	for (int j = 0; j < 2; j++)
+	  {
+	    pmap.Elem(rule->GetLine(1)[j]) = llines[0][j];
+	    pused.Elem(llines[0][j])++;
+	  }
+
+
+
+	int nlok = 2;
+
+
+	bool ok = false;
+
+	while (nlok >= 2)
+	  {
+
+	    if (nlok <= rule->GetNOldL())
+
+	      {
+		ok = 0;
+		
+		int maxline = (rule->GetLNearness(nlok) < MAX_NEARNESS) ? lnearness_class[rule->GetLNearness(nlok)] : maxlegalline;
+		// int maxline = maxlegalline;
+
+		while (!ok && lmap.Get(nlok) < maxline)
+		  {
+		    lmap.Elem(nlok)++;
+		    int locli = lmap.Get(nlok);
+
+		    if (lnearness.Get(locli) > rule->GetLNearness (nlok) ) continue;
+		    if (lused.Get(locli)) continue;
+
+
+		    ok = 1;
+
+		    INDEX_2 loclin = llines.Get(locli);
+		    Vec2d linevec = lpoints.Get(loclin.I2()) - lpoints.Get(loclin.I1());
+
+		    if (rule->CalcLineError (nlok, linevec) > maxerr)
+		      {
+			ok = 0;
+#ifdef LOCDEBUG
+			if(loctestmode)
+			  (*testout) << "not ok pos1" << endl;
+#endif
+			continue;
+		      }
+
+		    for (int j = 0; j < 2; j++)
+		      {
+			int refpi = rule->GetLine(nlok)[j];
+
+			if (pmap.Get(refpi) != 0)
+			  {
+			    if (pmap.Get(refpi) != loclin[j])
+			      {
+				ok = 0;
+#ifdef LOCDEBUG
+				if(loctestmode)
+				  (*testout) << "not ok pos2" << endl;
+#endif
+				break;
+			      }
+			  }
+			else
+			  {
+			    if (rule->CalcPointDist (refpi, lpoints.Get(loclin[j])) > maxerr
+				|| !legalpoints.Get(loclin[j])
+				|| pused.Get(loclin[j]))
+			      {
+				ok = 0;
+#ifdef LOCDEBUG
+				if(loctestmode)
+				  {
+				    (*testout) << "nok pos3" << endl;
+				    //if(rule->CalcPointDist (refpi, lpoints.Get(loclin[j])) > maxerr)
+				    //(*testout) << "r1" << endl;
+				    //if(!legalpoints.Get(loclin[j]))
+				    //(*testout) << "r2 legalpoints " << legalpoints << " loclin " << loclin << " j " << j << endl;
+				    //if(pused.Get(loclin[j]))
+				    //(*testout) << "r3" << endl;
+				  }
+#endif
+				break;
+			      }
+			  }
+		      }
+		  }
+
+		if (ok)
+		  {
+		    int locli = lmap.Get(nlok);
+		    INDEX_2 loclin = llines.Get(locli);
+
+		    lused.Elem (locli) = 1;
+		    for (int j = 0; j < 2; j++)
+		      {
+			pmap.Set(rule->GetLine (nlok)[j], loclin[j]);
+			pused.Elem(loclin[j])++;
+		      }
+
+		    nlok++;
+		  }
+		else
+		  {
+		    lmap.Elem(nlok) = 0;
+		    nlok--;
+
+		    lused.Elem (lmap.Get(nlok)) = 0;
+		    for (int j = 0; j < 2; j++)
+		      {
+			pused.Elem(llines.Get(lmap.Get(nlok))[j]) --;
+			if (! pused.Get (llines.Get (lmap.Get (nlok))[j]))
+			  pmap.Set (rule->GetLine (nlok)[j], 0);
+		      }
+		  }
+	      }
+
+	    else
+
+	      {
+		NgProfiler::RegionTimer reg(timers3[ri-1]);
+
+		// all lines are mapped !!
+
+		// map also all points:
+
+		int npok = 1;
+		int incnpok = 1;
+
+		pfixed.SetSize (pmap.Size());
+		for (int i = 0; i < pmap.Size(); i++)
+		  pfixed[i] = (pmap[i] >= 1);
+ 
+		while (npok >= 1)
+		  {
+
+		    if (npok <= rule->GetNOldP())
+
+		      {
+			if (pfixed.Get(npok))
+
+			  {
+			    if (incnpok)
+			      npok++;
+			    else
+			      npok--;
+			  }
+
+			else
+
+			  {
+			    ok = 0;
+
+			    if (pmap.Get(npok))
+			      pused.Elem(pmap.Get(npok))--;
+
+			    while (!ok && pmap.Get(npok) < maxlegalpoint)
+			      {
+				ok = 1;
+
+				pmap.Elem(npok)++;
+
+				if (pused.Get(pmap.Get(npok)))
+				  {
+				    ok = 0;
+				  }
+				else
+				  {
+				    if (rule->CalcPointDist (npok, lpoints.Get(pmap.Get(npok))) > maxerr 
+					|| !legalpoints.Get(pmap.Get(npok))) 
+                                    
+				      ok = 0;
+				  }
+			      }
+
+			    if (ok)
+			      {
+				pused.Elem(pmap.Get(npok))++;
+				npok++;
+				incnpok = 1;
+			      }
+
+			    else
+
+			      {
+				pmap.Elem(npok) = 0;
+				npok--;
+				incnpok = 0;
+			      }
+			  }
+		      }
+
+		    else
+
+		      {
+			NgProfiler::RegionTimer reg(timers2[ri-1]);
+
+			npok = rule->GetNOldP();
+			incnpok = 0;
+
+			if (ok)
+			  foundmap.Elem(ri)++; 
+
+#ifdef LOCDEBUG
+			if (loctestmode)
+			  (*testout) << "lines and points mapped" << endl;
+#endif
+
+			ok = 1;
+
+			// check orientations
+
+			for (int i = 1; i <= rule->GetNOrientations(); i++)
+			  {
+			    if (CW (lpoints.Get(pmap.Get(rule->GetOrientation(i).i1)),
+				    lpoints.Get(pmap.Get(rule->GetOrientation(i).i2)),
+				    lpoints.Get(pmap.Get(rule->GetOrientation(i).i3))) )
+			      {
+				ok = 0;
+#ifdef LOCDEBUG
+				if (loctestmode)
+				  (*testout) << "Orientation " << i << " not ok" << endl;
+#endif
+				break;
+			      }
+			  }
+
+
+			if (!ok) continue;
+
+			Vector oldu (2 * rule->GetNOldP());
+		      
+			for (int i = 1; i <= rule->GetNOldP(); i++)
+			  {
+			    Vec2d ui(rule->GetPoint(i), lpoints.Get(pmap.Get(i)));
+			    oldu (2*i-2) = ui.X();
+			    oldu (2*i-1) = ui.Y();
+			  }
+		      
+			rule -> SetFreeZoneTransformation (oldu, tolerance);
+
+		      
+			if (!ok) continue;
+			if (!rule->ConvexFreeZone())
+			  {
+			    ok = 0;
+#ifdef LOCDEBUG
+			    if (loctestmode) 
+			      (*testout) << "freezone not convex" << endl;
+#endif
+			    /*
+			      static int cnt = 0;
+			      cnt++;
+			      if (cnt % 100 == 0)
+			      {
+			      cout << "freezone not convex, cnt = " << cnt << "; rule = " << rule->Name() << endl;
+			      (*testout) << "freezone not convex, cnt = " << cnt << "; rule = " << rule->Name() << endl;
+			      (*testout) << "tol = " << tolerance << endl;
+			      (*testout) << "maxerr = " << maxerr << "; minerr = " << minelerr << endl;
+			      (*testout) << "freezone = " << rule->GetTransFreeZone() << endl;
+			      }
+			    */
+			  }
+
+			// check freezone:
+			if (!ok) continue;
+			for (int i = 1; i <= maxlegalpoint && ok; i++)
+			  {
+			    if ( !pused.Get(i) &&
+				 rule->IsInFreeZone (lpoints.Get(i)) )
+			      {
+				ok = 0;
+#ifdef LOCDEBUG
+				if (loctestmode)
+				  (*testout) << "Point " << i << " in freezone" << endl;
+#endif
+				break;
+			      }
+			  }
+
+			if (!ok) continue;
+			for (int i = maxlegalpoint+1; i <= lpoints.Size(); i++)
+			  {
+			    if ( rule->IsInFreeZone (lpoints.Get(i)) )
+			      {
+				ok = 0;
+#ifdef LOCDEBUG
+				if (loctestmode)
+				  (*testout) << "Point " << i << " in freezone" << endl;
+#endif
+				break;
+			      }
+			  }
+
+
+			if (!ok) continue;
+			for (int i = 1; i <= maxlegalline; i++)
+			  {
+			    if (!lused.Get(i) && 
+				rule->IsLineInFreeZone (lpoints.Get(llines.Get(i).I1()),
+							lpoints.Get(llines.Get(i).I2())))
+			      {
+				ok = 0;
+#ifdef LOCDEBUG
+				if (loctestmode)
+				  (*testout) << "line " << llines.Get(i).I1() << "-"
+					     << llines.Get(i).I2() << " in freezone" << endl;
+#endif
+				break;
+			      }
+			  }
+
+			if (!ok) continue;
+
+			for (int i = maxlegalline+1; i <= llines.Size(); i++)
+			  {
+			    if (rule->IsLineInFreeZone (lpoints.Get(llines.Get(i).I1()),
+							lpoints.Get(llines.Get(i).I2())))
+			      {
+				ok = 0;
+#ifdef LOCDEBUG
+				if (loctestmode)
+				  (*testout) << "line " << llines.Get(i).I1() << "-"
+					     << llines.Get(i).I2() << " in freezone" << endl;
+#endif
+				break;
+			      }
+			  }
+
+
+			/*
+			// check orientations
+
+			for (i = 1; i <= rule->GetNOrientations() && ok; i++)
+			{
+			if (CW (lpoints.Get(pmap.Get(rule->GetOrientation(i).i1)),
+			lpoints.Get(pmap.Get(rule->GetOrientation(i).i2)),
+			lpoints.Get(pmap.Get(rule->GetOrientation(i).i3))) )
+			{
+			ok = 0;
+			if (loctestmode)
+			(*testout) << "Orientation " << i << " not ok" << endl;
+			}
+			}
+			*/
+
+
+			if (!ok) continue;
+
+#ifdef LOCDEBUG
+			if (loctestmode)
+			  (*testout) << "rule ok" << endl;
+#endif
+
+			// Setze neue Punkte:
+			if (rule->GetNOldP() < rule->GetNP())
+			  {
+			    Vector newu(rule->GetOldUToNewU().Height());
+			    rule->GetOldUToNewU().Mult (oldu, newu);
+			    
+			    int oldnp = rule->GetNOldP();
+			    for (int i = oldnp + 1; i <= rule->GetNP(); i++)
+			      {
+				Point2d np = rule->GetPoint(i);
+				np.X() += newu (2 * (i-oldnp) - 2);
+				np.Y() += newu (2 * (i-oldnp) - 1);
+				
+				pmap.Elem(i) = lpoints.Append (np);
+			      }
+			  }
+
+			// Setze neue Linien:
+
+			for (int i = rule->GetNOldL() + 1; i <= rule->GetNL(); i++)
+			  {
+			    llines.Append (INDEX_2 (pmap.Get(rule->GetLine (i)[0]),
+						    pmap.Get(rule->GetLine (i)[1])));
+			  }
+
+
+			// delete old lines:
+			for (int i = 1; i <= rule->GetNDelL(); i++)
+			  dellines.Append (sortlines.Elem (lmap.Get(rule->GetDelLine(i))));
+			// dellines.Append (lmap.Get(rule->GetDelLine(i))));
+
+			// dellines.Append (lmap.Elem(rule->GetDelLines()));
+			// lmap[rule->GetDelLines()];
+
+
+			// insert new elements:
+
+			for (int i = 1; i <= rule->GetNE(); i++)
+			  {
+			    elements.Append (rule->GetElement(i));
+			    for (int j = 1; j <= elements.Get(i).GetNP(); j++)
+			      elements.Elem(i).PNum(j) = pmap.Get(elements.Get(i).PNum(j));
+			  }
+
+
+			double elerr = 0;
+			for (int i = 1; i <= elements.Size(); i++)
+			  {
+			    double hf;
+			    if (!mp.quad)
+			      hf = CalcElementBadness (lpoints, elements.Get(i));
+			    else
+			      hf = elements.Get(i).CalcJacobianBadness (lpoints) * 5;
+#ifdef LOCDEBUG
+			    if (loctestmode)
+			      (*testout) << "r " << rule->Name() << "bad = " << hf << endl;
+#endif
+			    if (hf > elerr) elerr = hf;
+			  }
+
+#ifdef LOCDEBUG
+			if (loctestmode)
+			  (*testout) << "error = " << elerr;
+#endif
+
+			canuse.Elem(ri) ++;
+
+			if (elerr < 0.99*minelerr)
+			  {
+#ifdef LOCDEBUG
+			    if (loctestmode)
+			      {
+				(*testout) << "rule = " << rule->Name() << endl;
+				(*testout) << "class = " << tolerance << endl;
+				(*testout) << "lpoints: " << endl;
+				for (int i = 1; i <= lpoints.Size(); i++)
+				  (*testout) << lpoints.Get(i) << endl;
+				(*testout) << "llines: " << endl;
+				for (int i = 1; i <= llines.Size(); i++)
+				  (*testout) << llines.Get(i).I1() << " " << llines.Get(i).I2() << endl;
+
+				(*testout) << "Freezone: ";
+				for (int i = 1; i <= rule -> GetTransFreeZone().Size(); i++)
+				  (*testout) << rule->GetTransFreeZone().Get(i) << endl;
+			      }
+#endif
+
+			    minelerr = elerr;
+			    found = ri;
+
+			    tempnewpoints = lpoints.Range (noldlp, lpoints.Size());
+			    tempnewlines = llines.Range (noldll, llines.Size());
+			    tempdellines = dellines;
+			    tempelements = elements;
+			  }
+
+			lpoints.SetSize (noldlp);
+			llines.SetSize (noldll);
+			dellines.SetSize (0);
+			elements.SetSize (0);
+			ok = 0;
+		      }
+		  }
+
+		nlok = rule->GetNOldL();
+
+		lused.Set (lmap.Get(nlok), 0);
+
+		for (int j = 1; j <= 2; j++)
+		  {
+		    int refpi = rule->GetPointNr (nlok, j);
+		    pused.Elem(pmap.Get(refpi))--;
+
+		    if (pused.Get(pmap.Get(refpi)) == 0)
+		      pmap.Set(refpi, 0);
+		  }
+	      }
+	  }
+      }
+
+
+    if (found)
+      {
+	lpoints.Append (tempnewpoints);
+	llines1.Append (tempnewlines);
+	dellines.Append (tempdellines);
+	elements.Append (tempelements);
+      }
+
+
+    return found;
+  }
+
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/ruler2.hpp b/contrib/Netgen/libsrc/meshing/ruler2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..afbe6b985a601d9d93ffa9220dc0930c3a075206
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/ruler2.hpp
@@ -0,0 +1,169 @@
+#ifndef FILE_NETRULE
+#define FILE_NETRULE
+
+///
+class netrule
+{
+private:
+  ///
+  typedef struct tf 
+  { float f1, f2, f3; }   threefloat;
+  
+  class threeint 
+  { 
+  public: int i1, i2, i3; 
+    threeint() { } 
+    threeint(int ai1, int ai2, int ai3) 
+    { i1 = ai1; i2 = ai2; i3 = ai3; } 
+  };
+
+
+  ///
+  int quality;
+  ///
+  char * name;
+  ///
+  Array<Point2d> points;
+  ///
+  Array<INDEX_2> lines;
+  ///
+  Array<Point2d> freezone, freezonelimit;
+  ///
+  Array<Array<Point2d>*> freezone_i;
+  ///
+  Array<Point2d> transfreezone;
+
+  ///
+  Array<int> dellines;
+  ///
+  Array<Element2d> elements;
+  ///
+  Array<threefloat> tolerances, linetolerances;
+  ///
+  Array<threeint> orientations;
+  ///
+  DenseMatrix oldutonewu, oldutofreearea, oldutofreearealimit;
+  ///
+  Array<DenseMatrix*> oldutofreearea_i;
+  ///
+  MatrixFixWidth<3> freesetinequ;
+
+  ///
+  Array<Vec2d> linevecs;
+
+  ///
+  int noldp, noldl;
+  ///
+  float fzminx, fzmaxx, fzminy, fzmaxy;
+
+  /// topological distance of line to base element
+  Array<int> lnearness;
+
+public:
+
+  ///
+  netrule ();
+  ///
+  ~netrule();
+
+  ///
+  int GetNP () const { return points.Size(); }
+  ///
+  int GetNL () const { return lines.Size(); }
+  ///
+  int GetNE () const { return elements.Size(); }
+  ///
+  int GetNOldP () const { return noldp; }
+  ///
+  int GetNOldL () const { return noldl; }
+  ///
+  int GetNDelL () const { return dellines.Size(); }
+  ///
+  int GetNOrientations () const { return orientations.Size(); }
+  ///
+  int GetQuality () const { return quality; }
+  ///
+  int GetLNearness (int li) const { return lnearness.Get(li); }
+
+  ///
+  const Point2d & GetPoint (int i) const { return points.Get(i); }
+  ///
+  const INDEX_2 & GetLine (int i) const { return lines.Get(i); }
+  ///
+  const Element2d & GetElement (int i) const { return elements.Get(i); }
+  ///
+  const threeint & GetOrientation (int i) const { return orientations.Get(i); }
+  ///
+  int GetDelLine (int i) const { return dellines.Get(i); }
+  ///
+  const Array<int> & GetDelLines() const { return dellines; }
+  ///
+  void GetFreeZone (Array<Point2d> & afreearea);
+  ///
+
+  double CalcPointDist (int pi, const Point2d & p) const
+  {
+    double dx = p.X() - points.Get(pi).X();
+    double dy = p.Y() - points.Get(pi).Y();
+    const threefloat * tfp = &tolerances.Get(pi);
+    return tfp->f1 * dx * dx + tfp->f2 * dx * dy + tfp->f3 * dy * dy;
+  }
+
+  ///
+  float CalcLineError (int li, const Vec2d & v) const;
+
+  ///
+  void SetFreeZoneTransformation (const Vector & u, int tolclass);
+
+  ///
+  bool IsInFreeZone (const Point2d & p) const
+  {
+    if (p.X() < fzminx || p.X() > fzmaxx ||
+	p.Y() < fzminy || p.Y() > fzmaxy) return 0;
+
+    for (int i = 0; i < transfreezone.Size(); i++)
+      {
+	if (freesetinequ(i, 0) * p.X() + 
+	    freesetinequ(i, 1) * p.Y() +
+	    freesetinequ(i, 2) > 0) return 0;
+      }
+    return 1;
+  }
+
+  ///
+  int IsLineInFreeZone (const Point2d & p1, const Point2d & p2) const
+  {
+    if ( (p1.X() > fzmaxx && p2.X() > fzmaxx) ||
+         (p1.X() < fzminx && p2.X() < fzminx) ||
+         (p1.Y() > fzmaxy && p2.Y() > fzmaxy) ||
+         (p1.Y() < fzminy && p2.Y() < fzminy) ) return 0;
+    return IsLineInFreeZone2 (p1, p2);
+  }
+  ///
+  int IsLineInFreeZone2 (const Point2d & p1, const Point2d & p2) const;
+  ///
+  int ConvexFreeZone () const;
+  ///
+  const Array<Point2d> & GetTransFreeZone () { return transfreezone; }
+
+  ///
+  int GetPointNr (int ln, int endp) const { return lines.Get(ln).I(endp); }
+
+  ///
+  const DenseMatrix & GetOldUToNewU () const { return oldutonewu; }
+  ///
+  const DenseMatrix & GetOldUToFreeArea () const { return oldutofreearea; }
+  ///
+  const char * Name () const { return name; }
+
+  ///
+  void LoadRule (istream & ist);
+};
+
+
+
+/** Draws 2D rules.
+    Visual testing of 2D meshing rules */
+extern void DrawRules ();
+#endif
+
diff --git a/contrib/Netgen/libsrc/meshing/ruler3.cpp b/contrib/Netgen/libsrc/meshing/ruler3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3b6f7a91aca3565e9b668a0fe30479793ab4b4aa
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/ruler3.cpp
@@ -0,0 +1,1136 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+extern double minother;
+extern double minwithoutother;
+
+
+static double CalcElementBadness (const Array<Point3d> & points,
+				  const Element & elem)
+{
+  double vol, l, l4, l5, l6;
+  if (elem.GetNP() != 4) 
+    {
+      if (elem.GetNP() == 5)
+	{
+	  double z = points.Get(elem.PNum(5)).Z();
+	  if (z > -1e-8) return 1e8;
+	  return (-1 / z) - z; //  - 2;
+	}
+      return 0;
+    }
+  
+  Vec3d v1 = points.Get(elem.PNum(2)) - points.Get(elem.PNum(1));
+  Vec3d v2 = points.Get(elem.PNum(3)) - points.Get(elem.PNum(1));
+  Vec3d v3 = points.Get(elem.PNum(4)) - points.Get(elem.PNum(1));
+  
+  vol = - (Cross (v1, v2) * v3);
+  l4 = Dist (points.Get(elem.PNum(2)), points.Get(elem.PNum(3)));
+  l5 = Dist (points.Get(elem.PNum(2)), points.Get(elem.PNum(4)));
+  l6 = Dist (points.Get(elem.PNum(3)), points.Get(elem.PNum(4)));
+
+  l = v1.Length() + v2.Length() + v3.Length() + l4 + l5 + l6;
+  
+  //  testout << "vol = " << vol << " l = " << l << endl;
+  if (vol < 1e-8) return 1e10;
+  //  (*testout) << "l^3/vol = " << (l*l*l / vol) << endl;
+  
+  double err = pow (l*l*l/vol, 1.0/3.0) / 12;
+  return err;
+}
+
+
+
+
+
+
+int Meshing3 :: ApplyRules 
+(
+ Array<Point3d> & lpoints,     // in: local points, out: old+new local points
+ Array<int> & allowpoint,      // in: 2 .. it is allowed to use pointi, 1..will be allowed later, 0..no means
+ Array<MiniElement2d> & lfaces,    // in: local faces, out: old+new local faces
+ INDEX lfacesplit,	       // for local faces in outer radius
+ INDEX_2_HASHTABLE<int> & connectedpairs,  // connected pairs for prism-meshing
+ Array<Element> & elements,    // out: new elements
+ Array<INDEX> & delfaces,      // out: face indices of faces to delete
+ int tolerance,                // quality class: 1 best 
+ double sloppy,                // quality strength
+ int rotind1,                  // how to rotate base element
+ float & retminerr             // element error 
+ )
+
+{
+  NgProfiler::RegionTimer regtot(97);
+
+  int i, j, k, ri, nfok, npok, incnpok, refpi, locpi, locfi, locfr;
+  float hf, err, minerr, teterr, minteterr;
+  char ok, found, hc;
+  vnetrule * rule;
+  Vector oldu, newu, newu1, newu2, allp;
+  Vec3d ui;
+  Point3d np;
+  int oldnp, noldlp, noldlf;
+  const MiniElement2d * locface = NULL;
+  int loktestmode;
+
+
+  Array<int> pused;        // point is already mapped
+  Array<char> fused;       // face is already mapped
+  Array<int> pmap;         // map of reference point to local point
+  Array<char> pfixed;      // point mapped by face-map
+  Array<int> fmapi;        // face in reference is mapped to face nr ...
+  Array<int> fmapr;        // face in reference is rotated to map 
+  Array<Point3d> transfreezone;  // transformed free-zone
+  INDEX_2_CLOSED_HASHTABLE<int> ledges(100); // edges in local environment
+  
+  Array<Point3d> tempnewpoints;
+  Array<MiniElement2d> tempnewfaces;
+  Array<int> tempdelfaces;
+  Array<Element> tempelements;
+  Array<Box3d> triboxes;         // bounding boxes of local faces
+
+  Array<int, PointIndex::BASE> pnearness;
+  Array<int> fnearness;
+
+  static int cnt = 0;
+  cnt++;
+  
+  delfaces.SetSize (0);
+  elements.SetSize (0);
+
+  // determine topological distance of faces and points to
+  // base element
+
+  pnearness.SetSize (lpoints.Size());
+  fnearness.SetSize (lfacesplit);
+
+  pnearness = INT_MAX/10;
+  for (j = 0; j < lfaces[0].GetNP(); j++)
+    pnearness[lfaces[0][j]] = 0;
+
+  NgProfiler::RegionTimer reg2(98);
+  
+  NgProfiler::StartTimer (90);
+
+  for (int loop = 0; loop < 2; loop++)
+    {
+
+      for (i = 0; i < lfacesplit; i++)
+	{
+	  const MiniElement2d & hface = lfaces[i];
+
+	  int minn = INT_MAX-1;
+	  for (j = 0; j < hface.GetNP(); j++)
+	    {
+	      int hi = pnearness[hface[j]];
+	      if (hi < minn) minn = hi;
+	    }
+	  if (minn < INT_MAX/10)
+	    for (j = 0; j < hface.GetNP(); j++)
+	      if (pnearness[hface[j]] > minn+1)
+		pnearness[hface[j]] = minn+1;
+	}
+
+      for (i = 1; i <= connectedpairs.GetNBags(); i++)
+	for (j = 1; j <= connectedpairs.GetBagSize(i); j++)
+	  {
+	    INDEX_2 edge;
+	    int val;
+	    connectedpairs.GetData (i, j, edge, val);
+
+	    if (pnearness[edge.I1()] > pnearness[edge.I2()] + 1)
+	      pnearness[edge.I1()] = pnearness[edge.I2()] + 1;
+
+	    if (pnearness[edge.I2()] > pnearness[edge.I1()] + 1)
+	      pnearness[edge.I2()] = pnearness[edge.I1()] + 1;
+	  }
+
+    }
+
+  for (i = 0; i < fnearness.Size(); i++)
+    {
+      int sum = 0;
+      for (j = 0; j < lfaces[i].GetNP(); j++)
+	sum += pnearness[lfaces[i][j]];
+      fnearness[i] = sum;
+    }
+
+  
+  NgProfiler::StopTimer (90);
+  NgProfiler::StartTimer (91);
+
+  // find bounding boxes of faces
+
+  triboxes.SetSize (lfaces.Size());
+  for (i = 0; i < lfaces.Size(); i++)
+    {
+      const MiniElement2d & face = lfaces[i];
+      triboxes[i].SetPoint (lpoints.Get(face[0]));
+      for (j = 1; j < face.GetNP(); j++)
+	triboxes[i].AddPoint (lpoints.Get(face[j]));
+    }
+
+  NgProfiler::StopTimer (91);
+  NgProfiler::StartTimer (92);
+
+  
+  bool useedges = 0;
+  for (ri = 0; ri < rules.Size(); ri++)
+    if (rules[ri]->GetNEd()) useedges = 1;
+
+  if (useedges)
+    {
+      ledges.SetSize (5 * lfacesplit);
+      
+      for (j = 0; j < lfacesplit; j++)
+	// if (fnearness[j] <= 5) 
+	  {
+	    const MiniElement2d & face = lfaces[j];
+	    int newp, oldp;
+	    
+	    newp = face[face.GetNP()-1];
+	    for (k = 0; k < face.GetNP(); k++)
+	      {
+		oldp = newp;
+		newp = face[k];
+		ledges.Set (INDEX_2::Sort(oldp, newp), 1);
+	      }
+	  }
+    }
+
+  NgProfiler::StopTimer (92);
+
+  NgProfiler::RegionTimer reg3(99);
+
+  pused.SetSize (lpoints.Size());
+  fused.SetSize (lfaces.Size());
+
+  found = 0;
+  minerr = tolfak * tolerance * tolerance;
+  minteterr = sloppy * tolerance;
+
+  if (testmode)
+    (*testout) << "cnt = " << cnt << " class = " << tolerance << endl;
+
+
+
+  // impossible, if no rule can be applied at any tolerance class
+  bool impossible = 1;
+
+
+  // check each rule:
+
+  for (ri = 1; ri <= rules.Size(); ri++)
+    { 
+      int base = (lfaces[0].GetNP() == 3) ? 100 : 200;
+      NgProfiler::RegionTimer regx1(base);
+      NgProfiler::RegionTimer regx(base+ri);
+
+      sprintf (problems.Elem(ri), "");
+
+      rule = rules.Get(ri);
+      
+      if (rule->GetNP(1) != lfaces[0].GetNP())
+	continue;
+
+      if (rule->GetQuality() > tolerance)
+	{
+	  if (rule->GetQuality() < 100) impossible = 0;
+
+	  if (testmode)
+	    sprintf (problems.Elem(ri), "Quality not ok");
+	  continue;
+	}
+      
+      if (testmode)
+	sprintf (problems.Elem(ri), "no mapping found");
+      
+      loktestmode = testmode || rule->TestFlag ('t') || tolerance > 5;
+
+      if (loktestmode)
+	(*testout) << "Rule " << ri << " = " << rule->Name() << endl;
+      
+      pmap.SetSize (rule->GetNP());
+      fmapi.SetSize (rule->GetNF());
+      fmapr.SetSize (rule->GetNF());
+      
+      fused = 0;
+      pused = 0;
+      pmap = 0;
+      fmapi = 0;
+      for (i = 1; i <= fmapr.Size(); i++)
+	fmapr.Set(i, rule->GetNP(i));
+      
+      fused[0] = 1;
+      fmapi[0] = 1;
+      fmapr[0] = rotind1;
+
+      
+      for (j = 1; j <= lfaces.Get(1).GetNP(); j++)
+	{
+	  locpi = lfaces[0].PNumMod (j+rotind1);
+	  pmap.Set (rule->GetPointNr (1, j), locpi);
+	  pused.Elem(locpi)++;
+	}
+
+      /*
+	map all faces
+	nfok .. first nfok-1 faces are mapped properly
+	*/
+
+      nfok = 2;
+      NgProfiler::RegionTimer regfa(300);
+      NgProfiler::RegionTimer regx2(base+50+ri);
+      while (nfok >= 2)
+	{
+	  
+	  if (nfok <= rule->GetNOldF())
+	    {
+	      // not all faces mapped
+
+	      ok = 0;
+	      locfi = fmapi.Get(nfok);
+	      locfr = fmapr.Get(nfok);
+
+	      int actfnp = rule->GetNP(nfok);
+
+	      while (!ok)
+		{
+		  locfr++;
+		  if (locfr == actfnp + 1)
+		    {
+		      locfr = 1;
+		      locfi++;
+		      if (locfi > lfacesplit) break;
+		    }
+		  
+		  
+		  if (fnearness.Get(locfi) > rule->GetFNearness (nfok) ||
+		      fused.Get(locfi) ||
+		      actfnp != lfaces.Get(locfi).GetNP() )
+		    {
+		      // face not feasible in any rotation
+
+		      locfr = actfnp;
+		    }
+		  else
+		    {
+		      
+		      ok = 1;
+		      
+		      locface = &lfaces.Get(locfi);
+
+		      
+		      // reference point already mapped differently ?
+		      for (j = 1; j <= actfnp && ok; j++)
+			{
+			  locpi = pmap.Get(rule->GetPointNr (nfok, j));
+			  
+			  if (locpi && locpi != locface->PNumMod(j+locfr))
+			    ok = 0;
+			}
+		      
+		      // local point already used or point outside tolerance ?
+		      for (j = 1; j <= actfnp && ok; j++)
+			{
+			  refpi = rule->GetPointNr (nfok, j);
+			  
+			  if (pmap.Get(refpi) == 0)
+			    {
+			      locpi = locface->PNumMod (j + locfr);
+
+			      if (pused.Get(locpi))
+				ok = 0;
+			      else
+				{
+				  const Point3d & lp = lpoints.Get(locpi);
+				  const Point3d & rp = rule->GetPoint(refpi);
+
+				  if ( Dist2 (lp, rp) * rule->PointDistFactor(refpi) > minerr)
+				    {
+				      impossible = 0;
+				      ok = 0;
+				    }
+				}
+			    }
+			}
+		    }
+		}
+	      
+	      
+	      if (ok)
+		{
+		  // map face nfok
+
+		  fmapi.Set (nfok, locfi);
+		  fmapr.Set (nfok, locfr);
+		  fused.Set (locfi, 1);
+		  
+		  for (j = 1; j <= rule->GetNP (nfok); j++)
+		    {
+		      locpi = locface->PNumMod(j+locfr);
+		      
+		      if (rule->GetPointNr (nfok, j) <= 3 &&
+			  pmap.Get(rule->GetPointNr(nfok, j)) != locpi)
+			(*testout) << "change face1 point, mark1" << endl;
+		      
+		      pmap.Set(rule->GetPointNr (nfok, j), locpi);
+		      pused.Elem(locpi)++;
+		    }
+		  
+		  nfok++;
+		}
+	      else
+		{
+		  // backtrack one face
+		  fmapi.Set (nfok, 0);
+		  fmapr.Set (nfok, rule->GetNP(nfok));
+		  nfok--;
+		  
+		  fused.Set (fmapi.Get(nfok), 0);
+		  for (j = 1; j <= rule->GetNP (nfok); j++)
+		    {
+		      refpi = rule->GetPointNr (nfok, j);
+		      pused.Elem(pmap.Get(refpi))--;
+		      
+		      if (pused.Get(pmap.Get(refpi)) == 0)
+			{
+			  pmap.Set(refpi, 0);
+			}
+		    }
+		}
+	    }
+	  
+	  else
+	    
+	    { 
+	      NgProfiler::RegionTimer regfb(301);
+
+	      // all faces are mapped
+	      // now map all isolated points:
+	      
+	      if (loktestmode)
+		{
+		  (*testout) << "Faces Ok" << endl;
+		  sprintf (problems.Elem(ri), "Faces Ok");
+		}
+	      
+	      npok = 1;
+	      incnpok = 1;
+	      
+	      pfixed.SetSize (pmap.Size());
+	      for (i = 1; i <= pmap.Size(); i++)
+		pfixed.Set(i, (pmap.Get(i) != 0) );
+	      
+	      while (npok >= 1)
+		{
+		  
+		  if (npok <= rule->GetNOldP())
+		    {
+		      
+		      if (pfixed.Get(npok))
+			
+			{
+			  if (incnpok)
+			    npok++;
+			  else
+			    npok--;
+			}
+		      
+		      else
+			
+			{
+			  locpi = pmap.Elem(npok);
+			  ok = 0;
+			  
+			  if (locpi)
+			    pused.Elem(locpi)--;
+			  
+			  while (!ok && locpi < lpoints.Size())
+			    {
+			      ok = 1;
+			      locpi++;
+			      
+			      if (pused.Get(locpi) || 
+				  pnearness.Get(locpi) > rule->GetPNearness(npok))
+				{
+				  ok = 0;
+				}
+			      else if (allowpoint.Get(locpi) != 2)
+				{
+				  ok = 0;
+				  if (allowpoint.Get(locpi) == 1)
+				    impossible = 0;
+				}
+			      else
+				{
+				  const Point3d & lp = lpoints.Get(locpi);
+				  const Point3d & rp = rule->GetPoint(npok);
+
+				  if ( Dist2 (lp, rp) * rule->PointDistFactor(npok) > minerr)
+				    {
+				      ok = 0;
+				      impossible = 0;
+				    }
+				}
+			    }
+			  
+			  
+			  if (ok)
+			    {
+			      pmap.Set (npok, locpi);
+			      
+			      if (npok <= 3)
+				(*testout) << "set face1 point, mark3" << endl;
+			      
+			      pused.Elem(locpi)++;
+			      npok++;
+			      incnpok = 1;
+			    }
+			  
+			  else
+			    
+			    {
+			      pmap.Set (npok, 0);
+			      
+			      if (npok <= 3)
+				(*testout) << "set face1 point, mark4" << endl;
+			      
+			      npok--;
+			      incnpok = 0;
+			    }
+			}
+		    }
+		  
+		  else
+		    
+		    {
+		      NgProfiler::RegionTimer regfa2(302);		      
+
+		      // all points are mapped
+		      
+		      if (loktestmode)
+			{
+			  (*testout) << "Mapping found!!: Rule " << rule->Name() << endl;
+			  for (i = 1; i <= pmap.Size(); i++)
+			    (*testout) << pmap.Get(i) << " ";
+			  (*testout) << endl;
+			  sprintf (problems.Elem(ri), "mapping found");
+			  (*testout) << rule->GetNP(1) << " = " << lfaces.Get(1).GetNP() << endl;
+			}
+		      
+		      ok = 1;
+		      
+		      
+		      // check mapedges:
+		      for (i = 1; i <= rule->GetNEd(); i++)
+			{
+			  INDEX_2 in2(pmap.Get(rule->GetEdge(i).i1),
+				      pmap.Get(rule->GetEdge(i).i2));
+			  in2.Sort();
+			  if (!ledges.Used (in2)) ok = 0;
+			}
+
+
+		      // check prism edges:
+		      for (i = 1; i <= rule->GetNE(); i++)
+			{
+			  const Element & el = rule->GetElement (i);
+			  if (el.GetType() == PRISM) 
+			    { 
+			      for (j = 1; j <= 3; j++)
+				{
+				  INDEX_2 in2(pmap.Get(el.PNum(j)),
+					      pmap.Get(el.PNum(j+3)));      
+				  in2.Sort();
+				  if (!connectedpairs.Used (in2)) ok = 0;
+				}
+			    }
+			  if (el.GetType() == PYRAMID) 
+			    { 
+			      if (loktestmode)
+				(*testout) << "map pyramid, rule = " << rule->Name() << endl;
+			      for (j = 1; j <= 2; j++)
+				{
+				  INDEX_2 in2;
+				  if (j == 1)
+				    {
+				      in2.I1() = pmap.Get(el.PNum(2));
+				      in2.I2() = pmap.Get(el.PNum(3));
+				    }
+				  else
+				    {
+				      in2.I1() = pmap.Get(el.PNum(1));
+				      in2.I2() = pmap.Get(el.PNum(4));
+				    }
+				  in2.Sort();
+				  if (!connectedpairs.Used (in2)) 
+				    {
+				      ok = 0;
+				      if (loktestmode)
+					(*testout) << "no pair" << endl;
+				    }
+				}
+			    }
+
+			}
+		      
+
+		      
+		      for (i = rule->GetNOldF() + 1; i <= rule->GetNF(); i++)
+			fmapi.Set(i, 0);
+		      
+
+		      if (ok)
+			{
+			  foundmap.Elem(ri)++;
+			}
+
+		      
+
+
+		      // deviation of existing points
+
+		      oldu.SetSize (3 * rule->GetNOldP());
+		      newu.SetSize (3 * (rule->GetNP() - rule->GetNOldP()));
+		      allp.SetSize (3 * rule->GetNP());
+		      
+		      for (i = 1; i <= rule->GetNOldP(); i++)
+			{
+			  const Point3d & lp = lpoints.Get(pmap.Get(i));
+			  const Point3d & rp = rule->GetPoint(i);
+			  oldu (3*i-3) = lp.X()-rp.X();
+                          oldu (3*i-2) = lp.Y()-rp.Y();
+			  oldu (3*i-1) = lp.Z()-rp.Z();
+			  
+			  allp (3*i-3) = lp.X();
+                          allp (3*i-2) = lp.Y();
+                          allp (3*i-1) = lp.Z();
+			}
+
+		      if (rule->GetNP() > rule->GetNOldP())
+			{
+			  newu.SetSize (rule->GetOldUToNewU().Height());
+			  rule->GetOldUToNewU().Mult (oldu, newu);
+			}
+
+		      //		      int idiff = 3 * (rule->GetNP()-rule->GetNOldP());
+		      int idiff = 3 * rule->GetNOldP();
+		      for (i = rule->GetNOldP()+1; i <= rule->GetNP(); i++)
+			{
+			  const Point3d & rp = rule->GetPoint(i);
+			  allp (3*i-3) = rp.X() + newu(3*i-3 - idiff);
+                          allp (3*i-2) = rp.Y() + newu(3*i-2 - idiff);
+                          allp (3*i-1) = rp.Z() + newu(3*i-1 - idiff);
+			}
+		      
+		      rule->SetFreeZoneTransformation (allp, 
+						       tolerance + int(sloppy));
+
+		      if (!rule->ConvexFreeZone())
+			{
+			  ok = 0;
+			  sprintf (problems.Elem(ri), "Freezone not convex");
+
+			  if (loktestmode)
+			    (*testout) << "Freezone not convex" << endl;
+			}
+
+		      if (loktestmode)
+			{
+			  const Array<Point3d> & fz = rule->GetTransFreeZone();
+			  (*testout) << "Freezone: " << endl;
+			  for (i = 1; i <= fz.Size(); i++)
+			    (*testout) << fz.Get(i) << endl;
+			}
+		      
+
+		      // check freezone:
+		      
+		      for (i = 1; i <= lpoints.Size(); i++)
+			{
+			  if ( !pused.Get(i) )
+			    {
+			      const Point3d & lp = lpoints.Get(i);
+
+			      if (rule->fzbox.IsIn (lp))
+				{
+				  if (rule->IsInFreeZone(lp))
+				    {
+				      if (loktestmode)
+					{
+					  (*testout) << "Point " << i 
+						     << " in Freezone" << endl;
+					  sprintf (problems.Elem(ri), 
+						   "locpoint %d in Freezone", i);
+					}
+				      ok = 0;
+				      break;
+				    }
+				}
+			    }
+			}
+
+		      for (i = 1; i <= lfaces.Size() && ok; i++)
+			{
+			  static Array<int> lpi(4);
+
+			  if (!fused.Get(i))
+			    { 
+			      int triin;
+			      const MiniElement2d & lfacei = lfaces.Get(i);
+
+			      if (!triboxes.Elem(i).Intersect (rule->fzbox))
+				triin = 0;
+			      else
+				{
+				  int li, lj;
+				  for (li = 1; li <= lfacei.GetNP(); li++)
+				    {
+				      int lpii = 0;
+				      int pi = lfacei.PNum(li);
+				      for (lj = 1; lj <= rule->GetNOldP(); lj++)
+					if (pmap.Get(lj) == pi)
+					  lpii = lj;
+				      lpi.Elem(li) = lpii;
+				    }
+
+
+				  if (lfacei.GetNP() == 3)
+				    {
+				      triin = rule->IsTriangleInFreeZone 
+					(
+					 lpoints.Get(lfacei.PNum(1)),
+					 lpoints.Get(lfacei.PNum(2)),
+					 lpoints.Get(lfacei.PNum(3)), lpi, 1
+					 );
+				    }
+				  else
+				    {
+				      triin = rule->IsQuadInFreeZone 
+					(
+					 lpoints.Get(lfacei.PNum(1)),
+					 lpoints.Get(lfacei.PNum(2)),
+					 lpoints.Get(lfacei.PNum(3)), 
+					 lpoints.Get(lfacei.PNum(4)), 
+					 lpi, 1
+					 );
+				    }
+				}
+
+
+			      if (triin == -1)
+				{
+				  ok = 0;
+				}
+			      
+			      if (triin == 1)
+				{
+#ifdef TEST_JS
+				  ok = 0;
+
+				  if (loktestmode)
+				    {
+				      (*testout) << "El with " << lfaces.Get(i).GetNP() << " points in freezone: "
+						 << lfaces.Get(i).PNum(1) << " - " 
+						 << lfaces.Get(i).PNum(2) << " - "
+						 << lfaces.Get(i).PNum(3) << " - "
+						 << lfaces.Get(i).PNum(4) << endl;
+				      for (int lj = 1; lj <= lfaces.Get(i).GetNP(); lj++)
+					(*testout) << lpoints.Get(lfaces.Get(i).PNum(lj)) << " ";
+
+				      (*testout) << endl;
+
+				      sprintf (problems.Elem(ri), "triangle (%d, %d, %d) in Freezone",
+					       lfaces.Get(i).PNum(1), lfaces.Get(i).PNum(2),
+					       lfaces.Get(i).PNum(3));
+				    }
+#else
+				  if (loktestmode)
+				    {
+				      if (lfacei.GetNP() == 3)
+					{
+					  (*testout) << "Triangle in freezone: "
+						     << lfacei.PNum(1) << " - " 
+						     << lfacei.PNum(2) << " - "
+						     << lfacei.PNum(3) 
+						     << ", or "
+						     << lpoints.Get(lfacei.PNum(1)) << " - " 
+						     << lpoints.Get(lfacei.PNum(2)) << " - "
+						     << lpoints.Get(lfacei.PNum(3)) 
+						     << endl;
+					  (*testout) << "lpi = " << lpi.Get(1) << ", " 
+						     << lpi.Get(2) << ", " << lpi.Get(3) << endl;
+					}
+				      else
+					  (*testout) << "Quad in freezone: "
+						     << lfacei.PNum(1) << " - " 
+						     << lfacei.PNum(2) << " - "
+						     << lfacei.PNum(3) << " - "
+						     << lfacei.PNum(4) 
+						     << ", or "
+						     << lpoints.Get(lfacei.PNum(1)) << " - " 
+						     << lpoints.Get(lfacei.PNum(2)) << " - "
+						     << lpoints.Get(lfacei.PNum(3)) << " - "
+						     << lpoints.Get(lfacei.PNum(4)) 
+						     << endl;
+
+				      sprintf (problems.Elem(ri), "triangle (%d, %d, %d) in Freezone",
+					       int(lfaces.Get(i).PNum(1)), 
+					       int(lfaces.Get(i).PNum(2)),
+					       int(lfaces.Get(i).PNum(3)));
+				    }	
+
+				  hc = 0;
+				  for (k = rule->GetNOldF() + 1; k <= rule->GetNF(); k++)
+				    {
+				      if (rule->GetPointNr(k, 1) <= rule->GetNOldP() &&
+					  rule->GetPointNr(k, 2) <= rule->GetNOldP() &&
+					  rule->GetPointNr(k, 3) <= rule->GetNOldP())
+					{
+					  for (j = 1; j <= 3; j++)
+					    if (lfaces.Get(i).PNumMod(j  ) == pmap.Get(rule->GetPointNr(k, 1)) &&
+						lfaces.Get(i).PNumMod(j+1) == pmap.Get(rule->GetPointNr(k, 3)) &&
+						lfaces.Get(i).PNumMod(j+2) == pmap.Get(rule->GetPointNr(k, 2)))
+					      {
+						fmapi.Elem(k) = i;
+						hc = 1;
+
+						
+ // 						(*testout) << "found from other side: " 
+//  							   << rule->Name() 
+//  							   << " ( " << pmap.Get (rule->GetPointNr(k, 1))
+//  							   << " - " << pmap.Get (rule->GetPointNr(k, 2))
+//  							   << " - " << pmap.Get (rule->GetPointNr(k, 3)) << " ) "
+//  							   << endl;
+
+						strcpy (problems.Elem(ri), "other");
+					      }
+					}
+				    }
+				  
+				  if (!hc)
+				    {
+				      if (loktestmode)
+					{
+					  (*testout) << "Triangle in freezone: "
+						     << lfaces.Get(i).PNum(1) << " - " 
+						     << lfaces.Get(i).PNum(2) << " - "
+						     << lfaces.Get(i).PNum(3) << endl;
+
+					  sprintf (problems.Elem(ri), "triangle (%d, %d, %d) in Freezone",
+						   int (lfaces.Get(i).PNum(1)), 
+						   int (lfaces.Get(i).PNum(2)),
+						   int (lfaces.Get(i).PNum(3)));
+					}
+				      ok = 0;
+				    }
+#endif
+				}
+			    }
+			   
+			}
+
+		      
+		      if (ok)
+			{
+			  err = 0;
+			  for (i = 1; i <= rule->GetNOldP(); i++)
+			    {
+			      hf = rule->CalcPointDist (i, lpoints.Get(pmap.Get(i)));
+			      if (hf > err) err = hf;
+			    }
+			  
+			  
+			  if (loktestmode)
+			    {
+			      (*testout) << "Rule ok" << endl;
+			      sprintf (problems.Elem(ri), "Rule ok, err = %f", err);
+			    }
+
+
+			  //			  newu = rule->GetOldUToNewU() * oldu;
+
+			  // set new points:
+			  
+			  oldnp = rule->GetNOldP();
+			  noldlp = lpoints.Size();
+			  noldlf = lfaces.Size();
+			  
+			  
+			  for (i = oldnp + 1; i <= rule->GetNP(); i++)
+			    {
+			      np = rule->GetPoint(i);
+			      np.X() += newu (3 * (i-oldnp) - 3);
+			      np.Y() += newu (3 * (i-oldnp) - 2);
+			      np.Z() += newu (3 * (i-oldnp) - 1);
+			      
+			      pmap.Elem(i) = lpoints.Append (np);
+			    }
+			  
+			  // Set new Faces:
+			  
+			  for (i = rule->GetNOldF() + 1; i <= rule->GetNF(); i++)
+			    if (!fmapi.Get(i))
+			      {
+				MiniElement2d nface(rule->GetNP(i));
+				for (j = 1; j <= nface.GetNP(); j++)
+				  nface.PNum(j) = pmap.Get(rule->GetPointNr (i, j));
+				
+				lfaces.Append (nface);
+			      }
+			  
+			  
+			  // Delete old Faces:
+
+			  for (i = 1; i <= rule->GetNDelF(); i++)
+			    delfaces.Append (fmapi.Get(rule->GetDelFace(i)));
+			  for (i = rule->GetNOldF()+1; i <= rule->GetNF(); i++)
+			    if (fmapi.Get(i))
+			      {
+				delfaces.Append (fmapi.Get(i));
+				fmapi.Elem(i) = 0;
+			      }
+			  
+
+			  // check orientation
+			  for (i = 1; i <= rule->GetNO() && ok; i++)
+			    {
+			      const fourint * fouri;
+			      
+			      fouri = &rule->GetOrientation(i);
+			      Vec3d v1 (lpoints.Get(pmap.Get(fouri->i1)), 
+					lpoints.Get(pmap.Get(fouri->i2)));
+			      Vec3d v2 (lpoints.Get(pmap.Get(fouri->i1)), 
+					lpoints.Get(pmap.Get(fouri->i3)));
+			      Vec3d v3 (lpoints.Get(pmap.Get(fouri->i1)), 
+					lpoints.Get(pmap.Get(fouri->i4)));
+
+			      Vec3d n;
+			      Cross (v1, v2, n);
+			      //if (n * v3 >= -1e-7*n.Length()*v3.Length()) // OR -1e-7???
+			      if (n * v3 >= -1e-9)
+				{
+				  if (loktestmode)
+				    {
+				      sprintf (problems.Elem(ri), "Orientation wrong");
+				      (*testout) << "Orientation wrong ("<< n*v3 << ")" << endl;
+				    }
+				  ok = 0;
+				}
+			    }
+
+			  
+
+			  // new points in free-zone ?
+			  for (i = rule->GetNOldP() + 1; i <= rule->GetNP() && ok; i++)
+			    if (!rule->IsInFreeZone (lpoints.Get(pmap.Get(i))))
+			      {
+				if (loktestmode)
+				  {
+				    (*testout) << "Newpoint " << lpoints.Get(pmap.Get(i))
+					       << " outside convex hull" << endl;
+				    sprintf (problems.Elem(ri), "newpoint outside convex hull");
+				  }
+				ok = 0;
+				
+			      }
+			  
+			  // insert new elements
+			  
+			  for (i = 1; i <= rule->GetNE(); i++)
+			    {
+			      elements.Append (rule->GetElement(i));
+			      for (j = 1; j <= elements.Get(i).NP(); j++)
+				elements.Elem(i).PNum(j) = pmap.Get(elements.Get(i).PNum(j));
+			    }
+			  
+
+			  // Calculate Element badness
+			  
+			  teterr = 0;
+			  for (i = 1; i <= elements.Size(); i++)
+			    {
+			      hf = CalcElementBadness (lpoints, elements.Get(i));
+			      if (hf > teterr) teterr = hf;
+			    }
+
+			  /*
+			    // keine gute Erfahrung am 25.1.2000, js
+			  if (ok && teterr < 100 &&
+			      (rule->TestFlag('b') || tolerance > 10) )
+			    {
+			      (*mycout) << "Reset teterr " 
+				   << rule->Name() 
+				   << " err = " << teterr 
+				   << endl;
+			      teterr = 1;
+			    }
+			  */
+
+			  // compare edgelength
+			  if (rule->TestFlag('l'))
+			    {
+			      double oldlen = 0;
+			      double newlen = 0;
+
+			      for (i = 1; i <= rule->GetNDelF(); i++)
+				{
+				  const Element2d & face = 
+				    rule->GetFace (rule->GetDelFace(i));
+				  for (j = 1; j <= 3; j++)
+				    {
+				      const Point3d & p1 =
+					lpoints.Get(pmap.Get(face.PNumMod(j)));
+				      const Point3d & p2 =
+					lpoints.Get(pmap.Get(face.PNumMod(j+1)));
+				      oldlen += Dist(p1, p2);
+				    }
+				}
+
+			      for (i = rule->GetNOldF()+1; i <= rule->GetNF(); i++)
+				{
+				  const Element2d & face = rule->GetFace (i);
+				  for (j = 1; j <= 3; j++)
+				    {
+				      const Point3d & p1 =
+					lpoints.Get(pmap.Get(face.PNumMod(j)));
+				      const Point3d & p2 =
+					lpoints.Get(pmap.Get(face.PNumMod(j+1)));
+				      newlen += Dist(p1, p2);
+				    }
+				}
+
+			      if (oldlen < newlen) 
+				{
+				  ok = 0;
+				  if (loktestmode)
+				    sprintf (problems.Elem(ri), "oldlen < newlen");
+				}
+			    }
+			  
+
+			  if (loktestmode)
+			    (*testout) << "ok = " << int(ok) 
+				       << "teterr = " << teterr 
+				       << "minteterr = " << minteterr << endl;
+
+
+			  if (ok && teterr < tolerance)
+			    {
+			      canuse.Elem(ri) ++;
+			      /*
+			      (*testout) << "can use rule " << rule->Name() 
+					 << ", err = " << teterr << endl;
+			      for (i = 1; i <= pmap.Size(); i++)
+				(*testout) << pmap.Get(i) << " ";
+			      (*testout) << endl;
+			      */
+
+			      if (strcmp (problems.Elem(ri), "other") == 0)
+				{
+				  if (teterr < minother)
+				    minother = teterr;
+				}
+			      else
+				{
+				  if (teterr < minwithoutother)
+				    minwithoutother = teterr;
+				}
+			    }
+
+
+			  if (teterr > minteterr) impossible = 0;
+
+			  if (ok && teterr < minteterr)
+			    {
+
+			      if (loktestmode)
+				(*testout) << "use rule" << endl;
+
+			      found = ri;
+			      minteterr = teterr;
+			      
+			      if (testmode)
+				{
+				  for (i = 1; i <= rule->GetNOldP(); i++)
+				    {
+				      (*testout) << "P" << i << ": Ref: "
+						 << rule->GetPoint (i) << "  is: "
+						 << lpoints.Get(pmap.Get(i)) << endl;
+				    }
+				}
+			      
+			      tempnewpoints.SetSize (0);
+			      for (i = noldlp+1; i <= lpoints.Size(); i++)
+				tempnewpoints.Append (lpoints.Get(i));
+			      
+			      tempnewfaces.SetSize (0);
+			      for (i = noldlf+1; i <= lfaces.Size(); i++)
+				tempnewfaces.Append (lfaces.Get(i));
+
+			      tempdelfaces.SetSize (0);
+			      for (i = 1; i <= delfaces.Size(); i++)
+				tempdelfaces.Append (delfaces.Get(i));
+			      
+			      tempelements.SetSize (0);
+			      for (i = 1; i <= elements.Size(); i++)
+				tempelements.Append (elements.Get(i));
+			    }
+			  
+
+			  lpoints.SetSize (noldlp);
+			  lfaces.SetSize (noldlf);
+			  delfaces.SetSize (0);
+			  elements.SetSize (0);
+			}
+		      
+		      npok = rule->GetNOldP();
+		      incnpok = 0;
+		    }
+		}
+	      
+	      nfok = rule->GetNOldF();
+	      
+	      for (j = 1; j <= rule->GetNP (nfok); j++)
+		{
+		  refpi = rule->GetPointNr (nfok, j);
+		  pused.Elem(pmap.Get(refpi))--;
+		  
+		  if (pused.Get(pmap.Get(refpi)) == 0)
+		    {
+		      pmap.Set(refpi, 0);
+		    }
+		}
+	      
+	    }
+	}
+      if (loktestmode)
+	(*testout) << "end rule" << endl;
+    }
+
+  if (found)
+    {
+      for (i = 1; i <= tempnewpoints.Size(); i++)
+	lpoints.Append (tempnewpoints.Get(i));
+      for (i = 1; i <= tempnewfaces.Size(); i++)
+	if (tempnewfaces.Get(i).PNum(1))
+	  lfaces.Append (tempnewfaces.Get(i));
+      for (i = 1; i <= tempdelfaces.Size(); i++)
+	delfaces.Append (tempdelfaces.Get(i));
+      for (i = 1; i <= tempelements.Size(); i++)
+	elements.Append (tempelements.Get(i));
+    }
+  
+  retminerr = minerr;
+
+
+  if (impossible && found == 0)
+    return -1;
+
+  return found;
+}
+}
diff --git a/contrib/Netgen/libsrc/meshing/ruler3.hpp b/contrib/Netgen/libsrc/meshing/ruler3.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fcbf8f59900b9ab9e4d7a78faacb27cadde3ff2b
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/ruler3.hpp
@@ -0,0 +1,210 @@
+#ifndef FILE_RULER3
+#define FILE_RULER3
+
+
+/**
+  3D element generation rule.
+ */
+class vnetrule
+{
+private:
+  /// rule is applicable for quality classes above this value
+  int quality;
+  /// name of rule
+  char * name;
+  /// point coordinates in reference position
+  Array<Point3d> points;
+  /// old and new faces in reference numbering
+  Array<Element2d> faces;
+  /// additional edges of rule
+  Array<twoint> edges;
+
+  /// points of freezone in reference coordinates
+  Array<Point3d> freezone;
+  /// points of freezone in reference coordinates if tolcalss to infty
+  Array<Point3d> freezonelimit;
+  /// point index, if point equal to mappoint, otherwise 0
+  Array<int> freezonepi;
+  /// faces of each convex part of freezone
+  Array<Array<threeint>*> freefaces;
+  /// set of points of each convex part of freezone
+  Array<Array<int>*> freesets;
+  /// points of transformed freezone
+  Array<Point3d> transfreezone;
+  /// edges of each convex part of freezone
+  Array<Array<twoint>*> freeedges;
+
+  /// face numbers to be deleted
+  Array<int> delfaces;
+  /// elements to be generated
+  Array<Element> elements;
+  /// tolerances for points and faces (used ??)
+  Array<double> tolerances, linetolerances;
+  /// transformation matrix 
+  DenseMatrix oldutonewu;
+  /// transformation matrix: deviation old point to dev. freezone
+  DenseMatrix * oldutofreezone;
+  /** transformation matrix: deviation old point to dev. freezone, 
+    quality class to infinity */
+  DenseMatrix * oldutofreezonelimit;
+
+  // can be deleted:
+  // BaseMatrix *outf, *outfl;
+
+  /**
+    a point is outside of convex part of freezone, 
+    iff mat * (point, 1) >= 0 for each component (correct ?)
+    */
+  Array<DenseMatrix*> freefaceinequ;
+  /// 
+  Array<fourint> orientations;
+  /**
+    flags specified in rule-description file:
+    t .. test rule
+    */
+  Array<char> flags;
+
+  /**
+    topological distance of face to base element
+    non-connected: > 100  (??) 
+    */
+  Array<int> fnearness;
+  Array<int> pnearness;
+  int maxpnearness;
+
+  /// number of old points in rule
+  int noldp;
+  /// number of new poitns in rule
+  int noldf;
+  /// box containing free-zone
+public:  
+  // double fzminx, fzmaxx, fzminy, fzmaxy, fzminz, fzmaxz;
+  Box3d fzbox;
+
+public:
+  
+  ///
+  vnetrule ();
+  ///
+  ~vnetrule ();
+  ///
+  int GetNP () const { return points.Size(); }
+  ///
+  int GetNF () const { return faces.Size(); }
+  ///
+  int GetNE () const { return elements.Size(); }
+  ///
+  int GetNO () const { return orientations.Size(); }
+  ///
+  int GetNEd () const { return edges.Size(); }
+  ///
+  int GetNOldP () const { return noldp; }
+  ///
+  int GetNOldF () const { return noldf; }
+  ///
+  int GetNDelF () const { return delfaces.Size(); }
+  ///
+  int GetQuality () const { return quality; }
+  ///
+  int GetFNearness (int fi) const { return fnearness.Get(fi); }
+  ///
+  int GetPNearness (int pi) const { return pnearness.Get(pi); }
+  ///
+  int GetMaxPNearness () const { return maxpnearness; }
+
+
+  ///
+  const Point3d & GetPoint (int i) const { return points.Get(i); }
+  ///
+  const Element2d & GetFace (int i) const { return faces.Get(i); }
+  ///
+  const Element & GetElement (int i) const { return elements.Get(i); }
+  ///
+  const twoint & GetEdge (int i) const { return edges.Get(i); }
+  ///
+  int GetDelFace (int i) const { return delfaces.Get(i); }
+  ///
+  int IsDelFace (int fn) const;
+  
+  ///
+  float CalcPointDist (int pi, const Point3d & p) const;
+  ///
+  double PointDistFactor (int pi) const
+    {
+      return tolerances.Get(pi);
+    }
+  ///
+  void SetFreeZoneTransformation (const Vector & allp,
+				  int tolclass);
+  ///
+  int IsInFreeZone (const Point3d & p) const;
+  /**
+    0 not in free-zone
+    1 in free-zone
+    -1 maybe 
+   */
+  int IsTriangleInFreeZone (const Point3d & p1, const Point3d & p2,
+                            const Point3d & p3, const Array<int> & pi, int newone);
+  ///
+  int IsQuadInFreeZone (const Point3d & p1, const Point3d & p2,
+			const Point3d & p3, const Point3d & p4,
+			const Array<int> & pi, int newone);
+  ///
+  int IsTriangleInFreeSet (const Point3d & p1, const Point3d & p2,
+                           const Point3d & p3, int fs, const Array<int> & pi, int newone);
+
+  ///
+  int IsQuadInFreeSet (const Point3d & p1, const Point3d & p2,
+		       const Point3d & p3, const Point3d & p4,
+		       int fs, const Array<int> & pi, int newone);
+  
+  ///
+  int ConvexFreeZone () const;
+  
+  /// if t1 and t2 are neighbourtriangles, NTP returns the opposite Point of t1 in t2
+  int NeighbourTrianglePoint (const threeint & t1, const threeint & t2) const;
+  ///
+  const Point3d & GetTransFreeZone (int i) { return transfreezone.Get(i); }
+
+  ///
+  int GetNP (int fn) const
+  { return faces.Get(fn).GetNP(); }
+  ///
+  int GetPointNr (int fn, int endp) const
+  { return faces.Get(fn).PNum(endp); }
+  ///
+  int GetPointNrMod (int fn, int endp) const
+  { return faces.Get(fn).PNumMod(endp); }
+  ///
+  const fourint & GetOrientation (int i) { return orientations.Get(i); }
+
+  ///
+  int TestFlag (char flag) const;
+
+  ///
+  const DenseMatrix & GetOldUToNewU () const { return oldutonewu; }
+  //
+  //  const DenseMatrix & GetOldUToFreeZone () const { return oldutofreezone; }
+  //
+  //  const DenseMatrix & GetOldUToFreeZoneLimit () const 
+  //    { return oldutofreezonelimit; }
+  ///
+  const char * Name () const { return name; }
+  ///
+  void LoadRule (istream & ist);
+
+  ///
+  const Array<Point3d> & GetTransFreeZone () { return transfreezone; }
+  ///
+  int TestOk () const;
+
+  ///
+  friend void TestRules ();
+  ///
+  //  friend void Plot3DRule (const ROT3D & r, char key);
+};
+
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/meshing/secondorder.cpp b/contrib/Netgen/libsrc/meshing/secondorder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5b5b5d7f55ba1001e43a96ef3dc520e044cc05b5
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/secondorder.cpp
@@ -0,0 +1,490 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+
+  void Refinement :: MakeSecondOrder (Mesh & mesh) const
+  {
+    const_cast<Refinement&> (*this).MakeSecondOrder(mesh);
+  }
+
+  
+  void Refinement :: MakeSecondOrder (Mesh & mesh)
+  {
+    int nseg, nse, ne;
+
+    mesh.ComputeNVertices();
+    mesh.SetNP(mesh.GetNV());
+  
+    INDEX_2_HASHTABLE<int> between(mesh.GetNP() + 5);
+
+
+    bool thinlayers = 0;
+    for (ElementIndex ei = 0; ei < mesh.GetNE(); ei++)
+      if (mesh[ei].GetType() == PRISM ||
+	  mesh[ei].GetType() == PRISM12)
+	thinlayers = 1;
+    
+
+    nseg = mesh.GetNSeg();
+    for (SegmentIndex si = 0; si < nseg; si++)
+      {
+	Segment & el = mesh.LineSegment(si);
+
+	INDEX_2 i2 = INDEX_2::Sort (el[0], el[1]);
+
+	if (between.Used(i2))
+	  el[2] = between.Get(i2);
+	else
+	  {
+	    Point<3> pb;
+	    EdgePointGeomInfo ngi;
+            PointBetween (mesh.Point (el[0]),
+                          mesh.Point (el[1]), 0.5,
+			  el.surfnr1, el.surfnr2,
+			  el.epgeominfo[0], el.epgeominfo[1],
+			  pb, ngi);
+	  
+	    el[2] = mesh.AddPoint (pb);
+	    between.Set (i2, el[2]);
+	  }
+      }
+
+    // refine surface elements
+    nse = mesh.GetNSE();
+    for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+      {
+	int j;
+	const Element2d & el = mesh.SurfaceElement(sei);
+
+	int onp(0);
+      
+	Element2d newel;
+	newel.SetIndex (el.GetIndex());
+
+	static int betw_trig[3][3] =
+	  { { 1, 2, 3 },
+	    { 0, 2, 4 },
+	    { 0, 1, 5 } };
+	static int betw_quad6[2][3] =
+	  { { 0, 1, 4 },
+	    { 3, 2, 5 } };
+	static int betw_quad8[4][3] =
+	  { { 0, 1, 4 },
+	    { 3, 2, 5 },
+	    { 0, 3, 6 },
+	    { 1, 2, 7 } };
+	int (*betw)[3] = NULL;
+      
+	switch (el.GetType())
+	  {
+	  case TRIG:
+	  case TRIG6:
+	    {
+	      betw = betw_trig;
+	      newel.SetType (TRIG6);
+	      onp = 3;
+	      break;
+	    }
+	  case QUAD:
+	  case QUAD6: 
+	  case QUAD8:
+	    {
+	      if (thinlayers)
+		{
+		  betw = betw_quad6;
+		  newel.SetType (QUAD6);
+		}
+	      else
+		{
+		  betw = betw_quad8;
+		  newel.SetType (QUAD8);
+		}
+	      onp = 4;
+	      break;
+	    }
+	  default:
+	    PrintSysError ("Unhandled element in secondorder:", int(el.GetType()));
+	  }
+
+	for (j = 0; j < onp; j++)
+	  newel[j] = el[j];
+      
+	int nnp = newel.GetNP();
+	for (j = 0; j < nnp-onp; j++)
+	  {
+	    int pi1 = newel[betw[j][0]];
+	    int pi2 = newel[betw[j][1]];
+	  
+	    INDEX_2 i2 = INDEX_2::Sort (pi1, pi2);
+	  
+	    if (between.Used(i2))
+	      newel[onp+j] = between.Get(i2);
+	    else
+	      {
+		Point<3> pb;
+		PointGeomInfo newgi;
+		PointBetween (mesh.Point (pi1),
+			      mesh.Point (pi2), 0.5, 
+			      mesh.GetFaceDescriptor(el.GetIndex ()).SurfNr(),
+			      el.GeomInfoPi (betw[j][0]+1),
+			      el.GeomInfoPi (betw[j][1]+1),
+			      pb, newgi);
+
+		newel[onp+j] = mesh.AddPoint (pb);
+		between.Set (i2, newel[onp+j]);
+	      }
+	  }
+      
+	mesh.SurfaceElement(sei) = newel;
+      }
+
+ 
+    //    int i, j;
+
+
+
+    // refine volume elements
+    ne = mesh.GetNE();
+    for (int i = 1; i <= ne; i++)
+      {
+	const Element & el = mesh.VolumeElement(i);
+	int onp(0);
+
+	Element newel;
+	newel.SetIndex (el.GetIndex());
+
+	static int betw_tet[6][3] =
+	  { { 0, 1, 4 },
+	    { 0, 2, 5 },
+	    { 0, 3, 6 },
+	    { 1, 2, 7 },
+	    { 1, 3, 8 },
+	    { 2, 3, 9 } };
+	static int betw_prism[6][3] =
+	  {
+	    { 0, 2, 6 },
+	    { 0, 1, 7 },
+	    { 1, 2, 8 },
+	    { 3, 5, 9 },
+	    { 3, 4, 10 },
+	    { 4, 5, 11 },
+	  };
+	int (*betw)[3] = NULL;
+
+	switch (el.GetType())
+	  {
+	  case TET:
+	  case TET10:
+	    {
+	      betw = betw_tet;
+	      newel.SetType (TET10);
+	      onp = 4;
+	      break;
+	    }
+	  case PRISM:
+	  case PRISM12:
+	    {
+	      betw = betw_prism;
+	      newel.SetType (PRISM12);
+	      onp = 6;
+	      break;
+	    }
+	  default:
+	    PrintSysError ("MakeSecondOrder, illegal vol type ", el.GetType());
+	  }
+
+
+	for (int j = 1; j <= onp; j++)
+	  newel.PNum(j) = el.PNum(j);
+	int nnp = newel.GetNP();
+
+	for (int j = 0; j < nnp-onp; j++)
+	  {
+	    INDEX_2 i2(newel[betw[j][0]],
+		       newel[betw[j][1]]);
+	    i2.Sort();
+	  
+	    if (between.Used(i2))
+	      newel.PNum(onp+1+j) = between.Get(i2);
+	    else
+	      {
+		newel.PNum(onp+1+j) = mesh.AddPoint
+		  (Center (mesh.Point(i2.I1()),
+			   mesh.Point(i2.I2())));
+		between.Set (i2, newel.PNum(onp+1+j));
+	      }
+	  }
+
+	mesh.VolumeElement (i) = newel;
+      }
+
+
+    // makes problems after linear mesh refinement, since
+    // 2nd order identifications are not removed
+    // update identification tables
+    for (int i = 1; i <= mesh.GetIdentifications().GetMaxNr(); i++)
+      {
+	Array<int,PointIndex::BASE> identmap;
+	mesh.GetIdentifications().GetMap (i, identmap);
+
+	for (INDEX_2_HASHTABLE<int>::Iterator it = between.Begin();
+	     it != between.End(); it++)
+	  {
+	      INDEX_2 i2;
+	      int newpi;
+	      between.GetData (it, i2, newpi);
+	      INDEX_2 oi2(identmap.Get(i2.I1()),
+			  identmap.Get(i2.I2()));
+	      oi2.Sort();
+	      if (between.Used (oi2))
+		{
+		  int onewpi = between.Get(oi2);
+		  mesh.GetIdentifications().Add (newpi, onewpi, i);
+		}
+	  }
+
+	/*
+	for (int j = 1; j <= between.GetNBags(); j++)
+	  for (int k = 1; k <= between.GetBagSize(j); k++)
+	    {
+	      INDEX_2 i2;
+	      int newpi;
+	      between.GetData (j, k, i2, newpi);
+	      INDEX_2 oi2(identmap.Get(i2.I1()),
+			  identmap.Get(i2.I2()));
+	      oi2.Sort();
+	      if (between.Used (oi2))
+		{
+		  int onewpi = between.Get(oi2);
+		  mesh.GetIdentifications().Add (newpi, onewpi, i);
+		}
+	    }
+	*/
+      }
+
+
+    //  mesh.mglevels++;
+    int oldsize = mesh.mlbetweennodes.Size();
+    mesh.mlbetweennodes.SetSize(mesh.GetNP());
+    for (int i = oldsize; i < mesh.GetNP(); i++)
+      mesh.mlbetweennodes[i] = INDEX_2(0,0);
+
+    /*
+    for (i = 1; i <= between.GetNBags(); i++)
+      for (j = 1; j <= between.GetBagSize(i); j++)
+	{
+	  INDEX_2 oldp;
+	  int newp;
+	  between.GetData (i, j, oldp, newp);
+	  mesh.mlbetweennodes.Elem(newp) = oldp;
+	}
+    */
+
+    for (INDEX_2_HASHTABLE<int>::Iterator it = between.Begin();
+	 it != between.End(); it++)
+      {
+	mesh.mlbetweennodes[between.GetData (it)] = between.GetHash(it);
+      }
+
+    mesh.ComputeNVertices();
+    mesh.RebuildSurfaceElementLists();
+    //  ValidateSecondOrder (mesh);
+  }
+
+
+  void Refinement :: ValidateSecondOrder (Mesh & mesh)
+  {
+    PrintMessage (3, "Validate mesh");
+    int np = mesh.GetNP();
+    int ne = mesh.GetNE();
+    // int i, j;
+    Array<INDEX_2> parents(np);
+  
+    for (int i = 1; i <= np; i++)
+      parents.Elem(i) = INDEX_2(0,0);
+
+    for (int i = 1; i <= ne; i++)
+      {
+	const Element & el = mesh.VolumeElement(i);
+	if (el.GetType() == TET10)
+	  {
+	    static int betweentab[6][3] =
+	      { { 1, 2, 5 },
+		{ 1, 3, 6 },
+		{ 1, 4, 7 },
+		{ 2, 3, 8 },
+		{ 2, 4, 9 },
+		{ 3, 4, 10 } };
+	    for (int j = 0; j < 6; j++)
+	      {
+		int f1 = el.PNum (betweentab[j][0]);
+		int f2 = el.PNum (betweentab[j][1]);
+		int son = el.PNum (betweentab[j][2]);
+		parents.Elem(son).I1() = f1;
+		parents.Elem(son).I2() = f2;
+	      }
+	  }
+      }
+
+    ValidateRefinedMesh (mesh, parents);
+  }
+
+
+  void Refinement ::
+  ValidateRefinedMesh (Mesh & mesh, 
+		       Array<INDEX_2> & parents)
+  {
+    // int i, j, k;
+  
+    // homotopy method
+
+    int ne = mesh.GetNE();
+
+    int cnttrials = 100;
+    int wrongels = 0;
+    for (int i = 1; i <= ne; i++)
+      if (mesh.VolumeElement(i).CalcJacobianBadness (mesh.Points()) > 1e10)
+	{
+	  wrongels++;
+	  mesh.VolumeElement(i).flags.badel = 1;
+	}
+      else
+	mesh.VolumeElement(i).flags.badel = 0;
+
+    double facok = 0;
+    double factry;
+
+    BitArray illegalels(ne);
+    illegalels.Clear();
+
+      
+    if (wrongels)
+      {
+	cout << "WARNING: " << wrongels << " illegal element(s) found" << endl;
+
+	int np = mesh.GetNP();
+	Array<Point<3> > should(np);
+	Array<Point<3> > can(np);
+
+	for (int i = 1; i <= np; i++)
+	  {
+	    should.Elem(i) = can.Elem(i) = mesh.Point(i);
+	  }
+
+	for (int i = 1; i <= parents.Size(); i++)
+	  {
+	    if (parents.Get(i).I1())
+	      can.Elem(i) = Center (can.Elem(parents.Get(i).I1()),
+				    can.Elem(parents.Get(i).I2()));
+	  }
+
+	BitArray boundp(np);
+	boundp.Clear();
+	for (int i = 1; i <= mesh.GetNSE(); i++)
+	  {
+	    const Element2d & sel = mesh.SurfaceElement(i);
+	    for (int j = 1; j <= sel.GetNP(); j++)
+	      boundp.Set(sel.PNum(j));
+	  }
+
+
+	(*testout) << "bpoints:" << endl;
+	for (int i = 1; i <= np; i++)
+	  if (boundp.Test(i))
+	    (*testout) << i << endl;
+
+	double lam = 0.5;
+
+	while (facok < 1-1e-8 && cnttrials > 0)
+	  {
+	    lam *= 4;
+	    if (lam > 2) lam = 2;
+
+	    do
+	      {
+		//	      cout << "trials: " << cnttrials << endl;
+		lam *= 0.5;
+		cnttrials--;
+
+		cout << "lam = " << lam << endl;
+
+		factry = lam + (1-lam) * facok;
+		cout << "trying: " << factry << endl;
+
+		for (int i = 1; i <= np; i++)
+		  if (boundp.Test(i))
+		    {
+		      for (int j = 0; j < 3; j++)
+			mesh.Point(i)(j) = 
+			  lam * should.Get(i)(j) +
+			  (1-lam) * can.Get(i)(j);
+		    }
+		  else
+		    mesh.Point(i) = Point<3> (can.Get(i));
+	      
+		//	      (*testout) << "bad els: " << endl;
+		wrongels = 0;
+		for (int i = 1; i <= ne; i++)
+		  {
+		    if (!illegalels.Test(i) && 
+			mesh.VolumeElement(i).
+			CalcJacobianBadness(mesh.Points()) > 1e10)
+		      {
+			wrongels++;
+			Element & el = mesh.VolumeElement(i);
+			el.flags.badel = 1;
+		     
+		      
+			if (lam < 1e-4)
+			  illegalels.Set(i);
+ 
+
+			/*
+			  (*testout) << i << ": ";
+			  for (j = 1; j <= el.GetNP(); j++)
+			  (*testout) << el.PNum(j) << " ";
+			  (*testout) << endl;
+			*/
+		      }
+		    else
+		      mesh.VolumeElement(i).flags.badel = 0;
+		  }
+		cout << "wrongels = " << wrongels << endl;
+	      }
+	    while (wrongels && cnttrials > 0);
+
+	    mesh.CalcSurfacesOfNode();
+	    MeshingParameters dummymp;
+	    mesh.ImproveMeshJacobian (dummymp, OPT_WORSTCASE);	      
+	  
+	    facok = factry;
+	    for (int i = 1; i <= np; i++)
+	      can.Elem(i) = mesh.Point(i);
+	  }
+      }
+
+
+      
+    for (int i = 1; i <= ne; i++)
+      {
+	if (illegalels.Test(i))
+	  {
+	    cout << "illegal element: " << i << endl;
+	    mesh.VolumeElement(i).flags.badel = 1;	
+	  }
+	else
+	  mesh.VolumeElement(i).flags.badel = 0;	
+      }
+  
+    /*
+      if (cnttrials <= 0)
+      {
+      cerr << "ERROR: Sorry, illegal elements:" << endl;
+      }
+    */
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/smoothing2.5.cpp b/contrib/Netgen/libsrc/meshing/smoothing2.5.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c9de8e21440a2d07b32bfc3a66b5230a190e1056
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/smoothing2.5.cpp
@@ -0,0 +1,265 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+#include <opti.hpp>
+
+namespace netgen
+{
+
+
+  void MeshOptimize2d :: ProjectBoundaryPoints(Array<int> & surfaceindex, 
+					       const Array<Point<3>* > & from, Array<Point<3>* > & dest)
+  {
+    for(int i=0; i<surfaceindex.Size(); i++)
+      {
+	if(surfaceindex[i] >= 0)
+	  {
+	    *dest[i] = *from[i];
+	    ProjectPoint(surfaceindex[i],*dest[i]);
+	  }
+      }
+      
+
+  }
+
+  void MeshOptimize2d :: ImproveVolumeMesh (Mesh & mesh)
+  {
+    
+    if (!faceindex)
+      {
+	PrintMessage (3, "Smoothing");
+
+	for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++)
+	  {
+	    ImproveVolumeMesh (mesh);
+	    if (multithread.terminate)
+	      throw NgException ("Meshing stopped");
+	  }
+	faceindex = 0;
+	return;
+      }
+      
+
+
+    static int timer = NgProfiler::CreateTimer ("MeshSmoothing 2D");
+    NgProfiler::RegionTimer reg (timer);
+
+
+
+    CheckMeshApproximation (mesh);
+
+    int i, j, k;
+    SurfaceElementIndex sei;
+
+    Array<SurfaceElementIndex> seia;
+    mesh.GetSurfaceElementsOfFace (faceindex, seia);
+
+    bool mixed = 0;
+    for (i = 0; i < seia.Size(); i++)
+      if (mesh[seia[i]].GetNP() != 3)
+	{
+	  mixed = 1;
+	  break;
+	}
+
+
+    int loci;
+    double fact;
+    bool moveisok;
+
+    PointGeomInfo ngi;
+    Point<3> origp;
+
+    Vector x(3);
+
+    Array<MeshPoint, PointIndex::BASE> savepoints(mesh.GetNP());
+
+    Array<int, PointIndex::BASE> nelementsonpoint(mesh.GetNP());
+    nelementsonpoint = 0;
+
+    for (i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & el = mesh[seia[i]];
+	for (j = 0; j < el.GetNP(); j++)
+	  nelementsonpoint[el[j]]++;
+      }
+
+
+    TABLE<SurfaceElementIndex,PointIndex::BASE> elementsonpoint(nelementsonpoint);
+    for (i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & el = mesh[seia[i]];
+	for (j = 0; j < el.GetNP(); j++)
+	  elementsonpoint.Add (el[j], seia[i]);
+      }
+    
+
+    JacobianPointFunction pf(mesh.Points(),mesh.VolumeElements());
+
+
+
+//     Opti2SurfaceMinFunction surfminf(mesh);
+//     Opti2EdgeMinFunction edgeminf(mesh);
+//     Opti2SurfaceMinFunctionJacobian surfminfj(mesh);
+
+    OptiParameters par;
+    par.maxit_linsearch = 8;
+    par.maxit_bfgs = 5;
+
+    int np = mesh.GetNP();
+    int ne = mesh.GetNE();
+
+    BitArray badnodes(np);
+    badnodes.Clear();
+
+    for (i = 1; i <= ne; i++)
+      {
+	const Element & el = mesh.VolumeElement(i);
+	double bad = el.CalcJacobianBadness (mesh.Points());
+	if (bad > 1)
+	  for (j = 1; j <= el.GetNP(); j++)
+	    badnodes.Set (el.PNum(j));
+      }
+
+
+    bool printeddot = 0;
+    char plotchar = '.';
+    int modplot = 1;
+    if (mesh.GetNP() > 1000)
+      {
+	plotchar = '+';
+	modplot = 10;
+      }
+    if (mesh.GetNP() > 10000)
+      {
+	plotchar = 'o';
+	modplot = 100;
+      }
+    int cnt = 0;
+
+
+    Array<SurfaceElementIndex> locelements(0);
+    Array<int> locrots(0);
+
+    for (PointIndex pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+      {
+	if (mesh[pi].Type() != SURFACEPOINT)
+	  continue;
+
+	if (multithread.terminate)
+	  throw NgException ("Meshing stopped");
+	
+	int surfi(-1);
+
+	if(elementsonpoint[pi].Size() == 0)
+	  continue;
+
+	Element2d & hel = mesh[elementsonpoint[pi][0]];
+
+	if(hel.GetIndex() != faceindex)
+	  continue;
+
+	cnt++;
+	if (cnt % modplot == 0 && writestatus)
+	  {
+	    printeddot = 1;
+	    PrintDot (plotchar);
+	  }
+
+		
+	int hpi = 0;
+	for (j = 1; j <= hel.GetNP(); j++)
+	  if (hel.PNum(j) == pi)
+	    {
+	      hpi = j;
+	      break;
+	    }
+	PointGeomInfo gi1 = hel.GeomInfoPi(hpi);
+	
+	locelements.SetSize(0);
+	locrots.SetSize (0);
+	
+	for (j = 0; j < elementsonpoint[pi].Size(); j++)
+	  {
+	    sei = elementsonpoint[pi][j];
+	    const Element2d & bel = mesh[sei];
+	    surfi = mesh.GetFaceDescriptor(bel.GetIndex()).SurfNr();
+	    
+	    locelements.Append (sei);
+	    
+	    for (k = 1; k <= bel.GetNP(); k++)
+	      if (bel.PNum(k) == pi)
+		{
+		  locrots.Append (k);
+		  break;
+		}
+	  }
+	 
+
+	double lh = mesh.GetH(mesh.Point(pi));
+	par.typx = lh;
+
+	pf.SetPointIndex(pi);
+	
+	x = 0;
+	bool pok = (pf.Func (x) < 1e10); 
+	
+	if (pok)
+	  {
+	    BFGS (x, pf, par);
+	    
+	    origp = mesh[pi];
+	    loci = 1;
+	    fact = 1;
+	    moveisok = false;
+	
+	    
+	    //optimizer loop (if whole distance is not possible, move only a bit!!!!)
+	    while (loci <= 5 && !moveisok)
+	      {
+		loci ++;
+		mesh[pi](0) = origp(0) + x(0)*fact;
+		mesh[pi](1) = origp(1) + x(1)*fact;
+		mesh[pi](2) = origp(2) + x(2)*fact;
+		fact = fact/2.;
+	    
+	    
+		//cout << "origp " << origp << " newp " << mesh[pi];
+	    
+		ngi = gi1;
+		moveisok = (ProjectPointGI (surfi, mesh[pi], ngi) != 0);
+
+		//cout << " projected " << mesh[pi] << endl;
+
+		// point lies on same chart in stlsurface
+		
+		if (moveisok)
+		  {
+		    for (j = 0; j < locelements.Size(); j++)
+		      mesh[locelements[j]].GeomInfoPi(locrots[j]) = ngi;
+
+		    //cout << "moved " << origp << " to " << mesh[pi] << endl;
+		  }
+		else
+		  {
+		    mesh[pi] = origp;
+		  }
+	    
+	      }
+	  }
+	else
+	  {
+	    cout << "el not ok (point " << pi << ": " << mesh[pi] << ")" << endl;
+	  }
+      }
+
+    if (printeddot)
+      PrintDot ('\n');
+  
+    CheckMeshApproximation (mesh);
+    mesh.SetNextTimeStamp();
+  }
+
+  
+}
diff --git a/contrib/Netgen/libsrc/meshing/smoothing2.cpp b/contrib/Netgen/libsrc/meshing/smoothing2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe47f8b88d0503f53cbd9607b750e6467af5f3ee
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/smoothing2.cpp
@@ -0,0 +1,903 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+#include <opti.hpp>
+
+namespace netgen
+{
+
+  static const double c_trig = 0.14433756;      // sqrt(3.0) / 12
+  static const double c_trig4 = 0.57735026;     // sqrt(3.0) / 3
+
+  inline double CalcTriangleBadness (double x2, double x3, double y3, 
+				     double metricweight, double h)
+  {
+    // badness = sqrt(3.0) / 12 * (\sum l_i^2) / area - 1 
+    // p1 = (0, 0), p2 = (x2, 0), p3 = (x3, y3);
+
+    double cir_2 = (x2*x2 + x3*x3 + y3*y3 - x2*x3);
+    double area = x2 * y3;
+    
+    if (area <= 1e-24 * cir_2)
+      return 1e10;
+    
+    double badness = c_trig4 * cir_2 / area - 1;
+    
+    if (metricweight > 0)
+      {
+	// add:  metricweight * (area / h^2 + h^2 / area - 2)
+
+	double areahh = area / (h * h);
+	badness += metricweight * (areahh + 1 / areahh - 2);
+      }
+    return badness;
+  }
+
+  
+  inline void CalcTriangleBadness (double x2, double x3, double y3, double metricweight,
+				   double h, double & badness, double & g1x, double & g1y)
+  {
+    // old: badness = sqrt(3.0) /36 * circumference^2 / area - 1 
+    // badness = sqrt(3.0) / 12 * (\sum l_i^2) / area - 1 
+    // p1 = (0, 0), p2 = (x2, 0), p3 = (x3, y3);
+
+
+    double cir_2 = 2* (x2*x2 + x3*x3 + y3*y3 - x2*x3);
+    double area = 0.5 * x2 * y3;
+
+    if (area <= 1e-24 * cir_2)
+      {
+	g1x = 0;
+	g1y = 0;
+	badness = 1e10;
+	return;
+      }
+
+    badness = c_trig * cir_2 / area - 1;
+
+    double c1 = -2 * c_trig / area;
+    double c2 = 0.5 * c_trig * cir_2 / (area * area);
+    g1x = c1 * (x2 + x3) + c2 * y3;
+    g1y = c1 * (y3)      + c2 * (x2-x3); 
+
+    if (metricweight > 0)
+      {
+	// area = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
+	// add:  metricweight * (area / h^2 + h^2 / area - 2)
+      
+	area = x2 * y3;
+	double dareax1 = -y3; 
+	double dareay1 = x3 - x2; 
+
+	double areahh = area / (h * h);
+	double fac = metricweight * (areahh - 1 / areahh) / area;
+
+	badness += metricweight * (areahh + 1 / areahh - 2);
+	g1x += fac * dareax1;
+	g1y += fac * dareay1; 
+      }
+  }
+
+
+
+
+
+  double CalcTriangleBadness (const Point3d & p1, 
+			      const Point3d & p2, 
+			      const Point3d & p3,
+			      double metricweight,
+			      double h)
+  {
+    // badness = sqrt(3.0) / 12 * (\sum l_i^2) / area - 1 
+    // p1 = (0, 0), p2 = (x2, 0), p3 = (x3, y3);
+
+    Vec3d e12(p1,p2);
+    Vec3d e13(p1,p3);
+    Vec3d e23(p2,p3);
+  
+    double l12_2 = e12.Length2();
+    double l13_2 = e13.Length2();
+    double l23_2 = e23.Length2();
+
+    double cir_2 = l12_2 + l13_2 + l23_2;
+    Vec3d area_v = Cross (e12, e13);
+    double area = 0.5 * area_v.Length();
+
+    if (area <= 1e-24 * cir_2)
+      return 1e10;
+
+    double badness = c_trig * cir_2 / area - 1;
+
+    if (metricweight > 0)
+      {
+	// area = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
+	// add:  metricweight * (area / h^2 + h^2 / area - 2)
+
+	const double areahh = area / (h * h);
+	badness += metricweight * (areahh + 1 / areahh - 2);
+      }
+
+    return badness;
+  }
+
+
+  double CalcTriangleBadness (const Point3d & p1, 
+			      const Point3d & p2, 
+			      const Point3d & p3,
+			      const Vec3d & n,
+			      double metricweight,
+			      double h)
+  {
+    Vec3d v1 (p1, p2);
+    Vec3d v2 (p1, p3);
+
+    Vec3d e1 = v1;
+    Vec3d e2 = v2;
+
+    e1 -= (e1 * n) * n;
+    e1 /= (e1.Length() + 1e-24);
+    e2 = Cross (n, e1);
+
+    return CalcTriangleBadness ( (e1 * v1), (e1 * v2), (e2 * v2), 
+				 metricweight, h);
+  }
+
+
+
+  class Opti2dLocalData
+  {
+  public:
+    const MeshOptimize2d * meshthis;
+    MeshPoint sp1; 
+    PointGeomInfo gi1;
+    Vec<3> normal, t1, t2;
+    Array<SurfaceElementIndex> locelements;
+    Array<int> locrots;
+    Array<double> lochs;
+  // static int locerr2;
+    double locmetricweight;
+    double loch;
+    int surfi, surfi2;
+    int uselocalh;
+  public:
+    Opti2dLocalData ()
+    {
+      locmetricweight = 0;
+    }
+  };
+
+
+  class Opti2SurfaceMinFunction : public MinFunction
+  {
+    const Mesh & mesh;
+    Opti2dLocalData & ld;
+  public:
+    Opti2SurfaceMinFunction (const Mesh & amesh,
+			     Opti2dLocalData & ald)
+      : mesh(amesh), ld(ald)
+    { } ;
+    virtual double FuncGrad (const Vector & x, Vector & g) const;
+    virtual double FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const;
+    virtual double Func (const Vector & x) const;
+  };
+  
+  double Opti2SurfaceMinFunction :: 
+  Func (const Vector & x) const
+  {
+    Vector g(x.Size());
+    return FuncGrad (x, g);
+  }
+
+
+  double Opti2SurfaceMinFunction :: 
+  FuncGrad (const Vector & x, Vector & grad) const
+  {
+    Vec<3> n, vgrad;
+    Point<3> pp1;
+    double g1x, g1y;
+    double badness, hbadness;
+
+    vgrad = 0;
+    badness = 0;
+
+    ld.meshthis -> GetNormalVector (ld.surfi, ld.sp1, ld.gi1, n);
+    pp1 = ld.sp1 + x(0) * ld.t1 + x(1) * ld.t2;
+
+    //  meshthis -> ProjectPoint (surfi, pp1);
+    // meshthis -> GetNormalVector (surfi, pp1, n);
+
+    for (int j = 0; j < ld.locelements.Size(); j++)
+      {
+	int roti = ld.locrots[j];
+	const Element2d & bel = mesh[ld.locelements[j]];
+
+	Vec<3> e1 = mesh[bel.PNumMod(roti + 1)] - pp1;
+	Vec<3> e2 = mesh[bel.PNumMod(roti + 2)] - pp1;
+
+	if (ld.uselocalh) ld.loch = ld.lochs[j];
+
+	double e1l = e1.Length();
+	if (Determinant(e1, e2, n) > 1e-8 * e1l * e2.Length())
+	  {
+	    e1 /= e1l;
+	    double e1e2 = e1 * e2;
+            e2 -= e1e2 * e1;
+	    double e2l = e2.Length();
+
+	    CalcTriangleBadness ( e1l, e1e2, e2l, ld.locmetricweight, ld.loch,
+				  hbadness, g1x, g1y);
+
+	    badness += hbadness;
+            vgrad += g1x * e1 + (g1y/e2l) * e2;
+	  }
+	else
+	  {
+	    // (*testout) << "very very bad badness" << endl;
+	    badness += 1e8;
+	  }
+      }
+
+    vgrad -=  (vgrad * n) * n;
+
+    grad(0) = vgrad * ld.t1;
+    grad(1) = vgrad * ld.t2;
+    return badness;
+  }
+
+
+
+
+  double Opti2SurfaceMinFunction :: 
+  FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const
+  {
+    Vec<3> n, vgrad;
+    Point<3> pp1;
+    double g1x, g1y;
+    double badness, hbadness;
+
+    vgrad = 0;
+    badness = 0;
+
+    ld.meshthis -> GetNormalVector (ld.surfi, ld.sp1, ld.gi1, n);
+
+    pp1 = ld.sp1 + x(0) * ld.t1 + x(1) * ld.t2;
+
+    for (int j = 0; j < ld.locelements.Size(); j++)
+      {
+	int roti = ld.locrots[j];
+
+	const Element2d & bel = mesh[ld.locelements[j]];
+
+	Vec<3> e1 = mesh[bel.PNumMod(roti + 1)] - pp1;
+	Vec<3> e2 = mesh[bel.PNumMod(roti + 2)] - pp1;
+
+	if (ld.uselocalh) ld.loch = ld.lochs[j];
+
+	double e1l = e1.Length();
+	if (Determinant(e1, e2, n) > 1e-8 * e1l * e2.Length())
+	  {
+	    e1 /= e1l;
+	    double e1e2 = e1 * e2;
+	    e2 -= e1e2 * e1;
+	    double e2l = e2.Length();
+	    CalcTriangleBadness ( e1l, e1e2, e2l, ld.locmetricweight, ld.loch,
+				  hbadness, g1x, g1y);
+
+	    badness += hbadness;
+            vgrad += g1x * e1 + (g1y / e2l) * e2;
+	  }
+	else
+	  {
+	    // (*testout) << "very very bad badness" << endl;
+	    badness += 1e8;
+	  }
+      }
+
+    vgrad -= (vgrad * n) * n;
+    deriv = dir(0) * (vgrad*ld.t1) + dir(1) * (vgrad*ld.t2);
+
+    return badness;
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+  class Opti2EdgeMinFunction : public MinFunction
+  {
+    const Mesh & mesh;
+    Opti2dLocalData & ld;
+
+  public:
+    Opti2EdgeMinFunction (const Mesh & amesh,
+			  Opti2dLocalData & ald)
+      : mesh(amesh), ld(ald) { } ;
+
+    virtual double FuncGrad (const Vector & x, Vector & g) const;
+    virtual double Func (const Vector & x) const;
+  };
+
+  double Opti2EdgeMinFunction :: Func (const Vector & x) const
+  {
+    Vector g(x.Size());
+    return FuncGrad (x, g);
+  }
+
+  double Opti2EdgeMinFunction :: FuncGrad (const Vector & x, Vector & grad) const
+  {
+    int j, rot;
+    Vec<3> n1, n2, v1, v2, e1, e2, vgrad;
+    Point<3> pp1;
+    Vec<2> g1;
+    double badness, hbadness;
+
+    vgrad = 0.0;
+    badness = 0;
+
+    pp1 = ld.sp1 + x(0) * ld.t1;
+    ld.meshthis -> ProjectPoint2 (ld.surfi, ld.surfi2, pp1);
+
+    for (j = 0; j < ld.locelements.Size(); j++)
+      {
+	rot = ld.locrots[j];
+	const Element2d & bel = mesh[ld.locelements[j]];
+
+	v1 = mesh[bel.PNumMod(rot + 1)] - pp1;
+	v2 = mesh[bel.PNumMod(rot + 2)] - pp1;
+
+	e1 = v1;
+	e2 = v2;
+	e1 /= e1.Length();
+	e2 -= (e1 * e2) * e1;
+	e2 /= e2.Length();
+
+	if (ld.uselocalh) ld.loch = ld.lochs[j];
+	CalcTriangleBadness ( (e1 * v1), (e1 * v2), (e2 * v2), ld.locmetricweight, ld.loch,
+			      hbadness, g1(0), g1(1));
+
+	badness += hbadness;
+        vgrad += g1(0) * e1 + g1(1) * e2;
+      }
+
+    ld.meshthis -> GetNormalVector (ld.surfi, pp1, n1);
+    ld.meshthis -> GetNormalVector (ld.surfi2, pp1, n2);
+
+    v1 = Cross (n1, n2);
+    v1.Normalize();
+
+    grad(0) = (vgrad * v1) * (ld.t1 * v1);
+
+    return badness;
+  }
+
+
+
+
+  class Opti2SurfaceMinFunctionJacobian : public MinFunction
+  {
+    const Mesh & mesh;
+    Opti2dLocalData & ld;
+
+  public:
+    Opti2SurfaceMinFunctionJacobian (const Mesh & amesh,
+				     Opti2dLocalData & ald)
+      : mesh(amesh), ld(ald)
+    { } ;
+    virtual double FuncGrad (const Vector & x, Vector & g) const;
+    virtual double FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const;
+    virtual double Func (const Vector & x) const;
+  };
+  
+  double Opti2SurfaceMinFunctionJacobian :: 
+  Func (const Vector & x) const
+  {
+    Vector g(x.Size());
+    return FuncGrad (x, g);
+  }
+
+
+  double Opti2SurfaceMinFunctionJacobian :: 
+  FuncGrad (const Vector & x, Vector & grad) const
+  {
+    // from 2d:
+
+    int lpi, gpi;
+    Vec<3> n, vgrad;
+    Point<3> pp1;
+    Vec2d g1, vdir;
+    double badness, hbad, hderiv;
+
+    vgrad = 0;
+    badness = 0;
+
+    ld.meshthis -> GetNormalVector (ld.surfi, ld.sp1, ld.gi1, n);
+
+    pp1 = ld.sp1 + x(0) * ld.t1 + x(1) * ld.t2;
+
+    //  meshthis -> ProjectPoint (surfi, pp1);
+    //  meshthis -> GetNormalVector (surfi, pp1, n);
+
+    static Array<Point2d> pts2d;
+    pts2d.SetSize(mesh.GetNP());
+
+    grad = 0;
+
+    for (int j = 1; j <= ld.locelements.Size(); j++)
+      {
+	lpi = ld.locrots.Get(j);
+	const Element2d & bel = 
+	  mesh[ld.locelements.Get(j)];
+      
+	gpi = bel.PNum(lpi);
+
+	for (int k = 1; k <= bel.GetNP(); k++)
+	  {
+	    PointIndex pi = bel.PNum(k);
+	    pts2d.Elem(pi) = Point2d (ld.t1 * (mesh.Point(pi) - ld.sp1), 
+				      ld.t2 * (mesh.Point(pi) - ld.sp1)); 
+	  }				    
+	pts2d.Elem(gpi) = Point2d (x(0), x(1));
+      
+
+	for (int k = 1; k <= 2; k++)
+	  {
+	    if (k == 1)
+	      vdir = Vec2d (1, 0);
+	    else
+	      vdir = Vec2d (0, 1);
+	  
+	    hbad = bel.
+	      CalcJacobianBadnessDirDeriv (pts2d, lpi, vdir, hderiv);
+            
+	    grad(k-1) += hderiv;
+	    if (k == 1)
+	      badness += hbad;
+	  }
+      }
+
+
+    /*
+      vgrad.Add (-(vgrad * n), n);
+
+      grad.Elem(1) = vgrad * t1;
+      grad.Elem(2) = vgrad * t2;
+    */
+    return badness;
+  }
+
+
+
+
+  double Opti2SurfaceMinFunctionJacobian :: 
+  FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const
+  {
+    // from 2d:
+
+    int j, k, lpi, gpi;
+    Vec<3> n, vgrad;
+    Point<3> pp1;
+    Vec2d g1, vdir;
+    double badness, hbad, hderiv;
+
+    vgrad = 0;
+    badness = 0;
+
+    ld.meshthis -> GetNormalVector (ld.surfi, ld.sp1, ld.gi1, n);
+
+    // pp1 = sp1;
+    //    pp1.Add2 (x.Get(1), t1, x.Get(2), t2);
+    pp1 = ld.sp1 + x(0) * ld.t1 + x(1) * ld.t2;
+
+    static Array<Point2d> pts2d;
+    pts2d.SetSize(mesh.GetNP());
+
+    deriv = 0;
+
+    for (j = 1; j <= ld.locelements.Size(); j++)
+      {
+	lpi = ld.locrots.Get(j);
+	const Element2d & bel = 
+	  mesh[ld.locelements.Get(j)];
+      
+	gpi = bel.PNum(lpi);
+
+	for (k = 1; k <= bel.GetNP(); k++)
+	  {
+	    PointIndex pi = bel.PNum(k);
+	    pts2d.Elem(pi) = Point2d (ld.t1 * (mesh.Point(pi) - ld.sp1), 
+				      ld.t2 * (mesh.Point(pi) - ld.sp1)); 
+	  }				    
+	pts2d.Elem(gpi) = Point2d (x(0), x(1));
+      
+
+	vdir = Vec2d (dir(0), dir(1));
+	  
+	hbad = bel.
+	  CalcJacobianBadnessDirDeriv (pts2d, lpi, vdir, hderiv);
+      
+	deriv += hderiv;
+	badness += hbad;
+      }
+
+
+    return badness;
+  }
+
+
+
+
+
+
+
+  MeshOptimize2d dummy;
+
+  MeshOptimize2d :: MeshOptimize2d ()
+  {
+    SetFaceIndex (0);
+    SetImproveEdges (0);
+    SetMetricWeight (0);
+    SetWriteStatus (1);
+  }
+
+
+  void MeshOptimize2d :: SelectSurfaceOfPoint (const Point<3> & p,
+					       const PointGeomInfo & gi)
+  {
+    ;
+  }
+
+  void MeshOptimize2d :: ImproveMesh (Mesh & mesh, const MeshingParameters & mp)
+  {
+    if (!faceindex)
+      {
+	PrintMessage (3, "Smoothing");
+
+	for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++)
+	  {
+	    ImproveMesh (mesh, mp);
+	    if (multithread.terminate)
+	      throw NgException ("Meshing stopped");
+	  }
+	faceindex = 0;
+	return;
+      }
+ 
+
+    static int timer = NgProfiler::CreateTimer ("MeshSmoothing 2D");
+    static int timer1 = NgProfiler::CreateTimer ("MeshSmoothing 2D start");
+
+    NgProfiler::RegionTimer reg (timer);
+    NgProfiler::StartTimer (timer1);
+
+    CheckMeshApproximation (mesh);
+
+    Opti2dLocalData ld;
+
+    // SurfaceElementIndex sei;
+
+    Array<SurfaceElementIndex> seia;
+    mesh.GetSurfaceElementsOfFace (faceindex, seia);
+
+    bool mixed = 0;
+    for (int i = 0; i < seia.Size(); i++)
+      if (mesh[seia[i]].GetNP() != 3)
+	{
+	  mixed = 1;
+	  break;
+	}
+
+    int loci;
+    double fact;
+    int moveisok;
+
+    PointGeomInfo ngi;
+    Point3d origp;
+
+
+
+    Vec3d n1, n2;
+    Vector x(2), xedge(1);
+
+    Array<MeshPoint, PointIndex::BASE> savepoints(mesh.GetNP());
+
+    ld.uselocalh = mp.uselocalh;
+
+
+    Array<int, PointIndex::BASE> nelementsonpoint(mesh.GetNP());
+    nelementsonpoint = 0;
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & el = mesh[seia[i]];
+	for (int j = 0; j < el.GetNP(); j++)
+	  nelementsonpoint[el[j]]++;
+      }
+
+    TABLE<SurfaceElementIndex,PointIndex::BASE> elementsonpoint(nelementsonpoint);
+
+    for (int i = 0; i < seia.Size(); i++)
+      {
+	const Element2d & el = mesh[seia[i]];
+	for (int j = 0; j < el.GetNP(); j++)
+	  elementsonpoint.Add (el[j], seia[i]);
+      }
+
+
+    ld.loch = mp.maxh;
+    ld.locmetricweight = metricweight;
+    ld.meshthis = this;
+
+
+
+    Opti2SurfaceMinFunction surfminf(mesh, ld);
+    Opti2EdgeMinFunction edgeminf(mesh, ld);
+    Opti2SurfaceMinFunctionJacobian surfminfj(mesh, ld);
+
+    OptiParameters par;
+    par.maxit_linsearch = 8;
+    par.maxit_bfgs = 5;
+
+    /*
+    int i, j, k;
+
+      if (improveedges)
+      for (i = 1; i <= mesh.GetNP(); i++)
+      if (mesh.PointType(i) == EDGEPOINT)
+      {
+      continue;
+      PrintDot ();
+      sp1 = mesh.Point(i);
+	  
+      locelements.SetSize(0);
+      locrots.SetSize (0);
+      lochs.SetSize (0);
+      surfi = surfi2 = surfi3 = 0;
+	  
+      for (j = 0; j < elementsonpoint[i].Size(); j++)
+      {
+      sei = elementsonpoint[i][j];
+      const Element2d * bel = &mesh[sei];
+	      
+      if (!surfi)
+      surfi = mesh.GetFaceDescriptor(bel->GetIndex()).SurfNr();
+      else if (surfi != mesh.GetFaceDescriptor(bel->GetIndex()).SurfNr())
+      {
+      if (surfi2 != 0 && surfi2 != 
+      mesh.GetFaceDescriptor(bel->GetIndex()).SurfNr())
+      surfi3 = mesh.GetFaceDescriptor(bel->GetIndex()).SurfNr();
+      else
+      surfi2 = mesh.GetFaceDescriptor(bel->GetIndex()).SurfNr();
+      }
+	      
+      locelements.Append (sei);
+	      
+      if (bel->PNum(1) == i)
+      locrots.Append (1);
+      else if (bel->PNum(2) == i)
+      locrots.Append (2);
+      else
+      locrots.Append (3);
+
+      if (uselocalh)
+      {
+      Point3d pmid = Center (mesh.Point(bel->PNum(1)),
+      mesh.Point(bel->PNum(2)),
+      mesh.Point(bel->PNum(3)));
+      lochs.Append (mesh.GetH(pmid));
+      }
+      }
+	  
+      if (surfi2 && !surfi3)
+      {
+      GetNormalVector (surfi, sp1, n1);
+      GetNormalVector (surfi2, sp1, n2);
+      t1 = Cross (n1, n2);
+	      
+      xedge = 0;
+      BFGS (xedge, edgeminf, par, 1e-6);
+	      
+      mesh.Point(i).X() += xedge.Get(1) * t1.X();
+      mesh.Point(i).Y() += xedge.Get(1) * t1.Y();
+      mesh.Point(i).Z() += xedge.Get(1) * t1.Z();
+      ProjectPoint2 (surfi, surfi2, mesh.Point(i));
+      }
+      }
+    */
+
+
+    bool printeddot = 0;
+    char plotchar = '.';
+    int modplot = 1;
+    if (mesh.GetNP() > 1000)
+      {
+	plotchar = '+';
+	modplot = 10;
+      }
+    if (mesh.GetNP() > 10000)
+      {
+	plotchar = 'o';
+	modplot = 100;
+      }
+    int cnt = 0;
+
+
+    NgProfiler::StopTimer (timer1);
+
+
+    for (PointIndex pi = PointIndex::BASE; pi < mesh.GetNP()+PointIndex::BASE; pi++)
+      if (mesh[pi].Type() == SURFACEPOINT)
+	{
+	  if (multithread.terminate)
+	    throw NgException ("Meshing stopped");
+	
+	  cnt++;
+	  if (cnt % modplot == 0 && writestatus)
+	    {
+	      printeddot = 1;
+	      PrintDot (plotchar);
+	    }
+
+	  if (elementsonpoint[pi].Size() == 0)
+	    continue;
+
+	
+	  ld.sp1 = mesh[pi];
+
+	  Element2d & hel = mesh[elementsonpoint[pi][0]];
+
+	  int hpi = 0;
+	  for (int j = 1; j <= hel.GetNP(); j++)
+	    if (hel.PNum(j) == pi)
+	      {
+		hpi = j;
+		break;
+	      }
+
+	  ld.gi1 = hel.GeomInfoPi(hpi);
+	  SelectSurfaceOfPoint (ld.sp1, ld.gi1);
+	  
+	  ld.locelements.SetSize(0);
+	  ld.locrots.SetSize (0);
+	  ld.lochs.SetSize (0);
+	
+	  for (int j = 0; j < elementsonpoint[pi].Size(); j++)
+	    {
+	      SurfaceElementIndex sei = elementsonpoint[pi][j];
+	      const Element2d & bel = mesh[sei];
+	      ld.surfi = mesh.GetFaceDescriptor(bel.GetIndex()).SurfNr();
+	    
+	      ld.locelements.Append (sei);
+	    
+	      for (int k = 1; k <= bel.GetNP(); k++)
+		if (bel.PNum(k) == pi)
+		  {
+		    ld.locrots.Append (k);
+		    break;
+		  }
+	      
+	      if (ld.uselocalh)
+		{
+		  Point3d pmid = Center (mesh[bel[0]], mesh[bel[1]], mesh[bel[2]]);
+		  ld.lochs.Append (mesh.GetH(pmid));
+		}
+	    }
+	  
+	  GetNormalVector (ld.surfi, ld.sp1, ld.gi1, ld.normal);
+	  ld.t1 = ld.normal.GetNormal ();
+	  ld.t2 = Cross (ld.normal, ld.t1);
+	  
+	  // save points, and project to tangential plane
+	  for (int j = 0; j < ld.locelements.Size(); j++)
+	    {
+	      const Element2d & el = mesh[ld.locelements[j]];
+	      for (int k = 0; k < el.GetNP(); k++)
+		savepoints[el[k]] = mesh[el[k]];
+	    }
+
+	  for (int j = 0; j < ld.locelements.Size(); j++)
+	    {
+	      const Element2d & el = mesh[ld.locelements[j]];
+	      for (int k = 0; k < el.GetNP(); k++)
+		{
+		  PointIndex hhpi = el[k];
+		  double lam = ld.normal * (mesh[hhpi] - ld.sp1);
+		  mesh[hhpi] -= lam * ld.normal;
+		}
+	    }
+	  
+	  x = 0;
+	  par.typx = ld.lochs[0];
+
+	  if (mixed)
+	    {
+	      //	      (*testout) << "vorher : " << surfminfj.Func (x) << endl;
+	      BFGS (x, surfminfj, par, 1e-6);
+	      //	      (*testout) << "nachher: " << surfminfj.Func (x) << endl;
+	      //	      (*testout) << "x = " << x << endl;
+	    }
+	  else
+	    {
+	      //	      (*testout) << "vorher : " << surfminf.Func (x) << endl;
+	      BFGS (x, surfminf, par, 1e-6);
+	      //	      (*testout) << "nachher: " << surfminf.Func (x) << endl;
+	      //	      (*testout) << "x = " << x << endl;
+	    }
+
+	
+	  origp = mesh[pi];
+	  loci = 1;
+	  fact = 1;
+	  moveisok = 0;
+
+	  // restore other points
+	  for (int j = 0; j < ld.locelements.Size(); j++)
+	    {
+	      const Element2d & el = mesh[ld.locelements[j]];
+	      for (int k = 0; k < el.GetNP(); k++)
+		{
+		  PointIndex hhpi = el[k];
+		  if (hhpi != pi) mesh[hhpi] = savepoints[hhpi];
+		}
+	    }
+
+	  
+	  //optimizer loop (if whole distance is not possible, move only a bit!!!!)
+	  while (loci <= 5 && !moveisok)
+	    {
+	      loci ++;
+              /*
+	      mesh[pi].X() = origp.X() + (x.Get(1) * t1.X() + x.Get(2) * t2.X())*fact;
+	      mesh[pi].Y() = origp.Y() + (x.Get(1) * t1.Y() + x.Get(2) * t2.Y())*fact;
+	      mesh[pi].Z() = origp.Z() + (x.Get(1) * t1.Z() + x.Get(2) * t2.Z())*fact;
+              */
+              Vec<3> hv = x(0) * ld.t1 + x(1) * ld.t2;
+              Point3d hnp = origp + Vec3d (hv);
+              mesh[pi](0) = hnp.X();
+              mesh[pi](1) = hnp.Y();
+              mesh[pi](2) = hnp.Z();
+
+	      fact = fact/2.;
+
+	      // ProjectPoint (surfi, mesh[pi]);
+	      // moveisok = CalcPointGeomInfo(surfi, ngi, mesh[pi]); 
+
+	      ngi = ld.gi1;
+	      moveisok = ProjectPointGI (ld.surfi, mesh[pi], ngi);
+	      // point lies on same chart in stlsurface
+	    
+	      if (moveisok)
+		{
+		  for (int j = 0; j < ld.locelements.Size(); j++)
+		    mesh[ld.locelements[j]].GeomInfoPi(ld.locrots[j]) = ngi;
+		}
+	      else
+		{
+		  mesh[pi] = Point<3> (origp);
+		}
+	    
+	    }
+	}
+
+    if (printeddot)
+      PrintDot ('\n');
+  
+    CheckMeshApproximation (mesh);
+    mesh.SetNextTimeStamp();
+  }
+
+  void MeshOptimize2d :: GetNormalVector(INDEX /* surfind */, const Point<3> & p, Vec<3> & nv) const
+  {
+    nv = Vec<3> (0, 0, 1);
+  }
+
+  void MeshOptimize2d :: GetNormalVector(INDEX surfind, const Point<3> & p, PointGeomInfo & gi, Vec<3> & n) const
+  {
+    GetNormalVector (surfind, p, n);
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/smoothing3.cpp b/contrib/Netgen/libsrc/meshing/smoothing3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bf2958848b7276bbe01a91137dd74f8e5347a592
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/smoothing3.cpp
@@ -0,0 +1,1838 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+#ifdef SOLIDGEOM
+#include <csg.hpp>
+#endif
+#include <opti.hpp>
+
+
+namespace netgen
+{
+  
+
+  double MinFunctionSum :: Func (const Vector & x) const
+  {
+    double retval = 0;
+    for(int i=0; i<functions.Size(); i++)
+      retval += functions[i]->Func(x);
+      
+    return retval;
+  }
+
+  void MinFunctionSum :: Grad (const Vector & x, Vector & g) const
+  {
+    g = 0.;
+    VectorMem<3> gi;
+    for(int i=0; i<functions.Size(); i++)
+      {
+	functions[i]->Grad(x,gi);
+	for(int j=0; j<g.Size(); j++)
+	  g[j] += gi[j];
+      }
+  }
+      
+
+  double MinFunctionSum :: FuncGrad (const Vector & x, Vector & g) const
+  {
+    double retval = 0;
+    g = 0.;
+    VectorMem<3> gi;
+    for(int i=0; i<functions.Size(); i++)
+      {
+	retval += functions[i]->FuncGrad(x,gi);
+	for(int j=0; j<g.Size(); j++)
+	  g[j] += gi[j];
+      }
+    return retval;
+  }
+
+  double MinFunctionSum :: FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const
+  {
+    double retval = 0;
+    deriv = 0.;
+    double derivi;
+    for(int i=0; i<functions.Size(); i++)
+      {
+	retval += functions[i]->FuncDeriv(x,dir,derivi);
+	deriv += derivi;
+      }
+    return retval;
+  }
+
+  double MinFunctionSum :: GradStopping (const Vector & x) const
+  {
+    double minfs(0), mini;
+    for(int i=0; i<functions.Size(); i++)
+      {
+	mini = functions[i]->GradStopping(x);
+	if(i==0 || mini < minfs)
+	  minfs = mini;
+      }
+    return minfs;
+  }
+
+
+  void MinFunctionSum :: AddFunction(MinFunction & fun)
+  {
+    functions.Append(&fun);
+  }
+  
+  const MinFunction & MinFunctionSum :: Function(int i) const
+  {
+    return *functions[i];
+  }
+  MinFunction & MinFunctionSum :: Function(int i)
+  {
+    return *functions[i];
+  }
+
+  PointFunction1 :: PointFunction1 (Mesh::T_POINTS & apoints, 
+				    const Array<INDEX_3> & afaces,
+				    const MeshingParameters & amp,
+				    double ah)
+    : points(apoints), faces(afaces), mp(amp)
+  {
+    h = ah;
+  }
+  
+
+  double PointFunction1 :: Func (const Vector & vp) const
+  {
+    double badness = 0;
+    Point<3> pp(vp(0), vp(1), vp(2));
+
+    for (int j = 0; j < faces.Size(); j++)
+      {
+	const INDEX_3 & el = faces[j];
+
+	double bad = CalcTetBadness (points[el.I1()], 
+				     points[el.I3()], 
+				     points[el.I2()], 
+				     pp, 0, mp);
+	badness += bad;
+      }
+ 
+    return badness;
+  }
+
+
+  double PointFunction1 :: 
+  FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const
+  {
+    VectorMem<3> hx;
+    const double eps = 1e-6;
+
+    double dirlen = dir.L2Norm();
+    if (dirlen < 1e-14)
+      {
+	deriv = 0;
+	return Func(x);
+      }
+
+    hx.Set(1, x);
+    hx.Add(eps * h / dirlen, dir);
+    double fr = Func (hx);
+    hx.Set(1, x);
+    hx.Add(-eps * h / dirlen, dir);
+    double fl = Func (hx);
+
+    deriv = (fr - fl) / (2 * eps * h) * dirlen;
+
+    return Func(x);
+  }
+
+
+  double PointFunction1 :: FuncGrad (const Vector & x, Vector & g) const
+  {
+    VectorMem<3> hx;
+    double eps = 1e-6;
+
+    hx = x;
+    for (int i = 0; i < 3; i++)
+      {
+	hx(i) = x(i) + eps * h;
+	double fr = Func (hx);
+	hx(i) = x(i) - eps * h;
+	double fl = Func (hx);
+	hx(i) = x(i);
+
+	g(i) = (fr - fl) / (2 * eps * h);
+      }
+
+    return Func(x);
+  }
+
+  double PointFunction1 :: GradStopping (const Vector & x) const
+  {
+    double f = Func(x);
+    return 1e-8 * f * f;
+  }
+
+
+  /* Cheap Functional depending of inner point inside triangular surface */
+
+  // is it used ????
+  class CheapPointFunction1 : public MinFunction
+  {
+    Mesh::T_POINTS & points;
+    const Array<INDEX_3> & faces;
+    DenseMatrix m;
+    double h;
+  public:
+    CheapPointFunction1 (Mesh::T_POINTS & apoints, 
+			 const Array<INDEX_3> & afaces,
+			 double ah);
+  
+    virtual double Func (const Vector & x) const;
+    virtual double FuncGrad (const Vector & x, Vector & g) const;
+  };
+
+  CheapPointFunction1 :: CheapPointFunction1 (Mesh::T_POINTS & apoints, 
+					      const Array<INDEX_3> & afaces,
+					      double ah)
+    : points(apoints), faces(afaces)
+  {
+    h = ah;
+  
+
+    int nf = faces.Size();
+
+    m.SetSize (nf, 4);
+  
+    for (int i = 1; i <= nf; i++)
+      {
+	const Point3d & p1 = points[faces.Get(i).I1()];
+	const Point3d & p2 = points[faces.Get(i).I2()];
+	const Point3d & p3 = points[faces.Get(i).I3()];
+	Vec3d v1 (p1, p2);
+	Vec3d v2 (p1, p3);
+	Vec3d n;
+	Cross (v1, v2, n);
+	n /= n.Length();
+
+	m.Elem(i, 1) = n.X();
+	m.Elem(i, 2) = n.Y();
+	m.Elem(i, 3) = n.Z();
+	m.Elem(i, 4) = - (n.X() * p1.X() + n.Y() * p1.Y() + n.Z() * p1.Z());
+      } 
+  }
+  
+  double CheapPointFunction1 :: Func (const Vector & vp) const
+  {
+
+    /*
+      int j;
+      double badness = 0;
+      Point3d pp(vp.Get(1), vp.Get(2), vp.Get(3));
+
+      for (j = 1; j <= faces.Size(); j++)
+      {
+      const INDEX_3 & el = faces.Get(j);
+
+      double bad = CalcTetBadness (points.Get(el.I1()), 
+      points.Get(el.I3()), 
+      points.Get(el.I2()), 
+      pp, 0);
+      badness += bad;
+      }
+    */
+
+    int i;
+    double badness = 0;
+    VectorMem<4> hv;
+    Vector res(m.Height());
+
+    for (i = 0;i < 3; i++)
+      hv(i) = vp(i);
+    hv(3) = 1;
+    m.Mult (hv, res);
+
+    for (i = 1; i <= res.Size(); i++)
+      {
+	if (res(i-1) < 1e-10)
+	  badness += 1e24;
+	else
+	  badness += 1 / res(i-1);
+      }
+ 
+    return badness;
+  }
+
+
+  double CheapPointFunction1 :: FuncGrad (const Vector & x, Vector & g) const
+  {
+    VectorMem<3> hx;
+    double eps = 1e-6;
+
+    hx = x;
+    for (int i = 0; i < 3; i++)
+      {
+	hx(i) = x(i) + eps * h;
+	double fr = Func (hx);
+	hx(i) = x(i) - eps * h;
+	double fl = Func (hx);
+	hx(i) = x(i);
+
+	g(i) = (fr - fl) / (2 * eps * h);
+      }
+
+    return Func(x);
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  /* ************* PointFunction **************************** */
+
+
+  class PointFunction 
+  {
+  public:
+    Mesh::T_POINTS & points;
+    const Mesh::T_VOLELEMENTS & elements;
+    TABLE<int,PointIndex::BASE> elementsonpoint;
+    const MeshingParameters & mp;
+    PointIndex actpind;
+    double h;
+  
+  public:
+    PointFunction (Mesh::T_POINTS & apoints, 
+		   const Mesh::T_VOLELEMENTS & aelements,
+		   const MeshingParameters & amp);
+  
+    virtual void SetPointIndex (PointIndex aactpind);
+    void SetLocalH (double ah) { h = ah; }
+    double GetLocalH () const { return h; }
+    virtual double PointFunctionValue (const Point<3> & pp) const;
+    virtual double PointFunctionValueGrad (const Point<3> & pp, Vec<3> & grad) const;
+    virtual double PointFunctionValueDeriv (const Point<3> & pp, const Vec<3> & dir, double & deriv) const;
+
+    int MovePointToInner ();
+  };
+
+
+  PointFunction :: PointFunction (Mesh::T_POINTS & apoints, 
+				  const Mesh::T_VOLELEMENTS & aelements,
+				  const MeshingParameters & amp)
+    : points(apoints), elements(aelements), elementsonpoint(apoints.Size()), mp(amp)
+  {
+    for (int i = 0; i < elements.Size(); i++)
+      if (elements[i].NP() == 4)
+        for (int j = 0; j < elements[i].NP(); j++)
+          elementsonpoint.Add (elements[i][j], i);  
+  }
+
+  void PointFunction :: SetPointIndex (PointIndex aactpind)
+  {
+    actpind = aactpind; 
+  }  
+
+  double PointFunction :: PointFunctionValue (const Point<3> & pp) const
+  {
+    double badness;
+    Point<3> hp;
+
+    badness = 0;
+
+    hp = points[actpind];
+    points[actpind] = Point<3> (pp);
+
+    for (int j = 0; j < elementsonpoint[actpind].Size(); j++)
+      {
+        const Element & el = elements[elementsonpoint[actpind][j]];
+	badness += CalcTetBadness (points[el[0]], points[el[1]], 
+				   points[el[2]], points[el[3]], -1, mp);
+      }
+  
+    points[actpind] = Point<3> (hp); 
+    return badness;
+  }
+
+
+  double PointFunction :: PointFunctionValueGrad (const Point<3> & pp, Vec<3> & grad) const
+  {
+    double f = 0;
+
+    Point<3> hp = points[actpind];
+    Vec<3> vgradi, vgrad(0,0,0);
+    points[actpind] = Point<3> (pp);
+
+    for (int j = 0; j < elementsonpoint[actpind].Size(); j++)
+      {
+        const Element & el = elements[elementsonpoint[actpind][j]];
+	for (int k = 0; k < 4; k++)
+	  if (el[k] == actpind)
+	    {
+	      f += CalcTetBadnessGrad (points[el[0]], points[el[1]], 
+                                       points[el[2]], points[el[3]], 
+                                       -1, k+1, vgradi, mp);
+
+              vgrad += vgradi;
+	    }
+      }
+
+    points[actpind] = Point<3> (hp); 
+
+    grad = vgrad;
+    return f;
+  }
+
+
+  double PointFunction :: PointFunctionValueDeriv (const Point<3> & pp, const Vec<3> & dir,
+						   double & deriv) const
+  {
+    Vec<3> vgradi, vgrad(0,0,0);
+
+    Point<3> hp = points[actpind];
+    points[actpind] = pp;
+    double f = 0;
+
+    for (int j = 0; j < elementsonpoint[actpind].Size(); j++)
+      {
+        const Element & el = elements[elementsonpoint[actpind][j]];
+
+	for (int k = 1; k <= 4; k++)
+	  if (el.PNum(k) == actpind)
+	    {
+	      f += CalcTetBadnessGrad (points[el.PNum(1)], 
+				       points[el.PNum(2)], 
+				       points[el.PNum(3)], 
+				       points[el.PNum(4)], -1, k, vgradi, mp);
+
+	      vgrad += vgradi;
+	    }
+      }
+
+    points[actpind] = Point<3> (hp); 
+    deriv = dir * vgrad;
+    return f;
+  }
+
+  int PointFunction :: MovePointToInner ()
+  {
+    // try point movement 
+    Array<Element2d> faces;
+  
+    for (int j = 0; j < elementsonpoint[actpind].Size(); j++)
+      {
+	const Element & el = 
+	  elements[elementsonpoint[actpind][j]];
+      
+	for (int k = 1; k <= 4; k++)
+	  if (el.PNum(k) == actpind)
+	    {
+	      Element2d face;
+	      el.GetFace (k, face);
+	      Swap (face.PNum(2), face.PNum(3));
+	      faces.Append (face);
+	    }
+      }
+  
+    Point3d hp;
+    int hi = FindInnerPoint (points, faces, hp);
+    if (hi)
+      {
+	// cout << "inner point found" << endl;
+	points[actpind] = Point<3> (hp);
+      }
+    else
+      ;
+    //      cout << "no inner point found" << endl;
+
+    /*
+    Point3d hp2;
+    int hi2 = FindInnerPoint (points, faces, hp2);
+    if (hi2)
+      {
+	cout << "new: inner point found" << endl;
+      }
+    else
+      cout << "new: no inner point found" << endl;
+  
+    (*testout) << "hi(orig) = " << hi << ", hi(new) = " << hi2;
+    if (hi != hi2) (*testout) << "hi different" << endl;
+    */
+
+    return hi;
+  }
+
+
+
+
+
+
+  class CheapPointFunction : public PointFunction
+  {
+    DenseMatrix m;
+  public:
+    CheapPointFunction (Mesh::T_POINTS & apoints, 
+			const Mesh::T_VOLELEMENTS & aelements,
+			const MeshingParameters & amp);
+    virtual void SetPointIndex (PointIndex aactpind);
+    virtual double PointFunctionValue (const Point<3> & pp) const;
+    virtual double PointFunctionValueGrad (const Point<3> & pp, Vec<3> & grad) const;
+  };
+
+
+  CheapPointFunction :: CheapPointFunction (Mesh::T_POINTS & apoints, 
+					    const Mesh::T_VOLELEMENTS & aelements,
+					    const MeshingParameters & amp)
+    : PointFunction (apoints, aelements, amp)
+  {
+    ;
+  }
+
+
+  void CheapPointFunction :: SetPointIndex (PointIndex aactpind)
+  {
+    actpind = aactpind; 
+
+    int ne = elementsonpoint[actpind].Size();
+    int i, j;
+    int pi1, pi2, pi3;
+
+    m.SetSize (ne, 4);
+
+    for (i = 0; i < ne; i++)
+      {
+	pi1 = 0;
+	pi2 = 0;
+	pi3 = 0;
+
+	const Element & el = elements[elementsonpoint[actpind][i]];
+	for (j = 1; j <= 4; j++)
+	  if (el.PNum(j) != actpind)
+	    {
+	      pi3 = pi2;
+	      pi2 = pi1;
+	      pi1 = el.PNum(j);
+	    }
+
+	const Point3d & p1 = points[pi1];
+	Vec3d v1 (p1, points[pi2]);
+	Vec3d v2 (p1, points[pi3]);
+	Vec3d n;
+	Cross (v1, v2, n);
+	n /= n.Length();
+
+	Vec3d v (p1, points[actpind]);
+	double c = v * n;
+      
+	if (c < 0)
+	  n *= -1;    
+      
+	// n is inner normal
+
+	m.Elem(i+1, 1) = n.X();
+	m.Elem(i+1, 2) = n.Y();
+	m.Elem(i+1, 3) = n.Z();
+	m.Elem(i+1, 4) = - (n.X() * p1.X() + n.Y() * p1.Y() + n.Z() * p1.Z());
+      }
+  }
+
+  double CheapPointFunction :: PointFunctionValue (const Point<3> & pp) const
+  {
+    VectorMem<4> p4;
+    Vector di;
+    int n = m.Height();
+
+    p4(0) = pp(0);
+    p4(1) = pp(1);
+    p4(2) = pp(2);
+    p4(3) = 1;
+
+    di.SetSize (n);
+    m.Mult (p4, di);
+  
+    double sum = 0;
+    for (int i = 0; i < n; i++)
+      {
+	if (di(i) > 0)
+	  sum += 1 / di(i);
+	else
+	  return 1e16;
+      }
+    return sum;
+  }
+
+
+
+
+  double CheapPointFunction :: PointFunctionValueGrad (const Point<3> & pp, Vec<3> & grad) const
+  {
+    VectorMem<4> p4;
+    Vector di;
+
+    int n = m.Height();
+
+    p4(0) = pp(0);
+    p4(1) = pp(1);
+    p4(2) = pp(2);
+    p4(3) = 1;
+
+    di.SetSize (n);
+    m.Mult (p4, di);
+  
+    double sum = 0;
+    grad = 0;
+    for (int i = 0; i < n; i++)
+      {
+	if (di(i) > 0)
+	  {
+	    double idi = 1 / di(i);
+	    sum += idi;
+	    grad(0) -= idi * idi * m(i, 0);
+	    grad(1) -= idi * idi * m(i, 1);
+	    grad(2) -= idi * idi * m(i, 2);
+	  }
+	else
+	  {
+	    return 1e16;
+	  }
+      }
+    return sum;
+  }
+
+
+
+
+
+
+
+
+  class Opti3FreeMinFunction : public MinFunction
+  { 
+    const PointFunction & pf;
+    Point<3> sp1;
+  
+  public:
+    Opti3FreeMinFunction (const PointFunction & apf);
+    void SetPoint (const Point<3> & asp1) { sp1 = asp1; }
+    virtual double Func (const Vector & x) const;
+    virtual double FuncGrad (const Vector & x, Vector & g) const;
+    virtual double FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const;  
+    virtual double GradStopping (const Vector & x) const;
+    virtual void ApproximateHesse (const Vector & x,
+				   DenseMatrix & hesse) const;
+  };
+
+  Opti3FreeMinFunction :: Opti3FreeMinFunction (const PointFunction & apf)
+    : pf(apf)
+  {
+    ;
+  }
+
+  double Opti3FreeMinFunction :: Func (const Vector & x) const
+  {
+    Point<3> pp;
+    for (int j = 0; j < 3; j++)
+      pp(j) = sp1(j) + x(j);
+    return pf.PointFunctionValue (pp);
+  }
+  
+  double Opti3FreeMinFunction :: FuncGrad (const Vector & x, Vector & grad) const
+  {
+    Vec<3> vgrad;
+    Point<3> pp;
+
+    for (int j = 0; j < 3; j++)
+      pp(j) = sp1(j) + x(j);
+
+    double val = pf.PointFunctionValueGrad (pp, vgrad);
+
+    for (int j = 0; j < 3; j++)
+      grad(j) = vgrad(j);
+
+    return val;
+  }
+
+  double Opti3FreeMinFunction :: FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const
+  {
+    Point<3> pp;
+
+    for (int j = 0; j < 3; j++)
+      pp(j) = sp1(j) + x(j);
+
+    Vec<3> vdir;
+    for (int j = 0; j < 3; j++)
+      vdir(j) = dir(j);
+
+    return pf.PointFunctionValueDeriv (pp, vdir, deriv);
+  }
+  
+  double Opti3FreeMinFunction :: GradStopping (const Vector & x) const
+  {
+    double f = Func(x);
+    return 1e-3 * f / pf.GetLocalH();
+  }
+
+
+  void Opti3FreeMinFunction :: ApproximateHesse (const Vector & x,
+						 DenseMatrix & hesse) const
+  {
+    int n = x.Size();
+
+    Vector hx;
+    hx.SetSize(n);
+
+    double eps = 1e-8;
+    double f, f11, f22; //, f12, f21
+
+    f = Func(x);
+  
+    for (int i = 1; i <= n; i++)
+      {
+	for (int j = 1; j < i; j++)
+	  {
+	    /*
+	      hx = x;
+	      hx.Elem(i) = x.Get(i) + eps;
+	      hx.Elem(j) = x.Get(j) + eps;
+	      f11 = Func(hx);
+	      hx.Elem(i) = x.Get(i) + eps;
+	      hx.Elem(j) = x.Get(j) - eps;
+	      f12 = Func(hx);
+	      hx.Elem(i) = x.Get(i) - eps;
+	      hx.Elem(j) = x.Get(j) + eps;
+	      f21 = Func(hx);
+	      hx.Elem(i) = x.Get(i) - eps;
+	      hx.Elem(j) = x.Get(j) - eps;
+	      f22 = Func(hx);
+	    */
+	    hesse.Elem(i, j) = hesse.Elem(j, i) = 0;
+	    //	    (f11 + f22 - f12 - f21) / (2 * eps * eps);
+	  }
+
+	hx = x;
+	hx(i-1) = x(i-1) + eps;
+	f11 = Func(hx);
+	hx(i-1) = x(i-1) - eps;
+	f22 = Func(hx);
+
+	hesse.Elem(i, i) = (f11 + f22 - 2 * f) / (eps * eps) + 1e-12;
+      }
+  }
+
+
+
+
+
+
+#ifdef SOLIDGEOM
+  class Opti3SurfaceMinFunction : public MinFunction
+  {
+    const PointFunction & pf;
+    Point3d sp1;
+    const Surface * surf;
+    Vec3d t1, t2;
+  
+  public:
+    Opti3SurfaceMinFunction (const PointFunction & apf);
+  
+    void SetPoint (const Surface * asurf, const Point3d & asp1);
+
+    void CalcNewPoint (const Vector & x, Point3d & np) const; 
+    virtual double Func (const Vector & x) const;
+    virtual double FuncGrad (const Vector & x, Vector & g) const;
+  };
+
+
+  Opti3SurfaceMinFunction :: Opti3SurfaceMinFunction (const PointFunction & apf)
+    : MinFunction(), pf(apf)
+  {
+    ;
+  }
+
+  void Opti3SurfaceMinFunction :: SetPoint (const Surface * asurf, const Point3d & asp1)
+  { 
+    Vec3d n;
+    sp1 = asp1; 
+    surf = asurf;
+  
+    Vec<3> hn;
+    surf -> GetNormalVector (sp1, hn);
+    n = hn;
+
+    n.GetNormal (t1);
+    t1 /= t1.Length();
+    t2 = Cross (n, t1);
+  }
+
+  
+  void Opti3SurfaceMinFunction :: CalcNewPoint (const Vector & x, 
+						Point3d & np) const
+  {
+    np.X() = sp1.X() + x.Get(1) * t1.X() + x.Get(2) * t2.X();
+    np.Y() = sp1.Y() + x.Get(1) * t1.Y() + x.Get(2) * t2.Y();
+    np.Z() = sp1.Z() + x.Get(1) * t1.Z() + x.Get(2) * t2.Z();
+
+    Point<3> hnp = np;
+    surf -> Project (hnp);
+    np = hnp;
+  }
+
+
+  double Opti3SurfaceMinFunction :: Func (const Vector & x) const
+  {
+    Point3d pp1;
+
+    CalcNewPoint (x, pp1);
+    return pf.PointFunctionValue (pp1);
+  }
+
+
+
+  double Opti3SurfaceMinFunction :: FuncGrad (const Vector & x, Vector & grad) const
+  {
+    Vec3d n, vgrad;
+    Point3d pp1;
+    VectorMem<3> freegrad;
+
+    CalcNewPoint (x, pp1);
+
+    double badness = pf.PointFunctionValueGrad (pp1, freegrad);
+    vgrad.X() = freegrad.Get(1);
+    vgrad.Y() = freegrad.Get(2);
+    vgrad.Z() = freegrad.Get(3);
+
+    Vec<3> hn;
+    surf -> GetNormalVector (pp1, hn);
+    n = hn;
+
+    vgrad -= (vgrad * n) * n;
+
+    grad.Elem(1) = vgrad * t1;
+    grad.Elem(2) = vgrad * t2;
+    
+    return badness;
+  }
+#endif
+  
+  
+  
+
+
+  
+  
+  
+#ifdef SOLIDGEOM
+  class Opti3EdgeMinFunction : public MinFunction
+  {
+    const PointFunction & pf;
+    Point3d sp1;
+    const Surface *surf1, *surf2;
+    Vec3d t1;
+  
+  public:
+    Opti3EdgeMinFunction (const PointFunction & apf);
+  
+    void SetPoint (const Surface * asurf1, const Surface * asurf2,
+		   const Point3d & asp1);
+    void CalcNewPoint (const Vector & x, Point3d & np) const; 
+    virtual double FuncGrad (const Vector & x, Vector & g) const;
+    virtual double Func (const Vector & x) const;
+  };
+
+  Opti3EdgeMinFunction :: Opti3EdgeMinFunction (const PointFunction & apf)
+    : MinFunction(), pf(apf)
+  {
+    ;
+  }
+  
+  void Opti3EdgeMinFunction :: SetPoint (const Surface * asurf1, 
+					 const Surface * asurf2, 
+					 const Point3d & asp1) 
+  { 
+    Vec3d n1, n2;
+    sp1 = asp1; 
+    surf1 = asurf1;
+    surf2 = asurf2;
+
+    Vec<3> hn1, hn2;
+    surf1 -> GetNormalVector (sp1, hn1);
+    surf2 -> GetNormalVector (sp1, hn2);
+    n1 = hn1;
+    n2 = hn2;
+    t1 = Cross (n1, n2);
+  }
+
+  void Opti3EdgeMinFunction :: CalcNewPoint (const Vector & x,
+					     Point3d & np) const
+{
+  np.X() = sp1.X() + x.Get(1) * t1.X();
+  np.Y() = sp1.Y() + x.Get(1) * t1.Y();
+  np.Z() = sp1.Z() + x.Get(1) * t1.Z();
+  Point<3> hnp = np;
+  ProjectToEdge (surf1, surf2, hnp);
+  np = hnp;
+}   
+
+double Opti3EdgeMinFunction :: Func (const Vector & x) const
+{
+  Vector g(x.Size());
+  return FuncGrad (x, g);
+}
+
+
+double Opti3EdgeMinFunction :: FuncGrad (const Vector & x, Vector & grad) const
+{
+  Vec3d n1, n2, v1, vgrad;
+  Point3d pp1;
+  double badness;
+  VectorMem<3> freegrad;
+
+  CalcNewPoint (x, pp1);
+
+
+  badness = pf.PointFunctionValueGrad (pp1, freegrad);
+
+  vgrad.X() = freegrad.Get(1);
+  vgrad.Y() = freegrad.Get(2);
+  vgrad.Z() = freegrad.Get(3);
+
+  Vec<3> hn1, hn2;
+  surf1 -> GetNormalVector (pp1, hn1);
+  surf2 -> GetNormalVector (pp1, hn2);
+  n1 = hn1;
+  n2 = hn2;
+
+  v1 = Cross (n1, n2);
+  v1 /= v1.Length();
+
+  grad.Elem(1) = (vgrad * v1) * (t1 * v1);
+  return badness;
+}
+#endif
+
+
+
+
+
+double CalcTotalBad (const Mesh::T_POINTS & points, 
+		     const Mesh::T_VOLELEMENTS & elements,
+		     const MeshingParameters & mp)
+{
+  double sum = 0;
+  double elbad;
+  
+  tets_in_qualclass.SetSize(20);
+  tets_in_qualclass = 0;
+
+  double teterrpow = mp.opterrpow;
+
+  for (int i = 1; i <= elements.Size(); i++)
+    {
+      elbad = pow (max2(CalcBad (points, elements.Get(i), 0, mp),1e-10),
+		   1/teterrpow);
+
+      int qualclass = int (20 / elbad + 1);
+      if (qualclass < 1) qualclass = 1;
+      if (qualclass > 20) qualclass = 20;
+      tets_in_qualclass.Elem(qualclass)++;
+
+      sum += elbad;
+    }
+  return sum;
+}
+
+int WrongOrientation (const Mesh::T_POINTS & points, const Element & el)
+{
+  const Point3d & p1 = points[el.PNum(1)];
+  const Point3d & p2 = points[el.PNum(2)];
+  const Point3d & p3 = points[el.PNum(3)];
+  const Point3d & p4 = points[el.PNum(4)];
+
+  Vec3d v1(p1, p2);
+  Vec3d v2(p1, p3);
+  Vec3d v3(p1, p4);
+  Vec3d n;
+
+  Cross (v1, v2, n);
+  double vol = n * v3;
+
+  return (vol > 0);
+}
+
+
+
+
+
+
+
+
+
+
+
+/* ************* JacobianPointFunction **************************** */
+
+
+
+
+// class JacobianPointFunction : public MinFunction
+// {
+// public:
+//   Mesh::T_POINTS & points;
+//   const Mesh::T_VOLELEMENTS & elements;
+//   TABLE<INDEX> elementsonpoint;
+//   PointIndex actpind;
+  
+// public:
+//   JacobianPointFunction (Mesh::T_POINTS & apoints, 
+// 			 const Mesh::T_VOLELEMENTS & aelements);
+  
+//   virtual void SetPointIndex (PointIndex aactpind);
+//   virtual double Func (const Vector & x) const;
+//   virtual double FuncGrad (const Vector & x, Vector & g) const;
+//   virtual double FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const;
+// };
+
+
+JacobianPointFunction :: 
+JacobianPointFunction (Mesh::T_POINTS & apoints, 
+		       const Mesh::T_VOLELEMENTS & aelements)
+  : points(apoints), elements(aelements), elementsonpoint(apoints.Size())
+{
+  INDEX i;
+  int j;
+  
+  for (i = 1; i <= elements.Size(); i++)
+    {
+      for (j = 1; j <= elements.Get(i).NP(); j++)
+	elementsonpoint.Add1 (elements.Get(i).PNum(j), i);  
+    }
+
+  onplane = false;
+}
+
+void JacobianPointFunction :: SetPointIndex (PointIndex aactpind)
+{
+  actpind = aactpind; 
+}  
+
+
+double JacobianPointFunction :: Func (const Vector & v) const
+{
+  int j;
+  double badness = 0;
+
+  Point<3> hp = points.Elem(actpind);
+
+  points.Elem(actpind) = hp + Vec<3> (v(0), v(1), v(2));
+
+  if(onplane)
+    points.Elem(actpind) -= (v(0)*nv(0)+v(1)*nv(1)+v(2)*nv(2)) * nv;
+
+
+  for (j = 1; j <= elementsonpoint.EntrySize(actpind); j++)
+    {
+      int eli = elementsonpoint.Get(actpind, j);
+      badness += elements.Get(eli).CalcJacobianBadness (points);
+    }
+  
+  points.Elem(actpind) = hp; 
+
+  return badness;
+}
+
+
+
+
+
+double JacobianPointFunction :: 
+FuncGrad (const Vector & x, Vector & g) const
+{
+  int j, k;
+  int lpi;
+  double badness = 0;//, hbad;
+
+  Point<3> hp = points.Elem(actpind);
+  points.Elem(actpind) = hp + Vec<3> (x(0), x(1), x(2));
+
+  if(onplane)
+    points.Elem(actpind) -= (x(0)*nv(0)+x(1)*nv(1)+x(2)*nv(2)) * nv;
+
+  Vec<3> hderiv;
+  //Vec3d vdir;
+  g.SetSize(3);
+  g = 0;
+
+  for (j = 1; j <= elementsonpoint.EntrySize(actpind); j++)
+    {
+      int eli = elementsonpoint.Get(actpind, j);
+      const Element & el = elements.Get(eli);
+
+      lpi = 0;
+      for (k = 1; k <= el.GetNP(); k++)
+	if (el.PNum(k) == actpind)
+	  lpi = k;
+      if (!lpi) cerr << "loc point not found" << endl;
+
+      badness += elements.Get(eli).
+	CalcJacobianBadnessGradient (points, lpi, hderiv);
+
+      for(k=0; k<3; k++)
+	g(k) += hderiv(k);
+	
+      /*
+      for (k = 1; k <= 3; k++)
+	{
+	  vdir = Vec3d(0,0,0);
+	  vdir.X(k) = 1;
+
+	  hbad = elements.Get(eli).
+	    CalcJacobianBadnessDirDeriv (points, lpi, vdir, hderiv);
+	  //(*testout) << "hderiv " << k << ": " << hderiv << endl;
+	  g.Elem(k) += hderiv;
+	  if (k == 1)
+	    badness += hbad;
+	}
+      */
+    }
+
+  if(onplane)
+    {
+      double scal = nv(0)*g(0) + nv(1)*g(1) + nv(2)*g(2);
+      g(0) -= scal*nv(0);
+      g(1) -= scal*nv(1);
+      g(2) -= scal*nv(2);
+    }
+
+  //(*testout) << "g = " << g << endl;
+
+  
+  points.Elem(actpind) = hp; 
+
+  return badness;
+}
+
+
+double JacobianPointFunction :: 
+FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const
+{
+  int j, k;
+  int lpi;
+  double badness = 0;
+
+  Point<3> hp = points.Elem(actpind);
+  points.Elem(actpind) = Point<3> (hp + Vec3d (x(0), x(1), x(2)));
+
+  if(onplane)
+    points.Elem(actpind) -= (Vec3d (x(0), x(1), x(2))*nv) * nv;
+
+  double hderiv;
+  deriv = 0;
+  Vec<3> vdir(dir(0), dir(1), dir(2));
+ 
+  if(onplane)
+    {
+      double scal = vdir * nv;
+      vdir -= scal*nv;
+    }
+
+  for (j = 1; j <= elementsonpoint.EntrySize(actpind); j++)
+    {
+      int eli = elementsonpoint.Get(actpind, j);
+      const Element & el = elements.Get(eli);
+
+      lpi = 0;
+      for (k = 1; k <= el.GetNP(); k++)
+	if (el.PNum(k) == actpind)
+	  lpi = k;
+      if (!lpi) cerr << "loc point not found" << endl;
+
+      badness += elements.Get(eli).
+	CalcJacobianBadnessDirDeriv (points, lpi, vdir, hderiv);
+      deriv += hderiv;
+    }
+  
+  points.Elem(actpind) = hp; 
+
+  return badness;
+  
+}
+
+
+
+
+
+
+
+
+
+
+#ifdef SOLIDGEOMxxxx
+void Mesh :: ImproveMesh (const CSGeometry & geometry, OPTIMIZEGOAL goal)
+{
+  INDEX i, eli;
+  int j;
+  int typ = 1;
+
+  if (!&geometry || geometry.GetNSurf() == 0)
+    {
+      ImproveMesh (goal);
+      return;
+    }
+
+  const char * savetask = multithread.task;
+  multithread.task = "Smooth Mesh";
+
+
+  TABLE<INDEX> surfelementsonpoint(points.Size());
+  Vector x(3), xsurf(2), xedge(1);
+  int surf, surf1, surf2, surf3;
+
+  int uselocalh = mparam.uselocalh;
+
+  (*testout) << setprecision(8);
+  (*testout) << "Improve Mesh" << "\n";
+  PrintMessage (3, "ImproveMesh");
+  //  (*mycout) << "Vol = " << CalcVolume (points, volelements) << endl;
+
+
+  for (i = 1; i <= surfelements.Size(); i++)
+    for (j = 1; j <= 3; j++)
+      surfelementsonpoint.Add1 (surfelements.Get(i).PNum(j), i);
+
+
+  PointFunction * pf;
+  if (typ == 1)
+    pf = new PointFunction(points, volelements);
+  else
+    pf = new CheapPointFunction(points, volelements);
+
+  //  pf->SetLocalH (h);
+  
+  Opti3FreeMinFunction freeminf(*pf);
+  Opti3SurfaceMinFunction surfminf(*pf);
+  Opti3EdgeMinFunction edgeminf(*pf);
+  
+  OptiParameters par;
+  par.maxit_linsearch = 20;
+  par.maxit_bfgs = 20;
+
+  int printmod = 1;
+  char printdot = '.';
+  if (points.Size() > 1000)
+    {
+      printmod = 10;
+      printdot = '+';
+    }
+  if (points.Size() > 10000)
+    {
+      printmod = 100;
+      printdot = '*';
+    }
+
+  for (i = 1; i <= points.Size(); i++)
+    {
+      //      if (ptyps.Get(i) == FIXEDPOINT) continue;
+      if (ptyps.Get(i) != INNERPOINT) continue;
+
+      if (multithread.terminate)
+	throw NgException ("Meshing stopped");
+      /*
+      if (multithread.terminate)
+	break;
+      */
+      multithread.percent = 100.0 * i /points.Size();
+
+      /*
+      if (points.Size() < 1000)
+	PrintDot ();
+      else
+	if (i % 10 == 0)
+	  PrintDot ('+');
+      */
+      if (i % printmod == 0) PrintDot (printdot);
+
+      //    (*testout) << "Now point " << i << "\n";
+      //    (*testout) << "Old: " << points.Get(i) << "\n";
+
+      pf->SetPointIndex (i);
+
+      //      if (uselocalh)
+      {
+	double lh = GetH (points.Get(i));
+	pf->SetLocalH (GetH (points.Get(i)));
+	par.typx = lh / 10;
+	//	  (*testout) << "lh(" << points.Get(i) << ") = " << lh << "\n";
+      }
+
+      surf1 = surf2 = surf3 = 0;
+
+      for (j = 1; j <= surfelementsonpoint.EntrySize(i); j++)
+	{
+	  eli = surfelementsonpoint.Get(i, j);
+	  int surfi = surfelements.Get(eli).GetIndex();
+
+	  if (surfi)
+	    {
+	      surf = GetFaceDescriptor(surfi).SurfNr();
+	    
+	      if (!surf1)
+		surf1 = surf;
+	      else if (surf1 != surf)
+		{
+		  if (!surf2)
+		    surf2 = surf;
+		  else if (surf2 != surf)
+		    surf3 = surf;
+		}
+	    }
+	  else
+	    {
+	      surf1 = surf2 = surf3 = 1;   // simulates corner point
+	    }
+	}
+
+
+      if (surf2 && !surf3)
+	{
+	  //      (*testout) << "On Edge" << "\n";
+	  /*
+	    xedge = 0;
+	    edgeminf.SetPoint (geometry.GetSurface(surf1),
+	    geometry.GetSurface(surf2), 
+	    points.Elem(i));
+	    BFGS (xedge, edgeminf, par);
+
+	    edgeminf.CalcNewPoint (xedge, points.Elem(i));
+	  */
+	}
+
+      if (surf1 && !surf2)
+	{
+	  //      (*testout) << "In Surface" << "\n";
+	  /*
+	    xsurf = 0;
+	    surfminf.SetPoint (geometry.GetSurface(surf1),
+	    points.Get(i));
+	    BFGS (xsurf, surfminf, par);
+   
+	    surfminf.CalcNewPoint (xsurf, points.Elem(i));
+	  */
+	}
+ 
+      if (!surf1)
+	{
+	  //      (*testout) << "In Volume" << "\n";
+	  x = 0;
+	  freeminf.SetPoint (points.Elem(i));
+	  //	  par.typx = 
+	  BFGS (x, freeminf, par);
+
+	  points.Elem(i).X() += x.Get(1);
+	  points.Elem(i).Y() += x.Get(2);
+	  points.Elem(i).Z() += x.Get(3);
+	}
+      
+      //    (*testout) << "New Point: " << points.Elem(i) << "\n" << "\n";
+    
+    }
+  PrintDot ('\n');
+  //  (*mycout) << "Vol = " << CalcVolume (points, volelements) << endl;
+
+  multithread.task = savetask;
+
+}
+#endif
+
+
+
+  
+void Mesh :: ImproveMesh (const MeshingParameters & mp, OPTIMIZEGOAL goal)
+{
+  int typ = 1;
+  
+  (*testout) << "Improve Mesh" << "\n";
+  PrintMessage (3, "ImproveMesh");
+
+  int np = GetNP();
+  int ne = GetNE();
+
+
+  Array<double,PointIndex::BASE> perrs(np);
+  perrs = 1.0;
+
+  double bad1 = 0;
+  double badmax = 0;
+
+  if (goal == OPT_QUALITY)
+    {
+      for (int i = 1; i <= ne; i++)
+	{
+	  const Element & el = VolumeElement(i);
+	  if (el.GetType() != TET)
+	    continue;
+	  
+	  double hbad = CalcBad (points, el, 0, mp);
+	  for (int j = 0; j < 4; j++)
+	    perrs[el[j]] += hbad;
+	  
+	  bad1 += hbad;
+	}
+      
+      for (PointIndex i = PointIndex::BASE; i < np+PointIndex::BASE; i++)
+	if (perrs[i] > badmax) 
+	  badmax = perrs[i];
+      badmax = 0;
+    }
+
+  if (goal == OPT_QUALITY)
+    {
+      bad1 = CalcTotalBad (points, volelements, mp);
+      (*testout) << "Total badness = " << bad1 << endl;
+      PrintMessage (5, "Total badness = ", bad1);
+    }
+  
+  Vector x(3);
+  
+  (*testout) << setprecision(8);
+  
+  //int uselocalh = mparam.uselocalh;
+
+
+  PointFunction * pf;
+
+  if (typ == 1)
+    pf = new PointFunction(points, volelements, mp);
+  else
+    pf = new CheapPointFunction(points, volelements, mp);
+
+  //  pf->SetLocalH (h);
+  
+  Opti3FreeMinFunction freeminf(*pf);
+
+  OptiParameters par;
+  par.maxit_linsearch = 20;
+  par.maxit_bfgs = 20;
+
+  Array<double, PointIndex::BASE> pointh (points.Size());
+
+  if(lochfunc)
+    {
+      for(int i=1; i<=points.Size(); i++)
+	pointh[i] = GetH(points.Get(i));
+    }
+  else
+    {
+      pointh = 0;
+      for(int i=0; i<GetNE(); i++)
+	{
+	  const Element & el = VolumeElement(i+1);
+	  double h = pow(el.Volume(points),1./3.);
+	  for(int j=1; j<=el.GetNV(); j++)
+	    if(h > pointh[el.PNum(j)])
+	      pointh[el.PNum(j)] = h;
+	}
+    }
+ 
+
+  int printmod = 1;
+  char printdot = '.';
+  if (points.Size() > 1000)
+    {
+      printmod = 10;
+      printdot = '+';
+    }
+  if (points.Size() > 10000)
+    {
+      printmod = 100;
+      printdot = '*';
+    }
+
+
+  const char * savetask = multithread.task;
+  multithread.task = "Smooth Mesh";
+  
+  for (PointIndex i = PointIndex::BASE; 
+       i < points.Size()+PointIndex::BASE; i++)
+    if ( (*this)[i].Type() == INNERPOINT && perrs[i] > 0.01 * badmax)
+      {
+	if (multithread.terminate)
+	  throw NgException ("Meshing stopped");
+
+	multithread.percent = 100.0 * (i+1-PointIndex::BASE) / points.Size();
+        /*
+	if (points.Size() < 1000)
+	  PrintDot ();
+	else
+	  if ( (i+1-PointIndex::BASE) % 10 == 0)
+	    PrintDot ('+');
+        */
+        if (  (i+1-PointIndex::BASE) % printmod == 0) PrintDot (printdot);
+
+	double lh = pointh[i];
+	pf->SetLocalH (lh);
+	par.typx = lh;
+
+	freeminf.SetPoint (points[i]);
+	pf->SetPointIndex (i);
+
+	x = 0;
+	int pok;
+	pok = freeminf.Func (x) < 1e10; 
+
+	if (!pok)
+	  {
+	    pok = pf->MovePointToInner ();
+
+	    freeminf.SetPoint (points[i]);
+	    pf->SetPointIndex (i);
+	  }
+
+	if (pok)
+	  {
+            //*testout << "start BFGS, pok" << endl;
+	    BFGS (x, freeminf, par);
+            //*testout << "BFGS complete, pok" << endl;
+	    points[i](0) += x(0);
+	    points[i](1) += x(1);
+	    points[i](2) += x(2);
+	  }
+      }
+  PrintDot ('\n');
+  
+  
+  delete pf;
+
+  multithread.task = savetask;
+
+  if (goal == OPT_QUALITY)
+    {
+      bad1 = CalcTotalBad (points, volelements, mp);
+      (*testout) << "Total badness = " << bad1 << endl;
+      PrintMessage (5, "Total badness = ", bad1);
+    }
+}
+
+
+
+
+// Improve Condition number of Jacobian, any elements  
+void Mesh :: ImproveMeshJacobian (const MeshingParameters & mp,
+				  OPTIMIZEGOAL goal, const BitArray * usepoint)
+{
+  int i, j;
+  
+  (*testout) << "Improve Mesh Jacobian" << "\n";
+  PrintMessage (3, "ImproveMesh Jacobian");
+
+  int np = GetNP();
+  int ne = GetNE();
+
+  
+  Vector x(3);
+  
+  (*testout) << setprecision(8);
+  
+  JacobianPointFunction pf(points, volelements);
+  
+
+  OptiParameters par;
+  par.maxit_linsearch = 20;
+  par.maxit_bfgs = 20;
+  
+  BitArray badnodes(np);
+  badnodes.Clear();
+
+  for (i = 1; i <= ne; i++)
+    {
+      const Element & el = VolumeElement(i);
+      double bad = el.CalcJacobianBadness (Points());
+      if (bad > 1)
+	for (j = 1; j <= el.GetNP(); j++)
+	  badnodes.Set (el.PNum(j));
+    }
+
+  Array<double, PointIndex::BASE> pointh (points.Size());
+
+  if(lochfunc)
+    {
+      for(i = 1; i<=points.Size(); i++)
+	pointh[i] = GetH(points.Get(i));
+    }
+  else
+    {
+      pointh = 0;
+      for(i=0; i<GetNE(); i++)
+	{
+	  const Element & el = VolumeElement(i+1);
+	  double h = pow(el.Volume(points),1./3.);
+	  for(j=1; j<=el.GetNV(); j++)
+	    if(h > pointh[el.PNum(j)])
+	      pointh[el.PNum(j)] = h;
+	}
+    }
+ 
+
+
+  const char * savetask = multithread.task;
+  multithread.task = "Smooth Mesh Jacobian";
+  
+  for (i = 1; i <= points.Size(); i++)
+    {
+      if ((*this)[PointIndex(i)].Type() != INNERPOINT)
+	continue;
+
+      if(usepoint && !usepoint->Test(i))
+	continue;
+
+      //(*testout) << "improvejac, p = " << i << endl;
+
+      if (goal == OPT_WORSTCASE && !badnodes.Test(i))
+	continue;
+      //	(*testout) << "smoot p " << i << endl;
+
+      /*
+	if (multithread.terminate)
+	break;
+      */
+      if (multithread.terminate)
+	throw NgException ("Meshing stopped");
+
+      multithread.percent = 100.0 * i / points.Size();
+
+      if (points.Size() < 1000)
+	PrintDot ();
+      else
+	if (i % 10 == 0)
+	  PrintDot ('+');
+
+      double lh = pointh[i];
+      par.typx = lh;
+
+      pf.SetPointIndex (i);
+
+      x = 0;
+      int pok = (pf.Func (x) < 1e10); 
+
+      if (pok)
+	{
+          //*testout << "start BFGS, Jacobian" << endl;
+	  BFGS (x, pf, par);
+          //*testout << "end BFGS, Jacobian" << endl;
+	  points.Elem(i)(0) += x(0);
+	  points.Elem(i)(1) += x(1);
+	  points.Elem(i)(2) += x(2);
+	}
+      else
+	{
+	  cout << "el not ok" << endl;
+	}
+    }
+  PrintDot ('\n');
+  
+
+  multithread.task = savetask;
+}
+
+
+
+
+// Improve Condition number of Jacobian, any elements  
+void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp,
+					   const BitArray & usepoint, 
+					   const Array< Vec<3>* > & nv,
+					   OPTIMIZEGOAL goal,
+					   const Array< Array<int,PointIndex::BASE>* > * idmaps)
+{
+  int i, j;
+  
+  (*testout) << "Improve Mesh Jacobian" << "\n";
+  PrintMessage (3, "ImproveMesh Jacobian");
+
+  int np = GetNP();
+  int ne = GetNE();
+
+  
+  Vector x(3);
+  
+  (*testout).precision(8);
+  
+  JacobianPointFunction pf(points, volelements);
+
+  Array< Array<int,PointIndex::BASE>* > locidmaps;
+  const Array< Array<int,PointIndex::BASE>* > * used_idmaps;
+
+  if(idmaps)
+    used_idmaps = idmaps;
+  else
+    {
+      used_idmaps = &locidmaps;
+      
+      for(i=1; i<=GetIdentifications().GetMaxNr(); i++)
+	{
+	  if(GetIdentifications().GetType(i) == Identifications::PERIODIC)
+	    {
+	      locidmaps.Append(new Array<int,PointIndex::BASE>);
+	      GetIdentifications().GetMap(i,*locidmaps.Last(),true);
+	    }
+	}
+    }
+
+  
+  bool usesum = (used_idmaps->Size() > 0);
+  MinFunctionSum pf_sum;
+  
+  JacobianPointFunction * pf2ptr = NULL;
+  if(usesum)
+    {
+      pf2ptr = new JacobianPointFunction(points, volelements);
+      pf_sum.AddFunction(pf);
+      pf_sum.AddFunction(*pf2ptr);
+    }
+  
+
+  OptiParameters par;
+  par.maxit_linsearch = 20;
+  par.maxit_bfgs = 20;
+  
+  BitArray badnodes(np);
+  badnodes.Clear();
+
+  for (i = 1; i <= ne; i++)
+    {
+      const Element & el = VolumeElement(i);
+      double bad = el.CalcJacobianBadness (Points());
+      if (bad > 1)
+	for (j = 1; j <= el.GetNP(); j++)
+	  badnodes.Set (el.PNum(j));
+    }
+
+  Array<double, PointIndex::BASE> pointh (points.Size());
+ 
+  if(lochfunc)
+    {
+      for(i=1; i<=points.Size(); i++)
+	pointh[i] = GetH(points.Get(i));
+    }
+  else
+    {
+      pointh = 0;
+      for(i=0; i<GetNE(); i++)
+	{
+	  const Element & el = VolumeElement(i+1);
+	  double h = pow(el.Volume(points),1./3.);
+	  for(j=1; j<=el.GetNV(); j++)
+	    if(h > pointh[el.PNum(j)])
+	      pointh[el.PNum(j)] = h;
+	}
+    }
+
+
+  const char * savetask = multithread.task;
+  multithread.task = "Smooth Mesh Jacobian";
+  
+  for (i = 1; i <= points.Size(); i++)
+    if ( usepoint.Test(i) )
+      {
+	//(*testout) << "improvejac, p = " << i << endl;
+
+	if (goal == OPT_WORSTCASE && !badnodes.Test(i))
+	  continue;
+	//	(*testout) << "smoot p " << i << endl;
+
+	/*
+	if (multithread.terminate)
+	  break;
+	*/
+	if (multithread.terminate)
+	  throw NgException ("Meshing stopped");
+
+	multithread.percent = 100.0 * i / points.Size();
+
+	if (points.Size() < 1000)
+	  PrintDot ();
+	else
+	  if (i % 10 == 0)
+	    PrintDot ('+');
+
+	double lh = pointh[i];//GetH(points.Get(i));
+	par.typx = lh;
+
+	pf.SetPointIndex (i);
+
+	int brother = -1;
+	if(usesum)
+	  {
+	    for(j=0; brother == -1 && j<used_idmaps->Size(); j++)
+	      {
+		if(i < (*used_idmaps)[j]->Size() + PointIndex::BASE)
+		  {
+		    brother = (*(*used_idmaps)[j])[i];
+		    if(brother == i || brother == 0)
+		      brother = -1;
+		  }
+	      }
+	    if(brother >= i)
+	      {
+		pf2ptr->SetPointIndex(brother);
+		pf2ptr->SetNV(*nv[brother-1]);
+	      }
+	  }
+
+	if(usesum && brother < i)
+	  continue;
+
+	//pf.UnSetNV(); x = 0;
+	//(*testout) << "before " << pf.Func(x);
+
+	pf.SetNV(*nv[i-1]);
+
+	x = 0;
+	int pok = (brother == -1) ? (pf.Func (x) < 1e10) : (pf_sum.Func (x) < 1e10);
+
+	if (pok)
+	  {
+	    
+	    if(brother == -1)
+	      BFGS (x, pf, par);
+	    else
+	      BFGS (x, pf_sum, par);
+
+
+	    for(j=0; j<3; j++)
+	      points.Elem(i)(j) += x(j);// - scal*nv[i-1].X(j);
+
+	    if(brother != -1)
+	      for(j=0; j<3; j++)
+		points.Elem(brother)(j) += x(j);// - scal*nv[brother-1].X(j);
+
+
+	  }
+	else
+	  {
+	    cout << "el not ok" << endl;
+	    (*testout) << "el not ok" << endl
+		       << "   func " << ((brother == -1) ? pf.Func(x) : pf_sum.Func (x)) << endl;
+	    if(brother != -1)
+	      (*testout) << "   func1 " << pf.Func(x) << endl
+			 << "   func2 " << pf2ptr->Func(x) << endl;
+	  }
+      }
+  
+  PrintDot ('\n');
+
+  delete pf2ptr;
+  for(i=0; i<locidmaps.Size(); i++)
+    delete locidmaps[i];
+
+  multithread.task = savetask;
+}
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/specials.cpp b/contrib/Netgen/libsrc/meshing/specials.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0a218e10a617d2df7cce174b7853646cd4a0f605
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/specials.cpp
@@ -0,0 +1,193 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+
+// A special function for Hermann Landes, Erlangen
+
+
+void CutOffAndCombine (Mesh & mesh, const Mesh & othermesh)
+{
+  int i, j;
+  int nse = othermesh.GetNSE();
+  int onp = othermesh.GetNP();
+
+  int ne = mesh.GetNE();
+
+  PrintMessage (1, "other mesh has ",
+		othermesh.GetNP(), " points, ",
+		othermesh.GetNSE(), " surface elements.");
+
+  Array<Box3d> otherbounds(nse);  
+  Box3d otherbox;
+
+  double maxh = 0;
+  for (i = 1; i <= nse; i++)
+    {
+      const Element2d & sel = othermesh.SurfaceElement(i);
+      sel.GetBox(othermesh.Points(), otherbounds.Elem(i));
+
+      double loch = othermesh.GetH (othermesh.Point (sel.PNum(1)));
+      otherbounds.Elem(i).Increase(loch);
+      if (loch > maxh) maxh = loch;
+    }
+
+  otherbox.SetPoint (othermesh.Point(1));
+  for (i = 1; i <= othermesh.GetNP(); i++)
+    otherbox.AddPoint (othermesh.Point(i));
+  otherbox.Increase (maxh);
+
+  for (i = 1; i <= ne; i++)
+    {
+      Box3d box;
+      int remove = 0;
+
+      const Element & el = mesh.VolumeElement(i);
+      el.GetBox(mesh.Points(), box);
+
+      if (i % 10000 == 0)
+	cout << "+" << flush;
+
+      if (box.Intersect(otherbox))
+	{
+	  for (j = 1; j <= nse && !remove; j++)
+	    if (box.Intersect(otherbounds.Get(j)))
+	      remove = 1;
+	}
+
+      if (remove)
+	mesh.VolumeElement(i).Delete();
+    }
+  cout << endl;
+
+  BitArray connected(mesh.GetNP());
+  connected.Clear();
+  for (i = 1; i <= mesh.GetNSE(); i++)
+    {
+      const Element2d & el = mesh.SurfaceElement(i);
+      for (j = 1; j <= 3; j++)
+	connected.Set(el.PNum(j));
+    }
+  
+  bool changed;
+  do
+    {
+      changed = 0;
+      for (i = 1; i <= mesh.GetNE(); i++)
+	{
+	  const Element & el = mesh.VolumeElement(i);
+	  int has = 0, hasnot = 0;
+	  if (el[0])
+	    {
+	      for (j = 0; j < 4; j++)
+		{
+		  if (connected.Test(el[j]))
+		    has = 1;
+		  else
+		    hasnot = 1;
+		}
+	      if (has && hasnot)
+		{
+		  changed = 1;
+		  for (j = 0; j < 4; j++)
+		    connected.Set (el[j]);
+		}
+	    }
+	}
+      cout << "." << flush;
+    }
+  while (changed);
+  cout << endl;
+
+  for (i = 1; i <= mesh.GetNE(); i++)
+    {
+      const Element & el = mesh.VolumeElement(i);
+      int hasnot = 0;
+      if (el[0])
+	{
+	  for (j = 0; j < 4; j++)
+	    {
+	      if (!connected.Test(el[j]))
+		hasnot = 1;
+	    }
+	  if (hasnot)
+	    mesh.VolumeElement(i).Delete();
+	}
+    }
+
+  mesh.Compress();
+  
+  mesh.FindOpenElements();
+  BitArray locked(mesh.GetNP());
+  locked.Set();
+  for (i = 1; i <= mesh.GetNOpenElements(); i++)
+    for (j = 1; j <= 3; j++)
+      locked.Clear (mesh.OpenElement(i).PNum(j));
+
+  for (i = 1; i <= locked.Size(); i++)
+    if (locked.Test(i))
+      {
+	mesh.AddLockedPoint (i);
+      }
+
+
+
+  
+  Array<int> pmat(onp);
+
+  for (i = 1; i <= onp; i++)
+    pmat.Elem(i) = mesh.AddPoint (othermesh.Point(i));
+
+  int fnum = 
+    mesh.AddFaceDescriptor (FaceDescriptor(0,0,1,0));
+
+  for (i = 1; i <= othermesh.GetNSE(); i++)
+    {
+      Element2d tri = othermesh.SurfaceElement(i);
+      for (j = 1; j <= 3; j++)
+	tri.PNum(j) = pmat.Get(tri.PNum(j));
+      tri.SetIndex(fnum);
+      mesh.AddSurfaceElement (tri);
+    }
+
+  for (i = 1; i <= onp; i++)
+    mesh.AddLockedPoint (pmat.Elem(i));
+
+  mesh.CalcSurfacesOfNode();
+  mesh.CalcLocalH(0.3);
+}
+
+
+
+
+void HelmholtzMesh (Mesh & mesh)
+{
+  int i;
+  double ri, ra, rinf;
+
+  cout << "ri = ";
+  cin >> ri;
+  cout << "ra = ";
+  cin >> ra;
+  cout << "rinf = ";
+  cin >> rinf;
+
+  double det = ri * ra * rinf - ri * ri * rinf;
+  double a = (ri - rinf) / det;
+  double b = (ri*ri - ra * rinf) / det;
+  for (i = 1; i <= mesh.GetNP(); i++)
+    {
+      Point<3> & p = mesh.Point(i);
+      double rold = sqrt (sqr(p(0)) + sqr(p(1)) + sqr(p(2)));
+      if (rold < ri) continue;
+
+      double rnew = 1 / (a * rold - b);
+      double fac = rnew / rold;
+      p(0) *= fac;
+      p(1) *= fac;
+      p(2) *= fac;
+    }
+}
+}
diff --git a/contrib/Netgen/libsrc/meshing/specials.hpp b/contrib/Netgen/libsrc/meshing/specials.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..700ba4596bf705f260ca5d7b355d5bd35c96fe7b
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/specials.hpp
@@ -0,0 +1,16 @@
+#ifndef FILE_SPECIALS
+#define FILE_SPECIALS
+
+/*
+
+  Very special implementations ..
+  
+ */
+
+
+///
+extern void CutOffAndCombine (Mesh & mesh, const Mesh & othermesh);
+
+extern void HelmholtzMesh (Mesh & mesh);
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/tetrarls.cpp b/contrib/Netgen/libsrc/meshing/tetrarls.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cb28648b6a641e8d50527d51dac512f0d9273847
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/tetrarls.cpp
@@ -0,0 +1,1466 @@
+namespace netgen
+{
+const char * tetrules[] = {
+"tolfak 0.5\n",\
+"\n",\
+"rule \"Free Tetrahedron\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0);\n",\
+"(0.5, 0.866, 0);\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.288, -0.816)\n",\
+"	{ 0.333 X1, 0.333 X2, 0.333 X3 }\n",\
+"	{ 0.333 Y1, 0.333 Y2, 0.333 Y3 } { };\n",\
+"\n",\
+"newfaces\n",\
+"(4, 1, 2);\n",\
+"(4, 2, 3);\n",\
+"(4, 3, 1);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1.6 P4, -0.2 P1, -0.2 P2, -0.2 P3 };\n",\
+"{ -0.5 P1, 0.5 P2, 0.5 P3, 0.5 P4 };\n",\
+"{ 0.5 P1, -0.5 P2, 0.5 P3, 0.5 P4 };\n",\
+"{ 0.5 P1, 0.5 P2, -0.5 P3, 0.5 P4 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron 60\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"flags c;\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 } ;\n",\
+"(0.5, 0.866, 0) { 0.5 };\n",\
+"(0.5, 0.288, -0.816) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 4, 3);\n",\
+"(4, 2, 3);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ -0.35 P1, 0.45 P2, 0.45 P3, 0.45 P4 };\n",\
+"{ 0.45 P1, -0.35 P2, 0.45 P3, 0.45 P4 };\n",\
+"{ -0.05 P1, -0.05 P2, 0.7 P3, 0.4 P4 };\n",\
+"\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.65 P3, 0.35 P4 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron 60 with edge(1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"flags c;\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.8 };\n",\
+"(0.5, 0.866, 0) { 0.8 };\n",\
+"(0.5, 0.288, -0.816) { 0.8 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"\n",\
+"mapedges\n",\
+"(3, 4);\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 4, 3);\n",\
+"(4, 2, 3);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 0.4 P1, 0.4 P4, 0.4 P3, -0.2 P2 };\n",\
+"{ 0.4 P2, 0.4 P4, 0.4 P3, -0.2 P1 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P4, 0.3334 P3 };\n",\
+"{ 0.3333 P2, 0.3333 P4, 0.3334 P3 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron Vis a Vis Point (1)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 0.866, 0) { 0.5 };\n",\
+"(0.5, 0.288, -0.816) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(4, 3, 1);\n",\
+"(4, 2, 3);\n",\
+"(4, 1, 2);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ -0.5 P1, 0.5 P2, 0.5 P3, 0.5 P4 };\n",\
+"{ 0.5 P1, -0.5 P2, 0.5 P3, 0.5 P4 };\n",\
+"{ 0.5 P1, 0.5 P2, -0.5 P3, 0.5 P4 };\n",\
+"{ 0.8 P1, -0.1 P2, -0.1 P3, 0.4 P4 };\n",\
+"{ -0.1 P1, 0.8 P2, -0.1 P3, 0.4 P4 };\n",\
+"{ -0.1 P1, -0.1 P2, 0.8 P3, 0.4 P4 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P2, 0.3334 P4 };\n",\
+"{ 0.7 P1, 0.3 P4 };\n",\
+"{ 0.7 P2, 0.3 P4 };\n",\
+"{ 0.7 P3, 0.3 P4 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron Vis a Vis Point with edge(1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 0.866, 0) { 0.5 };\n",\
+"(0.5, 0.288, -0.816) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"\n",\
+"mapedges\n",\
+"(1, 4);\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(4, 3, 1);\n",\
+"(4, 2, 3);\n",\
+"(4, 1, 2);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ -0.35 P1, 0.45 P2, 0.45 P3, 0.45 P4 };\n",\
+"{ 0.45 P1, -0.35 P2, 0.45 P3, 0.45 P4 };\n",\
+"{ 0.45 P1, 0.45 P2, -0.35 P3, 0.45 P4 };\n",\
+"{ -0.05 P1, 0.7 P2, -0.05 P3, 0.4 P4 };\n",\
+"{ -0.05 P1, -0.05 P2, 0.7 P3, 0.4 P4 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P2, 0.3334 P4 };\n",\
+"{ 0.65 P2, 0.35 P4 };\n",\
+"{ 0.65 P3, 0.35 P4 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron Vis a Vis Point with 2 edges (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 0.866, 0) { 0.5 };\n",\
+"(0.5, 0.288, -0.816) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"\n",\
+"mapedges\n",\
+"(1, 4);\n",\
+"(2, 4);\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(4, 3, 1);\n",\
+"(4, 2, 3);\n",\
+"(4, 1, 2);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ -0.35 P1, 0.45 P2, 0.45 P3, 0.45 P4 };\n",\
+"{ 0.45 P1, -0.35 P2, 0.45 P3, 0.45 P4 };\n",\
+"{ 0.45 P1, 0.45 P2, -0.35 P3, 0.45 P4 };\n",\
+"{ -0.05 P1, -0.05 P2, 0.7 P3, 0.4 P4 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P2, 0.3334 P4 };\n",\
+"{ 0.65 P3, 0.35 P4 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron Vis a Vis Point with 3 edges (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 0.866, 0) { 0.5 };\n",\
+"(0.5, 0.288, -0.816) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"\n",\
+"mapedges\n",\
+"(1, 4);\n",\
+"(2, 4);\n",\
+"(3, 4);\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(4, 3, 1);\n",\
+"(4, 2, 3);\n",\
+"(4, 1, 2);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ -0.35 P1, 0.45 P2, 0.45 P3, 0.45 P4 };\n",\
+"{ 0.45 P1, -0.35 P2, 0.45 P3, 0.45 P4 };\n",\
+"{ 0.45 P1, 0.45 P2, -0.35 P3, 0.45 P4 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P2, 0.3334 P4 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron Vis a Vis Triangle (1)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 0.866, 0) { 0.5 };\n",\
+"(0, 0, -0.816) { 0.5 };\n",\
+"(1, 0, -0.816) { 0.5 };\n",\
+"(0.5, 0.866, -0.816) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(4, 6, 5) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 4);\n",\
+"(2, 5, 4);\n",\
+"(2, 3, 6);\n",\
+"(2, 6, 5);\n",\
+"(3, 1, 4);\n",\
+"(3, 4, 6);\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"(4, 2, 3, 6);\n",\
+"(4, 2, 6, 5);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ -0.2 P1, 0.35 P2, 0.35 P3, -0.2 P4, 0.35 P5, 0.35 P6 };\n",\
+"{ 0.35 P1, -0.2 P2, 0.35 P3, 0.35 P4, -0.2 P5, 0.35 P6 };\n",\
+"{ 0.35 P1, 0.35 P2, -0.2 P3, 0.35 P4, 0.35 P5, -0.2 P6 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Octaeder 1\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.95 };\n",\
+"(0.5, 0.866, 0) { 0.95 };\n",\
+"(0.5, -0.288, -0.816) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(1, 0.578, -0.816) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\
+"(0, 0.578, -0.816) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\
+"\n",\
+"newfaces\n",\
+"(2, 3, 5);\n",\
+"(3, 1, 6);\n",\
+"(3, 6, 5);\n",\
+"(2, 5, 4);\n",\
+"(1, 4, 6);\n",\
+"(4, 5, 6);\n",\
+"\n",\
+"elements\n",\
+"(3, 4, 1, 2);\n",\
+"(3, 4, 2, 5);\n",\
+"(3, 4, 5, 6);\n",\
+"(3, 4, 6, 1);\n",\
+"\n",\
+"freezone\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 X2 } { } { };\n",\
+"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\
+"(0.5, -0.288, -0.816) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\
+"(-0.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 } { 1 Z4 };\n",\
+"( 1.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 } { 1 Z4 };\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Octaeder 2\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.95 };\n",\
+"(0.5, 0.866, 0) { 0.95 };\n",\
+"(0.5, -0.288, -0.816) { 0.5 };\n",\
+"(1, 0.578, -0.816) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0, 0.578, -0.816) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\
+"\n",\
+"newfaces\n",\
+"(2, 3, 5);\n",\
+"(3, 1, 6);\n",\
+"(3, 6, 5);\n",\
+"(2, 5, 4);\n",\
+"(1, 4, 6);\n",\
+"(4, 5, 6);\n",\
+"\n",\
+"elements\n",\
+"(3, 4, 1, 2);\n",\
+"(3, 4, 2, 5);\n",\
+"(3, 4, 5, 6);\n",\
+"(3, 4, 6, 1);\n",\
+"\n",\
+"freezone\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 X2 } { } { };\n",\
+"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\
+"(0.5, -0.288, -0.816) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\
+"(1, 0.578, -0.816) { 1 X5 } { 1 Y5 } { 1 Z5 };\n",\
+"\n",\
+"(0.9, 0.097, -0.544) { 0.333 X2, 0.333 X4, 0.333 X5 }\n",\
+"                     { 0.333 Y2, 0.333 Y4, 0.333 Y5 }\n",\
+"                     { 0.333 Z2, 0.333 Z4, 0.333 Z5 };\n",\
+"(0.9, 0.481, -0.272) { 0.333 X2, 0.333 X3, 0.333 X5 }\n",\
+"                     { 0.333 Y2, 0.333 Y3, 0.333 Y5 }\n",\
+"                     { 0.333 Z2, 0.333 Z3, 0.333 Z5 };\n",\
+"\n",\
+"(-0.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 } { 0.5 Z4, 0.5 Z5 };\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"rule \"Octaeder 2a\"\n",\
+"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.95 };\n",\
+"(0.5, 0.866, 0) { 0.95 };\n",\
+"(0.5, -0.288, -0.816) { 0.5 };\n",\
+"(1, 0.578, -0.816) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(3, 2, 5) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0, 0.578, -0.816)\n",\
+"	{ -1 X2, 1 X3, 1 X4 }\n",\
+"	{ -1 Y2, 1 Y3, 1 Y4 }\n",\
+"	{ -1 Z2, 1 Z3, 1 Z4 };\n",\
+"\n",\
+"newfaces\n",\
+"(1, 2, 4);\n",\
+"(3, 1, 6);\n",\
+"(3, 6, 5);\n",\
+"(2, 5, 4);\n",\
+"(1, 4, 6);\n",\
+"(4, 5, 6);\n",\
+"\n",\
+"elements\n",\
+"(3, 4, 1, 2);\n",\
+"(3, 4, 2, 5);\n",\
+"(3, 4, 5, 6);\n",\
+"(3, 4, 6, 1);\n",\
+"\n",\
+"freezone\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 X2 } { } { };\n",\
+"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\
+"(0.5, -0.288, -0.816) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\
+"(1, 0.578, -0.816) { 1 X5 } { 1 Y5 } { 1 Z5 };\n",\
+"\n",\
+"(0.9, 0.097, -0.544) { 0.333 X2, 0.333 X4, 0.333 X5 }\n",\
+"                     { 0.333 Y2, 0.333 Y4, 0.333 Y5 }\n",\
+"                     { 0.333 Z2, 0.333 Z4, 0.333 Z5 };\n",\
+"\n",\
+"(0.5, -0.097, -0.272) { 0.333 X2, 0.333 X4, 0.333 X1 }\n",\
+"                     { 0.333 Y2, 0.333 Y4, 0.333 Y1 }\n",\
+"                     { 0.333 Z2, 0.333 Z4, 0.333 Z1 };\n",\
+"\n",\
+"(-0.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 } { 0.5 Z4, 0.5 Z5 };\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Pyramid 1\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 };\n",\
+"(0.5, 0.866, 0) { 1 };\n",\
+"(0.5, -0.288, -0.816) { 1 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(1, 0.578, -0.816) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\
+"\n",\
+"newfaces\n",\
+"(1, 4, 3);\n",\
+"(2, 3, 5);\n",\
+"(2, 5, 4);\n",\
+"(4, 5, 3);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"(4, 2, 3, 5);\n",\
+"\n",\
+"\n",\
+"freezone\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 X2 } { } { };\n",\
+"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\
+"(0.5, -0.288, -0.816) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\
+"(0, 1, -1) { 0.5 X3, 0.5 X4 } { 1 Y3 } { 1 Z4 };\n",\
+"(1.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron 2 times 60\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.3 };\n",\
+"(0.5, 0.866, 0) { 0.3 };\n",\
+"(0.5, 0.288, -0.816) { 0.3 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"(2, 4, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"(1, 4, 3);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 0.4 P1, 0.4 P4, 0.4 P3, -0.2 P2 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Fill Tetrahedron (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.2 };\n",\
+"(0.5, 0.866, 0) { 0.2 };\n",\
+"(0.5, 0.288, -0.816) { 0.2 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"(2, 4, 3) del;\n",\
+"(3, 4, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newfaces\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron 120 (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 };\n",\
+"(0.5, 0.866, 0) { 1 };\n",\
+"(0.5, -0.674, -0.544) { 1 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.288, -0.816)\n",\
+"	{ -0.5 X1, -0.5 X2, 1 X3, 1 X4 }\n",\
+"	{ -0.5 Y1, -0.5 Y2, 1 Y3, 1 Y4}\n",\
+"	{ -0.5 Z1, -0.5 Z2, 1 Z3, 1 Z4};\n",\
+"\n",\
+"newfaces\n",\
+"(1, 5, 3);\n",\
+"(3, 5, 2);\n",\
+"(1, 4, 5);\n",\
+"(2, 5, 4);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 5);\n",\
+"(1, 4, 2, 5);\n",\
+"\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1.3 P5, -0.3 P1 };\n",\
+"{ 1.3 P5, -0.3 P2 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P5 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron 2 times 120 (1)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 };\n",\
+"(0.5, 0.866, 0) { 1 };\n",\
+"(0.5, -0.674, -0.544) { 0.8 };\n",\
+"(1.334, 0.77, -0.544) { 0.8 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"(3, 2, 5) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.288, -0.816) { 0.25 X1, -0.5 X2, 0.25 X3, 0.5 X4, 0.5 X5 }\n",\
+"		 { 0.25 Y1, -0.5 Y2, 0.25 Y3, 0.5 Y4, 0.5 Y5 }\n",\
+"		 { 0.25 Z1, -0.5 Z2, 0.25 Z3, 0.5 Z4, 0.5 Z5 };\n",\
+"\n",\
+"newfaces\n",\
+"(6, 3, 1);\n",\
+"(6, 1, 4);\n",\
+"(6, 4, 2);\n",\
+"(6, 2, 5);\n",\
+"(6, 5, 3);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 6);\n",\
+"(1, 4, 2, 6);\n",\
+"(2, 5, 3, 6);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1.4 P6, -0.4 P2 };\n",\
+"{ 1.4 P6, -0.4 P1 };\n",\
+"{ 1.4 P6, -0.4 P3 };\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"four Tetrahedron non convex (4)\"\n",\
+"\n",\
+"quality 4\n",\
+"flags l;\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.1 };\n",\
+"(0.5, 1, 0) { 0.1 };\n",\
+"(0.5, 0, -1) { 0.1 };\n",\
+"(0.5, 0.3, -0.3) { 0.1 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"(1, 5, 4) del;\n",\
+"(1, 3, 5) del;\n",\
+"\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.1, -0.1)\n",\
+"	 { 0.333 X1, 0.333 X2, 0.334 X5 }\n",\
+"	 { 0.333 Y1, 0.333 Y2, 0.334 Y5 }\n",\
+"	 { 0.333 Z1, 0.333 Z2, 0.334 Z5 };\n",\
+"\n",\
+"newfaces\n",\
+"(6, 2, 3) del;\n",\
+"(6, 4, 2) del;\n",\
+"(6, 5, 4) del;\n",\
+"(6, 3, 5) del;\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 6);\n",\
+"(1, 4, 2, 6);\n",\
+"(1, 5, 4, 6);\n",\
+"(1, 3, 5, 6);\n",\
+"\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1.5 P6, -0.5 P1 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"\n",\
+"\n",\
+"\n",\
+"freeset\n",\
+"1 6 2 3;\n",\
+"\n",\
+"freeset\n",\
+"1 6 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 6 5 4;\n",\
+"\n",\
+"freeset\n",\
+"1 6 4 2;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"five Tetrahedron non convex (4)\"\n",\
+"\n",\
+"quality 4\n",\
+"flags l;\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 1, 0) { 0.5 };\n",\
+"(0, 0.8, -0.2) { 0.5 };\n",\
+"(0, 0.2, -0.8) { 0.5 };\n",\
+"(0.5, 0, -1) { 0.5 };\n",\
+"\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 3, 4) del;\n",\
+"(1, 4, 5) del;\n",\
+"(1, 5, 6) del;\n",\
+"(1, 6, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.1, 0.1, -0.1)\n",\
+"	 { 0.75 X1, 0.05 X2, 0.05 X3, 0.05 X4, 0.05 X5, 0.05 X6 }\n",\
+"	 { 0.75 Y1, 0.05 Y2, 0.05 Y3, 0.05 Y4, 0.05 Y5, 0.05 Y6 }\n",\
+"	 { 0.75 Z1, 0.05 Z2, 0.05 Z3, 0.05 Z4, 0.05 Z5, 0.05 Z6 };\n",\
+"\n",\
+"newfaces\n",\
+"(7, 2, 3);\n",\
+"(7, 3, 4);\n",\
+"(7, 4, 5);\n",\
+"(7, 5, 6);\n",\
+"(7, 6, 2);\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 7);\n",\
+"(1, 3, 4, 7);\n",\
+"(1, 4, 5, 7);\n",\
+"(1, 5, 6, 7);\n",\
+"(1, 6, 2, 7);\n",\
+"\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 1.5 P7, -0.5 P1 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"{ 1 P7 };\n",\
+"\n",\
+"\n",\
+"\n",\
+"freeset\n",\
+"1 7 2 3;\n",\
+"\n",\
+"freeset\n",\
+"1 7 3 4;\n",\
+"\n",\
+"freeset\n",\
+"1 7 4 5;\n",\
+"\n",\
+"freeset\n",\
+"1 7 5 6;\n",\
+"\n",\
+"freeset\n",\
+"1 7 6 2;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"four Tetrahedron non convex (6)\"\n",\
+"\n",\
+"quality 6\n",\
+"flags l;\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 1, 0) { 0.5 };\n",\
+"(0.5, 0, -1) { 0.5 };\n",\
+"(0.5, 0.3, -0.3) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"(1, 5, 4) del;\n",\
+"(1, 3, 5) del;\n",\
+"\n",\
+"\n",\
+"newpoints\n",\
+"(0.095, 0.003, -0.003)\n",\
+"	 { 0.9 X1, 0.09 X2, 0.01 X5 }\n",\
+"	 { 0.9 Y1, 0.09 Y2, 0.01 Y5 }\n",\
+"	 { 0.9 Z1, 0.09 Z2, 0.01 Z5 };\n",\
+"\n",\
+"newfaces\n",\
+"(6, 2, 3) del;\n",\
+"(6, 4, 2) del;\n",\
+"(6, 5, 4) del;\n",\
+"(6, 3, 5) del;\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 6);\n",\
+"(1, 4, 2, 6);\n",\
+"(1, 5, 4, 6);\n",\
+"(1, 3, 5, 6);\n",\
+"\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1.499 P6, -0.5 P1, 0.001 P2 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"\n",\
+"\n",\
+"\n",\
+"freeset\n",\
+"1 6 2 3;\n",\
+"\n",\
+"freeset\n",\
+"1 6 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 6 5 4;\n",\
+"\n",\
+"freeset\n",\
+"1 6 4 2;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"four Tetrahedron non convex (6)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 1, 0) { 0.5 };\n",\
+"(0.5, 0, -1) { 0.5 };\n",\
+"(0.5, 0.4, -0.4) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"(4, 5, 2) del;\n",\
+"(5, 3, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.925, 0.02, -0.02)\n",\
+"	 { 0.05 X1, 0.9 X2, 0.05 X5 }\n",\
+"	 { 0.05 Y1, 0.9 Y2, 0.05 Y5 }\n",\
+"	 { 0.05 Z1, 0.9 Z2, 0.05 Z5 };\n",\
+"\n",\
+"newfaces\n",\
+"(3, 1, 6);\n",\
+"(1, 4, 6);\n",\
+"(4, 5, 6);\n",\
+"(5, 3, 6);\n",\
+"\n",\
+"elements\n",\
+"(3, 1, 2, 6);\n",\
+"(1, 4, 2, 6);\n",\
+"(4, 5, 2, 6);\n",\
+"(5, 3, 2, 6);\n",\
+"\n",\
+"orientations\n",\
+"(3, 1, 2, 5);\n",\
+"(1, 4, 2, 5);\n",\
+"(2, 4, 5, 1);\n",\
+"(3, 2, 5, 1);\n",\
+"(5, 4, 2, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1.5 P6, -0.5 P2 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"{ 1 P6 };\n",\
+"\n",\
+"freeset\n",\
+"3 1 2 6;\n",\
+"\n",\
+"freeset\n",\
+"1 4 2 6;\n",\
+"\n",\
+"freeset\n",\
+"4 5 2 6;\n",\
+"\n",\
+"freeset\n",\
+"5 3 2 6;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"three Tetrahedron non convex (4)\"\n",\
+"\n",\
+"quality 4\n",\
+"flags l;\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 1, 0) { 0.5 };\n",\
+"(0.5, 0, -1) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"(1, 3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.25, -0.25)\n",\
+"	 { 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 }\n",\
+"	 { 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 }\n",\
+"	 { 0.25 Z1, 0.25 Z2, 0.25 Z3, 0.25 Z4 };\n",\
+"\n",\
+"newfaces\n",\
+"(5, 2, 3);\n",\
+"(5, 4, 2);\n",\
+"(5, 3, 4);\n",\
+"\n",\
+"elements\n",\
+"(2, 3, 1, 5);\n",\
+"(3, 4, 1, 5);\n",\
+"(4, 2, 1, 5;\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 4, 3);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1.5 P5, -0.5 P1 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"freeset\n",\
+"1 4 2 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"three Tetrahedron non convex (6)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 1, 0) { 0.5 };\n",\
+"(0.5, 0, -1) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"(1, 3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.2, 0.1, -0.1)\n",\
+"	 { 0.7 X1, 0.1 X2, 0.1 X3, 0.1 X4 }\n",\
+"	 { 0.7 Y1, 0.1 Y2, 0.1 Y3, 0.1 Y4 }\n",\
+"	 { 0.7 Z1, 0.1 Z2, 0.1 Z3, 0.1 Z4 };\n",\
+"\n",\
+"newfaces\n",\
+"(5, 2, 3);\n",\
+"(5, 4, 2);\n",\
+"(5, 3, 4);\n",\
+"\n",\
+"elements\n",\
+"(2, 3, 1, 5);\n",\
+"(3, 4, 1, 5);\n",\
+"(4, 2, 1, 5;\n",\
+"\n",\
+"orientations\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1.5 P5, -0.5 P1 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 3 4 5;\n",\
+"\n",\
+"freeset\n",\
+"1 4 2 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"four Tetrahedron non convex (6)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 1, 0) { 0.5 };\n",\
+"(0.5, 0, -1) { 0.5 };\n",\
+"(0.5, 0.4, -0.4) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"(4, 5, 2) del;\n",\
+"(5, 3, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.7, 0.08, -0.08) { 0.6 X2, 0.2 X5 } { 0.2 Y5 } { 0.2 Z5 };\n",\
+"\n",\
+"newfaces\n",\
+"(3, 1, 6);\n",\
+"(1, 4, 6);\n",\
+"(4, 5, 6);\n",\
+"(5, 3, 6);\n",\
+"\n",\
+"elements\n",\
+"(3, 1, 2, 6);\n",\
+"(1, 4, 2, 6);\n",\
+"(4, 5, 2, 6);\n",\
+"(5, 3, 2, 6);\n",\
+"\n",\
+"\n",\
+"orientations\n",\
+"(3, 1, 2, 5);\n",\
+"(5, 1, 2, 4);\n",\
+"\n",\
+"freezone\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 X2 } { } { };\n",\
+"(0.5, 1, 0) { 1 X3 } { 1 Y3 } { };\n",\
+"(0.5, 0, -1) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\
+"(0.5, 0.4, -0.4) { 1 X5 } { 1 Y5 } { 1 Z5 };\n",\
+"(0.55, 0.12, -0.12) { 0.4 X2, 0.3 X5 } { 0.3 Y5 } { 0.3 Z5 };\n",\
+"\n",\
+"freeset\n",\
+"3 1 2 6;\n",\
+"\n",\
+"freeset\n",\
+"1 4 2 6;\n",\
+"\n",\
+"freeset\n",\
+"4 5 2 6;\n",\
+"\n",\
+"freeset\n",\
+"5 3 2 6;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron 2 in 60 (12)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 0.5 };\n",\
+"(0.5, 1, 0) { 0.5 };\n",\
+"(0.5, 0, -1) { 0.5 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.1, -0.1)\n",\
+"	{ 0.4 X1, 0.4 X2, 0.1 X3, 0.1 X4 }\n",\
+"	{ 0.4 Y1, 0.4 Y2, 0.1 Y3, 0.1 Y4 }\n",\
+"	{ 0.4 Z1, 0.4 Z2, 0.1 Z3, 0.1 Z4 };\n",\
+"\n",\
+"newfaces\n",\
+"(5, 2, 3);\n",\
+"(5, 3, 1);\n",\
+"(5, 4, 2);\n",\
+"(5, 1, 4);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 5);\n",\
+"(1, 2, 5, 4);\n",\
+"\n",\
+"freezone2\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1.5 P5, -0.25 P1, -0.25 P2 };\n",\
+"\n",\
+"freezonelimit\n",\
+"{ 1 P1 };\n",\
+"{ 1 P2 };\n",\
+"{ 1 P3 };\n",\
+"{ 1 P4 };\n",\
+"{ 1 P5 };\n",\
+"\n",\
+"freeset\n",\
+"1 2 3 5;\n",\
+"\n",\
+"freeset\n",\
+"1 2 4 5;\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Tetrahedron 120, but more than 180 (13)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 };\n",\
+"(0.5, 0.866, 0) { 1 };\n",\
+"(0.5, -0.866, 0) { 1 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"(1, 4, 2);\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0, -0.3) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\
+"\n",\
+"newfaces\n",\
+"(1, 5, 3);\n",\
+"(3, 5, 2);\n",\
+"(2, 5, 1);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 5);\n",\
+"\n",\
+"\n",\
+"freezone\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 X2 } { } { };\n",\
+"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\
+"(0.5, -0.1, -0.4)  { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Free Tetrahedron (14)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1.0 };\n",\
+"(0.5, 0.866, 0) { 1.0 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.288, -0.2) { 0.333 X2, 0.333 X3 } { 0.333 Y3 } { };\n",\
+"\n",\
+"newfaces\n",\
+"(4, 1, 2);\n",\
+"(4, 2, 3);\n",\
+"(4, 3, 1);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"freezone\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 X2 } { } { };\n",\
+"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\
+"(0.5, 0.288, -0.25) { 0.333 X2, 0.333 X3 } { 0.333 Y3 } { };\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Free Tetrahedron (15)\"\n",\
+"\n",\
+"quality 100\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1.0 };\n",\
+"(0.5, 0.866, 0) { 1.0 };\n",\
+"\n",\
+"mapfaces\n",\
+"(1, 2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.288, -0.1) { 0.333 X2, 0.333 X3 } { 0.333 Y3 } { };\n",\
+"\n",\
+"newfaces\n",\
+"(4, 1, 2);\n",\
+"(4, 2, 3);\n",\
+"(4, 3, 1);\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3, 4);\n",\
+"\n",\
+"freezone\n",\
+"(0, 0, 0);\n",\
+"(1, 0, 0) { 1 X2 } { } { };\n",\
+"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\
+"(0.5, 0.288, -0.15) { 0.333 X2, 0.333 X3 } { 0.333 Y3 } { };\n",\
+"\n",\
+"endrule\n",
+0};
+}
diff --git a/contrib/Netgen/libsrc/meshing/topology.cpp b/contrib/Netgen/libsrc/meshing/topology.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e620854cbf7a593f59bbdfd37308c3bb4cec73da
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/topology.cpp
@@ -0,0 +1,1726 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+  template <class T>
+  void QuickSortRec (FlatArray<T> & data,
+		     int left, int right)
+  {
+    int i = left;
+    int j = right;
+    T midval = data[(left+right)/2];
+  
+    do
+      {
+	while (data[i] < midval) i++;
+	while (midval < data[j]) j--;
+      
+	if (i <= j)
+	  {
+	    Swap (data[i], data[j]);
+	    i++; j--;
+	  }
+      }
+    while (i <= j);
+    if (left < j) QuickSortRec (data, left, j);
+    if (i < right) QuickSortRec (data, i, right);
+  }
+
+  template <class T>
+  void QuickSort (FlatArray<T> & data)
+  {
+    if (data.Size() > 1)
+      QuickSortRec (data, 0, data.Size()-1);
+  }
+
+
+
+
+
+
+  MeshTopology ::  MeshTopology (const Mesh & amesh)
+    : mesh(amesh)
+  {
+    buildedges = 1;
+    buildfaces = 1;
+    vert2element = 0;
+    vert2surfelement = 0;
+    vert2segment = 0;
+    timestamp = -1;
+
+    edge2vert.SetName ("edge2vert");
+    face2vert.SetName ("face2vert");
+    edges.SetName ("el2edge");
+    faces.SetName ("el2face");
+    surfedges.SetName ("surfel2edge");
+    segedges.SetName ("segment2edge");
+    surffaces.SetName ("surfel2face");
+    surf2volelement.SetName ("surfel2el");
+    face2surfel.SetName ("face2surfel");
+  }
+
+  MeshTopology :: ~MeshTopology ()
+  {
+    delete vert2element;
+    delete vert2surfelement;
+    delete vert2segment;
+  }
+
+  void MeshTopology :: Update()
+  {
+    static int timer = NgProfiler::CreateTimer ("topology");
+    NgProfiler::RegionTimer reg (timer);
+
+#ifdef PARALLEL
+    ParallelMeshTopology & paralleltop = mesh.GetParallelTopology();
+#endif
+
+    bool isparallel = 0;
+
+  
+    if (timestamp > mesh.GetTimeStamp()) return;
+  
+    int ne = mesh.GetNE();
+    int nse = mesh.GetNSE();
+    int nseg = mesh.GetNSeg();
+    int np = mesh.GetNP();
+    int nv = mesh.GetNV(); 
+    int nfa = 0;
+    int ned = edge2vert.Size();
+
+    if (id == 0)
+      PrintMessage (3, "Update mesh topology");
+
+    (*testout) << " UPDATE MESH TOPOLOGY " << endl; 
+    (*testout) << "ne   = " << ne << endl;
+    (*testout) << "nse  = " << nse << endl;
+    (*testout) << "nseg = " << nseg << endl;
+    (*testout) << "np   = " << np << endl;
+    (*testout) << "nv   = " << nv << endl;
+  
+    delete vert2element;
+    delete vert2surfelement;
+    delete vert2segment;
+  
+    Array<int,PointIndex::BASE> cnt(nv);
+    Array<int> vnums;
+
+    /*
+      generate:
+      vertex to element 
+      vertex to surface element
+      vertex to segment 
+    */
+
+
+    cnt = 0;
+    for (ElementIndex ei = 0; ei < ne; ei++)
+      {
+	const Element & el = mesh[ei];
+	for (int j = 0; j < el.GetNV(); j++)
+	  cnt[el[j]]++;
+      }
+
+    vert2element = new TABLE<int,PointIndex::BASE> (cnt);
+    for (ElementIndex ei = 0; ei < ne; ei++)
+      {
+	const Element & el = mesh[ei];
+	for (int j = 0; j < el.GetNV(); j++)
+	  vert2element->AddSave (el[j], ei+1);
+      }
+
+    cnt = 0;
+    for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+      {
+	const Element2d & el = mesh[sei];
+	for (int j = 0; j < el.GetNV(); j++)
+	  cnt[el[j]]++;
+      }
+
+    vert2surfelement = new TABLE<int,PointIndex::BASE> (cnt);
+    for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+      {
+	const Element2d & el = mesh[sei];
+	for (int j = 0; j < el.GetNV(); j++)
+	  vert2surfelement->AddSave (el[j], sei+1);
+      }
+
+    cnt = 0;
+    for (int i = 1; i <= nseg; i++)
+      {
+	const Segment & seg = mesh.LineSegment(i);
+	cnt[seg[0]]++;
+	cnt[seg[1]]++;
+      }
+ 
+    vert2segment = new TABLE<int,PointIndex::BASE> (cnt);
+    for (int i = 1; i <= nseg; i++)
+      {
+	const Segment & seg = mesh.LineSegment(i);
+	vert2segment->AddSave (seg[0], i);
+	vert2segment->AddSave (seg[1], i);
+      }
+
+
+    if (buildedges)
+      {
+	static int timer1 = NgProfiler::CreateTimer ("topology::buildedges");
+	NgProfiler::RegionTimer reg1 (timer1);
+
+	if (id == 0)
+	  PrintMessage (5, "Update edges ");
+      
+	edges.SetSize(ne);
+	surfedges.SetSize(nse); 
+	segedges.SetSize(nseg);
+
+	for (int i = 0; i < ne; i++)
+	  for (int j = 0; j < 12; j++)
+	    edges[i][j] = 0;
+	for (int i = 0; i < nse; i++)
+	  for (int j = 0; j < 4; j++)
+	    surfedges[i][j] = 0;
+
+	// keep existing edges
+	cnt = 0;
+	for (int i = 0; i < edge2vert.Size(); i++)
+	  cnt[edge2vert[i][0]]++;
+	TABLE<int,PointIndex::BASE> vert2edge (cnt);
+	for (int i = 0; i < edge2vert.Size(); i++)
+	  vert2edge.AddSave (edge2vert[i][0], i+1);
+
+	// ensure all coarse grid and intermediate level edges
+	cnt = 0;
+	for (int i = mesh.mlbetweennodes.Begin(); i < mesh.mlbetweennodes.End(); i++)
+	  {
+	    /*  JS, Oct 2009
+		int pa[2];
+		pa[0] = mesh.mlbetweennodes[i].I1();
+		pa[1] = mesh.mlbetweennodes[i].I2();
+		if (pa[0] > pa[1]) Swap (pa[0], pa[1]);
+		if (pa[0] > 0)
+		cnt.Elem(pa[0])++;
+	    */
+	    INDEX_2 parents = mesh.mlbetweennodes[i];
+	    parents.Sort();
+	    if (parents[0] >= PointIndex::BASE) cnt[parents[0]]++;
+	  }
+
+	TABLE<int,PointIndex::BASE> vert2vertcoarse (cnt);
+	for (int i = mesh.mlbetweennodes.Begin(); i < mesh.mlbetweennodes.End(); i++)
+	  {
+	    /*
+	      int pa[2];
+	      pa[0] = mesh.mlbetweennodes[i].I1();
+	      pa[1] = mesh.mlbetweennodes[i].I2();
+	      if (pa[0] > pa[1]) swap (pa[0], pa[1]);
+	      if (pa[0] > 0)
+	      vert2vertcoarse.AddSave1 (pa[0], pa[1]);
+	    */
+	    INDEX_2 parents = mesh.mlbetweennodes[i];
+	    parents.Sort();
+	    if (parents[0] > PointIndex::BASE) vert2vertcoarse.AddSave (parents[0], parents[1]);
+	  }
+
+
+	Array<int,PointIndex::BASE> edgenr(nv);
+	Array<int,PointIndex::BASE> edgeflag(nv);
+	Array<int> vertex2;
+
+	edgeflag = 0;
+
+	ned = edge2vert.Size();
+	Array<INDEX_3> missing;
+
+	for (int i = PointIndex::BASE; i < nv+PointIndex::BASE; i++)
+	  {
+	    vertex2.SetSize (0);
+
+	    for (int j = 0; j < vert2edge[i].Size(); j++)
+	      {
+		int ednr = vert2edge[i][j];
+		int i2 = edge2vert.Get(ednr)[1];
+		edgeflag[i2] = i;
+		edgenr[i2] = ednr;
+	      }
+
+	    for (int j = 0; j < vert2vertcoarse[i].Size(); j++)      // fix by Markus
+	      {
+		int v2 = vert2vertcoarse[i][j];
+		if (edgeflag[v2] < i)
+		  {
+		    *testout << "do we really need that ??????????????" << endl;
+		    // ned++;
+		    // edgenr[v2] = ned;
+		    edgeflag[v2] = i;
+		    vertex2.Append (v2);
+		    // missing.Append (INDEX_3(i,v2,ned));
+		  }
+	      }
+
+	    for (int j = 0; j < (*vert2element)[i].Size(); j++)
+	      {
+		int elnr = (*vert2element)[i][j];
+		const Element & el = mesh.VolumeElement (elnr);
+
+		int neledges = GetNEdges (el.GetType());
+		const ELEMENT_EDGE * eledges = GetEdges0 (el.GetType());
+	  
+		for (int k = 0; k < neledges; k++)
+		  {
+		    INDEX_2 edge(el[eledges[k][0]], el[eledges[k][1]]);
+		    edge.Sort();
+		    if (edge.I1() != i) continue;
+	     
+		    if (edgeflag[edge.I2()] < i)
+		      {
+			vertex2.Append (edge.I2());
+			edgeflag[edge.I2()] = i;
+		      }
+		  }
+	      }
+
+	    for (int j = 0; j < (*vert2surfelement)[i].Size(); j++)
+	      {
+		int elnr = (*vert2surfelement)[i][j];
+		const Element2d & el = mesh.SurfaceElement (elnr);
+
+		int neledges = GetNEdges (el.GetType());
+		const ELEMENT_EDGE * eledges = GetEdges0 (el.GetType());
+	  
+		for (int k = 0; k < neledges; k++)
+		  {
+		    INDEX_2 edge(el[eledges[k][0]], el[eledges[k][1]]);
+		    edge.Sort();
+		    if (edge.I1() != i) continue;
+	     
+		    if (edgeflag[edge.I2()] < i)
+		      {
+			vertex2.Append (edge.I2());
+			edgeflag[edge.I2()] = i;
+		      }
+		  }
+	      }
+
+	    for (int j = 0; j < (*vert2segment)[i].Size(); j++)
+	      {
+		int elnr = (*vert2segment)[i][j];
+		const Segment & el = mesh.LineSegment (elnr);
+
+		INDEX_2 edge(el[0], el[1]);
+		edge.Sort();
+		if (edge.I1() != i) continue;
+	     
+		if (edgeflag[edge.I2()] < i)
+		  {
+		    vertex2.Append (edge.I2());
+		    edgeflag[edge.I2()] = i;
+		  }   
+	      }
+
+	    
+	    QuickSort (vertex2);
+	    for (int j = 0; j < vertex2.Size(); j++)
+	      edgenr[vertex2[j]] = ++ned;
+
+	    for (int j = 0; j < vert2vertcoarse[i].Size(); j++)      // fix by Markus
+	      {
+		int v2 = vert2vertcoarse[i][j];
+		if (edgeflag[v2] < i)
+		  {
+		    // ned++;
+		    // edgenr[v2] = ned;
+		    // edgeflag[v2] = i;
+		    missing.Append (INDEX_3(i,v2,edgenr[v2]));
+		  }
+	      }
+
+	    for (int j = 0; j < (*vert2element)[i].Size(); j++)
+	      {
+		int elnr = (*vert2element)[i][j];
+		const Element & el = mesh.VolumeElement (elnr);
+
+		int neledges = GetNEdges (el.GetType());
+		const ELEMENT_EDGE * eledges = GetEdges0 (el.GetType());
+	  
+		for (int k = 0; k < neledges; k++)
+		  {
+		    INDEX_2 edge(el[eledges[k][0]], el[eledges[k][1]]);
+	      
+		    int edgedir = (edge.I1() > edge.I2());
+		    if (edgedir) swap (edge.I1(), edge.I2());
+	     
+		    if (edge.I1() != i)
+		      continue;
+
+		    int edgenum = edgenr[edge.I2()];
+		    if (edgedir) edgenum *= -1;
+		    edges.Elem(elnr)[k] = edgenum;
+		  }
+	      }
+
+	    for (int j = 0; j < (*vert2surfelement)[i].Size(); j++)
+	      {
+		int elnr = (*vert2surfelement)[i][j];
+		const Element2d & el = mesh.SurfaceElement (elnr);
+
+		int neledges = GetNEdges (el.GetType());
+		const ELEMENT_EDGE * eledges = GetEdges0 (el.GetType());
+	  
+		for (int k = 0; k < neledges; k++)
+		  {
+		    INDEX_2 edge(el[eledges[k][0]], el[eledges[k][1]]);
+	      
+		    int edgedir = (edge.I1() > edge.I2());
+		    if (edgedir) swap (edge.I1(), edge.I2());
+	     
+		    if (edge.I1() != i) continue;
+
+		    int edgenum = edgenr[edge.I2()];
+		    if (edgedir) edgenum *= -1;
+		    surfedges.Elem(elnr)[k] = edgenum;
+		  }
+	      }
+
+	    for (int j = 0; j < (*vert2segment)[i].Size(); j++)
+	      {
+		int elnr = (*vert2segment)[i][j];
+		const Segment & el = mesh.LineSegment (elnr);
+
+		INDEX_2 edge(el[0], el[1]);
+	      
+		int edgedir = (edge.I1() > edge.I2());
+		if (edgedir) swap (edge.I1(), edge.I2());
+	      
+		if (edge.I1() != i) continue;
+
+		int edgenum = edgenr[edge.I2()];
+		if (edgedir) edgenum *= -1;
+		segedges.Elem(elnr) = edgenum;
+	      }
+	  }
+
+
+
+	edge2vert.SetSize (ned);
+	for (int i = 1; i <= ne; i++)
+	  {
+	    const Element & el = mesh.VolumeElement (i);
+      
+	    int neledges = GetNEdges (el.GetType());
+	    const ELEMENT_EDGE * eledges = GetEdges0 (el.GetType());
+	  
+	    for (int k = 0; k < neledges; k++)
+	      {
+		INDEX_2 edge(el[eledges[k][0]], el[eledges[k][1]]);
+	  
+		int edgedir = (edge.I1() > edge.I2());
+		if (edgedir) swap (edge.I1(), edge.I2());
+
+		int edgenum = abs (edges.Elem(i)[k]);
+
+		edge2vert.Elem(edgenum)[0] = edge.I1();
+		edge2vert.Elem(edgenum)[1] = edge.I2();
+	      }
+	  }
+
+	/*
+	*testout << "edge 2 vert:" << endl;
+	for (int i = 0; i < edge2vert.Size(); i++)
+	  *testout << edge2vert[i][0] << " " << edge2vert[i][1] << endl;
+	  */
+
+	for (int i = 1; i <= nse; i++)
+	  {
+	    const Element2d & el = mesh.SurfaceElement (i);
+      
+	    int neledges = GetNEdges (el.GetType());
+	    const ELEMENT_EDGE * eledges = GetEdges0 (el.GetType());
+	  
+	    for (int k = 0; k < neledges; k++)
+	      {
+		INDEX_2 edge(el[eledges[k][0]], el[eledges[k][1]]);
+	  
+		int edgedir = (edge.I1() > edge.I2());
+		if (edgedir) swap (edge.I1(), edge.I2());
+
+		int edgenum = abs (surfedges.Elem(i)[k]);
+
+		edge2vert.Elem(edgenum)[0] = edge.I1();
+		edge2vert.Elem(edgenum)[1] = edge.I2();
+	      }
+	  }
+
+	for (int i = 1; i <= nseg; i++)
+	  {
+	    const Segment & el = mesh.LineSegment (i);
+      
+	    INDEX_2 edge(el[0], el[1]);
+	    int edgedir = (edge.I1() > edge.I2());
+	    if (edgedir) swap (edge.I1(), edge.I2());
+	  
+	    int edgenum = abs (segedges.Elem(i));
+	  
+	    edge2vert.Elem(edgenum)[0] = edge.I1();
+	    edge2vert.Elem(edgenum)[1] = edge.I2();
+	  }
+
+	for (int i = 1; i <= missing.Size(); i++)
+	  {
+	    INDEX_3 i3 = missing.Get(i);
+	    edge2vert.Elem(i3.I3())[0] = i3.I1();
+	    edge2vert.Elem(i3.I3())[1] = i3.I2();
+	  }
+	
+      
+	/*
+	  (*testout) << "edge table:" << endl;
+	  (*testout) << "edge2vert:" << endl;
+	  for (int i = 1; i <= edge2vert.Size(); i++)
+	  (*testout) << "edge " << i << ", v1,2 = " << edge2vert.Elem(i)[0] << ", " << edge2vert.Elem(i)[1] << endl;
+	  (*testout) << "surfedges:" << endl;
+	  for (int i = 1; i <= surfedges.Size(); i++)
+	  (*testout) << "el " << i << ", edges = " 
+	  << surfedges.Elem(i)[0] << ", "
+	  << surfedges.Elem(i)[1] << ", "
+	  << surfedges.Elem(i)[2] << endl;
+	*/ 
+     
+    
+      }
+
+
+    //  cout << "build edges done" << endl;
+
+
+
+    // generate faces
+    if (buildfaces) //  && mesh.GetDimension() == 3)
+      {
+	static int timer2 = NgProfiler::CreateTimer ("topology::buildfaces");
+	NgProfiler::RegionTimer reg2 (timer2);
+
+	if (id == 0)
+	  PrintMessage (5, "Update faces ");
+
+
+
+
+	faces.SetSize(ne);
+	surffaces.SetSize(nse);
+	
+	int oldnfa = face2vert.Size();
+
+	cnt = 0;
+	for (int i = 0; i < face2vert.Size(); i++)
+	  cnt[face2vert[i][0]]++;
+	TABLE<int,PointIndex::BASE> vert2oldface(cnt);
+	for (int i = 0; i < face2vert.Size(); i++)
+	  vert2oldface.AddSave (face2vert[i][0], i);
+
+
+	for (int elnr = 0; elnr < ne; elnr++)
+	  for (int j = 0; j < 6; j++)
+	    faces[elnr][j] = 0;
+	
+
+	int max_face_on_vertex = 0;
+	for (int i = PointIndex::BASE; i < nv+PointIndex::BASE; i++)
+	  {
+	    int onv = vert2oldface[i].Size() + (*vert2element)[i].Size() + (*vert2surfelement)[i].Size();
+	    max_face_on_vertex = max (onv, max_face_on_vertex);
+	  }
+	
+
+	/*
+	for (int pass = 1; pass <= 2; pass++)
+	  {
+	    nfa = oldnfa;
+	    for (int v = PointIndex::BASE; v < nv+PointIndex::BASE; v++)
+	      {
+		INDEX_3_CLOSED_HASHTABLE<int> vert2face(2*max_face_on_vertex+10); 
+		
+		for (int j = 0; j < vert2oldface[v].Size(); j++)
+		  {
+		    int fnr = vert2oldface[v][j];
+		    INDEX_3 face (face2vert[fnr].I1(),
+				  face2vert[fnr].I2(),
+				  face2vert[fnr].I3());
+		    vert2face.Set (face, fnr+1);
+		  }
+
+
+		// cout << "inherited faces: " << endl << vert2face << endl;
+
+
+		for (int j = 0; j < (*vert2element)[v].Size(); j++)
+		  {
+		    int elnr = (*vert2element)[v][j];
+		    const Element & el = mesh.VolumeElement (elnr);
+	  
+		    int nelfaces = GetNFaces (el.GetType());
+		    const ELEMENT_FACE * elfaces = GetFaces1 (el.GetType());
+	  
+		    for (int j = 0; j < nelfaces; j++)
+		      if (elfaces[j][3] == 0)
+	      
+			{ // triangle
+			  int facenum, facedir;
+			  INDEX_3 face(el.PNum(elfaces[j][0]),
+				       el.PNum(elfaces[j][1]),
+				       el.PNum(elfaces[j][2]));
+		      
+			  facedir = 0;
+			  if (face.I1() > face.I2())
+			    { swap (face.I1(), face.I2()); facedir += 1; }
+			  if (face.I2() > face.I3())
+			    { swap (face.I2(), face.I3()); facedir += 2; }
+			  if (face.I1() > face.I2())
+			    { swap (face.I1(), face.I2()); facedir += 4; }
+		      
+			  if (face.I1() != v) continue;
+		      
+			  if (vert2face.Used (face))
+			    facenum = vert2face.Get(face);
+			  else
+			    {
+			      nfa++;
+			      vert2face.Set (face, nfa);
+			      facenum = nfa;
+			  
+			      INDEX_4 hface(face.I1(),face.I2(),face.I3(),0);
+			      if (pass == 2) face2vert.Append (hface);
+			    }
+			  faces.Elem(elnr)[j] = 8*(facenum-1)+facedir+1;
+			}
+		
+		      else
+		    
+			{
+			  // quad
+			  int facenum, facedir;
+			  INDEX_4Q face4(el.PNum(elfaces[j][0]),
+					 el.PNum(elfaces[j][1]),
+					 el.PNum(elfaces[j][2]),
+					 el.PNum(elfaces[j][3]));
+		      
+			  facedir = 0;
+			  if (min2 (face4.I1(), face4.I2()) > 
+			      min2 (face4.I4(), face4.I3())) 
+			    {  // z - flip
+			      facedir += 1; 
+			      swap (face4.I1(), face4.I4());
+			      swap (face4.I2(), face4.I3());
+			    }
+			  if (min2 (face4.I1(), face4.I4()) >
+			      min2 (face4.I2(), face4.I3())) 
+			    {  // x - flip
+			      facedir += 2; 
+			      swap (face4.I1(), face4.I2());
+			      swap (face4.I3(), face4.I4());
+			    }
+			  if (face4.I2() > face4.I4())
+			    {  // diagonal flip
+			      facedir += 4; 
+			      swap (face4.I2(), face4.I4());
+			    }
+
+		
+			  INDEX_3 face(face4.I1(), face4.I2(), face4.I3());
+
+			  if (face.I1() != v) continue;
+		      
+			  if (vert2face.Used (face))
+			    {
+			      facenum = vert2face.Get(face);
+			    }
+			  else
+			    {
+			      nfa++;
+			      vert2face.Set (face, nfa);
+			      facenum = nfa;
+			  
+			      INDEX_4 hface(face4.I1(),face4.I2(),face4.I3(),face4.I4());
+			      if (pass == 2) face2vert.Append (hface);
+			    }
+		      
+			  faces.Elem(elnr)[j] = 8*(facenum-1)+facedir+1;
+			}
+		  }
+
+		for (int j = 0; j < (*vert2surfelement)[v].Size(); j++)
+		  {
+		    int elnr = (*vert2surfelement)[v][j];
+		    // cout << "surfelnr = " << elnr << endl;
+		    const Element2d & el = mesh.SurfaceElement (elnr);
+		
+		    const ELEMENT_FACE * elfaces = GetFaces1 (el.GetType());
+		
+		    if (elfaces[0][3] == 0)
+	    
+		      { // triangle
+	      
+			int facenum;
+			int facedir;
+		    
+			INDEX_3 face(el.PNum(elfaces[0][0]),
+				     el.PNum(elfaces[0][1]),
+				     el.PNum(elfaces[0][2]));
+		    
+			// cout << "face = " << face << endl;
+
+			facedir = 0;
+			if (face.I1() > face.I2())
+			  {
+			    swap (face.I1(), face.I2());
+			    facedir += 1;
+			  }
+			if (face.I2() > face.I3())
+			  {
+			    swap (face.I2(), face.I3());
+			    facedir += 2;
+			  }
+			if (face.I1() > face.I2())
+			  {
+			    swap (face.I1(), face.I2());
+			    facedir += 4;
+			  }
+		    
+			if (face.I1() != v) continue;
+		    
+			if (vert2face.Used (face))
+			  facenum = vert2face.Get(face);
+			else
+			  {
+			    nfa++;
+			    vert2face.Set (face, nfa);
+			    facenum = nfa;
+			
+			    INDEX_4 hface(face.I1(),face.I2(),face.I3(),0);
+			    if (pass == 2) face2vert.Append (hface);
+			  }
+
+			// cout << "face = " << face << " selnr = " << elnr << endl;
+			surffaces.Elem(elnr) = 8*(facenum-1)+facedir+1;
+			// face2surfel.Elem(facenum) = elnr;
+		      }
+	  
+		    else
+	    
+		      {
+			// quad
+			int facenum;
+			int facedir;
+		    
+			INDEX_4Q face4(el.PNum(elfaces[0][0]),
+				       el.PNum(elfaces[0][1]),
+				       el.PNum(elfaces[0][2]),
+				       el.PNum(elfaces[0][3]));
+		    
+			facedir = 0;
+			if (min2 (face4.I1(), face4.I2()) > 
+			    min2 (face4.I4(), face4.I3())) 
+			  {  // z - orientation
+			    facedir += 1; 
+			    swap (face4.I1(), face4.I4());
+			    swap (face4.I2(), face4.I3());
+			  }
+			if (min2 (face4.I1(), face4.I4()) >
+			    min2 (face4.I2(), face4.I3())) 
+			  {  // x - orientation
+			    facedir += 2; 
+			    swap (face4.I1(), face4.I2());
+			    swap (face4.I3(), face4.I4());
+			  }
+			if (face4.I2() > face4.I4())
+			  { 
+			    facedir += 4; 
+			    swap (face4.I2(), face4.I4());
+			  }
+		    
+			INDEX_3 face(face4.I1(), face4.I2(), face4.I3());
+			if (face.I1() != v) continue;
+		    
+			if (vert2face.Used (face))
+			  facenum = vert2face.Get(face);
+			else
+			  {
+			    nfa++;
+			    vert2face.Set (face, nfa);
+			    facenum = nfa;
+			
+			    INDEX_4 hface(face4.I1(),face4.I2(),face4.I3(),face4.I3());
+			    if (pass == 2) face2vert.Append (hface);
+			  }
+		    
+			surffaces.Elem(elnr) = 8*(facenum-1)+facedir+1;
+		      }
+		  }
+	      }
+
+	    face2vert.SetAllocSize (nfa);
+	    }
+	*/
+
+
+
+
+
+
+
+
+	for (int pass = 1; pass <= 2; pass++)
+	  {
+	    nfa = oldnfa;
+	    for (int v = PointIndex::BASE; v < nv+PointIndex::BASE; v++)
+	      {
+		int first_fa = nfa;
+
+		INDEX_3_CLOSED_HASHTABLE<int> vert2face(2*max_face_on_vertex+10); 
+		
+		for (int j = 0; j < vert2oldface[v].Size(); j++)
+		  {
+		    int fnr = vert2oldface[v][j];
+		    INDEX_3 face (face2vert[fnr].I1(),
+				  face2vert[fnr].I2(),
+				  face2vert[fnr].I3());
+		    vert2face.Set (face, fnr+1);
+		  }
+
+
+		if (pass == 2)
+		  for (int j = nfa; j < face2vert.Size(); j++)
+		    {
+		      if (face2vert[j][0] == v)
+			{
+			  INDEX_3 face (face2vert[j].I1(),
+					face2vert[j].I2(),
+					face2vert[j].I3());
+			  vert2face.Set (face, j+1);
+			  nfa++;
+			}
+		      else
+			break;
+		    }
+		
+
+		// cout << "inherited faces: " << endl << vert2face << endl;
+
+
+		for (int j = 0; j < (*vert2element)[v].Size(); j++)
+		  {
+		    int elnr = (*vert2element)[v][j];
+		    const Element & el = mesh.VolumeElement (elnr);
+	  
+		    int nelfaces = GetNFaces (el.GetType());
+		    const ELEMENT_FACE * elfaces = GetFaces1 (el.GetType());
+	  
+		    for (int j = 0; j < nelfaces; j++)
+		      if (elfaces[j][3] == 0)
+	      
+			{ // triangle
+			  int facenum, facedir;
+			  INDEX_3 face(el.PNum(elfaces[j][0]),
+				       el.PNum(elfaces[j][1]),
+				       el.PNum(elfaces[j][2]));
+		      
+			  facedir = 0;
+			  if (face.I1() > face.I2())
+			    { swap (face.I1(), face.I2()); facedir += 1; }
+			  if (face.I2() > face.I3())
+			    { swap (face.I2(), face.I3()); facedir += 2; }
+			  if (face.I1() > face.I2())
+			    { swap (face.I1(), face.I2()); facedir += 4; }
+		      
+			  if (face.I1() != v) continue;
+		      
+			  if (vert2face.Used (face))
+			    facenum = vert2face.Get(face);
+			  else
+			    {
+			      nfa++;
+			      vert2face.Set (face, nfa);
+			      facenum = nfa;
+			  
+			      INDEX_4 hface(face.I1(),face.I2(),face.I3(),0);
+			      face2vert.Append (hface);
+			    }
+			  faces.Elem(elnr)[j] = 8*(facenum-1)+facedir+1;
+			}
+		
+		      else
+		    
+			{
+			  // quad
+			  int facenum, facedir;
+			  INDEX_4Q face4(el.PNum(elfaces[j][0]),
+					 el.PNum(elfaces[j][1]),
+					 el.PNum(elfaces[j][2]),
+					 el.PNum(elfaces[j][3]));
+		      
+			  facedir = 0;
+			  if (min2 (face4.I1(), face4.I2()) > 
+			      min2 (face4.I4(), face4.I3())) 
+			    {  // z - flip
+			      facedir += 1; 
+			      swap (face4.I1(), face4.I4());
+			      swap (face4.I2(), face4.I3());
+			    }
+			  if (min2 (face4.I1(), face4.I4()) >
+			      min2 (face4.I2(), face4.I3())) 
+			    {  // x - flip
+			      facedir += 2; 
+			      swap (face4.I1(), face4.I2());
+			      swap (face4.I3(), face4.I4());
+			    }
+			  if (face4.I2() > face4.I4())
+			    {  // diagonal flip
+			      facedir += 4; 
+			      swap (face4.I2(), face4.I4());
+			    }
+
+		
+			  INDEX_3 face(face4.I1(), face4.I2(), face4.I3());
+
+			  if (face.I1() != v) continue;
+		      
+			  if (vert2face.Used (face))
+			    {
+			      facenum = vert2face.Get(face);
+			    }
+			  else
+			    {
+			      nfa++;
+			      vert2face.Set (face, nfa);
+			      facenum = nfa;
+			  
+			      INDEX_4 hface(face4.I1(),face4.I2(),face4.I3(),face4.I4());
+			      face2vert.Append (hface);
+			    }
+		      
+			  faces.Elem(elnr)[j] = 8*(facenum-1)+facedir+1;
+			}
+		  }
+
+		for (int j = 0; j < (*vert2surfelement)[v].Size(); j++)
+		  {
+		    int elnr = (*vert2surfelement)[v][j];
+		    // cout << "surfelnr = " << elnr << endl;
+		    const Element2d & el = mesh.SurfaceElement (elnr);
+		
+		    const ELEMENT_FACE * elfaces = GetFaces1 (el.GetType());
+		
+		    if (elfaces[0][3] == 0)
+	    
+		      { // triangle
+	      
+			int facenum;
+			int facedir;
+		    
+			INDEX_3 face(el.PNum(elfaces[0][0]),
+				     el.PNum(elfaces[0][1]),
+				     el.PNum(elfaces[0][2]));
+		    
+			// cout << "face = " << face << endl;
+
+			facedir = 0;
+			if (face.I1() > face.I2())
+			  {
+			    swap (face.I1(), face.I2());
+			    facedir += 1;
+			  }
+			if (face.I2() > face.I3())
+			  {
+			    swap (face.I2(), face.I3());
+			    facedir += 2;
+			  }
+			if (face.I1() > face.I2())
+			  {
+			    swap (face.I1(), face.I2());
+			    facedir += 4;
+			  }
+		    
+			if (face.I1() != v) continue;
+		    
+			if (vert2face.Used (face))
+			  facenum = vert2face.Get(face);
+			else
+			  {
+			    nfa++;
+			    vert2face.Set (face, nfa);
+			    facenum = nfa;
+			
+			    INDEX_4 hface(face.I1(),face.I2(),face.I3(),0);
+			    face2vert.Append (hface);
+			  }
+
+			surffaces.Elem(elnr) = 8*(facenum-1)+facedir+1;
+		      }
+	  
+		    else
+	    
+		      {
+			// quad
+			int facenum;
+			int facedir;
+		    
+			INDEX_4Q face4(el.PNum(elfaces[0][0]),
+				       el.PNum(elfaces[0][1]),
+				       el.PNum(elfaces[0][2]),
+				       el.PNum(elfaces[0][3]));
+		    
+			facedir = 0;
+			if (min2 (face4.I1(), face4.I2()) > 
+			    min2 (face4.I4(), face4.I3())) 
+			  {  // z - orientation
+			    facedir += 1; 
+			    swap (face4.I1(), face4.I4());
+			    swap (face4.I2(), face4.I3());
+			  }
+			if (min2 (face4.I1(), face4.I4()) >
+			    min2 (face4.I2(), face4.I3())) 
+			  {  // x - orientation
+			    facedir += 2; 
+			    swap (face4.I1(), face4.I2());
+			    swap (face4.I3(), face4.I4());
+			  }
+			if (face4.I2() > face4.I4())
+			  { 
+			    facedir += 4; 
+			    swap (face4.I2(), face4.I4());
+			  }
+		    
+			INDEX_3 face(face4.I1(), face4.I2(), face4.I3());
+			if (face.I1() != v) continue;
+		    
+			if (vert2face.Used (face))
+			  facenum = vert2face.Get(face);
+			else
+			  {
+			    nfa++;
+			    vert2face.Set (face, nfa);
+			    facenum = nfa;
+			
+			    INDEX_4 hface(face4.I1(),face4.I2(),face4.I3(),face4.I3());
+			    face2vert.Append (hface);
+			  }
+		    
+			surffaces.Elem(elnr) = 8*(facenum-1)+facedir+1;
+		      }
+		  }
+
+		// sort faces
+		// *testout << "faces = " << face2vert << endl;
+		if (pass == 1)
+		  {
+		    // *testout << "sort from " << first_fa << " to " << nfa << endl;
+		    for (int i = first_fa; i < nfa; i++)
+		      for (int j = first_fa+1; j < nfa; j++)
+			if (face2vert[j] < face2vert[j-1])
+			  Swap (face2vert[j-1], face2vert[j]);
+		  }
+		// *testout << "faces, sorted = " << face2vert << endl;
+	      }
+
+
+	    face2vert.SetAllocSize (nfa);
+
+	  }
+
+
+	// *testout << "face2vert = " << endl << face2vert << endl;
+
+
+
+
+
+
+
+	
+	face2surfel.SetSize (nfa);
+	face2surfel = 0;
+	for (int i = 1; i <= nse; i++)
+	  face2surfel.Elem(GetSurfaceElementFace(i)) = i;
+
+	/*
+	  cout << "build table complete" << endl;
+
+	  cout << "faces = " << endl;
+
+	  cout << "face2vert = " << endl << face2vert << endl;
+	  cout << "surffaces = " << endl << surffaces << endl;
+	  cout << "face2surfel = " << endl << face2surfel << endl;
+	*/
+
+	
+	surf2volelement.SetSize (nse);
+	for (int i = 1; i <= nse; i++)
+	  {
+	    surf2volelement.Elem(i)[0] = 0;
+	    surf2volelement.Elem(i)[1] = 0;
+	  }
+	for (int i = 1; i <= ne; i++)
+	  for (int j = 0; j < 6; j++)
+	    {
+	      int fnum = (faces.Get(i)[j]+7) / 8;
+	      if (fnum > 0 && face2surfel.Elem(fnum))
+		{
+		  int sel = face2surfel.Elem(fnum);
+		  surf2volelement.Elem(sel)[1] = 
+		    surf2volelement.Elem(sel)[0];
+		  surf2volelement.Elem(sel)[0] = i;
+		}
+	    }
+
+	face2vert.SetAllocSize (face2vert.Size());
+
+	// face table complete
+
+
+#ifdef PARALLEL
+	(*testout) << " RESET Paralleltop" << endl;
+	paralleltop.Reset ();
+#endif
+
+	Array<short int> face_els(nfa), face_surfels(nfa);
+	face_els = 0;
+	face_surfels = 0;
+	Array<int> hfaces;
+	for (int i = 1; i <= ne; i++)
+	  {
+	    GetElementFaces (i, hfaces);
+	    for (int j = 0; j < hfaces.Size(); j++)
+	      face_els[hfaces[j]-1]++;
+	  }
+	for (int i = 1; i <= nse; i++)
+	  face_surfels[GetSurfaceElementFace (i)-1]++;
+
+
+	if (ne)
+	  {
+	    int cnt_err = 0;
+	    for (int i = 0; i < nfa; i++)
+	      {
+		/*
+		  (*testout) << "face " << i << " has " << int(face_els[i]) << " els, " 
+		  << int(face_surfels[i]) << " surfels, tot = "
+		  << face_els[i] + face_surfels[i] << endl; 
+		*/
+		if (face_els[i] + face_surfels[i] == 1)
+		  {
+		    cnt_err++;
+#ifdef PARALLEL
+		    if ( ntasks > 1 )
+		      {
+			if ( !paralleltop.DoCoarseUpdate() ) continue;
+		      }
+		    else
+#endif
+		      {
+			(*testout) << "illegal face : " << i << endl;
+			(*testout) << "points = " << face2vert[i] << endl;
+			(*testout) << "pos = ";
+			for (int j = 0; j < 4; j++)
+			  if (face2vert[i].I(j+1) >= 1)
+			    (*testout) << mesh[(PointIndex)face2vert[i].I(j+1)] << " ";
+			(*testout) << endl;
+
+			FlatArray<int> vertels = GetVertexElements (face2vert[i].I(1));
+			for (int k = 0; k < vertels.Size(); k++)
+			  {
+			    int elfaces[10], orient[10];
+			    int nf = GetElementFaces (vertels[k], elfaces, orient);
+			    for (int l = 0; l < nf; l++)
+			      if (elfaces[l] == i)
+				{
+				  (*testout) << "is face of element " << vertels[k] << endl;
+			    
+				  if (mesh.coarsemesh && mesh.hpelements->Size() == mesh.GetNE() )
+				    {
+				      const HPRefElement & hpref_el =
+					(*mesh.hpelements) [ mesh.VolumeElement (vertels[k]).hp_elnr];
+				      (*testout) << "coarse eleme = " << hpref_el.coarse_elnr << endl;
+				    }
+
+				}
+			  }
+		      }
+		  }
+	      }
+
+	    if (cnt_err && ntasks == 1)
+	      cout << cnt_err << " elements are not matching !!!" << endl;
+
+	    if (cnt_err && ntasks > 1)
+	      isparallel = 1;
+	  }
+      }
+    
+
+#ifdef PARALLEL
+    if (mesh.GetDimension() == 3)
+      if (isparallel != (id != 0))
+	{
+	  cerr << " ****************************** " << endl;
+	  cerr << " **** wrong isparallel   ****** " << endl;
+	  cerr << " ****************************** " << endl;
+	}
+    
+    if (id != 0)    // if (isparallel)
+      {
+	paralleltop.Update();
+	if ( paralleltop.DoCoarseUpdate() )
+	  paralleltop.UpdateCoarseGrid();
+      }
+#endif
+ 
+ 
+  
+    /* 
+       for (i = 1; i <= ne; i++)
+       {
+       (*testout) << "Element " << i << endl;
+       (*testout) << "PNums " << endl; 
+       for( int l=1;l<=8;l++) *testout << mesh.VolumeElement(i).PNum(l) << "\t"; 
+       *testout << endl; 
+       (*testout) << "edges: " << endl;
+       for (j = 0; j < 9; j++)
+       (*testout) << edges.Elem(i)[j] << " ";
+       (*testout) << "faces: " << endl;
+       for (j = 0; j < 6; j++)m
+       (*testout) << faces.Elem(i)[j] << " ";
+       }
+
+       for (i = 1; i <= nse; i++)
+       {
+       (*testout) << "SElement " << i << endl;
+       (*testout) << "PNums " << endl; 
+       for( int l=1;l<=4;l++) *testout << mesh.SurfaceElement(i).PNum(l) << "\t"; 
+       *testout << endl; 
+       }
+    */
+    timestamp = NextTimeStamp();
+  }
+
+  
+
+
+
+  const Point3d * MeshTopology :: GetVertices (ELEMENT_TYPE et)
+  {
+    static Point3d segm_points [] = 
+      { Point3d (1, 0, 0),
+	Point3d (0, 0, 0) };
+  
+    static Point3d trig_points [] = 
+      { Point3d ( 1, 0, 0 ),
+	Point3d ( 0, 1, 0 ),
+	Point3d ( 0, 0, 0 ) };
+
+    static Point3d quad_points [] = 
+      { Point3d ( 0, 0, 0 ),
+	Point3d ( 1, 0, 0 ),
+	Point3d ( 1, 1, 0 ),
+	Point3d ( 0, 1, 0 ) };
+
+    static Point3d tet_points [] = 
+      { Point3d ( 1, 0, 0 ),
+	Point3d ( 0, 1, 0 ),
+	Point3d ( 0, 0, 1 ),
+	Point3d ( 0, 0, 0 ) };
+
+    static Point3d pyramid_points [] =
+      {
+	Point3d ( 0, 0, 0 ),
+	Point3d ( 1, 0, 0 ),
+	Point3d ( 1, 1, 0 ),
+	Point3d ( 0, 1, 0 ),
+	Point3d ( 0, 0, 1-1e-7 ),
+      };    
+  
+    static Point3d prism_points[] = 
+      {
+	Point3d ( 1, 0, 0 ),
+	Point3d ( 0, 1, 0 ),
+	Point3d ( 0, 0, 0 ),
+	Point3d ( 1, 0, 1 ),
+	Point3d ( 0, 1, 1 ),
+	Point3d ( 0, 0, 1 )
+      };
+
+
+    static Point3d hex_points [] = 
+      { Point3d ( 0, 0, 0 ),
+	Point3d ( 1, 0, 0 ),
+	Point3d ( 1, 1, 0 ),
+	Point3d ( 0, 1, 0 ),
+	Point3d ( 0, 0, 1 ),
+	Point3d ( 1, 0, 1 ),
+	Point3d ( 1, 1, 1 ),
+	Point3d ( 0, 1, 1 ) };
+
+
+    switch (et)
+      {
+      case SEGMENT:
+      case SEGMENT3:
+	return segm_points;
+
+      case TRIG:
+      case TRIG6:
+	return trig_points;
+
+      case QUAD:
+      case QUAD6:
+      case QUAD8:
+	return quad_points;
+
+      case TET:
+      case TET10:
+	return tet_points;
+
+      case PYRAMID:
+	return pyramid_points;
+
+      case PRISM:
+      case PRISM12:
+	return prism_points;
+
+      case HEX:
+	return hex_points;
+      default:
+	cerr << "Ng_ME_GetVertices, illegal element type " << et << endl;
+      }
+    return 0;
+  }
+
+
+
+
+
+
+
+
+  void MeshTopology :: GetElementEdges (int elnr, Array<int> & eledges) const
+  {
+    int ned = GetNEdges (mesh.VolumeElement(elnr).GetType());
+    eledges.SetSize (ned);
+    for (int i = 0; i < ned; i++)
+      eledges[i] = abs (edges.Get(elnr)[i]);
+  }
+  void MeshTopology :: GetElementFaces (int elnr, Array<int> & elfaces, bool withorientation) const
+  {
+    int nfa = GetNFaces (mesh.VolumeElement(elnr).GetType());
+    elfaces.SetSize (nfa);
+
+    if (!withorientation)
+
+      for (int i = 1; i <= nfa; i++)
+	{
+	  elfaces.Elem(i) = (faces.Get(elnr)[i-1]-1) / 8 + 1;
+	}
+    
+    else
+      
+      for (int i = 1; i <= nfa; i++)
+	{
+	  elfaces.Elem(i) = (faces.Get(elnr)[i-1]-1) / 8 + 1;
+	  int orient = (faces.Get(elnr)[i-1]-1) % 8;
+	  if(orient == 1 || orient == 2 || orient == 4 || orient == 7)
+	    elfaces.Elem(i) *= -1;
+	}
+  }
+
+  void MeshTopology :: GetElementEdgeOrientations (int elnr, Array<int> & eorient) const
+  {
+    int ned = GetNEdges (mesh.VolumeElement(elnr).GetType());
+    eorient.SetSize (ned);
+    for (int i = 1; i <= ned; i++)
+      eorient.Elem(i) = (edges.Get(elnr)[i-1] > 0) ? 1 : -1;
+  }
+
+  void MeshTopology :: GetElementFaceOrientations (int elnr, Array<int> & forient) const
+  {
+    int nfa = GetNFaces (mesh.VolumeElement(elnr).GetType());
+    forient.SetSize (nfa);
+    for (int i = 1; i <= nfa; i++)
+      forient.Elem(i) = (faces.Get(elnr)[i-1]-1) % 8;
+  }
+
+
+
+  int MeshTopology :: GetElementEdges (int elnr, int * eledges, int * orient) const
+  {
+    //  int ned = GetNEdges (mesh.VolumeElement(elnr).GetType());
+
+    if (mesh.GetDimension()==3 || 1)
+      {
+	if (orient)
+	  {
+	    for (int i = 0; i < 12; i++)
+	      {
+		if (!edges.Get(elnr)[i]) return i;
+		eledges[i] = abs (edges.Get(elnr)[i]);
+		orient[i] = (edges.Get(elnr)[i] > 0 ) ? 1 : -1;
+	      }
+	  }
+	else
+	  {
+	    for (int i = 0; i < 12; i++)
+	      {
+		if (!edges.Get(elnr)[i]) return i;
+		eledges[i] = abs (edges.Get(elnr)[i]);
+	      }
+	  }
+	return 12;
+      }
+    else
+      {
+	throw NgException("rethink implementation");
+	/*
+	  if (orient)
+	  {
+	  for (i = 0; i < 4; i++)
+	  {
+	  if (!surfedges.Get(elnr)[i]) return i;
+	  eledges[i] = abs (surfedges.Get(elnr)[i]);
+	  orient[i] = (surfedges.Get(elnr)[i] > 0 ) ? 1 : -1;
+	  }
+	  }
+	  else
+	  {
+	  if (!surfedges.Get(elnr)[i]) return i;
+	  for (i = 0; i < 4; i++)
+	  eledges[i] = abs (surfedges.Get(elnr)[i]);
+	  }
+	*/
+	return 4;
+	//      return GetSurfaceElementEdges (elnr, eledges, orient);
+      }
+  }
+
+  int MeshTopology :: GetElementFaces (int elnr, int * elfaces, int * orient) const
+  {
+    //  int nfa = GetNFaces (mesh.VolumeElement(elnr).GetType());
+    if (orient)
+      {
+	for (int i = 0; i < 6; i++)
+	  {
+	    if (!faces.Get(elnr)[i]) return i;
+	    elfaces[i] = (faces.Get(elnr)[i]-1) / 8 + 1;
+	    orient[i] = (faces.Get(elnr)[i]-1) % 8;
+	  }
+      }
+    else
+      {
+	for (int i = 0; i < 6; i++)
+	  {
+	    if (!faces.Get(elnr)[i]) return i;
+	    elfaces[i] = (faces.Get(elnr)[i]-1) / 8 + 1;
+	  }
+      }
+    return 6;
+  }
+
+  void MeshTopology :: GetSurfaceElementEdges (int elnr, Array<int> & eledges) const
+  {
+    int i;
+    if (mesh.GetDimension()==3 || 1)
+      {
+	int ned = GetNEdges (mesh.SurfaceElement(elnr).GetType());
+	eledges.SetSize (ned);
+	for (i = 1; i <= ned; i++)
+	  eledges.Elem(i) = abs (surfedges.Get(elnr)[i-1]);
+      }
+    else
+      {
+	cout << "surfeledge(" << elnr << ") = " << flush;
+	eledges.SetSize(1); 
+	eledges.Elem(1) = abs (segedges.Get(elnr));
+	cout << eledges.Elem(1) << endl;
+      }
+  }
+
+  int MeshTopology :: GetSurfaceElementFace (int elnr) const
+  {
+    return (surffaces.Get(elnr)-1) / 8 + 1;  
+  }
+
+  void MeshTopology :: 
+  GetSurfaceElementEdgeOrientations (int elnr, Array<int> & eorient) const
+  {
+    int ned = GetNEdges (mesh.SurfaceElement(elnr).GetType());
+    eorient.SetSize (ned);
+    for (int i = 1; i <= ned; i++)
+      eorient.Elem(i) = (surfedges.Get(elnr)[i-1] > 0) ? 1 : -1;
+  }
+
+  int MeshTopology :: GetSurfaceElementFaceOrientation (int elnr) const
+  {
+    return (surffaces.Get(elnr)-1) % 8;
+  }
+
+  int MeshTopology :: GetSurfaceElementEdges (int elnr, int * eledges, int * orient) const
+  {
+    int i;
+    if (mesh.GetDimension() == 3 || 1)
+      {
+	if (orient)
+	  {
+	    for (i = 0; i < 4; i++)
+	      {
+		if (!surfedges.Get(elnr)[i]) return i;
+		eledges[i] = abs (surfedges.Get(elnr)[i]);
+		orient[i] = (surfedges.Get(elnr)[i] > 0 ) ? 1 : -1;
+	      }
+	  }
+	else
+	  {
+	    for (i = 0; i < 4; i++)
+	      {
+		if (!surfedges.Get(elnr)[i]) return i;
+		eledges[i] = abs (surfedges.Get(elnr)[i]);
+	      }
+	  }
+	return 4;
+      }
+    else
+      {
+	eledges[0] = abs (segedges.Get(elnr));
+	if (orient)
+	  orient[0] = segedges.Get(elnr) > 0 ? 1 : -1;
+      }
+    return 1;
+  }
+
+
+  void MeshTopology :: GetFaceVertices (int fnr, Array<int> & vertices) const
+  {
+    vertices.SetSize(4);
+    int i;
+    for (i = 1; i <= 4; i++)
+      vertices.Elem(i) = face2vert.Get(fnr)[i-1];
+    if (vertices.Elem(4) == 0)
+      vertices.SetSize(3);
+  }
+
+  void MeshTopology :: GetFaceVertices (int fnr, int * vertices) const
+  {
+    for (int i = 0; i <= 3; i++)
+      vertices[i] = face2vert.Get(fnr)[i];
+  }
+
+
+  void MeshTopology :: GetEdgeVertices (int ednr, int & v1, int & v2) const
+  {
+    v1 = edge2vert.Get(ednr)[0];
+    v2 = edge2vert.Get(ednr)[1];
+  }
+
+
+  void MeshTopology :: GetFaceEdges (int fnr, Array<int> & fedges, bool withorientation) const
+  {
+    ArrayMem<int,4> pi(4);
+    ArrayMem<int,12> eledges;
+  
+    fedges.SetSize (0);
+    GetFaceVertices(fnr, pi);
+
+    // Sort Edges according to global vertex numbers 
+    // e1 = fmax, f2 
+    // e2 = fmax, f1 
+    // e3 = op e1(f2,f3) 
+    // e4 = op e2(f1,f3) 
+
+    /*  ArrayMem<int,4> fp; 
+	fp[0] = pi[0]; 
+	for(int k=1;k<pi.Size();k++) 
+	if(fp[k]>fp[0]) swap(fp[k],fp[0]); 
+  
+	fp[1] = fp[0]+ */ 
+  
+
+    //  GetVertexElements (pi[0], els);
+    FlatArray<int> els= GetVertexElements (pi[0]);
+
+    // find one element having all vertices of the face
+    for (int i = 0; i < els.Size(); i++)
+      {
+	const Element & el = mesh.VolumeElement(els[i]);
+	int nref_faces = GetNFaces (el.GetType());
+	const ELEMENT_FACE * ref_faces = GetFaces1 (el.GetType());
+	int nfa_ref_edges = GetNEdges (GetFaceType(fnr));
+      
+	int cntv = 0,fa=-1; 
+	for(int m=0;m<nref_faces;m++)
+	  { 
+	    cntv=0;
+	    for(int j=0;j<nfa_ref_edges && ref_faces[m][j]>0;j++)
+	      for(int k=0;k<pi.Size();k++)
+		{
+		  if(el[ref_faces[m][j]-1] == pi[k])
+		    cntv++;
+		}
+	    if (cntv == pi.Size())
+	      {
+		fa=m;
+		break;
+	      }
+	  }
+     
+	if(fa>=0)
+	  {
+	    const ELEMENT_EDGE * fa_ref_edges = GetEdges1 (GetFaceType(fnr)); 
+	    fedges.SetSize(nfa_ref_edges);
+	    GetElementEdges (els[i], eledges);
+	  
+	    for (int j = 0; j < eledges.Size(); j++)
+	      {
+		int vi1, vi2;
+		GetEdgeVertices (eledges[j], vi1, vi2);
+	    
+		bool has1 = 0;
+		bool has2 = 0;
+		for (int k = 0; k < pi.Size(); k++)
+		  {
+		    if (vi1 == pi[k]) has1 = 1;
+		    if (vi2 == pi[k]) has2 = 1;
+		  
+		  }
+	      
+		if (has1 && has2) // eledges[j] is on face 
+		  {
+		    // fedges.Append (eledges[j]);
+		    for(int k=0;k<nfa_ref_edges;k++)
+		      {
+			int w1 = el[ref_faces[fa][fa_ref_edges[k][0]-1]-1]; 
+			int w2 = el[ref_faces[fa][fa_ref_edges[k][1]-1]-1]; 
+
+			if(withorientation)
+			  {
+			    if(w1==vi1 && w2==vi2)
+			      fedges[k] = eledges[j];
+			    if(w1==vi2 && w2==vi1)
+			      fedges[k] = -eledges[j];
+			  }
+			else
+			  if((w1==vi1 && w2==vi2) || (w1==vi2 && w2==vi1))
+			    fedges[k] = eledges[j];
+		      }
+		  }
+	      }
+	  
+	    // *testout << " Face " << fnr << endl; 
+	    // *testout << " GetFaceEdges " << fedges << endl;
+	  
+	    return;
+	  }
+      }   
+  }
+
+
+  ELEMENT_TYPE MeshTopology :: GetFaceType (int fnr) const
+  {
+    if (face2vert.Get(fnr)[3] == 0) return TRIG; else return QUAD;
+  }
+
+
+  void MeshTopology :: GetVertexElements (int vnr, Array<int> & elements) const
+  {
+    if (vert2element)
+      {
+	int i; 
+	int ne = vert2element->EntrySize(vnr);
+	elements.SetSize(ne);
+	for (i = 1; i <= ne; i++)
+	  elements.Elem(i) = vert2element->Get(vnr, i);
+      }
+  }
+
+
+  FlatArray<int> MeshTopology :: GetVertexElements (int vnr) const
+  {
+    if (vert2element)
+      return (*vert2element)[vnr];
+    return FlatArray<int> (0,0);
+  }
+
+  FlatArray<int> MeshTopology :: GetVertexSurfaceElements (int vnr) const
+  {
+    if (vert2surfelement)
+      return (*vert2surfelement)[vnr];
+    return FlatArray<int> (0,0);
+  }
+
+
+  void MeshTopology :: GetVertexSurfaceElements( int vnr, 
+						 Array<int>& elements ) const
+  {
+    if (vert2surfelement)
+      {
+	int i;
+	int ne = vert2surfelement->EntrySize(vnr);
+	elements.SetSize(ne);
+	for (i = 1; i <= ne; i++)
+	  elements.Elem(i) = vert2surfelement->Get(vnr, i);
+      }
+  }
+
+
+  int MeshTopology :: GetVerticesEdge ( int v1, int v2 ) const
+  {
+    Array<int> elements_v1, elementedges;
+    GetVertexElements ( v1, elements_v1);
+    int edv1, edv2;
+
+    for ( int i = 0; i < elements_v1.Size(); i++ )
+      {
+	GetElementEdges( elements_v1[i], elementedges );
+	for ( int ed = 0; ed < elementedges.Size(); ed ++)
+	  {
+	    GetEdgeVertices( elementedges[ed], edv1, edv2 );
+	    if ( ( edv1 == v1 && edv2 == v2 ) || ( edv1 == v2 && edv2 == v1 ) )
+	      return elementedges[ed];
+	  }
+      }
+
+    return -1;
+  }
+
+
+
+  void MeshTopology :: 
+  GetSegmentVolumeElements ( int segnr, Array<int> & volels ) const
+  {
+    int v1, v2;
+    GetEdgeVertices ( GetSegmentEdge (segnr), v1, v2 );
+    Array<int> volels1, volels2;
+    GetVertexElements ( v1, volels1 );
+    GetVertexElements ( v2, volels2 );
+    volels.SetSize(0);
+
+    for ( int eli1=1; eli1 <= volels1.Size(); eli1++)
+      if ( volels2.Contains( volels1.Elem(eli1) ) )
+	volels.Append ( volels1.Elem(eli1) );
+  }
+
+  void MeshTopology :: 
+  GetSegmentSurfaceElements (int segnr, Array<int> & els) const
+  {
+    int v1, v2;
+    GetEdgeVertices ( GetSegmentEdge (segnr), v1, v2 );
+    Array<int> els1, els2;
+    GetVertexSurfaceElements ( v1, els1 );
+    GetVertexSurfaceElements ( v2, els2 );
+    els.SetSize(0);
+
+    for ( int eli1=1; eli1 <= els1.Size(); eli1++)
+      if ( els2.Contains( els1.Elem(eli1) ) )
+	els.Append ( els1.Elem(eli1) );
+  }
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/topology.hpp b/contrib/Netgen/libsrc/meshing/topology.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a453fa84dd62e38495651b8961416d58ac97a63b
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/topology.hpp
@@ -0,0 +1,683 @@
+#ifndef TOPOLOGY
+#define TOPOLOGY
+
+/**************************************************************************/
+/* File:   topology.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   27. Apr. 01                                                    */
+/**************************************************************************/
+
+/*
+    Mesh topology
+    (Elements, Faces, Edges, Vertices
+*/
+
+
+class MeshTopology
+{
+  const Mesh & mesh;
+  bool buildedges;
+  bool buildfaces;
+
+  MoveableArray<INDEX_2> edge2vert;
+  MoveableArray<INDEX_4> face2vert;
+  MoveableArray<int[12]> edges;
+  MoveableArray<int[6]> faces;
+  MoveableArray<int[4]> surfedges;
+  MoveableArray<int> segedges;
+  MoveableArray<int> surffaces;
+  MoveableArray<INDEX_2> surf2volelement;
+  MoveableArray<int> face2surfel;
+  TABLE<int,PointIndex::BASE> *vert2element;
+  TABLE<int,PointIndex::BASE> *vert2surfelement;
+  TABLE<int,PointIndex::BASE> *vert2segment;
+  int timestamp;
+public:
+  int GetNSurfedges() const {return surfedges.Size();}
+
+  MeshTopology (const Mesh & amesh);
+  ~MeshTopology ();
+
+  void SetBuildEdges (bool be)
+  { buildedges = be; }
+  void SetBuildFaces (bool bf)
+  { buildfaces = bf; }
+
+  bool HasEdges () const
+  { return buildedges; }
+  bool HasFaces () const
+  { return buildfaces; }
+
+  void Update();
+
+
+  int GetNEdges () const { return edge2vert.Size(); }
+  int GetNFaces () const { return face2vert.Size(); }
+
+  static inline int GetNVertices (ELEMENT_TYPE et);
+  static inline int GetNPoints (ELEMENT_TYPE et);
+  static inline int GetNEdges (ELEMENT_TYPE et);
+  static inline int GetNFaces (ELEMENT_TYPE et);
+
+  static const Point3d * GetVertices (ELEMENT_TYPE et);
+  inline static const ELEMENT_EDGE * GetEdges1 (ELEMENT_TYPE et);
+  inline static const ELEMENT_EDGE * GetEdges0 (ELEMENT_TYPE et);
+  inline static const ELEMENT_FACE * GetFaces1 (ELEMENT_TYPE et);
+  inline static const ELEMENT_FACE * GetFaces0 (ELEMENT_TYPE et);
+
+  
+  int GetSegmentEdge (int segnr) const { return abs(segedges[segnr-1]); }
+  int GetSegmentEdgeOrientation (int segnr) const { return sgn(segedges[segnr-1]); }
+
+  void GetSegmentEdge (int segnr, int & enr, int & orient) const
+  {
+    enr = abs(segedges.Get(segnr));
+    orient = segedges.Get(segnr) > 0 ? 1 : -1;
+  }
+
+  void GetElementEdges (int elnr, Array<int> & edges) const;
+  void GetElementFaces (int elnr, Array<int> & faces, bool withorientation = false) const;
+  void GetElementEdgeOrientations (int elnr, Array<int> & eorient) const;
+  void GetElementFaceOrientations (int elnr, Array<int> & forient) const;
+
+  int GetElementEdges (int elnr, int * edges, int * orient) const;
+  int GetElementFaces (int elnr, int * faces, int * orient) const;
+
+  void GetFaceVertices (int fnr, Array<int> & vertices) const;
+  void GetFaceVertices (int fnr, int * vertices) const;
+  void GetEdgeVertices (int enr, int & v1, int & v2) const;
+  const int * GetEdgeVerticesPtr (int enr) const { return &edge2vert[enr][0]; }
+  const int * GetFaceVerticesPtr (int fnr) const { return &face2vert[fnr][0]; }
+  void GetFaceEdges (int fnr, Array<int> & edges, bool withorientation = false) const;
+
+  ELEMENT_TYPE GetFaceType (int fnr) const;
+
+  void GetSurfaceElementEdges (int elnr, Array<int> & edges) const;
+  int GetSurfaceElementFace (int elnr) const;
+  void GetSurfaceElementEdgeOrientations (int elnr, Array<int> & eorient) const;
+  int GetSurfaceElementFaceOrientation (int elnr) const;
+
+  int GetSurfaceElementEdges (int elnr, int * edges, int * orient) const;
+
+  const int * GetElementEdgesPtr (int elnr) const { return &edges[elnr][0]; }
+  const int * GetSurfaceElementEdgesPtr (int selnr) const { return &surfedges[selnr][0]; }
+  const int * GetSegmentElementEdgesPtr (int selnr) const { return &segedges[selnr]; }
+
+  const int * GetElementFacesPtr (int elnr) const { return &faces[elnr][0]; }
+  const int * GetSurfaceElementFacesPtr (int selnr) const { return &surffaces[selnr]; }
+
+
+  void GetSurface2VolumeElement (int selnr, int & elnr1, int & elnr2) const
+  { 
+    elnr1 = surf2volelement.Get(selnr)[0];
+    elnr2 = surf2volelement.Get(selnr)[1];
+  }
+
+  int GetFace2SurfaceElement (int fnr) const { return face2surfel[fnr-1]; }
+  
+  void GetVertexElements (int vnr, Array<int> & elements) const;
+  FlatArray<int> GetVertexElements (int vnr) const;
+
+  void GetVertexSurfaceElements( int vnr, Array<int>& elements ) const;
+  FlatArray<int> GetVertexSurfaceElements (int vnr) const;
+
+
+  
+  int GetVerticesEdge ( int v1, int v2) const;
+  void GetSegmentVolumeElements ( int segnr, Array<int> & els ) const;
+  void GetSegmentSurfaceElements ( int segnr, Array<int> & els ) const;
+};
+
+
+
+
+
+
+
+
+
+
+int MeshTopology :: GetNVertices (ELEMENT_TYPE et)
+{
+  switch (et)
+    {
+    case SEGMENT:
+    case SEGMENT3:
+      return 2;
+
+    case TRIG:
+    case TRIG6:
+      return 3;
+
+    case QUAD:
+    case QUAD6:
+    case QUAD8:
+      return 4;
+
+    case TET:
+    case TET10:
+      return 4;
+
+    case PYRAMID:
+      return 5;
+
+    case PRISM:
+    case PRISM12:
+      return 6;
+
+    case HEX:
+      return 8;
+
+    default:
+      cerr << "Ng_ME_GetNVertices, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+int MeshTopology :: GetNPoints (ELEMENT_TYPE et)
+{
+  switch (et)
+    {
+    case SEGMENT:
+      return 2;
+    case SEGMENT3:
+      return 3;
+
+    case TRIG:
+      return 3;
+    case TRIG6:
+      return 6;
+
+    case QUAD:
+    case QUAD6:
+    case QUAD8:
+      return 4;
+
+    case TET:
+      return 4;
+    case TET10:
+      return 10;
+
+    case PYRAMID:
+      return 5;
+
+    case PRISM:
+    case PRISM12:
+      return 6;
+
+    case HEX:
+      return 8;
+
+    default:
+      cerr << "Ng_ME_GetNVertices, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+
+int MeshTopology :: GetNEdges (ELEMENT_TYPE et)
+{
+  switch (et)
+    {
+    case SEGMENT:
+    case SEGMENT3:
+      return 1;
+
+    case TRIG:
+    case TRIG6:
+      return 3;
+
+    case QUAD:
+    case QUAD6:
+    case QUAD8:
+      return 4;
+
+    case TET:
+    case TET10:
+      return 6;
+
+    case PYRAMID:
+      return 8;
+
+    case PRISM:
+    case PRISM12:
+      return 9;
+
+    case HEX:
+      return 12;
+
+    default:
+      cerr << "Ng_ME_GetNEdges, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+int MeshTopology :: GetNFaces (ELEMENT_TYPE et)
+{
+  switch (et)
+    {
+    case SEGMENT:
+    case SEGMENT3:
+      return 0;
+
+    case TRIG:
+    case TRIG6:
+      return 1;
+
+    case QUAD:
+    case QUAD6:
+    case QUAD8:
+      return 1;
+
+    case TET:
+    case TET10:
+      return 4;
+
+    case PYRAMID:
+      return 5;
+
+    case PRISM:
+    case PRISM12:
+      return 5;
+
+    case HEX:
+      return 6;
+
+    default:
+      cerr << "Ng_ME_GetNVertices, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+
+
+
+
+const ELEMENT_EDGE * MeshTopology :: GetEdges1 (ELEMENT_TYPE et)
+{
+  static int segm_edges[1][2] =
+    { { 1, 2 }};
+
+  static int trig_edges[3][2] =
+    { { 3, 1 },
+      { 2, 3 },        
+      { 1, 2 }};
+
+  static int quad_edges[4][2] =
+    { { 1, 2 },
+      { 3, 4 },
+      { 4, 1 },
+      { 2, 3 }};
+
+
+  static int tet_edges[6][2] =
+    { { 4, 1 },
+      { 4, 2 },
+      { 4, 3 }, 
+      { 1, 2 },
+      { 1, 3 },
+      { 2, 3 }};
+
+  static int prism_edges[9][2] =
+    { { 3, 1 },
+      { 1, 2 },
+      { 3, 2 },
+      { 6, 4 },
+      { 4, 5 },
+      { 6, 5 },
+      { 3, 6 },
+      { 1, 4 },
+      { 2, 5 }};
+
+  static int pyramid_edges[8][2] =
+    { { 1, 2 },
+      { 2, 3 },
+      { 1, 4 },
+      { 4, 3 },
+      { 1, 5 },
+      { 2, 5 },
+      { 3, 5 },
+      { 4, 5 }};
+
+  static int hex_edges[12][2] =
+    {
+      { 1, 2 },
+      { 3, 4 },
+      { 4, 1 },
+      { 2, 3 },
+      { 5, 6 },
+      { 7, 8 },
+      { 8, 5 },
+      { 6, 7 },
+      { 1, 5 },
+      { 2, 6 },
+      { 3, 7 },
+      { 4, 8 },
+    };
+
+  switch (et)
+    {
+    case SEGMENT:
+    case SEGMENT3:
+      return segm_edges;
+
+    case TRIG:
+    case TRIG6:
+      return trig_edges;
+
+    case QUAD:
+    case QUAD6:
+    case QUAD8:
+      return quad_edges;
+
+    case TET:
+    case TET10:
+      return tet_edges;
+
+    case PYRAMID:
+      return pyramid_edges;
+
+    case PRISM:
+    case PRISM12:
+      return prism_edges;
+
+    case HEX:
+      return hex_edges;
+    default:
+      cerr << "Ng_ME_GetEdges, illegal element type " << et << endl;
+    }
+   return 0;  
+}
+
+
+
+const ELEMENT_EDGE * MeshTopology :: GetEdges0 (ELEMENT_TYPE et)
+{
+  static int segm_edges[1][2] =
+    { { 0, 1 }};
+
+  static int trig_edges[3][2] =
+    { { 2, 0 },
+      { 1, 2 },        
+      { 0, 1 }};
+
+  static int quad_edges[4][2] =
+    { { 0, 1 },
+      { 2, 3 },
+      { 3, 0 },
+      { 1, 2 }};
+
+
+  static int tet_edges[6][2] =
+    { { 3, 0 },
+      { 3, 1 },
+      { 3, 2 }, 
+      { 0, 1 },
+      { 0, 2 },
+      { 1, 2 }};
+
+  static int prism_edges[9][2] =
+    { { 2, 0 },
+      { 0, 1 },
+      { 2, 1 },
+      { 5, 3 },
+      { 3, 4 },
+      { 5, 4 },
+      { 2, 5 },
+      { 0, 3 },
+      { 1, 4 }};
+
+  static int pyramid_edges[8][2] =
+    { { 0, 1 },
+      { 1, 2 },
+      { 0, 3 },
+      { 3, 2 },
+      { 0, 4 },
+      { 1, 4 },
+      { 2, 4 },
+      { 3, 4 }};
+
+  static int hex_edges[12][2] =
+    {
+      { 0, 1 },
+      { 2, 3 },
+      { 3, 0 },
+      { 1, 2 },
+      { 4, 5 },
+      { 6, 7 },
+      { 7, 4 },
+      { 5, 6 },
+      { 0, 4 },
+      { 1, 5 },
+      { 2, 6 },
+      { 3, 7 },
+    };
+
+  switch (et)
+    {
+    case SEGMENT:
+    case SEGMENT3:
+      return segm_edges;
+
+    case TRIG:
+    case TRIG6:
+      return trig_edges;
+
+    case QUAD:
+    case QUAD6:
+    case QUAD8:
+      return quad_edges;
+
+    case TET:
+    case TET10:
+      return tet_edges;
+
+    case PYRAMID:
+      return pyramid_edges;
+
+    case PRISM:
+    case PRISM12:
+      return prism_edges;
+
+    case HEX:
+      return hex_edges;
+    default:
+      cerr << "Ng_ME_GetEdges, illegal element type " << et << endl;
+    }
+   return 0;  
+}
+
+
+
+
+
+
+
+
+
+
+const ELEMENT_FACE * MeshTopology :: GetFaces1 (ELEMENT_TYPE et)
+{
+  static const int trig_faces[1][4] = 
+    { { 1, 2, 3, 0 } };
+  static const int quad_faces[1][4] = 
+    { { 1, 2, 3, 4 } };
+
+  static const int tet_faces[4][4] =
+    { { 4, 2, 3, 0 },
+      { 4, 3, 1, 0 },
+      { 4, 1, 2, 0 },
+      { 1, 3, 2, 0 } };
+  
+  static const int prism_faces[5][4] =
+    {
+      { 1, 3, 2, 0 },
+      { 4, 5, 6, 0 },
+      { 3, 1, 4, 6 },
+      { 1, 2, 5, 4 },
+      { 2, 3, 6, 5 } 
+    };
+
+  static const int pyramid_faces[5][4] =
+    {
+      { 1, 2, 5, 0 },
+      { 2, 3, 5, 0 },
+      { 3, 4, 5, 0 },
+      { 4, 1, 5, 0 },
+      { 1, 4, 3, 2 } 
+    };
+
+  static const int hex_faces[6][4] =
+    {
+      { 1, 4, 3, 2 },
+      { 5, 6, 7, 8 },
+      { 1, 2, 6, 5 },
+      { 2, 3, 7, 6 },
+      { 3, 4, 8, 7 },
+      { 4, 1, 5, 8 }
+    };
+
+
+  
+  switch (et)
+    {
+    case TRIG:
+    case TRIG6:
+      return trig_faces;
+
+    case QUAD:
+    case QUAD6:
+    case QUAD8:
+      return quad_faces;
+
+
+    case TET:
+    case TET10:
+      return tet_faces;
+
+    case PRISM:
+    case PRISM12:
+      return prism_faces;
+
+    case PYRAMID:
+      return pyramid_faces;
+
+    case SEGMENT:
+    case SEGMENT3:
+
+    case HEX:
+      return hex_faces;
+
+    default:
+      cerr << "Ng_ME_GetVertices, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+
+
+
+const ELEMENT_FACE * MeshTopology :: GetFaces0 (ELEMENT_TYPE et)
+{
+  static const int trig_faces[1][4] = 
+    { { 0, 1, 2, -1 } };
+  static const int quad_faces[1][4] = 
+    { { 0, 1, 2, 3 } };
+
+  static const int tet_faces[4][4] =
+    { { 3, 1, 2, -1 },
+      { 3, 2, 0, -1 },
+      { 3, 0, 1, -1 },
+      { 0, 2, 1, -1 } };
+  
+  static const int prism_faces[5][4] =
+    {
+      { 0, 2, 1, -1 },
+      { 3, 4, 5, -1 },
+      { 2, 0, 3, 5 },
+      { 0, 1, 4, 3 },
+      { 1, 2, 5, 4 } 
+    };
+
+  static const int pyramid_faces[5][4] =
+    {
+      { 0, 1, 4, -1 },
+      { 1, 2, 4, -1 },
+      { 2, 3, 4, -1 },
+      { 3, 0, 4, -1 },
+      { 0, 3, 2, 1 } 
+    };
+
+  static const int hex_faces[6][4] =
+    {
+      { 0, 3, 2, 1 },
+      { 4, 5, 6, 7 },
+      { 0, 1, 5, 4 },
+      { 1, 2, 6, 5 },
+      { 2, 3, 7, 6 },
+      { 3, 0, 4, 7 }
+    };
+
+
+  
+  switch (et)
+    {
+    case TRIG:
+    case TRIG6:
+      return trig_faces;
+
+    case QUAD:
+    case QUAD6:
+    case QUAD8:
+      return quad_faces;
+
+
+    case TET:
+    case TET10:
+      return tet_faces;
+
+    case PRISM:
+    case PRISM12:
+      return prism_faces;
+
+    case PYRAMID:
+      return pyramid_faces;
+
+    case SEGMENT:
+    case SEGMENT3:
+
+    case HEX:
+      return hex_faces;
+
+    default:
+      cerr << "Ng_ME_GetVertices, illegal element type " << et << endl;
+    }
+  return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/triarls.cpp b/contrib/Netgen/libsrc/meshing/triarls.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d82806e9bcbd9cf9f5b8a90306e1cee23c0bf4bc
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/triarls.cpp
@@ -0,0 +1,468 @@
+namespace netgen
+{
+const char * triarules[] = {
+"rule \"Free Triangle (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0) { 1.0, 0, 1.0 };\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.866) { 0.5 X2 } { };\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 0.7) { 0.5 X2 } { };\n",\
+"(0.5, 1.5) { 0.5 X2 } { };\n",\
+"(-0.5, 0.7) { 0.5 X2 } { };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.5, 0.866) { 0.5 X2 } { };\n",\
+"(0.5, 0.866) { 0.5 X2 } { };\n",\
+"(0.5, 0.866) { 0.5 X2 } { };\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"rule \"Free Triangle (5)\"\n",\
+"\n",\
+"quality 5\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0) { 1.0, 0, 1.0 };\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.5) { 0.5 X2 } { };\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 0.7) { 1 X2 } { };\n",\
+"(0, 0.7) { } { };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.5, 0.5) { 0.5 X2 } { };\n",\
+"(0.5, 0.5) { 0.5 X2 } { };\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Free Triangle (10)\"\n",\
+"\n",\
+"quality 10\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0) { 1.0, 0, 1.0 };\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.3) { 0.5 X2 } { };\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 0.5) { 1 X2 } { };\n",\
+"(0, 0.5) { } { };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.5, 0.3) { 0.5 X2 } { };\n",\
+"(0.5, 0.3) { 0.5 X2 } { };\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Free Triangle (20)\"\n",\
+"\n",\
+"quality 20\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0) { 1.0, 0, 1.0 };\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.1) { 0.5 X2 } { };\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1, 0.2) { 1 X2 } { };\n",\
+"(0, 0.2) { } { };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.5, 0.1) { 0.5 X2 } { };\n",\
+"(0.5, 0.1) { 0.5 X2 } { };\n",\
+"\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Right 60 (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0) { 0.5, 0, 1.0 };\n",\
+"(0.5, 0.866) { 0.6, 0, 0.8 };\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(-0.125, 0.6495) { -0.5 X2, 0.75 X3 } { -0.5 Y2, 0.75 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(0.25, 0.433) { 0.5 X3 } { 0.5 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Left 60 (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(0.5, 0.866);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.125, 0.6495) { 0.75 X2, 0.75 X3 } { 0.75 Y3 };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.75, 0.433) { 0.5 X2, 0.5 X3 } { 0.5 Y2, 0.5 Y3 };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Right 120 (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1.5, 0.866);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.866) { 1 X3, -1 X2 } { 1 Y3 };\n",\
+"\n",\
+"newlines\n",\
+"(1, 4);\n",\
+"(4, 3);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(1, 1.732) { -2 X2, 2 X3 } { 2 Y3 };\n",\
+"(0, 1.732) { -3 X2, 2 X3 } { 2 Y3 };\n",\
+"(-0.5, 0.866) { -2 X2, 1 X3 } {1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 4);\n",\
+"(2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Left 120 (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(-0.5, 0.866);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.866) { 1 X3, 1 X2 } { 1 Y3 };\n",\
+"\n",\
+"newlines\n",\
+"(3, 4);\n",\
+"(4, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 0.866) { 2 X2, 1 X3 } { 1 Y3 };\n",\
+"(1, 1.732) { 2 X2, 2 X3 } { 2 Y3 };\n",\
+"(0, 1.732) { -1 X2, 2 X3 } { 2 Y3 };\n",\
+"(-0.5, 0.866) { 1 X3 } {1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 4);\n",\
+"(2, 3, 4);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Left Right 120 (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(-0.5, 0.866);\n",\
+"(1.5, 0.866);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 1) del;\n",\
+"(2, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.866) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 };\n",\
+"\n",\
+"newlines\n",\
+"(3, 5);\n",\
+"(5, 4);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.5, 0.866) { 1 X4 } { 1 Y4 };\n",\
+"(1, 1.299) { -0.5 X2, 0.375 X3, 1.125 X4 } { -0.5 Y2, 0.375 Y3, 1.125 Y4 };\n",\
+"(0, 1.299) { 1.125 X3, 0.375 X4 } { 1.125 Y3, 0.375 Y4 };\n",\
+"(-0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 5);\n",\
+"(3, 1, 5);\n",\
+"(2, 4, 5);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"rule \"Fill Triangle\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(0.5, 0.866);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(2, 3) del;\n",\
+"(3, 1) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { 1 Y2 };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"Vis A Vis (1)\"\n",\
+"\n",\
+"quality 1\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(0.5, 0.866);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"\n",\
+"newpoints\n",\
+"\n",\
+"newlines\n",\
+"(1, 3);\n",\
+"(3, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(1.2, 0.693) { 0.8 X2, 0.8 X3 } { 0.8 Y2, 0.8 Y3 };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(-0.2, 0.693) { -0.6 X2, 0.8 X3 } { -0.6 Y2, 0.8 Y3 };\n",\
+"\n",\
+"freearea2\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { };\n",\
+"(0.75, 0.433) { 0.5 X2, 0.5 X3 } { 0.5 Y2, 0.5 Y3 };\n",\
+"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\
+"(0.25, 0.433) { 0.5 X3 } { 0.5 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 3);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"rule \"2 h Vis A Vis (1)\"\n",\
+"\n",\
+"quality 3\n",\
+"\n",\
+"mappoints\n",\
+"(0, 0);\n",\
+"(1, 0);\n",\
+"(1, 1.732);\n",\
+"(0, 1.732);\n",\
+"\n",\
+"maplines\n",\
+"(1, 2) del;\n",\
+"(3, 4) del;\n",\
+"\n",\
+"newpoints\n",\
+"(0.5, 0.866) { 0.25 X2, 0.25 X3, 0.25 X4 } { 0.25 Y2, 0.25 Y3, 0.25 Y4 };\n",\
+"\n",\
+"newlines\n",\
+"(1, 5);\n",\
+"(5, 4);\n",\
+"(3, 5);\n",\
+"(5, 2);\n",\
+"\n",\
+"freearea\n",\
+"(0, 0);\n",\
+"(1, 0) { 1 X2 } { 1 Y2 };\n",\
+"(1.5, 0.866) { 0.75 X2, 0.75 X3, -0.25 X4 } { 0.75 Y2, 0.75 Y3, -0.25 Y4 };\n",\
+"(1, 1.732) { 1 X3 } { 1 Y3 };\n",\
+"(0, 1.732) { 1 X4 } { 1 Y4 };\n",\
+"(-0.5, 0.866) { 0.75 X4, -0.25 X2, -0.25 X3 } { 0.75 Y4, -0.25 Y3 };\n",\
+"\n",\
+"elements\n",\
+"(1, 2, 5);\n",\
+"(3, 4, 5);\n",\
+"\n",\
+"endrule\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+"\n",\
+0};
+}
diff --git a/contrib/Netgen/libsrc/meshing/validate.cpp b/contrib/Netgen/libsrc/meshing/validate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..44b983cca03e9974492bb425496a430842e3cd89
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/validate.cpp
@@ -0,0 +1,591 @@
+
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+  void GetPureBadness(Mesh & mesh, Array<double> & pure_badness,
+		      const BitArray & isnewpoint)
+  {
+    //const int ne = mesh.GetNE();
+    const int np = mesh.GetNP();
+
+    pure_badness.SetSize(np+PointIndex::BASE+1);
+    pure_badness = -1;
+
+    Array< Point<3>* > backup(np);
+
+    for(int i=0; i<np; i++)
+      {
+	backup[i] = new Point<3>(mesh.Point(i+1));
+
+	if(isnewpoint.Test(i+PointIndex::BASE) &&
+	   mesh.mlbetweennodes[i+PointIndex::BASE][0] > 0)
+	  {
+	    mesh.Point(i+1) = Center(mesh.Point(mesh.mlbetweennodes[i+PointIndex::BASE][0]),
+				     mesh.Point(mesh.mlbetweennodes[i+PointIndex::BASE][1]));
+	  }
+      }
+    for (ElementIndex i = 0; i < mesh.GetNE(); i++)
+      {
+	double bad = mesh[i].CalcJacobianBadness (mesh.Points());
+	for(int j=0; j<mesh[i].GetNP(); j++)
+	  if(bad > pure_badness[mesh[i][j]])
+	    pure_badness[mesh[i][j]] = bad;
+
+	// save maximum
+	if(bad > pure_badness.Last())
+	  pure_badness.Last() = bad; 
+      }
+    
+    for(int i=0; i<np; i++)
+      {
+	mesh.Point(i+1) = *backup[i];
+	delete backup[i];
+      }
+  }
+
+
+  double Validate(const Mesh & mesh, Array<ElementIndex> & bad_elements,
+		  const Array<double> & pure_badness,
+		  double max_worsening, const bool uselocalworsening,
+		  Array<double> * quality_loss)
+  {
+    PrintMessage(3,"!!!! Validating !!!!");
+    //if(max_worsening > 0)
+    //  (*testout) << "badness " << counter++ << endl;
+
+    bad_elements.SetSize(0);
+
+    double loc_pure_badness = -1;
+
+    if(!uselocalworsening)
+      loc_pure_badness = pure_badness.Last(); // maximum is saved at last position
+
+
+    double worsening = -1;
+    ElementIndex ind;
+
+    if(quality_loss != NULL)
+      quality_loss->SetSize(mesh.GetNE());
+
+    for (ElementIndex i = 0; i < mesh.GetNE(); i++)
+      {
+	if(uselocalworsening)
+	  {
+	    loc_pure_badness = -1;
+	    for(int j=0; j<mesh[i].GetNP(); j++)
+	      if(pure_badness[mesh[i][j]] > loc_pure_badness)
+		loc_pure_badness = pure_badness[mesh[i][j]];
+	  }
+
+
+	double bad = mesh[i].CalcJacobianBadness (mesh.Points());
+	if (bad > 1e10 || 
+	    (max_worsening > 0 && bad > loc_pure_badness*max_worsening))
+	  bad_elements.Append(i);
+	  
+
+	if(max_worsening > 0)
+	  {
+	    double actw = bad/loc_pure_badness;
+	    if(quality_loss != NULL)
+	      (*quality_loss)[i] = actw;
+
+	    if(actw > worsening)
+	      {
+		worsening = actw;
+		ind = i;
+	      }
+	  }
+      }
+    return worsening;
+  }
+
+
+  void GetWorkingArea(BitArray & working_elements, BitArray & working_points,
+		      const Mesh & mesh, const Array<ElementIndex> & bad_elements,
+		      const int width)
+  {
+    working_elements.Clear();
+    working_points.Clear();
+
+    for(int i=0; i<bad_elements.Size(); i++)
+      {
+	working_elements.Set(bad_elements[i]);
+	const Element & el = mesh[bad_elements[i]];
+	for(int j=1; j<=el.GetNP(); j++)
+	  working_points.Set(el.PNum(j));
+      }
+    
+
+    for(int i=0; i<width; i++)
+      {
+	for(ElementIndex j=0; j<mesh.GetNE(); j++)
+	  {
+	    if(!working_elements.Test(j))
+	      {  
+		const Element & el = mesh[j];
+		bool set_active = false;
+		
+		for(int k=1; !set_active && k<=el.GetNP(); k++)
+		  set_active = working_points.Test(el.PNum(k));
+		
+		if(set_active)
+		  working_elements.Set(j);
+	      }
+	  }
+
+	for(ElementIndex j=0; j<mesh.GetNE(); j++)
+	  {
+	    if(working_elements.Test(j))
+	      {
+		const Element & el = mesh[j];
+		for(int k=1; k<=el.GetNP(); k++)
+		  working_points.Set(el.PNum(k));
+	      }
+	  }
+      }
+  }
+
+
+
+  void RepairBisection(Mesh & mesh, Array<ElementIndex> & bad_elements, 
+		       const BitArray & isnewpoint, const Refinement & refinement,
+		       const Array<double> & pure_badness, 
+		       double max_worsening, const bool uselocalworsening,
+		       const Array< Array<int,PointIndex::BASE>* > & idmaps)
+  {
+    ostringstream ostrstr;
+
+    const int maxtrials = 100;
+
+    //bool doit;
+    //cout << "DOIT: " << flush;
+    //cin >> doit;
+
+    int ne = mesh.GetNE();
+    int np = mesh.GetNP();
+
+    int numbadneighbours = 3;
+    const int numtopimprove = 3;
+
+    PrintMessage(1,"repairing");
+
+    PushStatus("Repair Bisection");
+
+    Array<Point<3>* > should(np);
+    Array<Point<3>* > can(np);
+    Array<Vec<3>* > nv(np);
+    for(int i=0; i<np; i++)
+      {
+	nv[i] = new Vec<3>;
+	should[i] = new Point<3>;
+	can[i] = new Point<3>;
+      }
+    
+    BitArray isboundarypoint(np),isedgepoint(np);
+    isboundarypoint.Clear();
+    isedgepoint.Clear();
+
+    for(int i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	const Segment & seg = mesh.LineSegment(i);
+	isedgepoint.Set(seg[0]);
+	isedgepoint.Set(seg[1]);
+      }
+
+    Array<int> surfaceindex(np);
+    surfaceindex = -1;
+    
+    for (int i = 1; i <= mesh.GetNSE(); i++)
+      {
+	const Element2d & sel = mesh.SurfaceElement(i);
+	for (int j = 1; j <= sel.GetNP(); j++)
+	  if(!isedgepoint.Test(sel.PNum(j)))
+	    {
+	      isboundarypoint.Set(sel.PNum(j));
+	      surfaceindex[sel.PNum(j) - PointIndex::BASE] = 
+		mesh.GetFaceDescriptor(sel.GetIndex()).SurfNr();
+	    }
+      }
+
+
+
+    Validate(mesh,bad_elements,pure_badness,
+	     ((uselocalworsening) ?  (0.8*(max_worsening-1.) + 1.) : (0.1*(max_worsening-1.) + 1.)),
+	     uselocalworsening); // -> larger working area
+    BitArray working_elements(ne);
+    BitArray working_points(np);
+
+    GetWorkingArea(working_elements,working_points,mesh,bad_elements,numbadneighbours);
+    //working_elements.Set();
+    //working_points.Set();
+
+    ostrstr.str("");
+    ostrstr << "worsening: " <<
+      Validate(mesh,bad_elements,pure_badness,max_worsening,uselocalworsening);
+    PrintMessage(4,ostrstr.str());
+
+    
+
+    int auxnum=0;
+    for(int i=1; i<=np; i++)
+      if(working_points.Test(i))
+	auxnum++;
+    
+    ostrstr.str("");
+    ostrstr << "Percentage working points: " << 100.*double(auxnum)/np;
+    PrintMessage(5,ostrstr.str());
+    
+
+    BitArray isworkingboundary(np);
+    for(int i=1; i<=np; i++)
+      if(working_points.Test(i) && isboundarypoint.Test(i))
+	isworkingboundary.Set(i);
+      else
+	isworkingboundary.Clear(i);
+
+
+    for(int i=0; i<np; i++)
+      *should[i] = mesh.Point(i+1);
+
+    
+    for(int i=0; i<np; i++)
+      {
+	if(isnewpoint.Test(i+PointIndex::BASE) && 
+	   //working_points.Test(i+PointIndex::BASE) && 
+	   mesh.mlbetweennodes[i+PointIndex::BASE][0] > 0)
+	  *can[i] = Center(*can[mesh.mlbetweennodes[i+PointIndex::BASE][0]-PointIndex::BASE],
+			   *can[mesh.mlbetweennodes[i+PointIndex::BASE][1]-PointIndex::BASE]);
+	else
+	  *can[i] = mesh.Point(i+1);
+      }
+
+
+    int cnttrials = 1;
+    
+    double lamedge = 0.5;
+    double lamface = 0.5;
+    
+    double facokedge = 0;
+    double facokface = 0;
+    double factryedge;
+    double factryface = 0;
+
+    double oldlamedge,oldlamface;
+
+    MeshOptimize2d * optimizer2d = refinement.Get2dOptimizer();
+    if(!optimizer2d)
+      {
+	cerr << "No 2D Optimizer!" << endl;
+	return;
+      }    
+
+    while ((facokedge < 1.-1e-8 || facokface < 1.-1e-8) && 
+	   cnttrials < maxtrials &&
+	   multithread.terminate != 1)
+      {
+	(*testout) << "   facokedge " << facokedge << " facokface " << facokface << " cnttrials " << cnttrials << endl
+		   << " perc. " << 95. * max2( min2(facokedge,facokface),
+					       double(cnttrials)/double(maxtrials)) << endl;
+
+	SetThreadPercent(95. * max2( min2(facokedge,facokface),
+				     double(cnttrials)/double(maxtrials)));
+
+	ostrstr.str("");
+	ostrstr << "max. worsening " << max_worsening;
+	PrintMessage(5,ostrstr.str());
+	oldlamedge = lamedge;
+	lamedge *= 6;
+	if (lamedge > 2)
+	  lamedge = 2;
+	   
+	if(1==1 || facokedge < 1.-1e-8)
+	  {
+	    for(int i=0; i<nv.Size(); i++)
+	      *nv[i] = Vec<3>(0,0,0);
+	    for (int i = 1; i <= mesh.GetNSE(); i++)
+	      {
+		const Element2d & sel = mesh.SurfaceElement(i);
+		Vec<3> auxvec = Cross(mesh.Point(sel.PNum(2))-mesh.Point(sel.PNum(1)),
+                                      mesh.Point(sel.PNum(3))-mesh.Point(sel.PNum(1)));
+		auxvec.Normalize();
+		for (int j = 1; j <= sel.GetNP(); j++)
+		  if(!isedgepoint.Test(sel.PNum(j)))
+		    *nv[sel.PNum(j) - PointIndex::BASE] += auxvec;
+	      }
+	    for(int i=0; i<nv.Size(); i++)
+	      nv[i]->Normalize();
+	    
+	    
+	    do  // move edges
+	      {
+		lamedge *= 0.5;
+		cnttrials++;
+		if(cnttrials % 10 == 0)
+		  max_worsening *= 1.1;
+		
+		
+		factryedge = lamedge + (1.-lamedge) * facokedge;
+
+		ostrstr.str("");
+		ostrstr << "lamedge = " << lamedge << ", trying: " << factryedge;
+		PrintMessage(5,ostrstr.str());
+		
+
+		for (int i = 1; i <= np; i++)
+		  {
+		    if (isedgepoint.Test(i))
+		      {
+			for (int j = 0; j < 3; j++)
+			  mesh.Point(i)(j) = 
+			    lamedge * (*should.Get(i))(j) +
+			    (1.-lamedge) * (*can.Get(i))(j);
+		      }
+		    else
+		      mesh.Point(i) = *can.Get(i);
+		  }
+		if(facokedge < 1.-1e-8)
+		  {
+		    ostrstr.str("");
+		    ostrstr << "worsening: " <<
+		      Validate(mesh,bad_elements,pure_badness,max_worsening,uselocalworsening);
+
+		    PrintMessage(5,ostrstr.str());
+		  }
+		else
+		  Validate(mesh,bad_elements,pure_badness,-1,uselocalworsening);
+
+
+		ostrstr.str("");
+		ostrstr << bad_elements.Size() << " bad elements";
+		PrintMessage(5,ostrstr.str());
+	      }
+	    while (bad_elements.Size() > 0 && 
+		   cnttrials < maxtrials &&
+		   multithread.terminate != 1);
+	  }
+
+	if(cnttrials < maxtrials &&
+	   multithread.terminate != 1)
+	  {
+	    facokedge = factryedge;
+	    
+	    // smooth faces
+	    mesh.CalcSurfacesOfNode();
+	    
+	    MeshingParameters dummymp;
+	    mesh.ImproveMeshJacobianOnSurface(dummymp,isworkingboundary,nv,OPT_QUALITY, &idmaps);
+	    
+	    for (int i = 1; i <= np; i++)
+	      *can.Elem(i) = mesh.Point(i);
+	    
+	    if(optimizer2d)
+	      optimizer2d->ProjectBoundaryPoints(surfaceindex,can,should);
+	  }
+
+
+	oldlamface = lamface;
+	lamface *= 6;
+	if (lamface > 2)
+	  lamface = 2;
+
+
+	if(cnttrials < maxtrials &&
+	   multithread.terminate != 1)
+	  {
+
+	    do  // move faces
+	      {
+		lamface *= 0.5;
+		cnttrials++;
+		if(cnttrials % 10 == 0)
+		  max_worsening *= 1.1;
+		factryface = lamface + (1.-lamface) * facokface;
+
+		ostrstr.str("");
+		ostrstr << "lamface = " << lamface << ", trying: " << factryface;
+		PrintMessage(5,ostrstr.str());
+		
+		
+		for (int i = 1; i <= np; i++)
+		  {
+		    if (isboundarypoint.Test(i))
+		      {
+			for (int j = 0; j < 3; j++)
+			  mesh.Point(i)(j) = 
+			    lamface * (*should.Get(i))(j) +
+			    (1.-lamface) * (*can.Get(i))(j);
+		      }
+		    else
+		      mesh.Point(i) = *can.Get(i);
+		  }
+
+		ostrstr.str("");
+		ostrstr << "worsening: " <<
+		  Validate(mesh,bad_elements,pure_badness,max_worsening,uselocalworsening);
+		PrintMessage(5,ostrstr.str());
+	
+
+		ostrstr.str("");
+		ostrstr << bad_elements.Size() << " bad elements";
+		PrintMessage(5,ostrstr.str());
+	      }
+	    while (bad_elements.Size() > 0 && 
+		   cnttrials < maxtrials &&
+		   multithread.terminate != 1);
+	  }
+
+
+
+	if(cnttrials < maxtrials &&
+	   multithread.terminate != 1)
+	  {
+	    facokface = factryface;
+	    // smooth interior
+	    
+	    mesh.CalcSurfacesOfNode();
+	    
+	    MeshingParameters dummymp;
+	    mesh.ImproveMeshJacobian (dummymp, OPT_QUALITY,&working_points);
+	    //mesh.ImproveMeshJacobian (OPT_WORSTCASE,&working_points);
+	  
+
+	    for (int i = 1; i <= np; i++)
+	      *can.Elem(i) = mesh.Point(i);
+	  }
+	  
+	//!
+	if((facokedge < 1.-1e-8 || facokface < 1.-1e-8) && 
+	   cnttrials < maxtrials &&
+	   multithread.terminate != 1)
+	  {
+	    MeshingParameters dummymp;
+	    MeshOptimize3d optmesh(dummymp);
+	    for(int i=0; i<numtopimprove; i++)
+	      {
+		optmesh.SwapImproveSurface(mesh,OPT_QUALITY,&working_elements,&idmaps);
+		optmesh.SwapImprove(mesh,OPT_QUALITY,&working_elements);
+		
+	      }	    
+
+	    //	    mesh.mglevels = 1;
+	    
+		
+	    ne = mesh.GetNE();
+	    working_elements.SetSize(ne);
+	    
+	    
+	    for (int i = 1; i <= np; i++)
+	      mesh.Point(i) = *should.Elem(i);
+	    
+	    Validate(mesh,bad_elements,pure_badness,
+		     ((uselocalworsening) ?  (0.8*(max_worsening-1.) + 1.) : (0.1*(max_worsening-1.) + 1.)),
+		     uselocalworsening);
+	    
+	    if(lamedge < oldlamedge || lamface < oldlamface)
+	      numbadneighbours++;
+	    GetWorkingArea(working_elements,working_points,mesh,bad_elements,numbadneighbours);
+	    for(int i=1; i<=np; i++)
+	      if(working_points.Test(i) && isboundarypoint.Test(i))
+		isworkingboundary.Set(i);
+	      else
+		isworkingboundary.Clear(i);
+	    auxnum=0;
+	    for(int i=1; i<=np; i++)
+	      if(working_points.Test(i))
+		auxnum++;
+
+	    
+	    ostrstr.str("");
+	    ostrstr << "Percentage working points: " << 100.*double(auxnum)/np;
+	    PrintMessage(5,ostrstr.str());
+	    
+	    for (int i = 1; i <= np; i++)
+	      mesh.Point(i) = *can.Elem(i);
+	  }
+	//!
+
+      }
+
+    MeshingParameters dummymp;
+    MeshOptimize3d optmesh(dummymp);
+    for(int i=0; i<numtopimprove && multithread.terminate != 1; i++)
+      {
+	optmesh.SwapImproveSurface(mesh,OPT_QUALITY,NULL,&idmaps);
+	optmesh.SwapImprove(mesh,OPT_QUALITY);
+	//mesh.UpdateTopology();
+      }
+    mesh.UpdateTopology();
+    /*
+    if(cnttrials < 100)
+      {
+	nv = Vec3d(0,0,0);
+	for (int i = 1; i <= mesh.GetNSE(); i++)
+	  {
+	    const Element2d & sel = mesh.SurfaceElement(i);
+	    Vec3d auxvec = Cross(mesh.Point(sel.PNum(2))-mesh.Point(sel.PNum(1)),
+				 mesh.Point(sel.PNum(3))-mesh.Point(sel.PNum(1)));
+	    auxvec.Normalize();
+	    for (int j = 1; j <= sel.GetNP(); j++)
+	      if(!isedgepoint.Test(sel.PNum(j)))
+		nv[sel.PNum(j) - PointIndex::BASE] += auxvec;
+	  }
+	for(int i=0; i<nv.Size(); i++)
+	  nv[i].Normalize();
+	
+
+	mesh.ImproveMeshJacobianOnSurface(isboundarypoint,nv,OPT_QUALITY);
+	mesh.CalcSurfacesOfNode();
+	    // smooth interior
+	    
+	
+	for (int i = 1; i <= np; i++)
+	  if(isboundarypoint.Test(i))
+	    can.Elem(i) = mesh.Point(i);
+	    
+	if(optimizer2d)
+	  optimizer2d->ProjectBoundaryPoints(surfaceindex,can,should);
+
+	
+	for (int i = 1; i <= np; i++)
+	  if(isboundarypoint.Test(i))
+	    for(int j=1; j<=3; j++)
+	      mesh.Point(i).X(j) = should.Get(i).X(j);
+      }
+    */
+
+
+    if(cnttrials == maxtrials)
+      {
+	for (int i = 1; i <= np; i++)
+	  mesh.Point(i) = *should.Get(i);
+
+	Validate(mesh,bad_elements,pure_badness,max_worsening,uselocalworsening);
+	
+	for(int i=0; i<bad_elements.Size(); i++)
+	  {
+	    ostrstr.str("");
+	    ostrstr << "bad element:" << endl
+		    << mesh[bad_elements[i]][0] << ": " << mesh.Point(mesh[bad_elements[i]][0]) << endl
+		    << mesh[bad_elements[i]][1] << ": " << mesh.Point(mesh[bad_elements[i]][1]) << endl
+		    << mesh[bad_elements[i]][2] << ": " << mesh.Point(mesh[bad_elements[i]][2]) << endl
+		    << mesh[bad_elements[i]][3] << ": " << mesh.Point(mesh[bad_elements[i]][3]);
+	    PrintMessage(5,ostrstr.str());
+	  }
+	for (int i = 1; i <= np; i++)
+	  mesh.Point(i) = *can.Get(i);
+      }
+
+    for(int i=0; i<np; i++)
+      {
+	delete nv[i];
+	delete can[i];
+	delete should[i];
+      }
+
+    PopStatus();
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/validate.hpp b/contrib/Netgen/libsrc/meshing/validate.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1cc01bec77ecd7e8898e35d99ca9d460b33d0305
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/validate.hpp
@@ -0,0 +1,21 @@
+#ifndef VALIDATE_HPP
+#define VALIDATE_HPP
+
+namespace netgen
+{
+  
+  void GetPureBadness(Mesh & mesh, Array<double> & pure_badness,
+		      const BitArray & isnewpoint);
+  double Validate(const Mesh & mesh, Array<ElementIndex> & bad_elements,
+		  const Array<double> & pure_badness, 
+		  double max_worsening, const bool uselocalworsening,
+		  Array<double> * quality_loss = NULL);
+  void RepairBisection(Mesh & mesh, Array<ElementIndex> & bad_elements, 
+		       const BitArray & isnewpoint, const Refinement & refinement,
+		       const Array<double> & pure_badness, 
+		       double max_worsening, const bool uselocalworsening,
+		       const Array< Array<int,PointIndex::BASE>* > & idmaps);
+
+}
+
+#endif // VALIDATE_HPP
diff --git a/contrib/Netgen/libsrc/meshing/zrefine.cpp b/contrib/Netgen/libsrc/meshing/zrefine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0032528e2bc353c3abf0e5124ece3ecfe73188e1
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/zrefine.cpp
@@ -0,0 +1,740 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+#include <csg.hpp>
+
+namespace netgen
+{
+
+  // find singular edges
+  void SelectSingularEdges (const Mesh & mesh, const CSGeometry & geom, 
+			    INDEX_2_HASHTABLE<int> & singedges,
+			    ZRefinementOptions & opt)
+  {
+    // edges selected in csg input file
+    for (int i = 1; i <= geom.singedges.Size(); i++)
+      {
+	//if(geom.singedges.Get(i)->maxhinit > 0)
+	//  continue; //!!!!
+
+	const SingularEdge & se = *geom.singedges.Get(i);
+	for (int j = 1; j <= se.segms.Size(); j++)
+	  {
+	    INDEX_2 i2 = se.segms.Get(j);
+	    singedges.Set (i2, 1);
+	  }
+      }
+
+    // edges interactively selected
+    for (int i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	const Segment & seg = mesh.LineSegment(i);
+	if (seg.singedge_left || seg.singedge_right)
+	  {
+	    INDEX_2 i2(seg[0], seg[1]);
+	    i2.Sort();
+	    singedges.Set (i2, 1);
+	  }
+      }
+  }
+
+
+  /**
+     Convert elements (vol-tets, surf-trigs) into prisms/quads
+  */
+  void MakePrismsSingEdge (Mesh & mesh, INDEX_2_HASHTABLE<int> & singedges)
+  {
+    // volume elements
+    for (int i = 1; i <= mesh.GetNE(); i++)
+      {
+	Element & el = mesh.VolumeElement(i);
+	if (el.GetType() != TET) continue;
+
+	for (int j = 1; j <= 3; j++)
+	  for (int k = j+1; k <= 4; k++)
+	    {
+	      INDEX_2 edge(el.PNum(j), el.PNum(k));
+	      edge.Sort();
+	      if (singedges.Used (edge))
+		{
+		  int pi3 = 1, pi4 = 1;
+		  while (pi3 == j || pi3 == k) pi3++;
+		  pi4 = 10 - j - k - pi3;
+		
+		  int p3 = el.PNum(pi3);
+		  int p4 = el.PNum(pi4);
+
+		  el.SetType(PRISM);
+		  el.PNum(1) = edge.I1();
+		  el.PNum(2) = p3;
+		  el.PNum(3) = p4;
+		  el.PNum(4) = edge.I2();
+		  el.PNum(5) = p3;
+		  el.PNum(6) = p4;
+		}
+	    }
+      }
+
+    // surface elements
+    for (int i = 1; i <= mesh.GetNSE(); i++)
+      {
+	Element2d & el = mesh.SurfaceElement(i);
+	if (el.GetType() != TRIG) continue;
+
+	for (int j = 1; j <= 3; j++)
+	  {
+	    int k = (j % 3) + 1;
+	    INDEX_2 edge(el.PNum(j), el.PNum(k));
+	    edge.Sort();
+
+	    if (singedges.Used (edge))
+	      {
+		int pi3 = 6-j-k;
+		int p3 = el.PNum(pi3);
+		int p1 = el.PNum(j);
+		int p2 = el.PNum(k);
+
+		el.SetType(QUAD);
+		el.PNum(1) = p2;
+		el.PNum(2) = p3;
+		el.PNum(3) = p3;
+		el.PNum(4) = p1;
+	      }
+	  }
+      }
+  }
+
+
+  /*
+    Convert tets and pyramids next to close (identified) points into prisms
+  */
+  void MakePrismsClosePoints (Mesh & mesh)
+  {
+    int i, j, k;
+    for (i = 1; i <= mesh.GetNE(); i++)
+      {
+	Element & el = mesh.VolumeElement(i);
+	if (el.GetType() == TET)
+	  {
+	    for (j = 1; j <= 3; j++)
+	      for (k = j+1; k <= 4; k++)
+		{
+		  INDEX_2 edge(el.PNum(j), el.PNum(k));
+		  edge.Sort();
+		  if (mesh.GetIdentifications().GetSymmetric (el.PNum(j), el.PNum(k)))
+		    {
+		      int pi3 = 1, pi4 = 1;
+		      while (pi3 == j || pi3 == k) pi3++;
+		      pi4 = 10 - j - k - pi3;
+		    
+		      int p3 = el.PNum(pi3);
+		      int p4 = el.PNum(pi4);
+		    
+		      el.SetType(PRISM);
+		      el.PNum(1) = edge.I1();
+		      el.PNum(2) = p3;
+		      el.PNum(3) = p4;
+		      el.PNum(4) = edge.I2();
+		      el.PNum(5) = p3;
+		      el.PNum(6) = p4;
+		    }
+		}
+	  }
+
+	if (el.GetType() == PYRAMID)
+	  {
+	    // pyramid, base face = 1,2,3,4
+	  
+	    for (j = 0; j <= 1; j++)
+	      {
+		int pi1 = el.PNum( (j+0) % 4 + 1);
+		int pi2 = el.PNum( (j+1) % 4 + 1);
+		int pi3 = el.PNum( (j+2) % 4 + 1);
+		int pi4 = el.PNum( (j+3) % 4 + 1);
+		int pi5 = el.PNum(5);
+
+		INDEX_2 edge1(pi1, pi4);
+		INDEX_2 edge2(pi2, pi3);
+		edge1.Sort();
+		edge2.Sort();
+		if (mesh.GetIdentifications().GetSymmetric (pi1, pi4) &&
+		    mesh.GetIdentifications().GetSymmetric (pi2, pi3))
+		  {
+		    //int p3 = el.PNum(pi3);
+		    //int p4 = el.PNum(pi4);
+		  
+		    el.SetType(PRISM);
+		    el.PNum(1) = pi1;
+		    el.PNum(2) = pi2;
+		    el.PNum(3) = pi5;
+		    el.PNum(4) = pi4;
+		    el.PNum(5) = pi3;
+		    el.PNum(6) = pi5;
+		  }
+	      }
+	  }
+      }
+  
+    for (i = 1; i <= mesh.GetNSE(); i++)
+      {
+	Element2d & el = mesh.SurfaceElement(i);
+	if (el.GetType() != TRIG) continue;
+
+	for (j = 1; j <= 3; j++)
+	  {
+	    k = (j % 3) + 1;
+	    INDEX_2 edge(el.PNum(j), el.PNum(k));
+	    edge.Sort();
+
+	    if (mesh.GetIdentifications().GetSymmetric (el.PNum(j), el.PNum(k)))
+	      {
+		int pi3 = 6-j-k;
+		int p3 = el.PNum(pi3);
+		int p1 = el.PNum(j);
+		int p2 = el.PNum(k);
+
+		el.SetType(QUAD);
+		el.PNum(1) = p2;
+		el.PNum(2) = p3;
+		el.PNum(3) = p3;
+		el.PNum(4) = p1;
+	      }
+	  }
+      }
+  }
+
+
+
+#ifdef OLD
+  void MakeCornerNodes (Mesh & mesh,
+			INDEX_HASHTABLE<int> & cornernodes)
+  {
+    int i, j;
+    int nseg = mesh.GetNSeg();
+    Array<int> edgesonpoint(mesh.GetNP());
+    for (i = 1; i <= mesh.GetNP(); i++)
+      edgesonpoint.Elem(i) = 0;
+
+    for (i = 1; i <= nseg; i++)
+      {
+	for (j = 1; j <= 2; j++)
+	  {
+	    int pi = (j == 1) ? 
+	      mesh.LineSegment(i)[0] :
+	      mesh.LineSegment(i)[1];
+	    edgesonpoint.Elem(pi)++;
+	  }
+      }
+
+    /*
+      cout << "cornernodes: ";
+      for (i = 1; i <= edgesonpoint.Size(); i++)
+      if (edgesonpoint.Get(i) >= 6)
+      {
+      cornernodes.Set (i, 1);
+      cout << i << " ";
+      }
+      cout << endl;
+    */
+    //  cornernodes.Set (5, 1);
+  }
+#endif
+
+
+  void RefinePrisms (Mesh & mesh, const CSGeometry * geom, 
+		     ZRefinementOptions & opt)
+  {
+    int i, j;
+    bool found, change;
+    int cnt = 0;
+
+
+    // markers for z-refinement:  p1, p2, levels  
+    // p1-p2 is an edge to be refined
+    Array<INDEX_3> ref_uniform;
+    Array<INDEX_3> ref_singular;
+    Array<INDEX_4 > ref_slices;
+
+    BitArray first_id(geom->identifications.Size());
+    first_id.Set();
+
+  
+    INDEX_2_HASHTABLE<int> & identpts = 
+      mesh.GetIdentifications().GetIdentifiedPoints ();
+
+    if (&identpts)
+      {
+	for (i = 1; i <= identpts.GetNBags(); i++)
+	  for (j = 1; j <= identpts.GetBagSize(i); j++)
+	    {
+	      INDEX_2 pair;
+	      int idnr;
+	      identpts.GetData(i, j, pair, idnr);
+	      const CloseSurfaceIdentification * csid = 
+		dynamic_cast<const CloseSurfaceIdentification*> 
+		(geom->identifications.Get(idnr));
+	      if (csid)
+		{
+		  if (!csid->GetSlices().Size())
+		    {
+		      if (first_id.Test (idnr))
+			{
+			  first_id.Clear(idnr);
+			  ref_uniform.Append (INDEX_3 (pair.I1(), pair.I2(), csid->RefLevels()));
+			  ref_singular.Append (INDEX_3 (pair.I1(), pair.I2(), csid->RefLevels1()));
+			  ref_singular.Append (INDEX_3 (pair.I2(), pair.I1(), csid->RefLevels2()));
+			}
+		    }
+		  else
+		    {   
+		      //const Array<double> & slices = csid->GetSlices();
+		      INDEX_4 i4;
+		      i4[0] = pair.I1();
+		      i4[1] = pair.I2();
+		      i4[2] = idnr;
+		      i4[3] = csid->GetSlices().Size();
+		      ref_slices.Append (i4);
+		    }
+		}
+	    }
+      }
+
+  
+  
+    Array<EdgePointGeomInfo> epgi;
+
+    while (1)
+      {
+	cnt++;
+	PrintMessage (3, "Z-Refinement, level = ", cnt);
+	INDEX_2_HASHTABLE<int> refedges(mesh.GetNSE()+1);
+
+
+	found = 0;
+	// mark prisms due to close surface flags:
+	int oldsize = ref_uniform.Size();
+	for (i = 1; i <= oldsize; i++)
+	  {
+	    int pi1 = ref_uniform.Get(i).I1();
+	    int pi2 = ref_uniform.Get(i).I2();
+	    int levels = ref_uniform.Get(i).I3();
+
+	    if (levels > 0)
+	      {
+		const Point3d & p1 = mesh.Point(pi1);
+		const Point3d & p2 = mesh.Point(pi2);
+		int npi(0);
+	      
+		INDEX_2 edge(pi1, pi2);
+		edge.Sort();
+		if (!refedges.Used(edge))
+		  {
+		    Point3d np = Center (p1, p2);
+		    npi = mesh.AddPoint (np);
+		    refedges.Set (edge, npi);
+		    found = 1;
+		  }
+
+		ref_uniform.Elem(i) = INDEX_3(pi1, npi, levels-1);
+		ref_uniform.Append (INDEX_3(pi2, npi, levels-1));
+	      }
+	  }
+	for (i = 1; i <= ref_singular.Size(); i++)
+	  {
+	    int pi1 = ref_singular.Get(i).I1();
+	    int pi2 = ref_singular.Get(i).I2();
+	    int levels = ref_singular.Get(i).I3();
+
+	    if (levels > 0)
+	      {
+		const Point3d & p1 = mesh.Point(pi1);
+		const Point3d & p2 = mesh.Point(pi2);
+		int npi;
+	      
+		INDEX_2 edge(pi1, pi2);
+		edge.Sort();
+		if (!refedges.Used(edge))
+		  {
+		    Point3d np = Center (p1, p2);
+		    npi = mesh.AddPoint (np);
+		    refedges.Set (edge, npi);
+		    found = 1;
+		  }
+		else
+		  npi = refedges.Get (edge);
+
+		ref_singular.Elem(i) = INDEX_3(pi1, npi, levels-1);
+	      }
+	  }
+
+	for (i = 1; i <= ref_slices.Size(); i++)
+	  {
+	    int pi1 = ref_slices.Get(i)[0];
+	    int pi2 = ref_slices.Get(i)[1];
+	    int idnr = ref_slices.Get(i)[2];
+	    int slicenr = ref_slices.Get(i)[3];
+
+	    if (slicenr > 0)
+	      {
+		const Point3d & p1 = mesh.Point(pi1);
+		const Point3d & p2 = mesh.Point(pi2);
+		int npi;
+
+		const CloseSurfaceIdentification * csid = 
+		  dynamic_cast<const CloseSurfaceIdentification*> 
+		  (geom->identifications.Get(idnr));
+
+	      
+		INDEX_2 edge(pi1, pi2);
+		edge.Sort();
+		if (!refedges.Used(edge))
+		  {
+		    const Array<double> & slices = csid->GetSlices();
+		    //(*testout) << "idnr " << idnr << " i " << i << endl;
+		    //(*testout) << "slices " << slices << endl;
+		    double slicefac = slices.Get(slicenr);
+		    double slicefaclast = 
+		      (slicenr == slices.Size()) ? 1 : slices.Get(slicenr+1);
+		    
+		    Point3d np = p1 + (slicefac / slicefaclast) * (p2-p1);
+		    //(*testout) << "slicenr " << slicenr << " slicefac " << slicefac << " quot " << (slicefac / slicefaclast) << " np " << np << endl;
+		    npi = mesh.AddPoint (np);
+		    refedges.Set (edge, npi);
+		    found = 1;
+		  }
+		else
+		  npi = refedges.Get (edge);
+		
+		ref_slices.Elem(i)[1] = npi;
+		ref_slices.Elem(i)[3] --;
+	      }
+	  }
+
+
+
+
+	for (i = 1; i <= mesh.GetNE(); i++)
+	  {
+	    Element & el = mesh.VolumeElement (i);
+	    if (el.GetType() != PRISM)
+	      continue;
+
+	    for (j = 1; j <= 3; j++)
+	      {
+		int pi1 = el.PNum(j);
+		int pi2 = el.PNum(j+3);
+		const Point3d & p1 = mesh.Point(pi1);
+		const Point3d & p2 = mesh.Point(pi2);
+
+		bool ref = 0;
+
+		/*
+		  if (Dist (p1, p2) > mesh.GetH (Center (p1, p2)))
+		  ref = 1;
+		*/
+
+		/*
+		  if (cnt <= opt.minref)
+		  ref = 1;
+		*/
+
+		/*
+		  if ((pi1 == 460 || pi2 == 460 ||
+		  pi1 == 461 || pi2 == 461) && cnt <= 8) ref = 1;
+		*/
+		if (ref == 1)
+		  {
+		    INDEX_2 edge(pi1, pi2);
+		    edge.Sort();
+		    if (!refedges.Used(edge))
+		      {
+			Point3d np = Center (p1, p2);
+			int npi = mesh.AddPoint (np);
+			refedges.Set (edge, npi);
+			found = 1;
+		      }
+		  }
+	      }
+	  }
+      
+	if (!found) break;
+
+	// build closure:
+	PrintMessage (5, "start closure");
+	do
+	  {
+	    PrintMessage (5, "start loop");
+	    change = 0;
+	    for (i = 1; i <= mesh.GetNE(); i++)
+	      {
+		Element & el = mesh.VolumeElement (i);
+		if (el.GetType() != PRISM)
+		  continue;
+	      
+		bool hasref = 0, hasnonref = 0;
+		for (j = 1; j <= 3; j++)
+		  {
+		    int pi1 = el.PNum(j);
+		    int pi2 = el.PNum(j+3);
+		    if (pi1 != pi2)
+		      {
+			INDEX_2 edge(pi1, pi2);
+			edge.Sort();
+			if (refedges.Used(edge))
+			  hasref = 1;
+			else 
+			  hasnonref = 1;
+		      }
+		  }
+
+		if (hasref && hasnonref)
+		  {
+		    //		  cout << "el " << i << " in closure" << endl;
+		    change = 1;
+		    for (j = 1; j <= 3; j++)
+		      {
+			int pi1 = el.PNum(j);
+			int pi2 = el.PNum(j+3);
+			const Point3d & p1 = mesh.Point(pi1);
+			const Point3d & p2 = mesh.Point(pi2);
+		      
+			INDEX_2 edge(pi1, pi2);
+			edge.Sort();
+			if (!refedges.Used(edge))
+			  {
+			    Point3d np = Center (p1, p2);
+			    int npi = mesh.AddPoint (np);
+			    refedges.Set (edge, npi);
+			  }
+		      }
+		  }
+	      }
+	  }
+	while (change);
+
+	PrintMessage (5, "Do segments");
+
+	//      (*testout) << "closure formed, np = " << mesh.GetNP() << endl;
+
+	int oldns = mesh.GetNSeg();
+
+	for (i = 1; i <= oldns; i++)
+	  {
+	    const Segment & el = mesh.LineSegment(i);
+
+	    INDEX_2 i2(el[0], el[1]);
+	    i2.Sort();
+	  
+	    int pnew;
+	    EdgePointGeomInfo ngi;
+      
+	    if (refedges.Used(i2))
+	      {
+		pnew = refedges.Get(i2);
+		//	      ngi = epgi.Get(pnew);
+	      }
+	    else
+	      {
+		continue;
+
+		// 	      Point3d pb;
+
+		// 	      /*
+		// 	      geom->PointBetween (mesh.Point (el[0]),
+		// 				  mesh.Point (el[1]),
+		// 				  el.surfnr1, el.surfnr2,
+		// 				  el.epgeominfo[0], el.epgeominfo[1],
+		// 				  pb, ngi);
+		// 	      */
+		// 	      pb = Center (mesh.Point (el[0]), mesh.Point (el[1]));
+
+		// 	      pnew = mesh.AddPoint (pb);
+	      
+		// 	      refedges.Set (i2, pnew);
+	      
+		// 	      if (pnew > epgi.Size())
+		// 		epgi.SetSize (pnew);
+		// 	      epgi.Elem(pnew) = ngi;
+	      }
+	  
+	    Segment ns1 = el;
+	    Segment ns2 = el;
+	    ns1[1] = pnew;
+	    ns1.epgeominfo[1] = ngi;
+	    ns2[0] = pnew;
+	    ns2.epgeominfo[0] = ngi;
+
+	    mesh.LineSegment(i) = ns1;
+	    mesh.AddSegment (ns2);
+	  }
+      
+	PrintMessage (5, "Segments done, NSeg = ", mesh.GetNSeg());
+
+	// do refinement
+	int oldne = mesh.GetNE();
+	for (i = 1; i <= oldne; i++)
+	  {
+	    Element & el = mesh.VolumeElement (i);
+	    if (el.GetNP() != 6)
+	      continue;
+
+	    int npi[3];
+	    for (j = 1; j <= 3; j++)
+	      {
+		int pi1 = el.PNum(j);
+		int pi2 = el.PNum(j+3);
+
+		if (pi1 == pi2)
+		  npi[j-1] = pi1;
+		else
+		  {
+		    INDEX_2 edge(pi1, pi2);
+		    edge.Sort();
+		    if (refedges.Used (edge))
+		      npi[j-1] = refedges.Get(edge);
+		    else
+		      {
+			/*
+			  (*testout) << "ERROR: prism " << i << " has hanging node !!" 
+			  << ", edge = " << edge << endl;
+			  cerr << "ERROR: prism " << i << " has hanging node !!" << endl;
+			*/
+			npi[j-1] = 0;
+		      }
+		  }
+	      }
+
+	    if (npi[0])
+	      {
+		Element nel1(6), nel2(6);
+		for (j = 1; j <= 3; j++)
+		  {
+		    nel1.PNum(j) = el.PNum(j);
+		    nel1.PNum(j+3) = npi[j-1];
+		    nel2.PNum(j) = npi[j-1];
+		    nel2.PNum(j+3) = el.PNum(j+3);
+		  }
+		nel1.SetIndex (el.GetIndex());
+		nel2.SetIndex (el.GetIndex());
+		mesh.VolumeElement (i) = nel1;
+		mesh.AddVolumeElement (nel2);
+	      }
+	  }
+
+      
+	PrintMessage (5, "Elements done, NE = ", mesh.GetNE());
+
+
+	// do surface elements
+	int oldnse = mesh.GetNSE();
+	//      cout << "oldnse = " << oldnse << endl;
+	for (i = 1; i <= oldnse; i++)
+	  {
+	    Element2d & el = mesh.SurfaceElement (i);
+	    if (el.GetType() != QUAD)
+	      continue;
+
+	    int index = el.GetIndex();
+	    int npi[2];
+	    for (j = 1; j <= 2; j++)
+	      {
+		int pi1, pi2;
+
+		if (j == 1)
+		  {
+		    pi1 = el.PNum(1);
+		    pi2 = el.PNum(4);
+		  }
+		else
+		  {
+		    pi1 = el.PNum(2);
+		    pi2 = el.PNum(3);
+		  }
+
+		if (pi1 == pi2)
+		  npi[j-1] = pi1;
+		else
+		  {
+		    INDEX_2 edge(pi1, pi2);
+		    edge.Sort();
+		    if (refedges.Used (edge))
+		      npi[j-1] = refedges.Get(edge);
+		    else
+		      {
+			npi[j-1] = 0;
+		      }
+		  }
+	      }
+
+	    if (npi[0])
+	      {
+		Element2d nel1(QUAD), nel2(QUAD);
+		for (j = 1; j <= 4; j++)
+		  {
+		    nel1.PNum(j) = el.PNum(j);
+		    nel2.PNum(j) = el.PNum(j);
+		  }
+		nel1.PNum(3) = npi[1];
+		nel1.PNum(4) = npi[0];
+		nel2.PNum(1) = npi[0];
+		nel2.PNum(2) = npi[1];
+		/*
+		  for (j = 1; j <= 2; j++)
+		  {
+		  nel1.PNum(j) = el.PNum(j);
+		  nel1.PNum(j+2) = npi[j-1];
+		  nel2.PNum(j) = npi[j-1];
+		  nel2.PNum(j+2) = el.PNum(j+2);
+		  }
+		*/
+		nel1.SetIndex (el.GetIndex());
+		nel2.SetIndex (el.GetIndex());
+
+		mesh.SurfaceElement (i) = nel1;
+		mesh.AddSurfaceElement (nel2);
+
+		int si = mesh.GetFaceDescriptor (index).SurfNr();
+
+		Point<3> hp = mesh.Point(npi[0]);
+		geom->GetSurface(si)->Project (hp);
+		mesh.Point (npi[0]).SetPoint (hp);
+
+		hp = mesh.Point(npi[1]);
+		geom->GetSurface(si)->Project (hp);
+		mesh.Point (npi[1]).SetPoint (hp);
+
+		//	      geom->GetSurface(si)->Project (mesh.Point(npi[0]));
+		//	      geom->GetSurface(si)->Project (mesh.Point(npi[1]));
+	      }
+	  }
+
+	PrintMessage (5, "Surface elements done, NSE = ", mesh.GetNSE());
+
+      }
+  }
+
+
+
+  void ZRefinement (Mesh & mesh, const NetgenGeometry * hgeom,
+		    ZRefinementOptions & opt)
+  {
+    const CSGeometry * geom = dynamic_cast<const CSGeometry*> (hgeom);
+    if (!geom) return;
+
+    INDEX_2_HASHTABLE<int> singedges(mesh.GetNSeg());
+
+    SelectSingularEdges (mesh, *geom, singedges, opt);
+    //MakePrismsSingEdge (mesh, singedges);
+    MakePrismsClosePoints (mesh);
+
+    RefinePrisms (mesh, geom, opt);
+  }
+
+
+
+  ZRefinementOptions :: ZRefinementOptions()
+  {
+    minref = 0;
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/occ/Makefile.am b/contrib/Netgen/libsrc/occ/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..a3c7bd4b379b1debf5637dc9d7d9cc8a89f918c0
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Makefile.am
@@ -0,0 +1,28 @@
+noinst_HEADERS = occgeom.hpp occmeshsurf.hpp \
+Partition_Inter2d.hxx Partition_Loop2d.hxx Partition_Loop.hxx \
+Partition_Inter3d.hxx Partition_Loop3d.hxx Partition_Spliter.hxx \
+Partition_Inter2d.ixx Partition_Loop2d.ixx Partition_Loop.ixx \
+Partition_Inter3d.ixx Partition_Loop3d.ixx Partition_Spliter.ixx \
+Partition_Inter2d.jxx Partition_Loop2d.jxx Partition_Loop.jxx \
+Partition_Inter3d.jxx Partition_Loop3d.jxx Partition_Spliter.jxx \
+utilities.h
+
+
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc/include  $(OCCFLAGS) $(TCL_INCLUDES)
+
+# $(OCC_INC_FLAG)
+
+METASOURCES = AUTO
+
+lib_LTLIBRARIES = libocc.la liboccvis.la
+
+libocc_la_SOURCES = Partition_Inter2d.cxx Partition_Inter3d.cxx \
+	Partition_Loop.cxx Partition_Loop2d.cxx Partition_Loop3d.cxx Partition_Spliter.cxx \
+	occconstruction.cpp occgenmesh.cpp occgeom.cpp occmeshsurf.cpp
+
+libocc_la_LIBADD = $(OCCLIBS)
+
+liboccvis_la_SOURCES = occpkg.cpp vsocc.cpp
+liboccvis_la_LIBADD = libocc.la
+
+
diff --git a/contrib/Netgen/libsrc/occ/Partition_Inter2d.cxx b/contrib/Netgen/libsrc/occ/Partition_Inter2d.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2835c610d7b9a1865638288688329e2da4c66c68
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Inter2d.cxx
@@ -0,0 +1,678 @@
+#ifdef OCCGEOMETRY
+
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R& D, LEG, PRINCIPIA R& D, BUREAU VERITAS
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License.
+//
+//  This library 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
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org
+//
+//
+//
+//  File   : Partition_Inter2d.cxx
+//  Author : Benedicte MARTIN
+//  Module : GEOM
+//  $Header: /cvs/netgen/netgen/libsrc/occ/Partition_Inter2d.cxx,v 1.5 2008/03/31 14:20:28 wabro Exp $
+
+//using namespace std;
+
+#include "Partition_Inter2d.ixx"
+
+#include "utilities.h"
+
+#include <BRepAdaptor_Curve.hxx>
+#include <BRepAlgo_AsDes.hxx>
+#include <BRepLib_MakeVertex.hxx>
+#include <BRep_Builder.hxx>
+#include <BRep_Tool.hxx>
+#include <Geom_Surface.hxx>
+#include <Precision.hxx>
+#include <TopExp.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopOpeBRepDS_Transition.hxx>
+#include <TopOpeBRep_EdgesIntersector.hxx>
+#include <TopOpeBRep_Point2d.hxx>
+#include <TopTools_ListIteratorOfListOfShape.hxx>
+#include <TopTools_ListOfShape.hxx>
+#include <TopTools_MapIteratorOfMapOfShape.hxx>
+#include <TopTools_MapOfShape.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Edge.hxx>
+#include <TopoDS_Vertex.hxx>
+#include <gp_Pnt.hxx>
+
+#ifdef DEB
+static Standard_Boolean TestEdges = 0;
+static Standard_Integer NbF2d = 0;
+static Standard_Integer NbE2d = 0;
+#endif
+
+//=======================================================================
+//function : getOtherShape
+//purpose  :
+//=======================================================================
+
+static TopoDS_Shape getOtherShape(const TopoDS_Shape&         theS,
+                                  const TopTools_ListOfShape& theSList)
+{
+  TopTools_ListIteratorOfListOfShape anIt( theSList );
+  for ( ; anIt.More(); anIt.Next() )
+    if (!theS.IsSame( anIt.Value() ))
+      return anIt.Value();
+
+  return TopoDS_Shape();
+}
+
+//=======================================================================
+//function : findVOnE
+//purpose  : on theE, find a vertex close to theV, such that an edge
+//           passing through it is an itersection of theF1 and theF2.
+//           theE intersects theE2 at theV
+//=======================================================================
+
+static Standard_Boolean findVOnE(const TopoDS_Vertex &         theV,
+                                 const TopoDS_Edge&            theE,
+                                 const TopoDS_Edge&            theE2,
+                                 const TopoDS_Shape&           theF1,
+                                 const TopoDS_Shape&           theF2,
+                                 const Handle(BRepAlgo_AsDes)& theAsDes,
+                                 TopoDS_Vertex &               theFoundV)
+{
+  Standard_Real MinDist2 = ::RealLast();
+  gp_Pnt P;
+
+  // check all vertices on theE
+  const TopTools_ListOfShape& aVList = theAsDes->Descendant( theE );
+  TopTools_ListIteratorOfListOfShape anIt( aVList );
+  if (anIt.More())
+    P = BRep_Tool::Pnt( theV );
+  for ( ; anIt.More(); anIt.Next() )
+  {
+    // check by distance
+    TopoDS_Vertex & V = TopoDS::Vertex( anIt.Value() );
+    Standard_Real dist2 = P.SquareDistance( BRep_Tool::Pnt( V ));
+    if (dist2 < MinDist2)
+      MinDist2 = dist2;
+    else
+      continue;
+
+    // V is a candidate if among edges passing through V there is one
+    // which is an intersection of theF1 and theF2
+    TopTools_ListIteratorOfListOfShape anEIt( theAsDes->Ascendant( V ));
+    Standard_Boolean isOk = Standard_False;
+    for (  ; !isOk && anEIt.More(); anEIt.Next() )
+    {
+      const TopoDS_Shape & E2 = anEIt.Value();
+      if ( theE2.IsSame( E2 ))
+        continue;
+      const TopTools_ListOfShape & aFList = theAsDes->Ascendant( E2 );
+      if (aFList.IsEmpty())
+        continue;
+      if ( theF1.IsSame( aFList.First() ))
+        isOk = theF2.IsSame( aFList.Last() );
+      else
+        isOk = theF2.IsSame( aFList.First() ) && theF1.IsSame( aFList.Last() );
+    }
+    if (isOk)
+      theFoundV = V;
+  }
+
+  if (theFoundV.IsNull())
+    return Standard_False;
+
+  // check that MinDist2 is not too large
+  Standard_Real f, l;
+  TopLoc_Location L;
+  Handle(Geom_Curve) aCurve = BRep_Tool::Curve( theE, L, f, l );
+  gp_Pnt P1 = aCurve->Value( f );
+  gp_Pnt P2 = aCurve->Value( 0.3 * f + 0.7 * l );
+  //gp_Pnt P2 = aCurve->Value( 0.5 * ( f + l ));
+  if (MinDist2 > P1.SquareDistance( P2 ))
+    return Standard_False;
+
+#ifdef DEB
+  MESSAGE("findVOnE: found MinDist = " << sqrt (MinDist2));
+#endif
+
+  return Standard_True;
+}
+
+//=======================================================================
+//function : AddVonE
+//purpose  : Put V in AsDes as intersection of E1 and E2.
+//           Check that vertex equal to V already exists on one
+//           of edges, in  such  a  case,  V  is  not added but
+//           existing vertex is updated to  be on E1 and E2 and
+//           is returned insead of V.
+//=======================================================================
+
+TopoDS_Vertex Partition_Inter2d::AddVonE(const TopoDS_Vertex& theV,
+                                         const TopoDS_Edge&   E1,
+                                         const TopoDS_Edge&   E2,
+                                         const Handle(BRepAlgo_AsDes)& AsDes,
+                                         const TopoDS_Face&   theF)
+
+{
+  //-------------------------------------------------------------
+  // test if the points of intersection already exist. If not,
+  // add as descendants of the edges.
+  // nb: theses points are only vertices of intersection.
+  //-------------------------------------------------------------
+  const TopTools_ListOfShape& VOnE1 = AsDes->Descendant(E1);
+  const TopTools_ListOfShape& VOnE2 = AsDes->Descendant(E2);
+  gp_Pnt                      P1,P2;
+  TopoDS_Vertex               V1,V2;
+  TopTools_ListIteratorOfListOfShape it;
+  BRep_Builder                       B;
+  TopAbs_Orientation                 O1,O2;
+  Standard_Real                      U1,U2;
+  Standard_Real                      Tol,Tol1,Tol2;
+  Standard_Boolean                   OnE1,OnE2;
+
+  TopoDS_Vertex V    = theV;
+
+  U1 = BRep_Tool::Parameter(V,E1);
+  U2 = BRep_Tool::Parameter(V,E2);
+  O1 = V.Orientation();
+  O2 = O1;
+  P1  = BRep_Tool::Pnt(V);
+  Tol = BRep_Tool::Tolerance( V );
+  OnE1 = OnE2 = Standard_False;
+
+  //-----------------------------------------------------------------
+  // Search if the point of intersection is a vertex of E1.
+  //-----------------------------------------------------------------
+  for (it.Initialize(VOnE1); it.More(); it.Next()) {
+    const TopoDS_Vertex& CV = TopoDS::Vertex( it.Value() );
+    if (V.IsSame( CV )) {
+      V1   = V;
+      OnE1 = Standard_True;
+      break;
+    }
+    P2 = BRep_Tool::Pnt( CV );
+    Tol1 = 1.1*(Tol + BRep_Tool::Tolerance( CV ));
+    if (P1.SquareDistance(P2) <= Tol1*Tol1) {
+      V    = CV;
+      V1   = V;
+      OnE1 = Standard_True;
+      break;
+    }
+  }
+  if (OnE1) {
+    //-----------------------------------------------------------------
+    // Search if the vertex found is still on E2.
+    //-----------------------------------------------------------------
+    for (it.Initialize(VOnE2); it.More(); it.Next()) {
+      if (V.IsSame( it.Value() )) {
+        OnE2 = Standard_True;
+        V2   = V;
+        break;
+      }
+    }
+  }
+  if (!OnE2) {
+    for (it.Initialize(VOnE2); it.More(); it.Next()) {
+      //-----------------------------------------------------------------
+      // Search if the point of intersection is a vertex of E2.
+      //-----------------------------------------------------------------
+      const TopoDS_Vertex& CV = TopoDS::Vertex( it.Value() );
+      P2 = BRep_Tool::Pnt( CV );
+      Tol2 = 1.1*(Tol + BRep_Tool::Tolerance( CV ));
+      if (P1.SquareDistance(P2) <= Tol2*Tol2) {
+        V  = CV;
+        V2 = V;
+        OnE2 = Standard_True;
+        break;
+      }
+    }
+  }
+
+
+  if (!OnE1 && !OnE2 && !theF.IsNull())
+  {
+    // if 3 faces intersects each others, 3 new edges on them must pass
+    // through one vertex but real intersection points of each
+    // pair of edges are sometimes more far than a tolerance.
+    // Try to analitically find vertices that E1 and E2 must pass trough
+
+    TopoDS_Shape F1 = getOtherShape( theF, AsDes->Ascendant( E1 ));
+    TopoDS_Shape F2 = getOtherShape( theF, AsDes->Ascendant( E2 ));
+    if (!F1.IsNull() && !F2.IsNull() && !F1.IsSame( F2 ))
+    {
+      OnE1 = findVOnE ( theV, E1, E2, F1, F2, AsDes, V1 );
+      OnE2 = findVOnE ( theV, E2, E1, F1, F2, AsDes, V2 );
+      if (OnE2) V = V2;
+      if (OnE1) V = V1;
+    }
+  }
+
+  if (OnE1 && OnE2) {
+    if (!V1.IsSame(V2)) {
+      // replace V1 with V2 on all edges V1 is on
+      Standard_Real UV1;
+      TopoDS_Edge   EWE1;
+      TopoDS_Vertex VI;
+      const TopTools_ListOfShape& EdgeWithV1 = AsDes->Ascendant(V1);
+
+      for (it.Initialize(EdgeWithV1); it.More(); it.Next()) {
+        EWE1  = TopoDS::Edge(it.Value());
+        VI = V1;
+        VI.Orientation(TopAbs_INTERNAL);
+        UV1 = BRep_Tool::Parameter(VI,EWE1);
+        VI = V2;
+        VI.Orientation(TopAbs_INTERNAL);
+        B.UpdateVertex( VI, UV1, EWE1, GetTolerance( VI, UV1, EWE1, AsDes));
+      }
+      AsDes->Replace(V1,V2);
+      V = V2;
+    }
+  }
+
+  // add existing vertices instead of new ones
+  if (!OnE1) {
+    if (OnE2) {
+      V.Orientation(TopAbs_INTERNAL);
+      B.UpdateVertex (V, U1, E1, GetTolerance( V, U1, E1, AsDes));
+    }
+    V.Orientation(O1);
+    AsDes->Add(E1,V);
+  }
+  if (!OnE2) {
+    if (OnE1) {
+      V.Orientation(TopAbs_INTERNAL);
+      B.UpdateVertex (V, U2, E2, GetTolerance( V, U2, E2, AsDes ));
+    }
+    V.Orientation(O2);
+    AsDes->Add(E2,V);
+  }
+
+  return V;
+}
+
+//=======================================================================
+//function : FindEndVertex
+//purpose  : Returns a vertex  from  <VertList> having parameter on
+//           <E>  closest  to  <f>  or  <l>.  <isFirst>  is True if
+//           found vertex is closer  to <f>. <DU> returns parameter
+//           difference.
+//=======================================================================
+
+TopoDS_Vertex Partition_Inter2d::FindEndVertex(const TopTools_ListOfShape& LV,
+                                               const Standard_Real f,
+                                               const Standard_Real l,
+                                               const TopoDS_Edge&  E,
+                                               Standard_Boolean&   isFirst,
+                                               Standard_Real&      minDU)
+{
+  TopoDS_Vertex endV;
+  Standard_Real U, endU, min;
+  minDU = 1.e10;
+
+  TopTools_ListIteratorOfListOfShape it;
+  it.Initialize(LV);
+  for (; it.More(); it.Next()) {
+    const TopoDS_Vertex& v = TopoDS::Vertex(it.Value());
+    U = BRep_Tool::Parameter(v, E);
+    min = Min( Abs(U-f), Abs(U-l) );
+    if (min < minDU) {
+      endV = v;
+      endU = U;
+      minDU = min;
+    }
+  }
+  if (Abs(endU-f) < Abs(endU-l))
+    isFirst = Standard_True;
+  else
+    isFirst = Standard_False;
+
+  return endV;
+}
+
+//=======================================================================
+//function : treatClosed
+//purpose  : add second vertex to closed edge. Vertex is one of <LV1>
+//=======================================================================
+
+static void treatClosed (const TopoDS_Edge& E1,
+                          const Standard_Real f,
+                          const Standard_Real l,
+                          TopTools_ListOfShape& LV1,
+                          TopTools_ListOfShape& /*LV2*/)
+{
+  Standard_Boolean isFirst=0;
+  Standard_Real    minDU = 1.e10;
+  TopoDS_Vertex endV;
+  endV = Partition_Inter2d::FindEndVertex(LV1, f,l, E1, isFirst,minDU);
+
+  if (minDU > Precision::PConfusion())
+    return; // not end point
+
+  Standard_Real newU;
+  if (isFirst)
+    newU = f + (l - f);
+  else
+    newU = l - (l - f);
+
+  // update end parameter
+  BRep_Builder B;
+  endV.Orientation(TopAbs_INTERNAL);
+  B.UpdateVertex(endV,newU,E1,BRep_Tool::Tolerance(endV));
+}
+
+//=======================================================================
+//function : EdgesPartition
+//purpose  :
+//=======================================================================
+
+static void EdgesPartition(const TopoDS_Face&            F,
+                           const TopoDS_Edge&            E1,
+                           const TopoDS_Edge&            E2,
+                           const Handle(BRepAlgo_AsDes)& AsDes,
+                           const TopTools_MapOfShape&    NewEdges,
+                           const Standard_Boolean        WithOri)
+{
+
+  Standard_Real f[3],l[3];
+  Standard_Real MilTol2;
+  Standard_Real Tol = Max (BRep_Tool::Tolerance(E1),
+                           BRep_Tool::Tolerance(E2));
+  MilTol2 = Tol * Tol * 10;
+
+  BRep_Tool::Range(E1, f[1], l[1]);
+  BRep_Tool::Range(E2, f[2], l[2]);
+
+  BRepAdaptor_Curve CE1(E1,F);
+  BRepAdaptor_Curve CE2(E2,F);
+
+  TopoDS_Edge                 EI[3]; EI[1] = E1; EI[2] = E2;
+  TopTools_ListOfShape        LV1; // new vertices at intersections on E1
+  TopTools_ListOfShape        LV2; // ... on E2
+  BRep_Builder                B;
+
+  // if E1 and E2 are results of intersection of F and two connex faces then
+  // no need to intersect edges, they can contact by vertices only
+  // (encounted an exception in TopOpeBRep_EdgesIntersector in such a case)
+  Standard_Boolean intersect = Standard_True;
+  TopTools_IndexedMapOfShape ME;
+  TopExp::MapShapes(F, TopAbs_EDGE, ME);
+  if (!ME.Contains(E1) && ! ME.Contains(E2)) { // if E1 and E2 are new on F
+    TopoDS_Shape F1, F2;
+    const TopTools_ListOfShape& LF1 = AsDes->Ascendant( E1 );
+    F1 = F.IsSame( LF1.First() ) ? LF1.Last() : LF1.First();
+    const TopTools_ListOfShape& LF2 = AsDes->Ascendant( E2 );
+    F2 = F.IsSame( LF2.First() ) ? LF2.Last() : LF2.First();
+    if (!F.IsSame(F2) && !F.IsSame(F1) ) {
+      TopExp_Explorer exp(F2, TopAbs_EDGE);
+      TopExp::MapShapes(F1, TopAbs_EDGE, ME);
+      for (; exp.More(); exp.Next()) {
+        if (ME.Contains( exp.Current())) {
+          intersect = Standard_False;
+          break;
+        }
+      }
+    }
+  }
+
+  if (intersect) {
+    //------------------------------------------------------
+    // compute the points of Intersection in 2D
+    //-----------------------------------------------------
+    // i.e. fill LV1 and LV2
+    TopOpeBRep_EdgesIntersector EInter;
+    EInter.SetFaces(F,F);
+    Standard_Real TolDub = 1.e-7;
+    EInter.ForceTolerances(TolDub,TolDub);
+    Standard_Boolean reducesegments = Standard_False;
+    EInter.Perform (E1,E2,reducesegments);
+
+    Standard_Boolean rejectreducedsegmentpoints = Standard_False;
+    EInter.InitPoint(rejectreducedsegmentpoints);
+    for ( ; EInter.MorePoint(); EInter.NextPoint() )
+    {
+      const TopOpeBRep_Point2d& P2D = EInter.Point();
+      const gp_Pnt&    P    = P2D.Value();
+      TopoDS_Vertex    V    = BRepLib_MakeVertex(P);
+
+      //-------------------------
+      // control the point found.
+      //-------------------------
+      gp_Pnt P1 = CE1.Value(P2D.Parameter(1));
+      gp_Pnt P2 = CE2.Value(P2D.Parameter(2));
+      Standard_Real sqd1 = P1.SquareDistance(P);
+      Standard_Real sqd2 = P2.SquareDistance(P);
+      if (sqd1 > MilTol2 || sqd2 > MilTol2  )
+        continue;
+
+      // add a new vertex to the both edges
+      Standard_Real toler = Max( Tol, sqrt( Max( sqd1, sqd2 )));
+      Standard_Integer i;
+      for (i = 1; i <= 2; i++) {
+        Standard_Real U = P2D.Parameter(i);
+        V.Orientation(TopAbs_INTERNAL);
+        B.UpdateVertex( V,U,EI[i], toler);
+        TopAbs_Orientation OO = TopAbs_REVERSED;
+        if (WithOri) {
+          if (P2D.IsVertex(i))
+            OO = P2D.Vertex(i).Orientation();
+          else if (P2D.Transition(i).Before() == TopAbs_OUT) {
+            OO = TopAbs_FORWARD;
+          }
+          V.Orientation(OO);
+          if (i == 1) LV1.Append(V);
+          else        LV2.Append(V);
+        }
+      }
+    }
+  } // if (intersect)
+
+  //----------------------------------
+  // Test the extremities of the edges.
+  //----------------------------------
+  // add to LV* vertices for vertex-vertex closeness
+  Standard_Real U1,U2;
+  Standard_Real TolConf2, TolConf;
+  TopoDS_Vertex V1[2],V2[2];
+  TopExp::Vertices(E1,V1[0],V1[1]);
+  TopExp::Vertices(E2,V2[0],V2[1]);
+
+  Standard_Integer i,j,k;
+  for (j = 0; j < 2; j++) {
+    if (V1[j].IsNull()) continue;
+    for ( k = 0; k < 2; k++) {
+      if (V2[k].IsNull()) continue;
+      gp_Pnt P1 = BRep_Tool::Pnt(V1[j]);
+      gp_Pnt P2 = BRep_Tool::Pnt(V2[k]);
+      TolConf = BRep_Tool::Tolerance(V1[j]) + BRep_Tool::Tolerance(V2[k]);
+      TolConf = Max (Tol, TolConf);
+      TolConf2 = TolConf * TolConf;
+      if (!intersect)
+        TolConf2 *= 100;
+      Standard_Real SqDist = P1.SquareDistance(P2);
+
+      if (SqDist <= TolConf2) {
+        TopoDS_Vertex V = BRepLib_MakeVertex(P1);
+        V.Orientation(TopAbs_INTERNAL);
+        U1 = (j == 0) ? f[1] : l[1];
+        U2 = (k == 0) ? f[2] : l[2];
+        B.UpdateVertex(V,U1,E1,TolConf);
+        B.UpdateVertex(V,U2,E2,TolConf);
+        LV1.Prepend(V.Oriented(V1[j].Orientation()));
+        LV2.Prepend(V.Oriented(V2[k].Orientation()));
+      }
+    }
+  }
+
+  Standard_Boolean AffichPurge = Standard_False;
+
+  if ( LV1.IsEmpty()) return;
+
+  //----------------------------------
+  // Purge of all the vertices.
+  //----------------------------------
+  // remove one of close vertices
+  TopTools_ListIteratorOfListOfShape it1LV1,it1LV2,it2LV1;
+  gp_Pnt P1,P2;
+  Standard_Boolean Purge = Standard_True;
+
+  while (Purge) {
+    i = 1;
+    Purge = Standard_False;
+    for (it1LV1.Initialize(LV1),it1LV2.Initialize(LV2);
+         it1LV1.More();
+         it1LV1.Next(),it1LV2.Next()) {
+      j = 1;
+      it2LV1.Initialize(LV1);
+      while (j < i) {
+        const TopoDS_Vertex& VE1 = TopoDS::Vertex(it1LV1.Value());
+        const TopoDS_Vertex& VE2 = TopoDS::Vertex(it2LV1.Value());
+        Standard_Real Tol1 = BRep_Tool::Tolerance( VE1 );
+        Standard_Real Tol2 = BRep_Tool::Tolerance( VE2 );
+        P1 = BRep_Tool::Pnt( VE1 );
+        P2 = BRep_Tool::Pnt( VE2 );
+        if (P1.IsEqual(P2, Tol1 + Tol2)) {
+          LV1.Remove(it1LV1);
+          LV2.Remove(it1LV2);
+          Purge = Standard_True;
+          break;
+        }
+        j++;
+        it2LV1.Next();
+      }
+      if (Purge) break;
+      i++;
+    }
+  }
+
+  // care of new closed edges, they always intersect with seam at end
+  if (V1[0].IsSame( V1[1] ) && NewEdges.Contains(E1) )
+    treatClosed (E1, f[1], l[1], LV1, LV2);
+  if (V2[0].IsSame( V2[1] ) && NewEdges.Contains(E2) )
+    treatClosed (E2, f[2], l[2], LV2, LV1);
+
+  //----------------
+  // Stocking vertex
+  //----------------
+
+  for ( it1LV1.Initialize( LV1 ); it1LV1.More(); it1LV1.Next())
+    Partition_Inter2d::AddVonE (TopoDS::Vertex( it1LV1.Value()),
+                                E1, E2, AsDes, F);
+}
+
+//=======================================================================
+//function : CompletPart2d
+//purpose  : Computes the intersections between the edges stored
+//           is AsDes as descendants of <F> . Intersections is computed
+//           between two edges if one of them is bound in NewEdges.
+//=======================================================================
+
+void Partition_Inter2d::CompletPart2d (const Handle(BRepAlgo_AsDes)&   AsDes,
+                                       const TopoDS_Face&              F,
+                                       const TopTools_MapOfShape&      NewEdges)
+{
+
+#ifdef DEB
+  NbF2d++;
+  NbE2d = 0;
+#endif
+
+  //Do not intersect the edges of a face
+  TopTools_IndexedMapOfShape EdgesOfFace;
+  TopExp::MapShapes( F, TopAbs_EDGE , EdgesOfFace);
+
+  //-------------------------------------------------------------------
+  // compute the intersection2D on the faces touched by the intersection3D
+  //-------------------------------------------------------------------
+  TopTools_ListIteratorOfListOfShape it1LE ;
+  TopTools_ListIteratorOfListOfShape it2LE ;
+
+  //-----------------------------------------------
+  // Intersection edge-edge.
+  //-----------------------------------------------
+  const TopTools_ListOfShape&        LE = AsDes->Descendant(F);
+  TopoDS_Vertex                      V1,V2;
+  Standard_Integer                   j, i = 1;
+
+  TopoDS_Face FF = F;
+  FF.Orientation(TopAbs_FORWARD);
+
+  for ( it1LE.Initialize(LE) ; it1LE.More(); it1LE.Next()) {
+    const TopoDS_Edge& E1 = TopoDS::Edge(it1LE.Value());
+    j = 1;
+    it2LE.Initialize(LE);
+
+    while (j < i && it2LE.More()) {
+      const TopoDS_Edge& E2 = TopoDS::Edge(it2LE.Value());
+      //----------------------------------------------------------
+      // Intersections of the new edges obtained by intersection
+      // between them and with the restrictions edges
+      //----------------------------------------------------------
+      if ( (!EdgesOfFace.Contains(E1) || !EdgesOfFace.Contains(E2)) &&
+           (NewEdges.Contains(E1) || NewEdges.Contains(E2)) ) {
+        EdgesPartition(FF,E1,E2,AsDes,NewEdges,Standard_True);
+      }
+      it2LE.Next();
+      j++;
+    }
+    i++;
+  }
+}
+
+//=======================================================================
+//function : GetTolerance
+//purpose  : Returns  tolerance  theV   must   have  atfer  its
+//           addition to theE with  theU parameter. theAsDes is
+//           used to find pcurves of theE
+//=======================================================================
+
+Standard_Real Partition_Inter2d::GetTolerance
+                         (const TopoDS_Vertex &         theV,
+                          const Standard_Real           theU,
+                          const TopoDS_Edge &           theE,
+                          const Handle(BRepAlgo_AsDes)& theAsDes)
+{
+  Standard_Real aTol = BRep_Tool::Tolerance( theV );
+  gp_Pnt aPnt = BRep_Tool::Pnt( theV );
+
+  // check point on 3D curve
+  Standard_Real f,l;
+  Handle(Geom_Curve) C = BRep_Tool::Curve( theE, f, l );
+  if (!C.IsNull())
+    aTol = Max ( aTol, aPnt.Distance( C->Value( theU )));
+
+  // check points on pcurves
+  const TopTools_ListOfShape& aFList = theAsDes->Ascendant( theE );
+  TopTools_ListIteratorOfListOfShape aFIt( aFList );
+  for (  ; aFIt.More(); aFIt.Next() )
+  {
+    const TopoDS_Face& F = TopoDS::Face( aFIt.Value() );
+    Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( theE, F, f, l );
+    if (!pcurve.IsNull())
+    {
+      gp_Pnt2d aPnt2d = pcurve->Value( theU );
+      TopLoc_Location L;
+      Handle(Geom_Surface) S = BRep_Tool::Surface( F, L );
+      gp_Pnt aPntOnS = S->Value( aPnt2d.X(), aPnt2d.Y() );
+      if (!L.IsIdentity())
+        aPntOnS.Transform( L.Transformation() );
+      aTol = Max ( aTol, aPnt.Distance( aPntOnS ));
+    }
+  }
+
+  return aTol;
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Inter2d.hxx b/contrib/Netgen/libsrc/occ/Partition_Inter2d.hxx
new file mode 100644
index 0000000000000000000000000000000000000000..cfd4a9ac0aa4db3e23ff3669a99b9546c71138f4
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Inter2d.hxx
@@ -0,0 +1,110 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Inter2d.hxx
+//  Module : GEOM
+
+#ifndef _Partition_Inter2d_HeaderFile
+#define _Partition_Inter2d_HeaderFile
+
+#ifndef _Handle_BRepAlgo_AsDes_HeaderFile
+#include <Handle_BRepAlgo_AsDes.hxx>
+#endif
+#ifndef _Standard_Real_HeaderFile
+#include <Standard_Real.hxx>
+#endif
+#ifndef _Standard_Boolean_HeaderFile
+#include <Standard_Boolean.hxx>
+#endif
+class BRepAlgo_AsDes;
+class TopoDS_Face;
+class TopTools_MapOfShape;
+class TopoDS_Vertex;
+class TopTools_ListOfShape;
+class TopoDS_Edge;
+
+
+#ifndef _Standard_HeaderFile
+#include <Standard.hxx>
+#endif
+#ifndef _Standard_Macro_HeaderFile
+#include <Standard_Macro.hxx>
+#endif
+
+class Partition_Inter2d  {
+
+public:
+
+   void* operator new(size_t,void* anAddress) 
+   {
+      return anAddress;
+   }
+   void* operator new(size_t size) 
+   { 
+      return Standard::Allocate(size); 
+   }
+   void  operator delete(void *anAddress) 
+   { 
+      if (anAddress) Standard::Free((Standard_Address&)anAddress); 
+   }
+   // Methods PUBLIC
+   // 
+   static  void CompletPart2d(const Handle(BRepAlgo_AsDes)& AsDes,const TopoDS_Face& F,const TopTools_MapOfShape& NewEdges) ;
+   static  TopoDS_Vertex FindEndVertex(const TopTools_ListOfShape& VertList,const Standard_Real f,const Standard_Real l,const TopoDS_Edge& E,Standard_Boolean& First,Standard_Real& DU) ;
+   static  TopoDS_Vertex AddVonE(const TopoDS_Vertex& V,const TopoDS_Edge& E1,const TopoDS_Edge& E2,const Handle(BRepAlgo_AsDes)& AsDes,const TopoDS_Face& F) ;
+   static  Standard_Real GetTolerance(const TopoDS_Vertex& theV,const Standard_Real theU,const TopoDS_Edge& theE,const Handle(BRepAlgo_AsDes)& theAsDes) ;
+
+
+
+
+protected:
+
+   // Methods PROTECTED
+   // 
+
+
+   // Fields PROTECTED
+   //
+
+
+private: 
+
+   // Methods PRIVATE
+   // 
+
+
+   // Fields PRIVATE
+   //
+
+
+};
+
+
+
+
+
+// other Inline functions and methods (like "C++: function call" methods)
+//
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Inter2d.ixx b/contrib/Netgen/libsrc/occ/Partition_Inter2d.ixx
new file mode 100644
index 0000000000000000000000000000000000000000..5dbe719329b7664e403cd69c0781828f0986042f
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Inter2d.ixx
@@ -0,0 +1,32 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Inter2d.ixx
+//  Module : GEOM
+
+#include <climits>
+#include "Partition_Inter2d.jxx"
+
+ 
+
+
diff --git a/contrib/Netgen/libsrc/occ/Partition_Inter2d.jxx b/contrib/Netgen/libsrc/occ/Partition_Inter2d.jxx
new file mode 100644
index 0000000000000000000000000000000000000000..2d08527511023d3fca6145069807e48fc2e9662d
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Inter2d.jxx
@@ -0,0 +1,50 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Inter2d.jxx
+//  Module : GEOM
+
+#include <mystdlib.h>  // netgen headers
+
+
+#ifndef _BRepAlgo_AsDes_HeaderFile
+#include <BRepAlgo_AsDes.hxx>
+#endif
+#ifndef _TopoDS_Face_HeaderFile
+#include <TopoDS_Face.hxx>
+#endif
+#ifndef _TopTools_MapOfShape_HeaderFile
+#include <TopTools_MapOfShape.hxx>
+#endif
+#ifndef _TopoDS_Vertex_HeaderFile
+#include <TopoDS_Vertex.hxx>
+#endif
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _TopoDS_Edge_HeaderFile
+#include <TopoDS_Edge.hxx>
+#endif
+#ifndef _Partition_Inter2d_HeaderFile
+#include "Partition_Inter2d.hxx"
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Inter3d.cxx b/contrib/Netgen/libsrc/occ/Partition_Inter3d.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e0c0363a791d459a8abd97cfa8d8814fa991003b
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Inter3d.cxx
@@ -0,0 +1,947 @@
+#ifdef OCCGEOMETRY
+
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Inter3d.cxx
+//  Author : Benedicte MARTIN
+//  Module : GEOM
+//  $Header: /cvs/netgen/netgen/libsrc/occ/Partition_Inter3d.cxx,v 1.6 2008/03/31 14:20:28 wabro Exp $
+
+//using namespace std;
+#include <climits>
+
+#include "Partition_Inter2d.hxx"
+#include "Partition_Inter3d.ixx"
+#include "utilities.h"
+
+#include <BRepAlgo_AsDes.hxx>
+#include <BRepAlgo_Image.hxx>
+#include <BRepLib.hxx>
+#include <BRepOffset_Tool.hxx>
+#include <BRep_Builder.hxx>
+#include <BRep_Tool.hxx>
+
+#include <TopExp.hxx>
+#include <TopExp_Explorer.hxx>
+
+#include <TopOpeBRepTool_BoxSort.hxx>
+#include <TopTools_DataMapIteratorOfDataMapOfShapeListOfShape.hxx>
+#include <TopTools_ListIteratorOfListOfShape.hxx>
+#include <TopTools_ListOfShape.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Compound.hxx>
+#include <TopoDS_Edge.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopoDS_Vertex.hxx>
+
+#ifdef DEB
+#include <DBRep.hxx>
+#endif
+
+#include <BRepLib_MakeVertex.hxx>
+#include <BRepTools.hxx>
+#include <Extrema_ExtPS.hxx>
+#include <Extrema_POnSurf.hxx>
+#include <Geom2dAPI_ProjectPointOnCurve.hxx>
+#include <Geom2d_Curve.hxx>
+#include <GeomAPI_ProjectPointOnCurve.hxx>
+#include <GeomAdaptor_Surface.hxx>
+#include <Geom_Curve.hxx>
+#include <Geom_RectangularTrimmedSurface.hxx>
+#include <Geom_SphericalSurface.hxx>
+#include <Geom_Surface.hxx>
+#include <Geom_TrimmedCurve.hxx>
+#include <Precision.hxx>
+#include <TColStd_MapOfInteger.hxx>
+#include <TopOpeBRepBuild_Builder.hxx>
+#include <TopOpeBRepDS_BuildTool.hxx>
+#include <TopOpeBRepDS_CurveExplorer.hxx>
+#include <TopOpeBRepDS_HDataStructure.hxx>
+#include <TopOpeBRepDS_Interference.hxx>
+#include <TopOpeBRepDS_PointIterator.hxx>
+#include <TopOpeBRepDS_Transition.hxx>
+#include <TopOpeBRepTool_CurveTool.hxx>
+#include <TopOpeBRepTool_GeomTool.hxx>
+#include <TopOpeBRepTool_OutCurveType.hxx>
+#include <TopOpeBRep_DSFiller.hxx>
+#include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
+#include <stdio.h>
+
+//=======================================================================
+//function : Partition_Inter3d
+//purpose  : 
+//=======================================================================
+
+Partition_Inter3d::Partition_Inter3d()
+{
+}
+//=======================================================================
+//function : Partition_Inter3d
+//purpose  : 
+//=======================================================================
+
+Partition_Inter3d::Partition_Inter3d(const Handle(BRepAlgo_AsDes)& AsDes)
+  :myAsDes(AsDes)
+{
+  mySectionEdgesAD = new BRepAlgo_AsDes;
+}
+
+//=======================================================================
+//function : CompletPart3d
+//purpose  : FaceShapeMap is just to know the shape a face belongs to
+//=======================================================================
+
+void Partition_Inter3d::CompletPart3d(const TopTools_ListOfShape& SetOfFaces1,
+				      const TopTools_DataMapOfShapeShape& FaceShapeMap)
+{
+  if (myAsDes.IsNull())
+    myAsDes = new BRepAlgo_AsDes;
+  
+  TopTools_ListIteratorOfListOfShape it;
+
+  //---------------------------------------------------------------
+  // Construction of bounding boxes.
+  //---------------------------------------------------------------
+
+  BRep_Builder B;
+  TopoDS_Compound CompOS;
+  B.MakeCompound(CompOS);
+  for (it.Initialize(SetOfFaces1); it.More(); it.Next())
+    B.Add(CompOS, it.Value());
+    
+  TopOpeBRepTool_BoxSort BOS;
+  BOS.AddBoxesMakeCOB(CompOS,TopAbs_FACE);
+
+  for (it.Initialize(SetOfFaces1); it.More(); it.Next()) {
+    TopoDS_Face F1 = TopoDS::Face(it.Value());
+    
+    // avoid intersecting faces of one shape
+    TopoDS_Shape S1;
+    if (FaceShapeMap.IsBound(F1)) S1 = FaceShapeMap.Find(F1);
+
+    // to filter faces sharing an edge
+    TopTools_IndexedMapOfShape EM;
+    TopExp::MapShapes( F1, TopAbs_EDGE, EM);
+    
+    TColStd_ListIteratorOfListOfInteger itLI = BOS.Compare(F1);
+    for (; itLI.More(); itLI.Next()) {
+      TopoDS_Face F2 = TopoDS::Face(BOS.TouchedShape(itLI));
+      if (F1.IsSame(F2) || IsDone(F1,F2))
+	continue;
+
+      TopoDS_Shape S2;
+      if (FaceShapeMap.IsBound(F2)) S2 = FaceShapeMap.Find(F2);
+      if (!S1.IsNull() && S1.IsSame(S2))
+	continue; // descendants of one shape
+
+      TopExp_Explorer expE (F2, TopAbs_EDGE);
+      for ( ; expE.More(); expE.Next())
+	if (EM.Contains( expE.Current() ))
+	  break;
+      if (expE.More())
+      {
+        // faces have a common edge, check if they are a tool and a face
+        // generated by the tool in another shape; in that case they are
+        // to be intersected
+        TopLoc_Location L1, L2;
+        Handle(Geom_Surface) S1 = BRep_Tool::Surface( F1, L1 );
+        Handle(Geom_Surface) S2 = BRep_Tool::Surface( F2, L2 );
+        if ( S1 != S2 || L1 != L2 )
+          continue;
+      }
+
+      F1.Orientation(TopAbs_FORWARD);
+      F2.Orientation(TopAbs_FORWARD);
+      FacesPartition(F1,F2);	  
+    }
+
+    // mark as modified a face which has at least one new edge
+    if (!myAsDes->HasDescendant( F1 ))
+      continue;
+    TopTools_ListIteratorOfListOfShape itE (myAsDes->Descendant( F1 ));
+    for ( ; itE.More(); itE.Next()) {
+      if (myNewEdges.Contains( itE.Value())) {
+	myTouched.Add( F1 );
+	break;
+      }
+    }
+  }
+}
+
+//=======================================================================
+//function : PutInBounds
+//purpose  : 
+//=======================================================================
+
+static void PutInBounds (const TopoDS_Face&          F,
+			 const TopoDS_Edge&          E,
+			 Handle(Geom2d_Curve)&       C2d)
+{
+  Standard_Real   umin,umax,vmin,vmax;
+  Standard_Real   f,l;
+  BRep_Tool::Range(E,f,l);
+
+  TopLoc_Location L; // Recup S avec la location pour eviter la copie.
+  Handle (Geom_Surface) S   = BRep_Tool::Surface(F,L);
+
+  if (S->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface))) {
+    S = (*(Handle_Geom_RectangularTrimmedSurface*)&S)->BasisSurface();
+  }
+  if (!S->IsUPeriodic() && !S->IsVPeriodic())
+    return;
+
+  BRepTools::UVBounds(F,umin,umax,vmin,vmax);
+
+  gp_Pnt2d Pf = C2d->Value(f);
+  gp_Pnt2d Pl = C2d->Value(l);
+  const Standard_Real Um = 0.34*f + 0.66*l;
+  gp_Pnt2d Pm = C2d->Value( Um );
+
+  // sometimes on shpere, pcurve is out of domain by V though S is
+  // UPeriodic, sometimes it is in domain but nontheless it has
+  // wrong position.
+  // Check pcurve position by 3D point
+  if (S->IsKind(STANDARD_TYPE( Geom_SphericalSurface )))
+  {
+    // get point on the surface
+    gp_Pnt Ps = S->Value( Pm.X(), Pm.Y() );
+    // get point on the edge
+    Handle(Geom_Curve) C = BRep_Tool::Curve( E, f, l );
+    gp_Pnt Pc = C->Value( Um );
+    // compare points
+    Standard_Real TolE = BRep_Tool::Tolerance( E );
+    if ( Pc.SquareDistance( Ps ) * 0.95 < TolE * TolE )
+      return; // OK
+
+    // find good UV for Pc: project Pc on S
+    GeomAdaptor_Surface  SA (S);
+    Extrema_ExtPS anExtPS (Pc, SA,
+                           SA.UResolution( TolE ), SA.VResolution( TolE ));
+    if (anExtPS.IsDone())
+    {
+      Standard_Integer i, nbExt = anExtPS.NbExt();
+      Extrema_POnSurf aPOnSurf;
+      for (i = 1; i <= nbExt; ++i )
+	if (anExtPS.Value( i ) <= TolE)               // V6.3
+	  // if (anExtPS.SquareDistance( i ) <= TolE)   // V6.5
+	  {
+          aPOnSurf = anExtPS.Point( i );
+          break;
+        }
+      if (i <= nbExt) {
+        // a point found
+        Standard_Real u, v;
+        aPOnSurf.Parameter( u, v );
+        gp_Pnt2d aGoodPm ( u, v );
+        C2d->Translate( Pm , aGoodPm );
+      }
+    }
+  }
+
+  //---------------
+  // Recadre en U.
+  //---------------
+  if (S->IsUPeriodic()) {
+    Standard_Real period  = S->UPeriod();
+    Standard_Real eps     = period*1.e-6;
+    Standard_Real minC    = Min(Pf.X(),Pl.X()); minC = Min(minC,Pm.X());
+    Standard_Real maxC    = Max(Pf.X(),Pl.X()); maxC = Max(maxC,Pm.X());
+    Standard_Real du = 0.;
+    if (minC< umin - eps) {
+      du = (int((umin - minC)/period) + 1)*period;
+    }
+    if (minC > umax + eps) {
+      du = -(int((minC - umax)/period) + 1)*period;
+    }
+    if (du != 0) {
+      gp_Vec2d T1(du,0.);
+      C2d->Translate(T1);
+      minC += du; maxC += du;
+    }
+    // Ajuste au mieux la courbe dans le domaine.
+    if (maxC > umax +100*eps) {
+      Standard_Real d1 = maxC - umax;
+      Standard_Real d2 = umin - minC + period;
+      if (d2 < d1) du =-period;
+      if ( du != 0.) {
+	gp_Vec2d T2(du,0.);
+	C2d->Translate(T2);
+      }
+    }
+  }
+  //------------------
+  // Recadre en V.
+  //------------------
+  if (S->IsVPeriodic()) {
+    Standard_Real period  = S->VPeriod();
+    Standard_Real eps     = period*1.e-6;
+    Standard_Real minC    = Min(Pf.Y(),Pl.Y()); minC = Min(minC,Pm.Y());
+    Standard_Real maxC    = Max(Pf.Y(),Pl.Y()); maxC = Max(maxC,Pm.Y());
+    Standard_Real dv = 0.;
+    if (minC< vmin - eps) {
+      dv = (int((vmin - minC)/period) + 1)*period;
+    }
+    if (minC > vmax + eps) {
+      dv = -(int((minC - vmax)/period) + 1)*period;
+    }
+    if (dv != 0) {
+      gp_Vec2d T1(0.,dv);
+      C2d->Translate(T1);
+      minC += dv; maxC += dv;
+    }
+    // Ajuste au mieux la courbe dans le domaine.
+    if (maxC > vmax +100*eps) {
+      Standard_Real d1 = maxC - vmax;
+      Standard_Real d2 = vmin - minC + period;
+      if (d2 < d1) dv =-period;
+      if ( dv != 0.) {
+	gp_Vec2d T2(0.,dv);
+	C2d->Translate(T2);
+      }
+    }
+  }
+}
+
+//=======================================================================
+//function : Inter3D
+//purpose  : 
+//=======================================================================
+
+void Partition_Inter3d::Inter3D(const TopoDS_Face& F1,
+				const TopoDS_Face& F2,
+				TopTools_ListOfShape& L)
+{
+  BRep_Builder B;
+  
+  // fill the data Structure
+  Handle(TopOpeBRepDS_HDataStructure) DatStr = new TopOpeBRepDS_HDataStructure();
+  TopOpeBRep_DSFiller DSFiller;
+  DSFiller.Insert(F1,F2,DatStr);
+
+  // define the GeomTool used by the DSFiller :
+  // compute BSpline of degree 1 on intersection curves.
+  Standard_Real tol3dAPPROX = 1e-7;
+  Standard_Real tol2dAPPROX = 1e-7;
+  TopOpeBRepTool_GeomTool GT2 (TopOpeBRepTool_APPROX);  
+  GT2.SetTolerances(tol3dAPPROX,tol2dAPPROX);
+  TopOpeBRepDS_BuildTool  BT(GT2);
+
+  // Perform Section
+  TopOpeBRepBuild_Builder TopB(BT);
+  TopB.Perform(DatStr);
+
+  // ===============
+  // Store new edges
+  // ===============
+  
+  L.Clear();
+  TopOpeBRepDS_CurveExplorer cex(DatStr->DS());
+  for (; cex.More(); cex.Next()) {
+    const TopOpeBRepDS_Curve& CDS = cex.Curve();
+    Standard_Integer ic = cex.Index();
+    Handle(Geom2d_Curve) pc1 = CDS.Curve1();
+    Handle(Geom2d_Curve) pc2 = CDS.Curve2();
+    
+    TopTools_ListIteratorOfListOfShape itLE = TopB.NewEdges(ic);
+    while (itLE.More()) {
+      TopoDS_Edge E = TopoDS::Edge(itLE.Value());
+      
+      PutInBounds (F1,E,pc1);
+      PutInBounds (F2,E,pc2);
+      
+      B.UpdateEdge (E,pc1,F1,0.);
+      B.UpdateEdge (E,pc2,F2,0.);
+      
+      L.Append (E);
+      
+      itLE.Next();
+      if (itLE.More()) {
+	pc1 = Handle(Geom2d_Curve)::DownCast(pc1->Copy());
+	pc2 = Handle(Geom2d_Curve)::DownCast(pc2->Copy());
+      }
+    }
+  }
+
+  // ========================
+  // store same domain faces 
+  // ========================
+
+
+  if ( DatStr->HasSameDomain( F1 ))
+  {
+    TopTools_ListOfShape emptyList;
+    if (!mySameDomainFM.IsBound(F1))
+      mySameDomainFM.Bind(F1,emptyList);
+    if (!mySameDomainFM.IsBound(F2))
+      mySameDomainFM.Bind(F2,emptyList);
+    mySameDomainFM(F1).Append(F2);
+    mySameDomainFM(F2).Append(F1);
+  }
+
+  // ====================
+  // Store section edges
+  // ====================
+
+  const TopOpeBRepDS_DataStructure& DS = DatStr->DS();
+  Standard_Integer j,i,nse = DS.NbSectionEdges();
+  if (nse == 0) return;
+
+    
+  TopoDS_Vertex V, sdeV1, sdeV2;
+  TopTools_MapOfShape MV;
+  TopTools_ListOfShape LSE; // list of section edges
+  TopoDS_Face dummyF;
+  
+  for (i = 1; i <= nse; i++)
+  {
+    const TopoDS_Edge & se = DS.SectionEdge(i);
+    if (! TopB.IsSplit(se,TopAbs_ON))
+      continue;
+    LSE.Append( se );
+
+    // add vertices where section edges interferes with other
+    // edges as its descendant in myAsDes
+    
+    TopoDS_Edge sde, oe; // same domain, other edge
+    if (DatStr->HasSameDomain(se)) {
+      sde = TopoDS::Edge( DatStr->SameDomain(se).Value() );
+      TopExp::Vertices( sde, sdeV1, sdeV2);
+    }
+    TColStd_MapOfInteger MIV; // indices of added edges
+    TopOpeBRepDS_PointIterator itP (DS.ShapeInterferences( se ));
+    itP.SupportKind( TopOpeBRepDS_EDGE );
+    // loop on intersections of se
+    for (; itP.More(); itP.Next()) {
+      oe = TopoDS::Edge( DS.Shape( itP.Support()));
+      if (itP.IsVertex()) {
+        // there is a vertex at intersection
+	if ( !MIV.Add( itP.Current() ))
+	  continue;
+	V = TopoDS::Vertex( DS.Shape( itP.Current()));
+	if ( !sde.IsNull() && (V.IsSame(sdeV1) || V.IsSame(sdeV2)) )
+	  oe = sde;
+	V = ReplaceSameDomainV( V , oe );
+	V.Orientation( TopAbs_INTERNAL);
+	B.UpdateVertex( V, itP.Parameter(), se, 0.); // AddVonE() sets real U
+      }
+      else {
+        // create a new vertex at the intersection point
+	const TopOpeBRepDS_Point& DSP = DS.Point( itP.Current());
+	V = BRepLib_MakeVertex( DSP.Point() );
+	V.Orientation( TopAbs_INTERNAL);
+	B.UpdateVertex( V, itP.Parameter(), se, DSP.Tolerance());
+	// make V be on the other edge
+	TopOpeBRepDS_PointIterator itOP (DS.ShapeInterferences( oe ));
+	for (; itOP.More(); itOP.Next()) {
+	  const TopOpeBRepDS_Point& ODSP = DS.Point( itOP.Current());
+	  if ( DSP.IsEqual (ODSP)) {
+	    B.UpdateVertex( V, itOP.Parameter(), TopoDS::Edge(oe), ODSP.Tolerance());
+	    break;
+	  }
+	}
+      }
+      // add V on the both intersecting edges
+      TopoDS_Vertex addedV = Partition_Inter2d::AddVonE( V,se,oe,myAsDes,dummyF);
+      if (!addedV.IsSame( V ))
+	mySameDomainVM.Bind (V, addedV); // equal vertex is already there
+
+      MV.Add( addedV ); // to ease storage of vertices of ON splits
+    }
+  }
+
+  // add section edge to the face it intersects and find
+  // splits ON that do not have same domain pair
+  
+  TopB.SplitSectionEdges(); // let TopB find ON splits
+
+  TopTools_MapOfShape SPM; // map of ON splits
+  TopTools_IndexedMapOfShape ME[2];
+  TopExp::MapShapes( F1, TopAbs_EDGE, ME[1]);
+  TopExp::MapShapes( F2, TopAbs_EDGE, ME[0]);
+
+  TopTools_ListIteratorOfListOfShape itSP, itLSE (LSE);
+  while ( itLSE.More() ) {
+
+    TopoDS_Edge se = TopoDS::Edge( itLSE.Value() );
+
+    // move itLSE to the next se
+    Standard_Integer ancRank = DS.AncestorRank(se);
+    if (ME[ancRank-1].Contains( se ))
+    {
+      LSE.Remove( itLSE ); // se is an edge of face it intersects
+      continue;
+    }
+    else
+    {
+      itLSE.Next();
+    }
+
+    const TopoDS_Face& F = (ancRank == 1) ? F2 : F1;
+
+    // add se to face but dont add twice
+    TopTools_ListIteratorOfListOfShape itE( myAsDes->Descendant( F ));
+    if (myAsDes->HasDescendant( F )) {
+      for ( ; itE.More(); itE.Next())
+	if (se.IsSame( itE.Value() ))
+	  break;
+    }
+    if (!itE.More())
+    {
+      myAsDes->Add( F, se );
+
+      // check se pcurve on F
+      Standard_Real tol, f,l, umin=1e100, umax=-1e100;
+      Handle(Geom2d_Curve) pc = BRep_Tool::CurveOnSurface( se, F, f,l);
+      if (pc.IsNull()) {
+	itSP.Initialize( TopB.Splits(se,TopAbs_ON) );
+	for ( ; itSP.More(); itSP.Next()) {
+	  const TopoDS_Edge& E = TopoDS::Edge ( itSP.Value());
+	  BRep_Tool::Range(E, f, l);
+	  umin = Min( umin, f);
+	  umax = Max( umax, l);
+	}
+	Handle(Geom_Curve) C3d = BRep_Tool::Curve( se, f, l);
+	if (umin < umax) // sometimes umin == umax for closed edge
+	  C3d = new Geom_TrimmedCurve( C3d, umin, umax);
+	pc = TopOpeBRepTool_CurveTool::MakePCurveOnFace (F,C3d,tol);
+	if (pc.IsNull()) {
+	  MESSAGE (" CANT BUILD PCURVE ");
+	}
+	B.UpdateEdge( se, pc, F, tol);
+      }
+    }
+
+    // to detect splits that do not have same domain pair
+    // ie which split a face into parts and not pass by its boundary
+    itSP.Initialize( TopB.Splits(se,TopAbs_ON) );
+    for ( ; itSP.More(); itSP.Next()) {
+      const TopoDS_Shape& SP = itSP.Value();
+      if (!SPM.Add( SP ))
+	SPM.Remove( SP );
+    }
+  }
+
+  // store vertices of ON splits and bind section edges to faces
+  
+  for (itLSE.Initialize (LSE); itLSE.More(); itLSE.Next())
+  {
+    const TopoDS_Shape& se = itLSE.Value();
+
+    Standard_Integer ancRank = DS.AncestorRank(se);
+    TopoDS_Face F = (ancRank == 1) ? F2 : F1;
+
+    // add vertices of ON splits which have no same domain pair
+    Standard_Boolean added = Standard_False;
+    itSP.Initialize( TopB.Splits(se,TopAbs_ON) );
+    for ( ; itSP.More(); itSP.Next())
+    {
+      if (!SPM.Contains( itSP.Value() ))
+	continue;
+      
+      const TopoDS_Edge& S = TopoDS::Edge ( itSP.Value());
+
+      added = Standard_True;
+      mySectionEdgesAD->Add( F, se );
+      
+      TopoDS_Vertex VS[2];
+      TopExp::Vertices (S, VS[0], VS[1]);
+      for (j=0; j<2; ++j)
+      {
+	if (mySameDomainVM.IsBound( VS[j] ))
+	  VS[j] = TopoDS::Vertex( mySameDomainVM( VS[j] ));
+	if ( !MV.Contains( VS[j] )) {
+	  // find equal vertex on se - point interference
+	  gp_Pnt P1 = BRep_Tool::Pnt( VS[j] );
+	  TopTools_ListIteratorOfListOfShape itV( myAsDes->Descendant(se) );
+	  for (; itV.More(); itV.Next()) {
+	    V = TopoDS::Vertex( itV.Value() );
+            if ( V.IsSame( VS[j] ))
+              break;
+	    gp_Pnt P2 = BRep_Tool::Pnt( V );
+	    if (P1.IsEqual( P2, Precision::Confusion())) {
+	      mySameDomainVM.Bind (VS[j], V);
+	      VS[j] = V;
+	      break;
+	    }
+	  }
+	  if (!itV.More())  // no interferences with edges
+	    myAsDes->Add( se, VS[j]);
+	}
+
+        // add ends of ON splits to F in order to detect later
+        // if a split is on face in IsSplitOn()
+	mySectionEdgesAD->Add( F, VS[j]);
+      }
+      // in the descendants of F, first go ends of an ON split and
+      // then a split itself
+      mySectionEdgesAD->Add( F, S );
+    }
+    if (!added)
+      mySectionEdgesAD->Add( F, se );
+    
+    myNewEdges.Add( se );
+  }
+}
+
+//=======================================================================
+//function : FacesPartition
+//purpose  : 
+//=======================================================================
+
+void Partition_Inter3d::FacesPartition(const TopoDS_Face& F1,
+				       const TopoDS_Face& F2)
+     //(const TopTools_DataMapOfShapeListOfShape& /*SetOfFaces2*/)
+{
+  TopTools_ListOfShape LInt;
+
+  Inter3D (F1,F2,LInt);
+  
+  StorePart3d (F1,F2,LInt);
+}
+
+//=======================================================================
+//function : SetDone
+//purpose  : 
+//=======================================================================
+
+void Partition_Inter3d::SetDone(const TopoDS_Face& F1, 
+				const TopoDS_Face& F2)
+{
+  if (!myDone.IsBound(F1)) {
+    TopTools_ListOfShape emptyList;
+    myDone.Bind(F1,emptyList);
+  }
+  myDone(F1).Append(F2);
+  if (!myDone.IsBound(F2)) {
+    TopTools_ListOfShape emptyList;
+    myDone.Bind(F2,emptyList);
+  }
+  myDone(F2).Append(F1);
+}
+
+//=======================================================================
+//function : IsDone
+//purpose  : 
+//=======================================================================
+
+Standard_Boolean Partition_Inter3d::IsDone(const TopoDS_Face& F1, 
+					   const TopoDS_Face& F2) 
+
+  const 
+{
+  if (myDone.IsBound(F1)) {
+    TopTools_ListIteratorOfListOfShape it (myDone(F1));
+    for (; it.More(); it.Next()) {
+      if (it.Value().IsSame(F2)) return Standard_True;
+    }
+  }
+  return Standard_False;
+}
+
+//=======================================================================
+//function : StorePart3d
+//purpose  : 
+//=======================================================================
+
+void Partition_Inter3d::StorePart3d(const TopoDS_Face& F1, 
+				    const TopoDS_Face& F2, 
+				    const TopTools_ListOfShape& LInt)
+{
+  if (!LInt.IsEmpty()) {
+    myAsDes->Add( F1,LInt);
+    myAsDes->Add( F2,LInt);
+
+    TopTools_ListIteratorOfListOfShape it(LInt);
+    for (; it.More(); it.Next()) {
+
+      TopoDS_Edge E = TopoDS::Edge(it.Value());
+
+      BRep_Builder B;
+      B.SameParameter(E,Standard_False);
+      BRepLib::SameParameter(E,1.0e-7);
+      
+      myNewEdges.Add(E);
+    }
+  }
+  SetDone(F1,F2);
+}
+
+//=======================================================================
+//function : TouchedFaces
+//purpose  : 
+//=======================================================================
+
+TopTools_MapOfShape& Partition_Inter3d::TouchedFaces()
+{
+  return myTouched;
+}
+
+//=======================================================================
+//function : AsDes
+//purpose  : 
+//=======================================================================
+
+Handle(BRepAlgo_AsDes) Partition_Inter3d::AsDes() const 
+{
+  return myAsDes;
+}
+
+//=======================================================================
+//function : NewEdges
+//purpose  : 
+//=======================================================================
+
+TopTools_MapOfShape& Partition_Inter3d::NewEdges() 
+{
+  return myNewEdges;
+}
+
+//=======================================================================
+//function : Affiche
+//purpose  : 
+//=======================================================================
+
+void Partition_Inter3d::Affiche(const TopTools_ListOfShape& SetOfFaces) const
+{
+#ifdef DEB
+  char PSection[1024];
+  char *section=PSection;
+  Standard_Integer i = 0;
+  Standard_Real j=1;
+  TopTools_ListOfShape aList;
+  TopTools_ListIteratorOfListOfShape it;
+  for (it.Initialize(SetOfFaces); it.More(); it.Next()) {
+    const TopoDS_Shape& OS = it.Value();
+    aList=myAsDes->Descendant(OS);
+    MESSAGE ( " the number of items stored in the list " << j << " :  " << aList.Extent() )
+    j++;
+    TopTools_ListIteratorOfListOfShape itaList;
+    for (itaList.Initialize(aList); itaList.More(); itaList.Next()) {
+      const TopoDS_Shape& SS = itaList.Value();
+      i++;
+      sprintf(PSection,"section_%d",i);
+      DBRep::Set(section,SS);  
+    }
+  }
+#endif
+}
+
+//=======================================================================
+//function : SameDomain
+//purpose  : 
+//=======================================================================
+
+const TopTools_ListOfShape& Partition_Inter3d::SameDomain(const TopoDS_Face& F) const
+{
+  if (mySameDomainFM.IsBound( F ))
+    return mySameDomainFM (F);
+
+  static TopTools_ListOfShape emptyList;
+  return emptyList;
+}
+
+//=======================================================================
+//function : HasSameDomainF
+//purpose  : Return true if F has same domain faces
+//=======================================================================
+
+Standard_Boolean Partition_Inter3d::HasSameDomainF(const TopoDS_Shape& F) const
+{
+  return mySameDomainFM.IsBound( F );
+}
+
+//=======================================================================
+//function : IsSameDomain
+//purpose  : Return true if F1 and F2 are same domain faces
+//=======================================================================
+
+Standard_Boolean Partition_Inter3d::IsSameDomainF(const TopoDS_Shape& F1,
+						 const TopoDS_Shape& F2) const
+{
+  if (mySameDomainFM.IsBound( F1 )) {
+    TopTools_ListIteratorOfListOfShape it (mySameDomainFM( F1 ));
+    for (; it.More(); it.Next()) 
+      if (F2.IsSame( it.Value()))
+	return Standard_True;
+  }
+  return F1.IsSame( F2 );
+}
+
+//=======================================================================
+//function : ReplaceSameDomainV
+//purpose  : return same domain vertex of  V if it was replaced
+//           and make this vertex to be on E too, else return V
+//=======================================================================
+
+TopoDS_Vertex Partition_Inter3d::ReplaceSameDomainV(const TopoDS_Vertex& V,
+						    const TopoDS_Edge&   E) const
+{
+  TopoDS_Vertex SDV = V;
+  if (mySameDomainVM.IsBound( V )) {
+
+    TopoDS_Vertex V1,V2;
+    TopExp::Vertices(E,V1,V2);
+    Standard_Boolean isClosed = V1.IsSame( V2 ) && V.IsSame(V1);
+
+    SDV = TopoDS::Vertex( mySameDomainVM(V) );
+    Standard_Real tol = BRep_Tool::Tolerance( V );
+    BRep_Builder B;
+    SDV.Orientation( V.Orientation());
+
+    if (isClosed) {
+      Standard_Real f, l;
+      BRep_Tool::Range (E, f, l);
+      Standard_Boolean isFirst = IsEqual( BRep_Tool::Parameter(V,E), f );
+      B.UpdateVertex(SDV, (isFirst ? f : l), E, tol);
+      SDV.Reverse();
+      B.UpdateVertex(SDV, (isFirst ? l : f), E, tol);
+    }
+    else
+      B.UpdateVertex (SDV, BRep_Tool::Parameter(V,E), E, tol);
+      
+  }
+  return SDV;
+}
+
+//=======================================================================
+//function : SectionEdgesAD
+//purpose  : 
+//=======================================================================
+
+Handle(BRepAlgo_AsDes) Partition_Inter3d::SectionEdgesAD() const
+{
+  return mySectionEdgesAD;
+}
+
+//=======================================================================
+//function : IsSectionEdge
+//purpose  : return True if  E  is  an  edge  of  a face and it
+//           intersects an other face
+//=======================================================================
+
+Standard_Boolean
+  Partition_Inter3d::IsSectionEdge(const TopoDS_Edge& E) const
+{
+  return mySectionEdgesAD->HasAscendant(E);
+}
+
+//=======================================================================
+//function : HasSectionEdge
+//purpose  : return True if an  edge  of  F intersects an other
+//           face or F is intersected by edge of an other face
+//=======================================================================
+
+Standard_Boolean
+  Partition_Inter3d::HasSectionEdge(const TopoDS_Face& F) const
+{
+  return mySectionEdgesAD->HasDescendant(F);
+}
+
+//=======================================================================
+//function : IsSplitOn
+//purpose  : return True if NewE is split of OldE on F
+//=======================================================================
+
+Standard_Boolean
+  Partition_Inter3d::IsSplitOn(const TopoDS_Edge& NewE,
+			       const TopoDS_Edge& OldE,
+			       const TopoDS_Face& F) const
+{
+  if (! mySectionEdgesAD->HasDescendant(F))
+    return Standard_False;
+
+  TopTools_ListIteratorOfListOfShape itE ( mySectionEdgesAD->Descendant(F) );
+  for ( ; itE.More(); itE.Next()) {
+    if ( itE.Value().ShapeType() != TopAbs_EDGE ||
+	! OldE.IsSame ( itE.Value() ))
+      continue;
+    // an edge encountered, its vertices and a split come next
+    itE.Next();
+    if (!itE.More()) break;
+    const TopoDS_Shape& V3 = itE.Value();
+    if (V3.ShapeType() != TopAbs_VERTEX) continue;
+    itE.Next();
+    if (!itE.More()) break;
+    const TopoDS_Shape& V4 = itE.Value();
+    if (V4.ShapeType() != TopAbs_VERTEX) continue;
+
+    TopoDS_Vertex V1, V2;
+    TopExp::Vertices( OldE, V1, V2);
+    
+    if ( V1.IsSame(V2) &&
+	(V1.IsSame(V3) || V1.IsSame(V4)) ) {
+      // closed old edge; use the split for the test 
+      itE.Next();
+      if (!itE.More()) break;
+      const TopoDS_Edge& split = TopoDS::Edge( itE.Value() );
+      // check distance at middle point of NewE
+      Standard_Real f1,l1, f2,l2;
+      Handle(Geom2d_Curve) PC1 = BRep_Tool::CurveOnSurface( split, F ,f1,l1);
+      if (!PC1.IsNull()) {
+	Handle(Geom2d_Curve) PC2 = BRep_Tool::CurveOnSurface(NewE, F ,f2,l2);
+	gp_Pnt2d P = PC2->Value( 0.5*(f2+l2) );
+	Geom2dAPI_ProjectPointOnCurve proj (P, PC1, f1, l1);
+	if (proj.NbPoints() &&
+	    proj.LowerDistance() <= Precision::Confusion())
+	  return Standard_True;
+      }
+      else {
+        Handle(Geom_Curve) C1 = BRep_Tool::Curve( split ,f1,l1);
+	Handle(Geom_Curve) C2 = BRep_Tool::Curve( NewE  ,f2,l2);
+	gp_Pnt P = C2->Value( 0.5*(f2+l2) );
+	GeomAPI_ProjectPointOnCurve proj (P, C1, f1, l1);
+	if (proj.NbPoints() &&
+	    proj.LowerDistance() <= Precision::Confusion())
+	  return Standard_True;
+      }
+    }
+    else {
+      Standard_Real u3 = BRep_Tool::Parameter( TopoDS::Vertex(V3), OldE);
+      Standard_Real u4 = BRep_Tool::Parameter( TopoDS::Vertex(V4), OldE);
+
+      Standard_Real f,l, u;
+      BRep_Tool::Range( NewE, f,l);
+      u = 0.5*(f+l);
+      f = Min(u3,u4);
+      l = Max(u3,u4);
+
+      if (u <= l && u >= f)
+        return Standard_True;
+    }
+  }
+  return Standard_False;
+}
+
+//=======================================================================
+//function : SectionEdgeFaces
+//purpose  : return faces cut by section edge
+//=======================================================================
+
+const TopTools_ListOfShape&
+  Partition_Inter3d::SectionEdgeFaces(const TopoDS_Edge& SecE) const
+{
+  return mySectionEdgesAD->Ascendant( SecE );
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Inter3d.hxx b/contrib/Netgen/libsrc/occ/Partition_Inter3d.hxx
new file mode 100644
index 0000000000000000000000000000000000000000..d8be2c59837514d5a5dd8cd0b5846ab8513a4654
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Inter3d.hxx
@@ -0,0 +1,143 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Inter3d.hxx
+//  Module : GEOM
+
+#ifndef _Partition_Inter3d_HeaderFile
+#define _Partition_Inter3d_HeaderFile
+
+#ifndef _Handle_BRepAlgo_AsDes_HeaderFile
+#include <Handle_BRepAlgo_AsDes.hxx>
+#endif
+#ifndef _TopTools_DataMapOfShapeListOfShape_HeaderFile
+#include <TopTools_DataMapOfShapeListOfShape.hxx>
+#endif
+#ifndef _TopTools_MapOfShape_HeaderFile
+#include <TopTools_MapOfShape.hxx>
+#endif
+#ifndef _TopTools_DataMapOfShapeShape_HeaderFile
+#include <TopTools_DataMapOfShapeShape.hxx>
+#endif
+#ifndef _Standard_Boolean_HeaderFile
+#include <Standard_Boolean.hxx>
+#endif
+class BRepAlgo_AsDes;
+class TopTools_ListOfShape;
+class TopTools_DataMapOfShapeShape;
+class TopoDS_Face;
+class TopTools_MapOfShape;
+class TopoDS_Shape;
+class TopoDS_Vertex;
+class TopoDS_Edge;
+
+
+#ifndef _Standard_HeaderFile
+#include <Standard.hxx>
+#endif
+#ifndef _Standard_Macro_HeaderFile
+#include <Standard_Macro.hxx>
+#endif
+
+class Partition_Inter3d  {
+
+public:
+
+   void* operator new(size_t,void* anAddress) 
+   {
+      return anAddress;
+   }
+   void* operator new(size_t size) 
+   { 
+      return Standard::Allocate(size); 
+   }
+   void  operator delete(void *anAddress) 
+   { 
+      if (anAddress) Standard::Free((Standard_Address&)anAddress); 
+   }
+   // Methods PUBLIC
+   // 
+   Partition_Inter3d();
+   Partition_Inter3d(const Handle(BRepAlgo_AsDes)& AsDes);
+   void CompletPart3d(const TopTools_ListOfShape& SetOfFaces1,const TopTools_DataMapOfShapeShape& FaceShapeMap) ;
+   void FacesPartition(const TopoDS_Face& F1,const TopoDS_Face& F2) ;
+   Standard_Boolean IsDone(const TopoDS_Face& F1,const TopoDS_Face& F2) const;
+   TopTools_MapOfShape& TouchedFaces() ;
+   Handle_BRepAlgo_AsDes AsDes() const;
+   TopTools_MapOfShape& NewEdges() ;
+   Standard_Boolean HasSameDomainF(const TopoDS_Shape& F) const;
+   Standard_Boolean IsSameDomainF(const TopoDS_Shape& F1,const TopoDS_Shape& F2) const;
+   const TopTools_ListOfShape& SameDomain(const TopoDS_Face& F) const;
+   TopoDS_Vertex ReplaceSameDomainV(const TopoDS_Vertex& V,const TopoDS_Edge& E) const;
+   Handle_BRepAlgo_AsDes SectionEdgesAD() const;
+   Standard_Boolean IsSectionEdge(const TopoDS_Edge& E) const;
+   Standard_Boolean HasSectionEdge(const TopoDS_Face& F) const;
+   Standard_Boolean IsSplitOn(const TopoDS_Edge& NewE,const TopoDS_Edge& OldE,const TopoDS_Face& F) const;
+   const TopTools_ListOfShape& SectionEdgeFaces(const TopoDS_Edge& SecE) const;
+
+
+
+
+
+protected:
+
+   // Methods PROTECTED
+   // 
+
+
+   // Fields PROTECTED
+   //
+
+
+private: 
+
+   // Methods PRIVATE
+   // 
+   void Inter3D(const TopoDS_Face& F1,const TopoDS_Face& F2,TopTools_ListOfShape& LInt) ;
+   void StorePart3d(const TopoDS_Face& F1,const TopoDS_Face& F2,const TopTools_ListOfShape& LInt1) ;
+   void SetDone(const TopoDS_Face& F1,const TopoDS_Face& F2) ;
+   void Affiche(const TopTools_ListOfShape& SetOfFaces) const;
+
+
+   // Fields PRIVATE
+   //
+   Handle_BRepAlgo_AsDes myAsDes;
+   TopTools_DataMapOfShapeListOfShape myDone;
+   TopTools_MapOfShape myTouched;
+   TopTools_MapOfShape myNewEdges;
+   Handle_BRepAlgo_AsDes mySectionEdgesAD;
+   TopTools_DataMapOfShapeListOfShape mySameDomainFM;
+   TopTools_DataMapOfShapeShape mySameDomainVM;
+
+
+};
+
+
+
+
+
+// other Inline functions and methods (like "C++: function call" methods)
+//
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Inter3d.ixx b/contrib/Netgen/libsrc/occ/Partition_Inter3d.ixx
new file mode 100644
index 0000000000000000000000000000000000000000..0775cc99c9259f7e8b85677f916546c9fd6aca20
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Inter3d.ixx
@@ -0,0 +1,31 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Inter3d.ixx
+//  Module : GEOM
+
+#include "Partition_Inter3d.jxx"
+
+ 
+
+
diff --git a/contrib/Netgen/libsrc/occ/Partition_Inter3d.jxx b/contrib/Netgen/libsrc/occ/Partition_Inter3d.jxx
new file mode 100644
index 0000000000000000000000000000000000000000..5804ba81e831c0a636bd6a8965e01f4011455ed6
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Inter3d.jxx
@@ -0,0 +1,53 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Inter3d.jxx
+//  Module : GEOM
+
+#ifndef _BRepAlgo_AsDes_HeaderFile
+#include <BRepAlgo_AsDes.hxx>
+#endif
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _TopTools_DataMapOfShapeShape_HeaderFile
+#include <TopTools_DataMapOfShapeShape.hxx>
+#endif
+#ifndef _TopoDS_Face_HeaderFile
+#include <TopoDS_Face.hxx>
+#endif
+#ifndef _TopTools_MapOfShape_HeaderFile
+#include <TopTools_MapOfShape.hxx>
+#endif
+#ifndef _TopoDS_Shape_HeaderFile
+#include <TopoDS_Shape.hxx>
+#endif
+#ifndef _TopoDS_Vertex_HeaderFile
+#include <TopoDS_Vertex.hxx>
+#endif
+#ifndef _TopoDS_Edge_HeaderFile
+#include <TopoDS_Edge.hxx>
+#endif
+#ifndef _Partition_Inter3d_HeaderFile
+#include "Partition_Inter3d.hxx"
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop.cxx b/contrib/Netgen/libsrc/occ/Partition_Loop.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..41432907280b325fdf56e73eed40766278e65d78
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop.cxx
@@ -0,0 +1,473 @@
+#ifdef OCCGEOMETRY
+
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Loop.cxx
+//  Author : Benedicte MARTIN
+//  Module : GEOM
+//  $Header: /cvs/netgen/netgen/libsrc/occ/Partition_Loop.cxx,v 1.6 2008/03/31 14:20:28 wabro Exp $
+
+//using namespace std;
+#include <cstdio>
+#include <climits>
+
+#include "Partition_Loop.ixx"
+
+#include "utilities.h"
+
+#include <BRep_Builder.hxx>
+#include <BRepAlgo_FaceRestrictor.hxx>
+#include <BRep_Tool.hxx>
+
+#include <Geom2d_Curve.hxx>
+#include <Geom_Surface.hxx>
+
+#include <TopTools_SequenceOfShape.hxx>
+#include <TopTools_ListIteratorOfListOfShape.hxx>
+#include <TopTools_MapOfShape.hxx>
+#include <TopTools_MapIteratorOfMapOfShape.hxx>
+#include <TopTools_MapOfOrientedShape.hxx>
+#include <TopTools_DataMapOfShapeShape.hxx>
+#include <TopTools_DataMapIteratorOfDataMapOfShapeListOfShape.hxx>
+
+#include <gp_Pnt.hxx>
+#include <gp_Pnt2d.hxx>
+
+#include <TopoDS.hxx>
+#include <TopoDS_Vertex.hxx>
+#include <TopoDS_Wire.hxx>
+#include <TopoDS_Iterator.hxx>
+
+#include <Precision.hxx>
+#include <BRep_TVertex.hxx>
+#include <BRep_TEdge.hxx>
+
+#include <TopExp.hxx>
+#include <TopExp_Explorer.hxx>
+
+static char* name = new char[100];
+static int nbe = 0;
+
+//=======================================================================
+//function : Partition_Loop
+//purpose  : 
+//=======================================================================
+Partition_Loop::Partition_Loop()
+{
+}
+
+//=======================================================================
+//function : Init
+//purpose  : 
+//=======================================================================
+void Partition_Loop::Init(const TopoDS_Face& F)
+{
+  myConstEdges.Clear(); 
+  myNewWires  .Clear();
+  myNewFaces  .Clear();
+  myFace = F;
+}
+
+//=======================================================================
+//function : AddConstEdge
+//purpose  : 
+//=======================================================================
+void Partition_Loop::AddConstEdge (const TopoDS_Edge& E)
+{
+  myConstEdges.Append(E);
+}
+
+
+//=======================================================================
+//function : FindDelta
+//purpose  : 
+//=======================================================================
+static Standard_Real FindDelta(TopTools_ListOfShape& LE,
+			       const TopoDS_Face& F)
+{
+  Standard_Real dist, f, l;
+  Standard_Real d = Precision::Infinite();
+  TopTools_ListIteratorOfListOfShape itl;
+
+  for ( itl.Initialize(LE); itl.More(); itl.Next()) {
+    const TopoDS_Edge& E = TopoDS::Edge(itl.Value());
+    Handle(Geom2d_Curve) C = BRep_Tool::CurveOnSurface(E,F,f,l);
+    gp_Pnt2d p = C->Value(f);
+    gp_Pnt2d pp = C->Value(l);
+    Standard_Real d1 = p.Distance(pp);
+    if (d1<d) { d=d1;}
+  }
+  dist = d ;
+  return dist;
+}
+
+//=======================================================================
+//function : SelectEdge
+//purpose  : Find the edge <NE> connected <CE> by the vertex <CV> in the list <LE>.
+//           <NE> Is erased  of the list. If <CE> is too in the list <LE> 
+//			 with the same orientation, it's erased of the list 
+//=======================================================================
+static Standard_Boolean  SelectEdge(const TopoDS_Face&    F,
+				    const TopoDS_Edge&    CE,
+				    const TopoDS_Vertex&  CV,
+				    TopoDS_Edge&          NE,
+				    TopTools_ListOfShape& LE)
+{
+  TopTools_ListIteratorOfListOfShape itl;
+  NE.Nullify();
+  for ( itl.Initialize(LE); itl.More(); itl.Next()) {
+    if (itl.Value().IsEqual(CE)) {
+      LE.Remove(itl);
+      break;
+    }
+  }
+
+  if (LE.Extent() > 1) {
+    //--------------------------------------------------------------
+    // Several possible edges.   
+    // - Test the edges differents of CE 
+    //--------------------------------------------------------------
+    Standard_Real   cf, cl, f, l;
+    TopoDS_Face FForward = F;
+    Handle(Geom2d_Curve) Cc, C;
+    FForward.Orientation(TopAbs_FORWARD);
+			
+    Cc = BRep_Tool::CurveOnSurface(CE,FForward,cf,cl);
+    Standard_Real dist,distmin  = 100*BRep_Tool::Tolerance(CV);
+    Standard_Real uc,u;
+    if (CE.Orientation () == TopAbs_FORWARD) uc = cl;
+    else                                     uc = cf;
+
+    gp_Pnt2d P2,PV = Cc->Value(uc); 
+
+    Standard_Real delta = FindDelta(LE,FForward);
+
+    for ( itl.Initialize(LE); itl.More(); itl.Next()) {
+      const TopoDS_Edge& E = TopoDS::Edge(itl.Value());
+      if (!E.IsSame(CE)) {
+	C = BRep_Tool::CurveOnSurface(E,FForward,f,l);
+	if (E.Orientation () == TopAbs_FORWARD) u = f;
+	else                                    u = l;
+	P2 = C->Value(u);
+	dist = PV.Distance(P2);
+	if (dist <= distmin){
+	  distmin = dist;
+	}
+				
+      }
+    }
+
+    Standard_Real anglemax = - PI;
+    TopoDS_Edge   SelectedEdge;	
+    for ( itl.Initialize(LE); itl.More(); itl.Next()) {
+      const TopoDS_Edge& E = TopoDS::Edge(itl.Value());
+      if (!E.IsSame(CE)) {
+	C = BRep_Tool::CurveOnSurface(E,FForward,f,l);
+	if (E.Orientation () == TopAbs_FORWARD) u = f;
+	else                                    u = l;
+	P2 = C->Value(u);
+	dist = PV.Distance(P2);
+	if (dist <= distmin + (1./3)*delta){ 
+	  gp_Pnt2d PC, P;
+	  gp_Vec2d CTg1, CTg2, Tg1, Tg2;
+	  Cc->D2(uc, PC, CTg1, CTg2);
+	  C->D2(u, P, Tg1, Tg2);
+
+	  Standard_Real angle;
+
+	  if (CE.Orientation () == TopAbs_REVERSED && E.Orientation () == TopAbs_FORWARD) {
+	    angle = CTg1.Angle(Tg1.Reversed());
+	  }
+	  else if (CE.Orientation () == TopAbs_FORWARD && E.Orientation () == TopAbs_REVERSED) {
+	    angle = (CTg1.Reversed()).Angle(Tg1);
+	  }
+	  else if (CE.Orientation () == TopAbs_REVERSED && E.Orientation () == TopAbs_REVERSED) {
+	    angle = CTg1.Angle(Tg1);
+	  }
+	  else if (CE.Orientation () == TopAbs_FORWARD && E.Orientation () == TopAbs_FORWARD) {
+	    angle = (CTg1.Reversed()).Angle(Tg1.Reversed());
+	  }
+	  if (angle >= anglemax) {
+	    anglemax = angle ;
+	    SelectedEdge = E;	
+	  }
+	}
+      }
+    }
+    for ( itl.Initialize(LE); itl.More(); itl.Next()) {
+      const TopoDS_Edge& E = TopoDS::Edge(itl.Value());
+      if (E.IsEqual(SelectedEdge)) {
+	NE = TopoDS::Edge(E);
+	LE.Remove(itl);
+	break;
+      }
+    }					
+  }
+  else if (LE.Extent() == 1) {
+    NE = TopoDS::Edge(LE.First());
+    LE.RemoveFirst();
+  }
+  else {
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
+//=======================================================================
+//function : SamePnt2d
+//purpose  : 
+//=======================================================================
+static Standard_Boolean  SamePnt2d(TopoDS_Vertex  V,
+				   TopoDS_Edge&   E1,
+				   TopoDS_Edge&   E2,
+				   TopoDS_Face&   F)
+{
+  Standard_Real   f1,f2,l1,l2;
+  gp_Pnt2d        P1,P2;
+  TopoDS_Shape aLocalF = F.Oriented(TopAbs_FORWARD);
+  TopoDS_Face FF = TopoDS::Face(aLocalF);
+  Handle(Geom2d_Curve) C1 = BRep_Tool::CurveOnSurface(E1,FF,f1,l1);  
+  Handle(Geom2d_Curve) C2 = BRep_Tool::CurveOnSurface(E2,FF,f2,l2);  
+  if (E1.Orientation () == TopAbs_FORWARD) P1 = C1->Value(f1);
+  else                                     P1 = C1->Value(l1);
+  
+  if (E2.Orientation () == TopAbs_FORWARD) P2 = C2->Value(l2);
+  else                                     P2 = C2->Value(f2);
+  Standard_Real Tol  = 100*BRep_Tool::Tolerance(V);
+  Standard_Real Dist = P1.Distance(P2);
+  return Dist < Tol; 
+}
+
+//=======================================================================
+//function : PurgeNewEdges
+//purpose  : 
+//=======================================================================
+static void  PurgeNewEdges(TopTools_ListOfShape& ConstEdges,
+			   const TopTools_MapOfOrientedShape&          UsedEdges)
+{
+  TopTools_ListIteratorOfListOfShape it(ConstEdges);
+  while ( it.More()) {
+    const TopoDS_Shape& NE = it.Value();
+    if (!UsedEdges.Contains(NE)) {
+      ConstEdges.Remove(it);
+    }
+    else {
+      it.Next();
+    }
+  }  
+}
+
+//=======================================================================
+//function : StoreInMVE
+//purpose  : 
+//=======================================================================
+static void StoreInMVE (const TopoDS_Face& F,
+			TopoDS_Edge& E,
+			TopTools_DataMapOfShapeListOfShape& MVE )
+
+{ 
+  TopoDS_Vertex V1, V2;
+  TopTools_ListOfShape Empty;
+
+  TopExp::Vertices(E,V1,V2);
+  if (!MVE.IsBound(V1)) {
+    MVE.Bind(V1,Empty);
+  }
+  MVE(V1).Append(E);
+	
+  if (!MVE.IsBound(V2)) {
+    MVE.Bind(V2,Empty);
+  }
+  MVE(V2).Append(E);
+}
+
+//=======================================================================
+//function : Perform
+//purpose  : 
+//=======================================================================
+void Partition_Loop::Perform()
+{
+
+  TopTools_DataMapOfShapeListOfShape MVE;
+  TopTools_DataMapIteratorOfDataMapOfShapeListOfShape Mapit, Mapit1;  
+  TopTools_ListIteratorOfListOfShape                  itl;
+  TopoDS_Vertex                                       V1,V2;
+
+  //-----------------------------------
+  // Construction map vertex => edges
+  //-----------------------------------
+  for (itl.Initialize(myConstEdges); itl.More(); itl.Next()) {
+    TopoDS_Edge& E = TopoDS::Edge(itl.Value());
+    StoreInMVE(myFace,E,MVE);
+  }
+
+  //----------------------------------------------
+  // Construction of all the wires and of all the new faces. 
+  //----------------------------------------------
+  TopTools_MapOfOrientedShape UsedEdges;
+
+  while (!MVE.IsEmpty()) {
+    TopoDS_Vertex    VF,CV;
+    TopoDS_Edge      CE,NE,EF;
+    TopoDS_Wire      NW;
+    BRep_Builder     B;
+    Standard_Boolean End= Standard_False;
+
+    B.MakeWire(NW);
+    //--------------------------------
+    // EF first edge.
+    //--------------------------------
+    Mapit.Initialize(MVE);
+    EF = CE = TopoDS::Edge(Mapit.Value().First());
+
+    TopExp::Vertices(CE,V1,V2);
+    //--------------------------------
+    // VF first vertex 
+    //--------------------------------
+    if (CE.Orientation() == TopAbs_FORWARD) { 
+      CV = VF = V1;
+    }
+    else  { 
+      CV = VF = V2;
+    }
+    if (!MVE.IsBound(CV)) continue;
+    for ( itl.Initialize(MVE(CV)); itl.More(); itl.Next()) {
+      if (itl.Value().IsEqual(CE)) {
+	MVE(CV).Remove(itl);
+	break;
+      }
+    }
+
+    int i = 0;
+    while (!End) { 
+      //-------------------------------
+      // Construction of a wire.
+      //-------------------------------
+      TopExp::Vertices(CE,V1,V2);
+      if (!CV.IsSame(V1)) CV = V1; else CV = V2; 
+      B.Add (NW,CE);
+      UsedEdges.Add(CE);
+
+      //--------------
+      // stop test
+      //--------------			
+      if (!MVE.IsBound(CV) || MVE(CV).IsEmpty() || CV.IsSame(VF) ) {
+	if (CV.IsSame(VF)) {
+	  if (MVE(CV).Extent() == 1 ) MVE.UnBind(CV);
+	  else {
+	    for ( itl.Initialize(MVE(CV)); itl.More(); itl.Next()) {
+	      if (itl.Value().IsEqual(CE)) {
+		MVE(CV).Remove(itl);
+		break;
+	      }
+	    }
+	  }
+	}
+	End=Standard_True;
+      } 
+
+      //--------------
+      // select edge
+      //--------------
+      else {
+	Standard_Boolean find = SelectEdge(myFace,CE,CV,NE,MVE(CV));
+	if (find) {
+	  CE=NE;
+	  if (MVE(CV).IsEmpty()) MVE.UnBind(CV);
+	  if (CE.IsNull() ) {
+	    MESSAGE ( " CE is  NULL !!! " )
+	    End=Standard_True;
+	  }
+	}
+	else {
+	  MESSAGE ( " edge doesn't exist " )
+	  End=Standard_True;
+	}
+      }
+    }
+
+    //-----------------------------
+    // Test if the wire is closed  
+    //-----------------------------
+    if (VF.IsSame(CV) && SamePnt2d(VF,EF,CE,myFace)) {
+    }
+    else{
+      MESSAGE ( "wire not closed" )
+    }
+    myNewWires.Append (NW);			
+  }
+
+  PurgeNewEdges(myConstEdges,UsedEdges);
+
+}
+
+
+//=======================================================================
+//function : NewWires
+//purpose  : 
+//=======================================================================
+const TopTools_ListOfShape&  Partition_Loop::NewWires() const 
+{  
+  return myNewWires;
+}
+
+//=======================================================================
+//function : NewFaces
+//purpose  : 
+//=======================================================================
+const TopTools_ListOfShape&  Partition_Loop::NewFaces() const 
+{  
+  return myNewFaces;
+}
+ 
+//=======================================================================
+//function : WiresToFaces
+//purpose  : 
+//=======================================================================
+void  Partition_Loop::WiresToFaces() 
+{  
+  if (!myNewWires.IsEmpty()) {
+    BRepAlgo_FaceRestrictor FR;
+
+    TopAbs_Orientation OriF = myFace.Orientation();
+    TopoDS_Shape aLocalS = myFace.Oriented(TopAbs_FORWARD);
+
+    FR.Init (TopoDS::Face(aLocalS),Standard_False);
+    TopTools_ListIteratorOfListOfShape it(myNewWires);
+    for (; it.More(); it.Next()) {
+      FR.Add(TopoDS::Wire(it.Value()));
+    }
+
+    FR.Perform();
+    
+    if (FR.IsDone()) {
+      for (; FR.More(); FR.Next()) {
+	myNewFaces.Append(FR.Current().Oriented(OriF));
+      }
+    }
+  }
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop.hxx b/contrib/Netgen/libsrc/occ/Partition_Loop.hxx
new file mode 100644
index 0000000000000000000000000000000000000000..56e05e260bf5c0486f9e66943f87090800fbf52d
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop.hxx
@@ -0,0 +1,118 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Loop.hxx
+//  Module : GEOM
+
+#ifndef _Partition_Loop_HeaderFile
+#define _Partition_Loop_HeaderFile
+
+#ifndef _TopoDS_Face_HeaderFile
+#include <TopoDS_Face.hxx>
+#endif
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _TopTools_DataMapOfShapeListOfShape_HeaderFile
+#include <TopTools_DataMapOfShapeListOfShape.hxx>
+#endif
+class TopoDS_Face;
+class TopoDS_Edge;
+class TopTools_ListOfShape;
+
+
+#ifndef _Standard_HeaderFile
+#include <Standard.hxx>
+#endif
+#ifndef _Standard_Macro_HeaderFile
+#include <Standard_Macro.hxx>
+#endif
+
+class Partition_Loop  {
+
+public:
+
+   inline void* operator new(size_t,void* anAddress) 
+   {
+      return anAddress;
+   }
+   inline void* operator new(size_t size) 
+   { 
+      return Standard::Allocate(size); 
+   }
+   inline void  operator delete(void *anAddress) 
+   { 
+      if (anAddress) Standard::Free((Standard_Address&)anAddress); 
+   }
+   //    inline void  operator delete(void *anAddress, size_t size) 
+   //      { 
+   //        if (anAddress) Standard::Free((Standard_Address&)anAddress,size); 
+   //      }
+   // Methods PUBLIC
+   // 
+   Partition_Loop();
+   void Init(const TopoDS_Face& F) ;
+   void AddConstEdge(const TopoDS_Edge& E) ;
+   void Perform() ;
+   const TopTools_ListOfShape& NewWires() const;
+   void WiresToFaces() ;
+   const TopTools_ListOfShape& NewFaces() const;
+
+
+
+
+protected:
+
+   // Methods PROTECTED
+   // 
+
+
+   // Fields PROTECTED
+   //
+
+
+private: 
+
+   // Methods PRIVATE
+   // 
+
+
+   // Fields PRIVATE
+   //
+   TopoDS_Face myFace;
+   TopTools_ListOfShape myConstEdges;
+   TopTools_ListOfShape myNewWires;
+   TopTools_ListOfShape myNewFaces;
+
+
+};
+
+
+
+
+
+// other inline functions and methods (like "C++: function call" methods)
+//
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop.ixx b/contrib/Netgen/libsrc/occ/Partition_Loop.ixx
new file mode 100644
index 0000000000000000000000000000000000000000..1c40e72540c987da0b82df3b8754ea72add677d1
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop.ixx
@@ -0,0 +1,31 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Loop.ixx
+//  Module : GEOM
+
+#include "Partition_Loop.jxx"
+
+ 
+
+
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop.jxx b/contrib/Netgen/libsrc/occ/Partition_Loop.jxx
new file mode 100644
index 0000000000000000000000000000000000000000..dd86f05c3d83bea2f5f581673fa79f67bc153a14
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop.jxx
@@ -0,0 +1,41 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Loop.jxx
+//  Module : GEOM
+
+#ifndef _TopoDS_Face_HeaderFile
+#include <TopoDS_Face.hxx>
+#endif
+#ifndef _TopoDS_Edge_HeaderFile
+#include <TopoDS_Edge.hxx>
+#endif
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _TopTools_DataMapOfShapeShape_HeaderFile
+#include <TopTools_DataMapOfShapeShape.hxx>
+#endif
+#ifndef _Partition_Loop_HeaderFile
+#include "Partition_Loop.hxx"
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop2d.cxx b/contrib/Netgen/libsrc/occ/Partition_Loop2d.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..40872bd9305083c17251732dc3ffe7fffaa21beb
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop2d.cxx
@@ -0,0 +1,1145 @@
+#ifdef OCCGEOMETRY
+
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  CEA/DEN, EDF R& D
+//
+//
+//
+//  File   : Partition_Loop2d.cxx
+//  Author : Benedicte MARTIN
+//  Module : GEOM
+//  $Header: /cvs/netgen/netgen/libsrc/occ/Partition_Loop2d.cxx,v 1.6 2008/03/31 14:20:28 wabro Exp $
+
+//using namespace std;
+#include <climits>
+#include "Partition_Loop2d.ixx"
+
+#include "utilities.h"
+#include <stdio.h>
+
+#include <BRepAdaptor_Curve2d.hxx>
+#include <BRepAdaptor_Surface.hxx>
+#include <BRepAlgo_AsDes.hxx>
+#include <BRepAlgo_FaceRestrictor.hxx>
+#include <BRepOffset_DataMapOfShapeReal.hxx>
+#include <BRepTopAdaptor_FClass2d.hxx>
+#include <BRep_Builder.hxx>
+#include <BRep_Tool.hxx>
+#include <Geom2dInt_GInter.hxx>
+#include <Geom2d_Curve.hxx>
+#include <IntRes2d_IntersectionPoint.hxx>
+#include <Precision.hxx>
+#include <TColStd_MapOfInteger.hxx>
+#include <TColStd_SequenceOfReal.hxx>
+#include <TopExp.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopTools_DataMapIteratorOfDataMapOfShapeListOfShape.hxx>
+#include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
+#include <TopTools_DataMapOfShapeInteger.hxx>
+// #include <TopTools_DataMapOfShapeReal.hxx>    V6.5
+#include <TopTools_DataMapOfShapeShape.hxx>
+#include <TopTools_IndexedMapOfShape.hxx>
+#include <TopTools_ListIteratorOfListOfShape.hxx>
+#include <TopTools_MapIteratorOfMapOfShape.hxx>
+#include <TopTools_MapOfOrientedShape.hxx>
+#include <TopTools_MapOfShape.hxx>
+#include <TopTools_SequenceOfShape.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Iterator.hxx>
+#include <TopoDS_Vertex.hxx>
+#include <TopoDS_Wire.hxx>
+#include <gp_Pnt.hxx>
+#include <gp_Pnt2d.hxx>
+
+//=======================================================================
+//function : Partition_Loop2d
+//purpose  :
+//=======================================================================
+
+Partition_Loop2d::Partition_Loop2d()
+{
+}
+
+//=======================================================================
+//function : Init
+//purpose  : Init with <F> the set of edges must have
+//           pcurves on <F>.
+//=======================================================================
+
+void Partition_Loop2d::Init(const TopoDS_Face& F)
+{
+  myConstEdges.Clear();
+  myNewWires  .Clear();
+  myNewFaces  .Clear();
+  myFace = F;
+  myFaceOri = myFace.Orientation();
+  myFace.Orientation( TopAbs_FORWARD );
+}
+
+//=======================================================================
+//function : AddConstEdge
+//purpose  : Add <E> as unique edge in the result.
+//=======================================================================
+
+void Partition_Loop2d::AddConstEdge (const TopoDS_Edge& E)
+{
+#ifdef DEB
+  Standard_Real f,l;
+  Handle(Geom2d_Curve) pc = BRep_Tool::CurveOnSurface( E, myFace, f,l);
+  if (pc.IsNull()) {
+    INFOS( "AddConstEdge(): EDGE W/O PCURVE on FACE");
+  } else
+#endif
+  {
+    myConstEdges.Append(E);
+  }
+}
+
+void Partition_Loop2d::AddSectionEdge (const TopoDS_Edge& E)
+{
+#ifdef DEB
+  Standard_Real f,l;
+  Handle(Geom2d_Curve) pc = BRep_Tool::CurveOnSurface( E, myFace, f,l);
+  if (pc.IsNull())
+    pc = BRep_Tool::CurveOnSurface( E, myFace, f,l);
+  gp_Vec2d Tg1;
+  gp_Pnt2d PC;
+  pc->D1(0.5*(f+l), PC, Tg1);
+  if (Tg1.Magnitude()  <= gp::Resolution()) {
+    MESSAGE ("");
+  }
+  if (pc.IsNull()) {
+    INFOS( "AddConstEdge(): EDGE W/O PCURVE on FACE");
+  } else
+#endif
+  {
+    myConstEdges.Append(E);
+    myConstEdges.Append(E.Reversed());
+    mySectionEdges.Add( E );
+  }
+}
+
+//=======================================================================
+//function : preciseU
+//purpose  : find u such that the 3D point on theE is just out of tolerance
+//           of theV
+//=======================================================================
+
+static Standard_Real preciseU (const BRepAdaptor_Surface&  theSurf,
+                               const TopoDS_Edge&          theE,
+                               const TopoDS_Vertex&        theV,
+                               const Handle(Geom2d_Curve)& theC,
+                               const Standard_Boolean      theFirstEnd)
+{
+  Standard_Boolean isForward = ( theE.Orientation () == TopAbs_FORWARD );
+  if (theFirstEnd) isForward = !isForward;
+
+  // find the first point in 2d and 3d
+  Standard_Real f,l;
+  BRep_Tool::Range( theE, f, l );
+  Standard_Real u0 = isForward ? l : f;
+  gp_Pnt2d aP2d0 = theC->Value( u0 );
+  gp_Pnt aPnt0 = theSurf.Value( aP2d0.X(), aP2d0.Y() );
+
+  // shift in 2d and 3d
+  Standard_Real du = ( l - f ) / 100, du3d = 0;
+  if (isForward)
+    du = -du;
+
+  // target parameter
+  Standard_Real u;
+
+  while (du3d < ::RealSmall())
+  {
+    // u for test
+    u = u0 + du;
+    du *= 10; // for the next iteration: increase du untill du3d is large enough
+
+    // find out how u is far from u0 in 3D
+    gp_Pnt2d aP2d  = theC->Value( u );
+    gp_Pnt aPnt  = theSurf.Value( aP2d.X(), aP2d.Y() );
+    du3d = aPnt0.Distance( aPnt );
+  }
+
+  // find u such that the 3D point is just out of tolerance of theV
+  Standard_Real tolV = BRep_Tool::Tolerance( theV ) + Precision::Confusion();
+  u = u0 + du * tolV / du3d;
+
+  // check that u is within the range
+  if ( isForward ? (u < f) : (u > l) )
+    u = u0 + du;
+
+  return u;
+}
+
+//=======================================================================
+//function : SelectEdge
+//purpose  : Find in the list <LE> the edge <NE> connected with <CE> by
+//           the vertex <CV>.
+//           <NE> is removed from the list. If <CE> is in <LE>
+//           with the same orientation, it's removed from the list
+//=======================================================================
+
+static Standard_Boolean  SelectEdge(const BRepAdaptor_Surface& Surf,
+                                    const TopoDS_Edge&    CE,
+                                    const TopoDS_Vertex&  CV,
+                                    TopoDS_Edge&          NE,
+                                    const TopTools_ListOfShape& LE)
+{
+  NE.Nullify();
+
+  if (LE.Extent() > 1) {
+    //--------------------------------------------------------------
+    // Several possible edges.
+    // - Test the edges differents of CE
+    //--------------------------------------------------------------
+    TopoDS_Face FForward = Surf.Face();
+    TopoDS_Edge aPrevNE;
+
+    gp_Vec2d CTg1, Tg1, CTg2, Tg2;
+    gp_Pnt2d PC, P;
+
+    Standard_Real f, l;
+    Handle(Geom2d_Curve) Cc, C;
+    Cc = BRep_Tool::CurveOnSurface(CE,FForward,f,l);
+
+    Standard_Boolean isForward = ( CE.Orientation () == TopAbs_FORWARD );
+    Standard_Real uc, u, du = Precision::PConfusion();
+    uc = isForward ? ( l - du ) : ( f + du );
+    Cc->D1(uc, PC, CTg1);
+    if (!isForward) CTg1.Reverse();
+
+    Standard_Real anglemin = 3 * PI, tolAng = 1.e-8;
+
+    // select an edge whose first derivative is most left of CTg1
+    // ie an angle between Tg1 and CTg1 is least
+    TopTools_ListIteratorOfListOfShape itl;
+    for ( itl.Initialize(LE); itl.More(); itl.Next()) {
+      const TopoDS_Edge& E = TopoDS::Edge(itl.Value());
+      if (E.IsSame(CE))
+        continue;
+      if (! CV.IsSame( TopExp::FirstVertex( E, Standard_True )))
+        continue;
+
+      isForward = ( E.Orientation () == TopAbs_FORWARD );
+
+      // get E curve
+      C = BRep_Tool::CurveOnSurface(E,FForward,f,l);
+      // get the first derivative Tg1
+      u = isForward ? ( f + du ) : ( l - du );
+      C->D1(u, P, Tg1);
+      if (!isForward) Tg1.Reverse();
+
+      // -PI < angle < PI
+      Standard_Real angle = Tg1.Angle(CTg1);
+
+      if (PI - Abs(angle) <= tolAng)
+      {
+        // an angle is too close to PI; assure that an angle sign really
+        // reflects an edge position: +PI - an edge is worst,
+        // -PI - an edge is best.
+        u = preciseU( Surf, CE, CV, Cc, Standard_False);
+        gp_Vec2d CTg;
+        Cc->D1(u, PC, CTg);
+        if (CE.Orientation() == TopAbs_REVERSED) CTg.Reverse();
+
+        u = preciseU( Surf, E, CV, C, Standard_True);
+        C->D1(u, P, Tg1);
+        if (!isForward) Tg1.Reverse();
+
+        angle = Tg1.Angle(CTg);
+      }
+
+      Standard_Boolean isClose = ( Abs( angle - anglemin ) <= tolAng );
+      if (angle <= anglemin) {
+        if (isClose)
+          aPrevNE = NE;
+        else
+          aPrevNE.Nullify();
+        anglemin = angle ;
+        NE = E;
+      }
+      else
+        if (isClose)
+          aPrevNE = E;
+
+    }
+    if (!aPrevNE.IsNull()) {
+      // select one of close edges, the most left one.
+      Cc = BRep_Tool::CurveOnSurface( NE, FForward, f, l );
+      uc = preciseU( Surf, NE, CV, Cc, Standard_True);
+      Cc->D1(uc, PC, CTg1);
+      if (NE.Orientation() != TopAbs_FORWARD) CTg1.Reverse();
+      
+      u = preciseU( Surf, aPrevNE, CV, C, Standard_True);
+      C->D1(u, P, Tg1);
+      if (aPrevNE.Orientation() != TopAbs_FORWARD) Tg1.Reverse();
+
+      if ( Tg1.Angle(CTg1) < 0)
+        NE = aPrevNE;
+    }
+  }
+  else if (LE.Extent() == 1) {
+    NE = TopoDS::Edge(LE.First());
+  }
+  else {
+    return Standard_False;
+  }
+  return !NE.IsNull();
+}
+
+//=======================================================================
+//function : SamePnt2d
+//purpose  :
+//=======================================================================
+
+static Standard_Boolean  SamePnt2d(const TopoDS_Vertex& V1,
+                                   const TopoDS_Edge&   E1,
+                                   const TopoDS_Vertex& V2,
+                                   const TopoDS_Edge&   E2,
+                                   const TopoDS_Face&   F)
+{
+  Standard_Real   f1,f2,l1,l2;
+  Handle(Geom2d_Curve) C1 = BRep_Tool::CurveOnSurface(E1,F,f1,l1);
+  Handle(Geom2d_Curve) C2 = BRep_Tool::CurveOnSurface(E2,F,f2,l2);
+
+  gp_Pnt2d P1 = C1->Value( BRep_Tool::Parameter(V1,E1));
+  gp_Pnt2d P2 = C2->Value( BRep_Tool::Parameter(V2,E2));
+
+  Standard_Real Tol  = 100 * BRep_Tool::Tolerance(V1);
+  Standard_Real Dist = P1.Distance(P2);
+  return Dist < Tol;
+}
+
+
+//=======================================================================
+//function : StoreInMVE
+//purpose  :
+//=======================================================================
+
+static void StoreInMVE (const TopoDS_Face& /*F*/,
+                        TopoDS_Edge& E,
+                        TopTools_DataMapOfShapeListOfShape& MVE )
+
+{
+  TopoDS_Vertex V1, V2;
+  TopTools_ListOfShape Empty;
+
+  TopExp::Vertices(E,V1,V2);
+  if (!MVE.IsBound(V1)) {
+    MVE.Bind(V1,Empty);
+  }
+  MVE(V1).Append(E);
+
+  if (!MVE.IsBound(V2)) {
+    MVE.Bind(V2,Empty);
+  }
+  MVE(V2).Append(E);
+}
+
+//=======================================================================
+//function : RemoveFromMVE
+//purpose  :
+//=======================================================================
+
+static void RemoveFromMVE(const TopoDS_Edge& E,
+                          TopTools_DataMapOfShapeListOfShape& MVE)
+{
+  TopTools_ListIteratorOfListOfShape itl;
+  TopoDS_Vertex  V1,V2;
+  TopExp::Vertices (E,V1,V2);
+  if (MVE.IsBound(V1))
+    for ( itl.Initialize(MVE(V1)); itl.More(); itl.Next()) {
+      if (itl.Value().IsEqual(E)) {
+        MVE(V1).Remove(itl);
+        break;
+      }
+    }
+  if (MVE.IsBound(V2))
+    for ( itl.Initialize(MVE(V2)); itl.More(); itl.Next()) {
+      if (itl.Value().IsEqual(E)) {
+        MVE(V2).Remove(itl);
+        break;
+      }
+    }
+}
+//=======================================================================
+//function : addConnected
+//purpose  : add to <EM> all edges reachable from <E>
+//=======================================================================
+
+static void addConnected(const TopoDS_Shape& E,
+                         TopTools_MapOfShape& EM,
+                         TopTools_MapOfShape& VM,
+                         const TopTools_DataMapOfShapeListOfShape& MVE)
+{
+  // Loop on vertices of E
+  TopoDS_Iterator itV ( E );
+  for ( ; itV.More(); itV.Next()) {
+
+    if ( ! VM.Add ( itV.Value() )) continue;
+
+    // Loop on edges sharing V
+    TopTools_ListIteratorOfListOfShape itE( MVE( itV.Value() ) );
+    for (; itE.More(); itE.Next()) {
+      if ( EM.Add( itE.Value() ))
+        addConnected ( itE.Value(), EM, VM, MVE );
+    }
+  }
+}
+//=======================================================================
+//function : canPassToOld
+//purpose  :
+//=======================================================================
+
+// static Standard_Boolean canPassToOld (const TopoDS_Shape& V,
+//                                    TopTools_MapOfShape& UsedShapesMap,
+//                                    const TopTools_DataMapOfShapeListOfShape& MVE,
+//                                    const TopTools_MapOfShape& SectionEdgesMap)
+// {
+//   TopTools_ListIteratorOfListOfShape itE( MVE(V) );
+//   // Loop on edges sharing V
+//   for (; itE.More(); itE.Next()) {
+//     if ( !UsedShapesMap.Add( itE.Value() ))
+//       continue; // already checked
+
+//     if ( !SectionEdgesMap.Contains( itE.Value() ))
+//       return Standard_True; // WE PASSED
+
+//     TopoDS_Iterator itV( itE.Value() );
+//     // Loop on vertices of an edge
+//     for (; itV.More(); itV.Next()) {
+//       if ( !UsedShapesMap.Add( itV.Value() ))
+//      continue; // already checked
+//       else
+//      return canPassToOld( itV.Value(), UsedShapesMap, MVE, SectionEdgesMap);
+//     }
+//   }
+//   return Standard_False;
+// }
+
+//=======================================================================
+//function : MakeDegenAndSelect
+//purpose  : Find parameter of intersection of <CE> with <DE> and
+//           select an edge with its parameter closest to found one.
+//           Return new degenerated edge trimming <DE> by found parameters
+//=======================================================================
+
+static TopoDS_Edge MakeDegenAndSelect(const TopoDS_Edge& CE,
+                                      const TopoDS_Vertex& CV,
+                                      TopoDS_Edge& NE,
+                                      TopTools_SequenceOfShape& EdgesSeq,
+                                      TColStd_SequenceOfReal& USeq,
+                                      const TopoDS_Edge& DE)
+{
+  if (EdgesSeq.Length() < 3) {
+    if (CE == EdgesSeq.First())
+      NE = TopoDS::Edge( EdgesSeq.Last() );
+    else
+      NE = TopoDS::Edge( EdgesSeq.First() );
+    return DE;
+  }
+
+  // find parameter on DE where it intersects CE
+
+  Standard_Real U1;
+  Standard_Integer i, nb = EdgesSeq.Length();
+  for (i=1; i<= nb; ++i) {
+    if (CE == EdgesSeq(i)) {
+      U1 = USeq(i);
+      break;
+    }
+  }
+
+  // select NE with param closest to U1 thus finding U2 for a new degen edge
+
+  Standard_Real U2, dU, dUmin = 1.e100;
+  Standard_Boolean isReversed = ( DE.Orientation() == TopAbs_REVERSED );
+  for (i=1; i<= nb; ++i) {
+    dU = USeq(i) - U1;
+    if (isReversed ? (dU > 0) : (dU < 0))
+        continue;
+    dU = Abs( dU );
+    if ( dU  > dUmin || IsEqual( dU, 0.))
+      continue;
+    const TopoDS_Edge& E = TopoDS::Edge ( EdgesSeq(i) );
+    if ( ! CV.IsSame( TopExp::FirstVertex( E , Standard_True )))
+      continue;
+    NE = E;
+    dUmin = dU + Epsilon(dU);
+    U2 = USeq(i);
+  }
+
+  // make a new degenerated edge
+  TopoDS_Edge NewDegen = TopoDS::Edge ( DE.EmptyCopied() );
+
+  Standard_Real Tol = BRep_Tool::Tolerance( CV );
+  TopoDS_Vertex V = CV;
+
+  BRep_Builder B;
+  V.Orientation( NewDegen.Orientation() );
+  B.UpdateVertex( V, U1, NewDegen, Tol);
+  B.Add ( NewDegen , V );
+
+  V.Reverse();
+  B.UpdateVertex( V, U2, NewDegen, Tol);
+  B.Add ( NewDegen , V );
+
+  return NewDegen;
+}
+
+//=======================================================================
+//function : prepareDegen
+//purpose  : Intersect <DegEdge> with edges bound to its vertex in <MVE>
+//           and store intersection parameter on <DegEdge> in
+//           <USeq> as well as the edges them-self in <EdgesSeq>.
+//           Bind <DegEdgeIndex> to vertex of <DegEdge> in <MVDEI>
+//=======================================================================
+
+static void prepareDegen (const TopoDS_Edge&                        DegEdge,
+                          const TopoDS_Face&                        F,
+                          const TopTools_DataMapOfShapeListOfShape& MVE,
+                          TopTools_SequenceOfShape&                 EdgesSeq,
+                          TColStd_SequenceOfReal&                   USeq,
+                          TopTools_DataMapOfShapeInteger&           MVDEI,
+                          const Standard_Integer                    DegEdgeIndex)
+{
+  const TopoDS_Vertex& V = TopExp::FirstVertex ( DegEdge );
+  MVDEI.Bind ( V, DegEdgeIndex );
+
+  const TopTools_ListOfShape& EdgesList = MVE ( V );
+  // if only 2 edges come to degenerated one, no pb in selection and
+  // no need to intersect them, just simulate asked data
+  Standard_Boolean doIntersect =  ( EdgesList.Extent() > 2 );
+
+  BRepAdaptor_Curve2d DC, C;
+  Geom2dInt_GInter InterCC;
+  Standard_Real Tol = Precision::PConfusion();
+  if ( doIntersect )
+    DC.Initialize( DegEdge, F );
+
+  // avoid intersecting twice the same edge
+  BRepOffset_DataMapOfShapeReal EUMap ( EdgesList.Extent() );
+  // TopTools_DataMapOfShapeReal EUMap ( EdgesList.Extent() );   // V6.5
+
+  Standard_Real U, f, l;
+  BRep_Tool::Range (DegEdge, f, l);
+
+  TopTools_ListIteratorOfListOfShape itE (EdgesList);
+  for (; itE.More(); itE.Next()) {
+
+    const TopoDS_Edge& E = TopoDS::Edge ( itE.Value() );
+
+    if ( !doIntersect) {
+      U = 0.; // it won't be used
+    }
+    else if ( BRep_Tool::IsClosed( E, F )) {
+      // seam edge: select U among f and l
+      Standard_Boolean first = Standard_True;
+      if ( V.IsSame ( TopExp::FirstVertex( E, Standard_True ) ))
+        first = Standard_False;
+      if ( DegEdge.Orientation() == TopAbs_REVERSED )
+        first = !first;
+      U = first ? f : l;
+    }
+    else if ( EUMap.IsBound( E ) ) {
+      // same edge already bound
+      U = EUMap( E );
+    }
+    else {
+      // intersect 2d curves
+      C.Initialize( E, F );
+      InterCC.Perform ( DC, C , Tol, Tol );
+      if (! InterCC.IsDone() || InterCC.NbPoints() == 0) {
+        MESSAGE ( "NO 2d INTERSECTION ON DEGENERATED EDGE" );
+        continue;
+      }
+      // hope there is only one point of intersection
+      U = InterCC.Point( 1 ).ParamOnFirst();
+    }
+    USeq.Append ( U );
+    EdgesSeq.Append ( E );
+  }
+}
+//=======================================================================
+//function : Perform
+//purpose  : Make loops.
+//=======================================================================
+
+void Partition_Loop2d::Perform()
+{
+
+  Standard_Integer NbConstEdges = myConstEdges.Extent();
+  TopTools_DataMapOfShapeListOfShape MVE(NbConstEdges) , MVE2(NbConstEdges);
+  TopTools_DataMapIteratorOfDataMapOfShapeListOfShape Mapit;
+  TopTools_ListIteratorOfListOfShape itl;
+  TopoDS_Vertex V1,V2;
+  BRepAdaptor_Surface Surface ( myFace, Standard_False );
+
+  // degenerated edges and parameters of their 2d intersection with other edges
+  TopoDS_Edge                    DE [2];
+  TopTools_SequenceOfShape       SEID [2]; // seq of edges intersecting degenerated
+  TColStd_SequenceOfReal         SeqU [2]; // n-th U corresponds to n-th edge in SEID
+  TopTools_DataMapOfShapeInteger MVDEI(2); // map vertex - degenerated edge index
+  Standard_Integer               iDeg = 0; // index of degenerated edge [0,1]
+
+  //---------------------------------------------------------
+  // Construction map vertex => edges, find degenerated edges
+  //---------------------------------------------------------
+  for (itl.Initialize(myConstEdges); itl.More(); itl.Next()) {
+    TopoDS_Edge& E = TopoDS::Edge(itl.Value());
+    if ( BRep_Tool::Degenerated( E )) {
+      if (DE[0].IsNull()) DE[0] = E;
+      else                DE[1] = E;
+    }
+    else
+      StoreInMVE(myFace,E,MVE);
+  }
+
+  // fill data for degenerated edges
+  if ( ! DE[0].IsNull() )
+    prepareDegen ( DE[0], myFace, MVE, SEID[0], SeqU[0], MVDEI, 0);
+  if ( ! DE[1].IsNull() )
+    prepareDegen ( DE[1], myFace, MVE, SEID[1], SeqU[1], MVDEI, 1);
+
+
+  // to detect internal wires
+  Standard_Boolean isInternCW = 0;
+  MVE2 = MVE;
+
+
+  //------------------------------
+  // Construction of all the wires
+  //------------------------------
+  // first, we collect wire edges in WEL list looking for same edges that
+  // will be then removed possibly exploding a wire into parts;
+  // second, build wire(s)
+
+  while (!MVE.IsEmpty()) {
+
+    TopoDS_Vertex    VF,CV;
+    TopoDS_Edge      CE,NE,EF;
+    TopoDS_Wire      NW;
+    BRep_Builder     B;
+    Standard_Boolean End = Standard_False;
+    TopTools_ListOfShape WEL;
+
+    Mapit.Initialize(MVE);
+    if (Mapit.Value().IsEmpty()) {
+      MVE.UnBind(Mapit.Key());
+      continue;
+    }
+
+    // EF first edge.
+    EF = CE = TopoDS::Edge(Mapit.Value().First());
+    // VF first vertex
+    VF = TopExp::FirstVertex( CE, Standard_True);
+
+    isInternCW = Standard_True;
+
+    TopTools_MapOfShape addedEM  (NbConstEdges); // map of edges added to WEL
+    TopTools_MapOfShape doubleEM (NbConstEdges); // edges encountered twice in WEL
+
+    //-------------------------------
+    // Construction of a wire.
+    //-------------------------------
+    while (!End) {
+
+      // only a seam is allowed twice in a wire, the others should be removed
+      if (addedEM.Add ( CE ) || BRep_Tool::IsClosed( CE, myFace ) )
+        WEL.Append( CE );
+      else {
+        doubleEM.Add( CE );
+        RemoveFromMVE (CE,MVE2);
+        TopoDS_Edge CERev = CE;
+        CERev.Reverse();
+        RemoveFromMVE (CERev,MVE2);
+      }
+
+      RemoveFromMVE (CE,MVE);
+
+      CV = TopExp::LastVertex( CE, Standard_True);
+
+      if (isInternCW && !mySectionEdges.Contains(CE))
+        // wire is internal if all edges are section ones
+        isInternCW = Standard_False;
+
+      if (MVDEI.IsBound( CV )) { // CE comes to the degeneration
+        iDeg = MVDEI( CV );
+        TopoDS_Edge NewDegen;
+        NewDegen = MakeDegenAndSelect( CE, CV, NE, SEID[iDeg], SeqU[iDeg], DE[iDeg]);
+        WEL.Append( NewDegen );
+        CE = NE;
+        End = CV.IsSame( VF );
+        continue;
+      }
+
+      //--------------
+      // stop test
+      //--------------
+      if (MVE(CV).IsEmpty()) {
+        End=Standard_True;
+        MVE.UnBind(CV);
+      }
+      else if (CV.IsSame(VF) && SamePnt2d(CV,CE, VF,EF, myFace) ) {
+        End = Standard_True;
+      }
+      else {
+        //----------------------------
+        // select new current edge
+        //----------------------------
+        if (! SelectEdge (Surface,CE,CV,NE,MVE(CV))) {
+          MESSAGE ( " NOT CLOSED WIRE " );
+          End=Standard_True;
+        }
+        else
+          CE = NE;
+      }
+    } // while ( !End )
+
+
+    // WEL is built, built wire(s)
+
+
+    itl.Initialize( WEL );
+    if ( doubleEM.IsEmpty()) { // no double edges
+      B.MakeWire( NW );
+      for (; itl.More(); itl.Next())
+        B.Add ( NW, itl.Value());
+      if (isInternCW) myInternalWL.Append(NW);
+      else            myNewWires.Append  (NW);
+    }
+
+    else {
+      // remove double and degenerated edges from WEL
+      while (itl.More()) {
+        const TopoDS_Edge& E = TopoDS::Edge ( itl.Value() );
+        if ( doubleEM.Contains( E ) || BRep_Tool::Degenerated( E ))
+          WEL.Remove( itl );
+        else
+           itl.Next();
+      }
+      if ( WEL.IsEmpty())
+        continue;
+      // remove double edges from SEID and SeqU
+      Standard_Integer i,j;
+      for (j=0; j<2; ++j) {
+        for (i=1; i<=SEID[j].Length(); ++i) {
+          if (doubleEM.Contains( SEID[j].Value(i))) {
+            SEID[j].Remove( i );
+            SeqU[j].Remove( i-- );
+          }
+        }
+      }
+      // removal of doulbe edges can explode a wire into parts,
+      // make new wires of them.
+      // A Loop like previous one but without 2d check
+      while ( !WEL.IsEmpty() ) {
+        CE = TopoDS::Edge( WEL.First() );
+        WEL.RemoveFirst();
+        B.MakeWire( NW );
+        VF = TopExp::FirstVertex ( CE, Standard_True);
+
+        End = Standard_False;
+        while ( !End) {
+          B.Add( NW, CE );
+          CV = TopExp::LastVertex  ( CE, Standard_True);
+
+          if (MVDEI.IsBound( CV )) {   // CE comes to the degeneration
+            iDeg = MVDEI( CV );
+            TopoDS_Edge NewDegen;
+            NewDegen = MakeDegenAndSelect( CE, CV, NE, SEID[iDeg], SeqU[iDeg], DE[iDeg]);
+            B.Add( NW, NewDegen );
+            End = CV.IsSame( VF );
+            CE = NE;
+            if (!NE.IsNull()) { // remove NE from WEL
+              for (itl.Initialize( WEL ); itl.More(); itl.Next())
+                if ( NE == itl.Value()) {
+                  WEL.Remove( itl );
+                  break;
+                }
+            }
+          }  // end degeneration
+
+          else {
+            if (CV.IsSame( VF )) {
+              End = Standard_True;
+              continue;
+            }
+            // edges in WEL most often are well ordered
+            // so try to iterate until the End
+            Standard_Boolean add = Standard_False;
+            itl.Initialize(WEL);
+            while ( itl.More() && !End) {
+              NE = TopoDS::Edge( itl.Value() );
+              if ( CV.IsSame( TopExp::FirstVertex( NE, Standard_True ))) {
+                WEL.Remove( itl );
+                if (add)
+                  B.Add( NW, CE );
+                CE = NE;
+                add = Standard_True;
+                CV = TopExp::LastVertex( CE, Standard_True);
+                if (MVDEI.IsBound( CV ) || CV.IsSame( VF ))
+                  break;
+              }
+              else
+                itl.Next();
+            }
+            if (!add)
+              End = Standard_True;
+          }
+        } // !End
+
+        myInternalWL.Append( NW );
+      }
+    } // end building new wire(s) from WEL
+
+  } // end Loop on MVE
+
+  // all wires are built
+
+
+  // ============================================================
+  // select really internal wires i.e. those from which we can`t
+  // pass to an old (not section) edge
+  // ============================================================
+
+  Standard_Integer nbIW = myInternalWL.Extent();
+  if (nbIW == 0)
+    return;
+
+  if ( myNewWires.Extent() != 1 && nbIW > 1) {
+    TopTools_MapOfShape outerEM (NbConstEdges); // edges connected to non-section ones
+    TopTools_MapOfShape visitedVM (NbConstEdges);
+    for ( itl.Initialize( myConstEdges ); itl.More(); itl.Next()) {
+      if ( ! mySectionEdges.Contains( itl.Value() ))
+        addConnected (itl.Value(), outerEM, visitedVM, MVE2);
+    }
+    // if an edge of a wire is in <outerEM>, the wire is not internal
+    TopExp_Explorer expIWE;
+    TopTools_ListIteratorOfListOfShape itIW ( myInternalWL );
+    while (itIW.More()) {
+      expIWE.Init ( itIW.Value() , TopAbs_EDGE );
+      if ( outerEM.Contains( expIWE.Current() )) {
+        myNewWires.Append ( itIW.Value() );
+        myInternalWL.Remove( itIW ); // == itIW.Next()
+      }
+      else
+        itIW.Next();
+    }
+  }
+}
+//=======================================================================
+//function : isHole
+//purpose  :
+//=======================================================================
+
+static Standard_Boolean isHole (const TopoDS_Wire& W,
+                                const TopoDS_Face& F)
+{
+  BRep_Builder B;
+  TopoDS_Shape newFace = F.EmptyCopied();
+  B.Add(newFace,W.Oriented(TopAbs_FORWARD));
+  BRepTopAdaptor_FClass2d classif (TopoDS::Face(newFace),
+                                   Precision::PConfusion());
+  return (classif.PerformInfinitePoint() == TopAbs_IN);
+}
+
+//=======================================================================
+//function : IsInside
+//purpose  : check if W1 is inside W2. Suppose W2 is not a hole !!!!
+//=======================================================================
+
+static Standard_Boolean isInside(const TopoDS_Face& F,
+                                 const TopoDS_Wire& W1,
+                                 const TopoDS_Wire& W2)
+{
+  // make a face with wire W2
+  BRep_Builder B;
+  TopoDS_Shape aLocalShape = F.EmptyCopied();
+  TopoDS_Face newFace = TopoDS::Face(aLocalShape);
+  B.Add(newFace,W2);
+
+  // get any 2d point of W1
+  TopExp_Explorer exp(W1,TopAbs_EDGE);
+  if (BRep_Tool::Degenerated( TopoDS::Edge( exp.Current() )))
+    exp.Next();
+  const TopoDS_Edge& e = TopoDS::Edge(exp.Current());
+  Standard_Real f,l;
+  Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface(e,F,f,l);
+  gp_Pnt2d pt2d(C2d->Value( 0.5 * ( f + l )));
+
+  BRepTopAdaptor_FClass2d classif(newFace,Precision::PConfusion());
+  return (classif.Perform(pt2d) == TopAbs_IN);
+}
+
+//=======================================================================
+//function : NewWires
+//purpose  : Returns the list of wires performed.
+//           can be an empty list.
+//=======================================================================
+
+const TopTools_ListOfShape&  Partition_Loop2d::NewWires() const
+{
+  return myNewWires;
+}
+
+//=======================================================================
+//function : NewFaces
+//purpose  : Returns the list of faces.
+//Warning  : The method <WiresToFaces> as to be called before.
+//           can be an empty list.
+//=======================================================================
+
+const TopTools_ListOfShape&  Partition_Loop2d::NewFaces() const
+{
+  return myNewFaces;
+}
+
+//=======================================================================
+//function : findEqual
+//purpose  : move wires form <WL> to <EqWL> pairs of wires build of the same edges
+//=======================================================================
+
+static void findEqual (TopTools_ListOfShape& WL,
+                       TopTools_DataMapOfShapeShape& EqWM,
+                       const TopoDS_Face& F)
+{
+  TopTools_ListIteratorOfListOfShape it1, it2;
+  Standard_Integer i,j;
+  TColStd_MapOfInteger IndMap;
+  for (it1.Initialize(WL), i=1;  it1.More();  it1.Next(), i++) {
+
+    if (IndMap.Contains(i)) continue;
+    const TopoDS_Wire& Wire1 = TopoDS::Wire( it1.Value());
+
+    for (it2.Initialize(WL), j=1;  it2.More();  it2.Next(), j++) {
+
+      if (j <= i || IndMap.Contains(j)) continue;
+
+      TopTools_IndexedMapOfShape EdgesMap;
+      TopExp::MapShapes (Wire1, TopAbs_EDGE, EdgesMap);
+
+      const TopoDS_Shape& Wire2 = it2.Value();
+      TopoDS_Iterator itE ( Wire2);
+      for (; itE.More(); itE.Next()) {
+        if ( !EdgesMap.Contains( itE.Value()) )
+          break;
+      }
+      if (!itE.More()) { // all edges are same
+        if (isHole( Wire1, F)) {
+          EqWM.Bind ( Wire1, Wire2 );
+        }
+        else {
+          EqWM.Bind ( Wire2, Wire1 );
+        }
+        IndMap.Add(i);
+        IndMap.Add(j);
+        break;
+      }
+    }
+  }
+  // clear WL
+  it1.Initialize(WL);
+  i=1;
+  while (it1.More()) {
+    if (IndMap.Contains(i))
+      WL.Remove(it1); // next node becomes current and with Next() we would miss it
+    else
+      it1.Next();
+    i++;
+  }
+}
+
+//=======================================================================
+//function : classify
+//purpose  : bind to a wire a list of internal wires
+//=======================================================================
+
+static void classify(const TopTools_DataMapOfShapeShape& EqWM,
+                     BRepAlgo_AsDes& OuterInner,
+                     const TopoDS_Face& F)
+{
+  TopTools_DataMapIteratorOfDataMapOfShapeShape it1, it2;
+
+  for (it1.Initialize(EqWM);  it1.More();  it1.Next()) {
+    // find next after it1.Value()
+    for (it2.Initialize(EqWM);  it2.More();  it2.Next())
+      if (it1.Value().IsSame( it2.Value() ))
+      {
+        it2.Next();
+        break;
+      }
+    for ( ;  it2.More();  it2.Next()) {
+      const TopoDS_Wire& Wire1 = TopoDS::Wire( it1.Value() );
+      const TopoDS_Wire& Wire2 = TopoDS::Wire( it2.Value() );
+      if (isInside(F, Wire1, Wire2))
+        OuterInner.Add (Wire2, Wire1);
+      else if (isInside(F, Wire2, Wire1))
+        OuterInner.Add (Wire1, Wire2);
+    }
+  }
+}
+//=======================================================================
+//function : WiresToFaces
+//purpose  : Build faces from the wires result.
+//           <EdgeImage> serves to  find  original edge by new
+//           one. <Section> contains edges resulting from face
+//           intersections
+//=======================================================================
+
+void  Partition_Loop2d::WiresToFaces(const BRepAlgo_Image& )
+{
+  Standard_Integer nbW = myNewWires.Extent() + myInternalWL.Extent();
+  if (nbW==0)
+    return;
+
+  BRepAlgo_FaceRestrictor FR;
+  FR.Init (myFace,Standard_False);
+
+  // FaceRestrictor is instable in rather simple cases
+  // (ex. a single face of bellecoque.brep splited by 10 planes:
+  // sometimes 1-2 faces are missing ).
+  // So we use it as less as possible: no holes -> make faces by hands
+
+
+  // are there holes in myFace ?
+  Standard_Boolean hasOldHoles = Standard_False;
+  TopoDS_Iterator itOldW (myFace);
+  if ( itOldW.More()) {
+    const TopoDS_Wire& FirstOldWire = TopoDS::Wire( itOldW.Value() );
+    itOldW.Next();
+    hasOldHoles = itOldW.More() || isHole( FirstOldWire, myFace);
+  }
+  if (myInternalWL.IsEmpty() && !hasOldHoles) {
+    // each wire bounds one face
+    BRep_Builder B;
+    TopTools_ListIteratorOfListOfShape itNW (myNewWires);
+    for (; itNW.More(); itNW.Next()) {
+      TopoDS_Face NF = TopoDS::Face ( myFace.EmptyCopied() );
+      B.Add ( NF, itNW.Value() );
+      NF.Orientation( myFaceOri);
+      myNewFaces.Append ( NF );
+    }
+    return;
+  }
+
+  // FaceRestrictor can't classify wires build on all the same edges
+  // and gives incorrect result in such cases (ex. a plane cut into 2 parts by cylinder)
+  // We must make faces of equal wires separately. One of equal wires makes a
+  // hole in a face and should come together with outer wires of face.
+  // The other of a wires pair bounds a face that may have holes in turn.
+
+  // Find equal wires among internal wires
+  TopTools_DataMapOfShapeShape EqWM; // key is a hole part of a pair of equal wires
+  findEqual (myInternalWL, EqWM, myFace);
+
+  if (!EqWM.IsEmpty()) { // there are equal wires
+
+    if (hasOldHoles)
+      myInternalWL.Append( myNewWires ); // an old wire can be inside an equal wire
+
+    // classify equal wire pairs
+    BRepAlgo_AsDes OuterInner;
+    classify (EqWM,OuterInner,myFace);
+
+    // make face of most internal of equal wires and its inner wires
+    while ( !EqWM.IsEmpty()) {
+
+      TopTools_ListOfShape prevHolesL; // list of hole-part of previous most internal equal wires
+
+      // find most internal wires among pairs (key - hole, value - outer part)
+      TopTools_DataMapIteratorOfDataMapOfShapeShape it(EqWM);
+      Standard_Integer nbEqW = EqWM.Extent(); // protection against infinite loop
+      for ( ; it.More(); it.Next()) {
+
+        TopoDS_Wire outerW = TopoDS::Wire ( it.Value() );
+        if (  OuterInner.HasDescendant( outerW ) && // has internal
+             ! OuterInner.Descendant( outerW ).IsEmpty() )
+          continue;
+
+        FR.Add( outerW );
+
+        // add internal wires that are inside of outerW
+        TopTools_ListIteratorOfListOfShape itIW (myInternalWL);
+        while ( itIW.More()) {
+          TopoDS_Wire IW = TopoDS::Wire ( itIW.Value() );
+          if ( isInside (myFace, IW, outerW)) {
+            FR.Add (IW);
+            myInternalWL.Remove( itIW ); // == itIW.Next() !!!
+          }
+          else
+            itIW.Next();
+        }
+
+        // the hole-part of current pair of equal wires will be in the next new face
+        prevHolesL.Append ( it.Key() );
+
+      } // Loop on map of equal pairs searching for innermost wires
+
+      // make faces
+      FR.Perform();
+      if (FR.IsDone()) {
+        for (; FR.More(); FR.Next())
+          myNewFaces.Append(FR.Current());
+      }
+
+      FR.Clear();
+
+      // add hole-parts to FaceRestrictor,
+      // remove them from the EqWM,
+      // remove found wires as internal of resting classified wires
+      Standard_Boolean clearOuterInner =  ( prevHolesL.Extent() < EqWM.Extent() );
+      TopTools_ListIteratorOfListOfShape itPrev (prevHolesL);
+      for (; itPrev.More(); itPrev.Next()) {
+        TopoDS_Wire& Hole = TopoDS::Wire ( itPrev.Value() );
+        FR.Add ( Hole );
+        if (clearOuterInner) {
+          const TopoDS_Wire& outerW = TopoDS::Wire ( EqWM.Find( Hole ) );
+          // Loop on wires including outerW
+          TopTools_ListIteratorOfListOfShape itO( OuterInner.Ascendant( outerW ));
+          for (; itO.More(); itO.Next()) {
+            TopTools_ListOfShape& innerL = OuterInner.ChangeDescendant( itO.Value() );
+            TopTools_ListIteratorOfListOfShape itI (innerL);
+            // Loop on internal wires of current including wire
+            for (; itI.More(); itI.Next())
+              if ( outerW.IsSame( itI.Value() )) {
+                innerL.Remove( itI );   break;
+              }
+          }
+        }
+        EqWM.UnBind ( Hole );
+      }
+
+      if (nbEqW == EqWM.Extent())
+      {
+        // error: pb with wires classification
+#ifdef DEB
+        MESSAGE("Partition_Loop2d::WiresToFaces(), pb with wires classification");
+#endif
+        break;
+      }
+
+    } // while (!EqWM.IsEmpty)
+
+  } //  if !EqWM.IsEmpty()
+
+  myNewWires.Append ( myInternalWL );
+
+  TopTools_ListIteratorOfListOfShape itW (myNewWires);
+  for (; itW.More(); itW.Next()) {
+    TopoDS_Wire& W = TopoDS::Wire ( itW.Value() );
+    FR.Add(W);
+  }
+  FR.Perform();
+  for (; FR.IsDone() && FR.More(); FR.Next())
+    myNewFaces.Append(FR.Current());
+
+
+  TopTools_ListIteratorOfListOfShape itNF (myNewFaces);
+  for (; itNF.More(); itNF.Next())
+    itNF.Value().Orientation( myFaceOri );
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop2d.hxx b/contrib/Netgen/libsrc/occ/Partition_Loop2d.hxx
new file mode 100644
index 0000000000000000000000000000000000000000..bdf1c2531df5ddb654088ca314ff6fe9c13f66db
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop2d.hxx
@@ -0,0 +1,106 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  CEA/DEN, EDF R&D
+//
+//
+//
+//  File   : Partition_Loop2d.hxx
+//  Module : GEOM
+
+#ifndef _Partition_Loop2d_HeaderFile
+#define _Partition_Loop2d_HeaderFile
+
+#ifndef _TopoDS_Face_HeaderFile
+#include <TopoDS_Face.hxx>
+#endif
+#ifndef _TopAbs_Orientation_HeaderFile
+#include <TopAbs_Orientation.hxx>
+#endif
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _TopTools_MapOfShape_HeaderFile
+#include <TopTools_MapOfShape.hxx>
+#endif
+class TopoDS_Face;
+class TopoDS_Edge;
+class TopTools_ListOfShape;
+class BRepAlgo_Image;
+
+
+#ifndef _Standard_HeaderFile
+#include <Standard.hxx>
+#endif
+#ifndef _Standard_Macro_HeaderFile
+#include <Standard_Macro.hxx>
+#endif
+
+class Partition_Loop2d  {
+
+public:
+
+   void* operator new(size_t,void* anAddress) 
+   {
+      return anAddress;
+   }
+   void* operator new(size_t size) 
+   { 
+      return Standard::Allocate(size); 
+   }
+   void  operator delete(void *anAddress) 
+   { 
+      if (anAddress) Standard::Free((Standard_Address&)anAddress); 
+   }
+   // Methods PUBLIC
+   // 
+   Partition_Loop2d();
+   void Init(const TopoDS_Face& F) ;
+   void AddConstEdge(const TopoDS_Edge& E) ;
+   void AddSectionEdge(const TopoDS_Edge& E) ;
+   void Perform() ;
+   const TopTools_ListOfShape& NewWires() const;
+   void WiresToFaces(const BRepAlgo_Image& EdgeImage) ;
+   const TopTools_ListOfShape& NewFaces() const;
+
+
+
+
+
+protected:
+
+   // Methods PROTECTED
+   // 
+
+
+   // Fields PROTECTED
+   //
+
+
+private: 
+
+   // Methods PRIVATE
+   // 
+
+
+   // Fields PRIVATE
+   //
+   TopoDS_Face myFace;
+   TopAbs_Orientation myFaceOri;
+   TopTools_ListOfShape myConstEdges;
+   TopTools_ListOfShape myNewWires;
+   TopTools_ListOfShape myNewFaces;
+   TopTools_ListOfShape myInternalWL;
+   TopTools_MapOfShape mySectionEdges;
+
+
+};
+
+
+
+
+
+// other Inline functions and methods (like "C++: function call" methods)
+//
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop2d.ixx b/contrib/Netgen/libsrc/occ/Partition_Loop2d.ixx
new file mode 100644
index 0000000000000000000000000000000000000000..2d35fd5c711b05df86cf40de88ec2d3c356b4998
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop2d.ixx
@@ -0,0 +1,14 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  CEA/DEN, EDF R&D
+//
+//
+//
+//  File   : Partition_Loop2d.ixx
+//  Module : GEOM
+
+#include "Partition_Loop2d.jxx"
+
+ 
+
+
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop2d.jxx b/contrib/Netgen/libsrc/occ/Partition_Loop2d.jxx
new file mode 100644
index 0000000000000000000000000000000000000000..555c16c809d26712d52dba2a8ef0523eadf05262
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop2d.jxx
@@ -0,0 +1,24 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  CEA/DEN, EDF R&D
+//
+//
+//
+//  File   : Partition_Loop2d.jxx
+//  Module : GEOM
+
+#ifndef _TopoDS_Face_HeaderFile
+#include <TopoDS_Face.hxx>
+#endif
+#ifndef _TopoDS_Edge_HeaderFile
+#include <TopoDS_Edge.hxx>
+#endif
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _BRepAlgo_Image_HeaderFile
+#include <BRepAlgo_Image.hxx>
+#endif
+#ifndef _Partition_Loop2d_HeaderFile
+#include "Partition_Loop2d.hxx"
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop3d.cxx b/contrib/Netgen/libsrc/occ/Partition_Loop3d.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3e9acc3ccb06c9131e474e7cb527f43fa60333ff
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop3d.cxx
@@ -0,0 +1,356 @@
+#ifdef OCCGEOMETRY
+
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  CEA/DEN, EDF R&D
+//
+//
+//
+//  File   : Partition_Loop3d.cxx
+//  Module : GEOM
+
+//using namespace std;
+#include <climits>
+#include "Partition_Loop3d.ixx"
+
+#include <TopExp_Explorer.hxx>
+#include <TopExp.hxx>
+#include <BRep_Builder.hxx>
+#include <TopTools_MapOfShape.hxx>
+#include <TopTools_ListIteratorOfListOfShape.hxx>
+#include <TopoDS_Shell.hxx>
+#include <TopoDS_Iterator.hxx>
+#include <TopoDS.hxx>
+#include <TopTools_MapIteratorOfMapOfShape.hxx>
+#include <gp_Vec.hxx>
+#include <gp_Pnt.hxx>
+#include <Geom2d_Curve.hxx>
+#include <BRep_Tool.hxx>
+#include <Geom_Surface.hxx>
+#include <gp_Pnt2d.hxx>
+#include <gp_Vec2d.hxx>
+#include <gp_Dir2d.hxx>
+#include <Geom_Curve.hxx>
+
+//=======================================================================
+//function : Partition_Loop3d
+//purpose  : 
+//=======================================================================
+
+Partition_Loop3d::Partition_Loop3d()
+{
+}
+
+//=======================================================================
+//function : AddConstFaces
+//purpose  : Add faces of <S> as unique faces in the result.
+//=======================================================================
+
+void Partition_Loop3d::AddConstFaces(const TopoDS_Shape& S) 
+{
+  TopExp_Explorer FaceExp(S, TopAbs_FACE);
+  for (; FaceExp.More(); FaceExp.Next())
+    myFaces.Append( FaceExp.Current() );
+
+  TopExp::MapShapesAndAncestors(S, TopAbs_EDGE, TopAbs_FACE, myEFMap);
+}
+
+//=======================================================================
+//function : AddSectionFaces
+//purpose  : Add faces of <S> as double faces in the result.
+//=======================================================================
+
+void Partition_Loop3d::AddSectionFaces(const TopoDS_Shape& S) 
+{
+  AddConstFaces( S );
+  AddConstFaces( S.Reversed() );
+}
+
+//=======================================================================
+//function : MakeShells
+//purpose  : Make and return shells. 
+//           <AvoidFacesMap> can contain faces that must not be
+//           added to result shells.
+//=======================================================================
+
+const TopTools_ListOfShape&
+  Partition_Loop3d::MakeShells (const TopTools_MapOfOrientedShape& AvoidFacesMap)
+{
+  myNewShells.Clear();
+  
+  BRep_Builder Builder;
+  TopTools_MapOfShape CheckedEdgesMap;
+  TopTools_MapOfOrientedShape AddedFacesMap;
+  
+  TopTools_ListIteratorOfListOfShape itF (myFaces);
+  for (; itF.More(); itF.Next())
+  {
+    const TopoDS_Shape& FF = itF.Value();
+    if (AvoidFacesMap.Contains( FF ) ||
+	! AddedFacesMap.Add( FF ) )
+      continue;
+
+    // make a new shell
+    TopoDS_Shell Shell;
+    Builder.MakeShell(Shell);
+    Builder.Add(Shell,FF);
+
+    // clear the maps from shapes added to previous Shell
+    TopTools_MapIteratorOfMapOfShape itEM (CheckedEdgesMap);
+    for (; itEM.More(); itEM.Next()) {
+      TopTools_ListOfShape& FL = myEFMap.ChangeFromKey( itEM.Key());
+      TopTools_ListIteratorOfListOfShape it (FL);
+      while ( it.More()) {
+        if (AddedFacesMap.Contains( it.Value()))
+          FL.Remove( it );
+        else
+          it.Next();
+      }
+    }
+    CheckedEdgesMap.Clear();
+
+    
+    // loop on faces added to Shell; add their neighbor faces to Shell and so on
+    TopoDS_Iterator itAddedF (Shell);
+    for (; itAddedF.More(); itAddedF.Next())
+    {
+      const TopoDS_Face& F = TopoDS::Face (itAddedF.Value());
+
+      // loop on edges of F; find a good neighbor face of F by E
+      TopExp_Explorer EdgeExp(F, TopAbs_EDGE);
+      for (; EdgeExp.More(); EdgeExp.Next())
+      {
+        const TopoDS_Edge& E = TopoDS::Edge( EdgeExp.Current());
+	if (! CheckedEdgesMap.Add( E ))
+	  continue;
+
+	// candidate faces list
+        const TopTools_ListOfShape& FL = myEFMap.ChangeFromKey(E);
+        if (FL.IsEmpty())
+          continue;
+	// select one of neighbors
+        TopoDS_Face SelF;
+        if (FL.Extent() == 2) {
+          if (! F.IsSame( FL.First() ))
+            SelF = TopoDS::Face( FL.First() );
+          else if (!F.IsSame( FL.Last() ))
+            SelF = TopoDS::Face( FL.Last() );
+        }
+        else {
+          // check if a face already added to Shell shares E
+	  TopTools_ListIteratorOfListOfShape it (FL);
+          Standard_Boolean found = Standard_False;
+          for (; !found && it.More(); it.Next())
+            if (F != it.Value())
+              found = AddedFacesMap.Contains( it.Value() );
+          if (found)
+            continue;
+          // select basing on geometrical check
+          Standard_Boolean GoodOri, inside;
+          Standard_Real dot, MaxDot = -100;
+          TopTools_ListOfShape TangFL; // tangent faces
+          for ( it.Initialize( FL ) ; it.More(); it.Next()) {
+            const TopoDS_Face& NeighborF = TopoDS::Face( it.Value());
+            if (NeighborF.IsSame( F ))
+              continue;
+            inside = Partition_Loop3d::IsInside( E, F, NeighborF, 1, dot, GoodOri);
+            if (!GoodOri)
+              continue;
+            if (!inside)
+              dot = -dot - 3;
+            if (dot < MaxDot)
+              continue;
+            if ( IsEqual( dot, MaxDot))
+              TangFL.Append(SelF);
+            else
+              TangFL.Clear();
+            MaxDot = dot;
+            SelF = NeighborF;
+          }
+          if (!TangFL.IsEmpty()) {
+            for (it.Initialize( TangFL ); it.More(); it.Next()) {
+              const TopoDS_Face& NeighborF = TopoDS::Face( it.Value());
+              if (Partition_Loop3d:: IsInside( E, SelF , NeighborF, 0, dot, GoodOri))
+                SelF = NeighborF;
+            }
+          }
+        }
+        if (!SelF.IsNull() &&
+	    AddedFacesMap.Add( SelF ) &&
+	    !AvoidFacesMap.Contains( SelF )) 
+          Builder.Add( Shell, SelF);
+
+      } // loop on edges of F
+      
+    } // loop on the faces added to Shell
+
+    // Shell is complete
+    myNewShells.Append( Shell );
+
+  } // loop on myFaces
+
+
+  // prepare to the next call
+  myFaces.Clear();
+  myEFMap.Clear();
+
+  return myNewShells;
+}
+
+
+
+//=======================================================================
+//function : Normal
+//purpose  : 
+//=======================================================================
+
+gp_Vec Partition_Loop3d::Normal(const TopoDS_Edge& E,
+				const TopoDS_Face& F)
+{
+  gp_Vec Norm, V1, V2;
+  Standard_Real First, Last;
+  gp_Pnt Ps;
+
+  Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface (E, F, First, Last);
+  Handle(Geom_Surface) Sf = BRep_Tool::Surface(F);
+
+  gp_Pnt2d p = C2d->Value( 0.5*(First+Last) );
+  Sf->D1(p.X(), p.Y(), Ps, V1, V2);
+  Norm = V1.Crossed(V2);
+
+  if (F.Orientation() == TopAbs_REVERSED ) 
+    Norm.Reverse();
+
+  return Norm;
+}
+
+//=======================================================================
+//function : NextNormal
+//purpose  : find normal to F at point a little inside F near the middle of E
+//warning  : E must be properly oriented in F.
+//=======================================================================
+
+static gp_Vec NextNormal(const TopoDS_Edge& E,
+			 const TopoDS_Face& F)
+{
+  Standard_Real First, Last;
+
+  Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface (E, F, First, Last);
+  Handle(Geom_Surface) Sf = BRep_Tool::Surface(F);
+
+  gp_Pnt2d p;
+  gp_Vec2d v;
+  C2d->D1( 0.5*(First+Last), p, v);
+  if (E.Orientation() != F.Orientation())
+    v.Reverse();
+  gp_Dir2d dir( -v.Y(), v.X() ); // dir inside F
+  
+  Standard_Real duv = 1e-6; // this is not Ok and may give incorrect result if
+  // resolutionUV of compared faces is very different. To have a good result,
+  //it is necessary to get normal to faces at points equidistant from E in 3D
+  
+  p.SetX( p.X() + dir.X()*duv );
+  p.SetY( p.Y() + dir.Y()*duv );
+  
+  gp_Pnt Ps;
+  gp_Vec Norm, V1, V2, VV1, VV2;
+  Sf->D1( p.X(), p.Y(), Ps, V1, V2);
+  Norm = V1.Crossed(V2);
+
+  if (F.Orientation() == TopAbs_REVERSED ) 
+    Norm.Reverse();
+
+  return Norm;
+}
+
+
+//=======================================================================
+//function : FindEinF
+//purpose  : find E in F
+//=======================================================================
+
+static TopoDS_Edge FindEinF(const TopoDS_Edge& E,
+			    const TopoDS_Face& F)
+{
+  TopExp_Explorer expl (F, TopAbs_EDGE);
+  for (; expl.More(); expl.Next()) 
+    if( E.IsSame( expl.Current() ))
+      return TopoDS::Edge(expl.Current());
+  TopoDS_Edge nullE;
+  return nullE;
+}
+
+//=======================================================================
+//function : IsInside
+//purpose  : check if <F2> is inside <F1> by edge <E>.
+//           if <CountDot>, compute <Dot>: scalar production of
+//           normalized  vectors  pointing  inside  faces,  and
+//           check if faces are oriented well for sewing
+//=======================================================================
+
+Standard_Boolean Partition_Loop3d::IsInside(const TopoDS_Edge& E,
+					    const TopoDS_Face& F1,
+					    const TopoDS_Face& F2,
+					    const Standard_Boolean CountDot,
+					    Standard_Real& Dot,
+					    Standard_Boolean& GoodOri) 
+{
+  Standard_Real f, l;
+  gp_Pnt P;
+  gp_Vec Vc1, Vc2, Vin1, Vin2, Nf1, Nf2;
+  Handle(Geom_Curve) Curve = BRep_Tool::Curve(E,f,l);
+  Curve->D1( 0.5*(f + l), P, Vc2);
+  TopoDS_Edge E1, E2 = FindEinF (E, F2);
+  if (E2.Orientation() == TopAbs_REVERSED ) Vc2.Reverse();
+
+  Nf1 = Normal(E,F1);
+  Nf2 = Normal(E,F2);
+
+  Standard_Real sin =
+    Nf1.CrossSquareMagnitude(Nf2) / Nf1.SquareMagnitude() / Nf2.SquareMagnitude();
+  Standard_Boolean tangent = sin < 0.001;
+
+  Standard_Boolean inside = 0;
+  if (tangent) {
+    E1 = FindEinF (E, F1);
+    gp_Vec NNf1 = NextNormal(E1,F1);
+    gp_Vec NNf2 = NextNormal(E2,F2);
+    Vin2 = NNf2.Crossed(Vc2);
+    inside = Vin2 * NNf1 < 0;
+  }
+  else {
+    Vin2 = Nf2.Crossed(Vc2);
+    inside = Vin2 * Nf1 < 0;
+  }
+  
+  if (!CountDot) return inside;
+
+  if (tangent)
+    Vin2 = Nf2.Crossed(Vc2);
+  else
+    E1 = FindEinF (E, F1);
+    
+  Vc1 = Vc2;
+  if (E1.Orientation() != E2.Orientation()) 
+    Vc1.Reverse();
+  Vin1 = Nf1.Crossed(Vc1);
+
+  if (tangent) {
+    Standard_Real N1N2 = Nf1 * Nf2;
+    GoodOri = (Vin2 * Vin1 < 0) ? N1N2 > 0 : N1N2 < 0;
+  }
+  else {
+    Standard_Real V1N2 = Vin1 * Nf2;
+    GoodOri = ( inside ? V1N2 <= 0 : V1N2 >= 0);
+  }
+
+  Vin1.Normalize();
+  Vin2.Normalize();
+  
+  Dot = Vin2 * Vin1;
+  
+  return inside;
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop3d.hxx b/contrib/Netgen/libsrc/occ/Partition_Loop3d.hxx
new file mode 100644
index 0000000000000000000000000000000000000000..52abd115e99529dd53702ea1d9a768adc2242320
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop3d.hxx
@@ -0,0 +1,102 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  CEA/DEN, EDF R&D
+//
+//
+//
+//  File   : Partition_Loop3d.hxx
+//  Module : GEOM
+
+#ifndef _Partition_Loop3d_HeaderFile
+#define _Partition_Loop3d_HeaderFile
+
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _TopTools_IndexedDataMapOfShapeListOfShape_HeaderFile
+#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
+#endif
+#ifndef _Standard_Boolean_HeaderFile
+#include <Standard_Boolean.hxx>
+#endif
+#ifndef _Standard_Real_HeaderFile
+#include <Standard_Real.hxx>
+#endif
+class TopoDS_Shape;
+class TopTools_ListOfShape;
+class TopTools_MapOfOrientedShape;
+class TopoDS_Edge;
+class TopoDS_Face;
+class gp_Vec;
+
+
+#ifndef _Standard_HeaderFile
+#include <Standard.hxx>
+#endif
+#ifndef _Standard_Macro_HeaderFile
+#include <Standard_Macro.hxx>
+#endif
+
+class Partition_Loop3d  {
+
+public:
+
+   void* operator new(size_t,void* anAddress) 
+   {
+      return anAddress;
+   }
+   void* operator new(size_t size) 
+   { 
+      return Standard::Allocate(size); 
+   }
+   void  operator delete(void *anAddress) 
+   { 
+      if (anAddress) Standard::Free((Standard_Address&)anAddress); 
+   }
+   // Methods PUBLIC
+   // 
+   Partition_Loop3d();
+   void AddConstFaces(const TopoDS_Shape& S) ;
+   void AddSectionFaces(const TopoDS_Shape& S) ;
+   const TopTools_ListOfShape& MakeShells(const TopTools_MapOfOrientedShape& AvoidFacesMap) ;
+   static  Standard_Boolean IsInside(const TopoDS_Edge& E,const TopoDS_Face& F1,const TopoDS_Face& F2,const Standard_Boolean CountDot,Standard_Real& Dot,Standard_Boolean& GoodOri) ;
+   static  gp_Vec Normal(const TopoDS_Edge& E,const TopoDS_Face& F) ;
+
+
+
+
+
+protected:
+
+   // Methods PROTECTED
+   // 
+
+
+   // Fields PROTECTED
+   //
+
+
+private: 
+
+   // Methods PRIVATE
+   // 
+
+
+   // Fields PRIVATE
+   //
+   TopTools_ListOfShape myNewShells;
+   TopTools_ListOfShape myFaces;
+   TopTools_IndexedDataMapOfShapeListOfShape myEFMap;
+
+
+};
+
+
+
+
+
+// other Inline functions and methods (like "C++: function call" methods)
+//
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop3d.ixx b/contrib/Netgen/libsrc/occ/Partition_Loop3d.ixx
new file mode 100644
index 0000000000000000000000000000000000000000..a661b3242dafae68300a92a3cf42e7074ce5aeb0
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop3d.ixx
@@ -0,0 +1,14 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  CEA/DEN, EDF R&D
+//
+//
+//
+//  File   : Partition_Loop3d.ixx
+//  Module : GEOM
+
+#include "Partition_Loop3d.jxx"
+
+ 
+
+
diff --git a/contrib/Netgen/libsrc/occ/Partition_Loop3d.jxx b/contrib/Netgen/libsrc/occ/Partition_Loop3d.jxx
new file mode 100644
index 0000000000000000000000000000000000000000..9b654f41bb1a23a11c80d65fe9128e5a5830798d
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Loop3d.jxx
@@ -0,0 +1,30 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  CEA/DEN, EDF R&D
+//
+//
+//
+//  File   : Partition_Loop3d.jxx
+//  Module : GEOM
+
+#ifndef _TopoDS_Shape_HeaderFile
+#include <TopoDS_Shape.hxx>
+#endif
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _TopTools_MapOfOrientedShape_HeaderFile
+#include <TopTools_MapOfOrientedShape.hxx>
+#endif
+#ifndef _TopoDS_Edge_HeaderFile
+#include <TopoDS_Edge.hxx>
+#endif
+#ifndef _TopoDS_Face_HeaderFile
+#include <TopoDS_Face.hxx>
+#endif
+#ifndef _gp_Vec_HeaderFile
+#include <gp_Vec.hxx>
+#endif
+#ifndef _Partition_Loop3d_HeaderFile
+#include "Partition_Loop3d.hxx"
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Spliter.cxx b/contrib/Netgen/libsrc/occ/Partition_Spliter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b7de7d919bc29f4139ad3cbb0bad25ebcd92a968
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Spliter.cxx
@@ -0,0 +1,2168 @@
+#ifdef OCCGEOMETRY
+
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Spliter.cxx
+//  Author : Benedicte MARTIN
+//  Module : GEOM
+//  $Header: /cvs/netgen/netgen/libsrc/occ/Partition_Spliter.cxx,v 1.7 2008/03/31 14:20:28 wabro Exp $
+
+//using namespace std;
+#include <climits>
+#include "Partition_Inter2d.hxx"
+#include "Partition_Inter3d.hxx"
+#include "Partition_Loop2d.hxx"
+#include "Partition_Loop3d.hxx"
+#include "Partition_Spliter.ixx"
+
+#include "utilities.h"
+
+#include <Precision.hxx>
+#include <TopAbs_Orientation.hxx>
+#include <TopExp.hxx>
+#include <TopExp_Explorer.hxx>
+
+#include <TopTools_DataMapIteratorOfDataMapOfShapeListOfShape.hxx>
+#include <TopTools_DataMapOfShapeListOfShape.hxx>
+#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
+#include <TopTools_IndexedMapOfShape.hxx>
+#include <TopTools_ListIteratorOfListOfShape.hxx>
+#include <TopTools_ListOfShape.hxx>
+#include <TopTools_MapIteratorOfMapOfShape.hxx>
+#include <TopTools_SequenceOfShape.hxx>
+
+#include <Geom2d_Curve.hxx>
+#include <Geom_Curve.hxx>
+#include <Geom_Surface.hxx>
+#include <Geom_TrimmedCurve.hxx>
+#include <gp_Pnt.hxx>
+#include <gp_Pnt2d.hxx>
+#include <gp_Vec.hxx>
+
+#include <TopoDS.hxx>
+#include <TopoDS_Compound.hxx>
+#include <TopoDS_Edge.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopoDS_Iterator.hxx>
+#include <TopoDS_Shell.hxx>
+#include <TopoDS_Solid.hxx>
+#include <TopoDS_Vertex.hxx>
+#include <TopoDS_Wire.hxx>
+
+#include <BRepBndLib.hxx>
+#include <BRepClass3d_SolidClassifier.hxx>
+#include <BRepLib.hxx>
+#include <BRep_Tool.hxx>
+
+#include <Extrema_ExtPC.hxx>
+#include <GeomAdaptor_Curve.hxx>
+#include <TopOpeBRepTool_CurveTool.hxx>
+
+#ifdef DEB
+//# define PART_PERF
+#endif
+
+#ifdef PART_PERF
+# include <OSD_Chronometer.hxx>
+#endif
+
+//=======================================================================
+//function : isClosed
+//purpose  : check id a shape is closed, ie is a solid or a closed shell
+//=======================================================================
+
+static Standard_Boolean isClosed(const TopoDS_Shape& theShape)
+{
+  Standard_Boolean isClosed = (theShape.ShapeType() == TopAbs_SOLID);
+
+  if (!isClosed && theShape.ShapeType() == TopAbs_SHELL) {
+    TopTools_IndexedDataMapOfShapeListOfShape MEF;
+    TopExp::MapShapesAndAncestors(theShape, TopAbs_EDGE, TopAbs_FACE, MEF);
+    for (Standard_Integer i=1;  isClosed && i<=MEF.Extent();  ++i)
+      isClosed = ( MEF(i).Extent() != 1 );
+  }
+  
+  return isClosed;
+}
+
+//=======================================================================
+//function : Partition_Spliter
+//purpose  : constructor
+//=======================================================================
+
+Partition_Spliter::Partition_Spliter()
+{
+  myAsDes = new BRepAlgo_AsDes;
+  Clear();
+}
+
+//=======================================================================
+//function : AddTool
+//purpose  : add cutting tool that will _NOT_ be in result
+//=======================================================================
+
+void Partition_Spliter::AddTool(const TopoDS_Shape& S)
+{
+  if (S.ShapeType() < TopAbs_SOLID) { // compound or compsolid
+    TopoDS_Iterator it (S);
+    for (; it.More(); it.Next())
+    {
+      AddTool( it.Value());
+      myFaceShapeMap.Bind( it.Value(), S ); // to know compound by shape
+    }
+    return;
+  }
+
+  for (TopExp_Explorer exp(S,TopAbs_FACE); exp.More(); exp.Next())
+  {
+    myMapTools.Add(exp.Current());
+    myFaceShapeMap.Bind( exp.Current(), S );
+  }
+  if (isClosed( S ))
+    myClosedShapes.Add( S );
+}
+
+//=======================================================================
+//function : AddShape
+//purpose  : add object Shape to be splited
+//=======================================================================
+
+void Partition_Spliter::AddShape(const TopoDS_Shape& S)
+{
+  if (S.ShapeType() < TopAbs_SOLID) { // compound or compsolid
+    TopoDS_Iterator it (S);
+    for (; it.More(); it.Next())
+    {
+      AddShape( it.Value());
+      myFaceShapeMap.Bind( it.Value(), S ); // to know compound by shape
+    }
+    return;
+  }
+
+  TopExp_Explorer exp(S,TopAbs_FACE);
+  if (!exp.More()) { // do not split edges and vertices
+    //myBuilder.Add( myShape, S );
+    return;
+  }
+
+  Standard_Integer nbFacesBefore = myMapFaces.Extent(); // not to add twice the same S
+  for (; exp.More(); exp.Next()) {
+    const TopoDS_Shape & aFace = exp.Current();
+    if ( ! myFaceShapeMap.IsBound( aFace )) // keep shape of tool face added as object
+      myFaceShapeMap.Bind( aFace, S );
+    if (myMapFaces.Add( aFace ))
+      myImagesFaces.SetRoot( aFace );
+  }
+
+  if (nbFacesBefore == myMapFaces.Extent())
+    return;
+
+  // solids must be processed before all
+  if (S.ShapeType() == TopAbs_SOLID)
+    myListShapes.Prepend(S);
+  else
+    myListShapes.Append(S);
+
+  if (isClosed( S ))
+    myClosedShapes.Add( S );
+
+}
+
+//=======================================================================
+//function : Shape
+//purpose  : return resulting compound
+//=======================================================================
+
+TopoDS_Shape Partition_Spliter::Shape() const
+{
+  return myShape;
+}
+
+//=======================================================================
+//function : Clear
+//purpose  : clear fields
+//=======================================================================
+
+void Partition_Spliter::Clear()
+{
+  myDoneStep = TopAbs_SHAPE;
+  
+  myListShapes.Clear();
+  myMapFaces.Clear();
+  myMapTools.Clear();
+  myEqualEdges.Clear();
+  myNewSection.Clear();
+  myClosedShapes.Clear();
+  mySharedFaces.Clear();
+  myWrappingSolid.Clear();
+  myFaceShapeMap.Clear();
+  
+  myInternalFaces.Clear();
+  myIntNotClFaces.Clear();
+  
+  myAsDes->Clear();
+  myImagesFaces.Clear();
+  myImagesEdges.Clear();
+  myImageShape.Clear();
+  
+  //  myInter3d = Partition_Inter3d(myAsDes);
+  Partition_Inter3d hinter3d (myAsDes);
+  myInter3d = hinter3d;
+  
+  myAddedFacesMap.Clear();
+
+}
+
+//=======================================================================
+//function : Compute
+//purpose  : produce a result
+//=======================================================================
+
+void Partition_Spliter::Compute(const TopAbs_ShapeEnum Limit)
+{
+  if ((Limit != TopAbs_SHAPE && myDoneStep == Limit) ||
+      (Limit == TopAbs_SHAPE && myDoneStep == TopAbs_SOLID))
+    return;
+  
+  myBuilder.MakeCompound( myShape );
+  
+  TopTools_MapIteratorOfMapOfShape it;
+  TopTools_ListIteratorOfListOfShape itl;
+  TopExp_Explorer exp;
+
+#ifdef PART_PERF
+  OSD_Chronometer aCron;
+#endif
+
+  if (myDoneStep > TopAbs_VERTEX) {
+
+    TopTools_ListOfShape aListFaces;
+    aListFaces = myImagesFaces.Roots();
+    for (it.Initialize(myMapTools); it.More(); it.Next())
+      aListFaces.Append(it.Key());
+
+#ifdef PART_PERF
+    aCron.Start();
+#endif
+
+    //-----------------------------------------------
+    // Intersection between faces
+    //-----------------------------------------------
+    // result is in myAsDes as a map Face - list of new edges;
+    // special care is done for section edges, same domain faces and vertices:
+    // data about them is inside myInter3d
+    myInter3d.CompletPart3d(aListFaces, myFaceShapeMap);
+
+#ifdef PART_PERF
+    MESSAGE("+++ CompletPart3d()");
+    aCron.Show( cout );
+    aCron.Reset();
+    aCron.Start();
+#endif
+    //-----------------------------------------------
+    // Intersection of edges
+    //-----------------------------------------------
+
+    // add tool faces which must be reconstructed to myMapFaces too
+    FindToolsToReconstruct();
+
+#ifdef PART_PERF
+    MESSAGE("+++ FindToolsToReconstruct()");
+    aCron.Show( cout );
+    aCron.Reset();
+    aCron.Start();
+#endif
+
+    // add existing vertices to edges of object faces in myAsDes
+    TopTools_MapOfShape DoneEM;
+    for ( it.Initialize(myMapFaces); it.More(); it.Next()) {
+      const TopoDS_Shape& F  = it.Key();
+      TopoDS_Face FForward = TopoDS::Face(F.Oriented(TopAbs_FORWARD));
+      for (exp.Init(FForward,TopAbs_EDGE); exp.More(); exp.Next()) {
+        const TopoDS_Edge& E = TopoDS::Edge( exp.Current() );
+        myAsDes->Add(FForward,E);
+        if (DoneEM.Add(E)) {
+          TopoDS_Iterator itV(E);
+          for (; itV.More(); itV.Next()) {
+            const TopoDS_Vertex& V = TopoDS::Vertex( itV.Value());
+            myAsDes->Add(E, myInter3d.ReplaceSameDomainV( V, E ));
+          }
+        }
+      }
+    }
+
+    // intersect edges that are descendants of a face in myAsDes
+    TopTools_MapOfShape& Modif = myInter3d.TouchedFaces();
+    for ( it.Initialize(Modif); it.More(); it.Next()) {
+      const TopoDS_Face& F  = TopoDS::Face(it.Key());
+      Partition_Inter2d::CompletPart2d (myAsDes, F, myInter3d.NewEdges());
+    }
+    // now myAsDes contains also new vertices made at edge intersection as
+    // descendant of edges both new and old
+
+    myDoneStep = TopAbs_VERTEX;
+    
+#ifdef PART_PERF
+    MESSAGE("+++ CompletPart2d()");
+    aCron.Show( cout );
+    aCron.Reset();
+    aCron.Start();
+#endif
+  } //   if (myDoneStep > TopAbs_VERTEX)
+  
+  if (Limit == TopAbs_VERTEX) {
+    // add new vertices to myShape
+    for ( it.Initialize( myInter3d.NewEdges() ); it.More(); it.Next()) {
+      if (! myAsDes->HasDescendant( it.Key() ))
+        continue;
+      itl.Initialize( myAsDes->Descendant( it.Key() ));
+      for (; itl.More(); itl.Next()) 
+        myBuilder.Add ( myShape, itl.Value() );
+    }
+    return;
+  }
+  
+
+  if (myDoneStep > TopAbs_EDGE) {
+
+    //-----------------------------------------------
+    //-----------------------------------------------
+    // ------- Reconstruction of all the edges.------
+    //-----------------------------------------------
+    //-----------------------------------------------
+
+    // ==============
+    // cut new edges
+    // ==============
+    TopTools_ListOfShape LSE; // all edge splits
+    for ( it.Initialize(myInter3d.NewEdges()); it.More(); it.Next()) {
+
+      TopoDS_Vertex V1,V2;
+      TopoDS_Edge EE = TopoDS::Edge(it.Key());
+
+      TopTools_ListOfShape aListV, aListF;
+      aListV = myAsDes->Descendant(EE); // intersection vertices
+      aListF = myAsDes->Ascendant(EE);  // intersected faces
+
+      if (aListV.IsEmpty())
+        continue;  // new edge does not intersect any other edge
+
+      // Add end vertices to new edges only if 
+      // one face is Tool and the other is Shape
+      Standard_Boolean isTool1 = ! myMapFaces.Contains( aListF.First() );
+      Standard_Boolean isTool2 = ! myMapFaces.Contains( aListF.Last() );
+      if (isTool1 || isTool2)
+      {
+        TopExp::Vertices(EE,V1,V2);
+	Standard_Real Tol = Max (BRep_Tool::Tolerance( V1 ),
+				 BRep_Tool::Tolerance( V2 ));
+
+        gp_Pnt P1 = BRep_Tool::Pnt(V1);
+        gp_Pnt P2 = BRep_Tool::Pnt(V2);
+        Standard_Boolean AddV1 = Standard_True;
+        Standard_Boolean AddV2 = Standard_True;
+
+        // add only if there is no intersection at end vertex
+        for (itl.Initialize(aListV); itl.More(); itl.Next()) {
+          const TopoDS_Vertex& Ve = TopoDS::Vertex(itl.Value()) ;
+          Standard_Real Tol2 = Max ( Tol, BRep_Tool::Tolerance( Ve ));
+          Tol2 *= Tol2;
+          gp_Pnt P = BRep_Tool::Pnt(Ve);
+          if (AddV1 && P.SquareDistance(P1) <= Tol2)
+            AddV1 = Standard_False;
+
+          if (AddV2 && P.SquareDistance(P2) <= Tol2) 
+            AddV2 = Standard_False;
+        }
+
+        if (AddV1) {
+          aListV.Append(V1);
+          myAsDes->Add(EE,V1);
+        }
+
+        if (AddV2) {
+          aListV.Append(V2);
+          myAsDes->Add(EE,V2);
+        }
+      }
+
+      // cut new edges
+      Standard_Integer NbV=aListV.Extent() ;
+      if (NbV>1 || (NbV==1 && V1.IsSame(V2)) ) {
+        TopTools_ListOfShape LNE;
+        MakeEdges (EE,aListV, LNE);
+        myImagesEdges.Bind(EE,LNE);
+	LSE.Append( LNE );
+      }
+    }
+
+    // ==============
+    // cut old edges
+    // ==============
+    for ( it.Initialize(myMapFaces); it.More(); it.Next()) {
+      for (exp.Init( it.Key(), TopAbs_EDGE); exp.More(); exp.Next()) {
+        const TopoDS_Edge& EE = TopoDS::Edge( exp.Current() );
+        if ( myImagesEdges.HasImage( EE ))
+          continue;
+        TopTools_ListOfShape  LNE;
+        const TopTools_ListOfShape& aListVV = myAsDes->Descendant(EE);
+        MakeEdges (EE, aListVV, LNE);
+        myImagesEdges.Bind(EE,LNE);
+	LSE.Append( LNE );
+      }
+    }
+#ifdef PART_PERF
+    MESSAGE("+++ Cut Edges");
+    aCron.Show( cout );
+    aCron.Reset();
+    aCron.Start();
+#endif
+
+    // process same domain section edges
+    MergeEqualEdges( LSE );
+    
+    myDoneStep = TopAbs_EDGE;
+    
+#ifdef PART_PERF
+    MESSAGE("+++ MergeEqualEdges()");
+    aCron.Show( cout );
+    aCron.Reset();
+    aCron.Start();
+#endif
+  }  //   if (myDoneStep > TopAbs_EDGE) 
+
+  if (Limit == TopAbs_EDGE) {
+    // add splits of old edges
+    TopTools_ListIteratorOfListOfShape itNE;
+    for (itl.Initialize( myListShapes );itl.More();itl.Next()) {
+      if (myMapTools.Contains( itl.Value() ))
+        continue; // skip tool faces
+      for ( exp.Init( itl.Value(), TopAbs_EDGE ); exp.More(); exp.Next()) {
+	itNE.Initialize( myImagesEdges.Image( exp.Current() ));
+	for ( ; itNE.More(); itNE.Next())
+	  myBuilder.Add ( myShape, itNE.Value() );
+      }
+    }
+    // add splits of new edges
+    for ( it.Initialize( myInter3d.NewEdges() ); it.More(); it.Next()) {
+      itNE.Initialize( myImagesEdges.Image( it.Key() ));
+      for (; itNE.More(); itNE.Next())
+        myBuilder.Add ( myShape, itNE.Value() );
+    }
+    return;
+  }
+  
+  
+  //-----------------------------------------------
+  // split faces
+  //-----------------------------------------------
+
+  if (myDoneStep > TopAbs_FACE) {
+    
+    for (itl.Initialize(myListShapes);itl.More();itl.Next()) {
+      TopoDS_Shape FacesComp = MakeFaces ( itl.Value());
+      // there is a cunning here: myImagesFaces keeps faces made by Loop2d
+      // but some of them may be replaced with splits of same domain face
+      // and myImageShape keeps ultimate result
+      myImageShape.Bind( itl.Value(), FacesComp );
+    }
+    
+    myDoneStep = TopAbs_FACE;
+#ifdef PART_PERF
+    MESSAGE("+++ MakeFaces()");
+    aCron.Show( cout );
+    aCron.Reset();
+    aCron.Start();
+#endif
+  }
+  
+  if (Limit == TopAbs_WIRE ||
+      Limit == TopAbs_FACE)   {
+    for (itl.Initialize(myListShapes);itl.More();itl.Next()) {
+      if ( myMapTools.Contains( itl.Value() ))
+	continue; // no result needed for a tool face
+      const TopoDS_Shape& FacesComp = myImageShape.Image( itl.Value() ).First();
+      for ( exp.Init( FacesComp, Limit); exp.More(); exp.Next())
+	myBuilder.Add ( myShape, exp.Current());
+    }
+    return;
+  }
+
+  
+  //-----------------------------------------------
+  // split and add solids and shells
+  //-----------------------------------------------
+
+  Standard_Boolean makeSolids = (Limit == TopAbs_SHAPE ||
+				 Limit < TopAbs_SHELL);
+  for (itl.Initialize(myListShapes);itl.More();itl.Next())
+  {
+    const TopoDS_Shape & S = itl.Value();
+    if (S.ShapeType() > TopAbs_SHELL)
+      continue;
+
+    TopTools_ListOfShape NSL; // new shape list
+    MakeShells (S , NSL);
+    if (makeSolids && S.ShapeType() == TopAbs_SOLID )
+      MakeSolids( S, NSL );
+
+    // store new shells or solids
+    TopTools_ListIteratorOfListOfShape itNSL (NSL);
+    for ( ; itNSL.More(); itNSL.Next()) 
+      myBuilder.Add (myShape, itNSL.Value());
+  }
+#ifdef PART_PERF
+    MESSAGE("+++ MakeShells()");
+    aCron.Show( cout );
+#endif
+
+  //-----------------------------------------------
+  // add split faces
+  //-----------------------------------------------
+
+  for (itl.Initialize(myListShapes);itl.More();itl.Next())
+  {
+    const TopoDS_Shape & S = itl.Value();
+    if (S.ShapeType() != TopAbs_FACE ||
+        myMapTools.Contains( S ))
+      continue; 
+    TopoDS_Iterator itS( myImageShape.Image(S).First() );
+    for (; itS.More(); itS.Next())
+      if (! myAddedFacesMap.Contains( itS.Value() ))
+        myBuilder.Add (myShape, itS.Value());
+  }
+
+  myDoneStep = makeSolids ? TopAbs_SOLID : TopAbs_SHELL;
+  
+}
+
+//=======================================================================
+//function : MakeSolids
+//purpose  : make solids out of Shells
+//=======================================================================
+
+void Partition_Spliter::MakeSolids(const TopoDS_Shape &   theSolid,
+                                   TopTools_ListOfShape & theShellList)
+{
+  // for a solid wrapping other shells or solids without intersection,
+  // it is necessary to find shells making holes in it
+
+  TopTools_ListOfShape aNewSolids; // result
+  TopTools_ListOfShape aHoleShells;
+  TopoDS_Shape anInfinitePointShape;
+
+  Standard_Boolean isWrapping = myWrappingSolid.Contains( theSolid );
+  if (!isWrapping && !theShellList.IsEmpty())
+  {
+    // check if theSolid initially has internal shells
+    TopoDS_Iterator aShellExp (theSolid);
+    aShellExp.Next();
+    isWrapping = aShellExp.More();
+  }
+  
+  TopTools_ListIteratorOfListOfShape aShellIt(theShellList);
+  for ( ; aShellIt.More(); aShellIt.Next())
+  {
+    const TopoDS_Shape & aShell = aShellIt.Value();
+
+    // check if a shell is a hole
+    if (isWrapping && IsInside (anInfinitePointShape, aShell))
+      aHoleShells.Append( aShell );
+    else
+    {
+      // make a solid from a shell
+      TopoDS_Solid Solid;
+      myBuilder.MakeSolid( Solid );
+      myBuilder.Add (Solid, aShell);
+
+      aNewSolids.Append (Solid);
+    }
+  }
+
+  // find an outer shell most close to each hole shell
+  TopTools_DataMapOfShapeShape aInOutMap;
+  for (aShellIt.Initialize( aHoleShells ); aShellIt.More(); aShellIt.Next())
+  {
+    const TopoDS_Shape & aHole = aShellIt.Value();
+    TopTools_ListIteratorOfListOfShape aSolisIt (aNewSolids);
+    for ( ; aSolisIt.More(); aSolisIt.Next())
+    {
+      const TopoDS_Shape & aSolid = aSolisIt.Value();
+      if (! IsInside( aHole, aSolid ))
+        continue;
+
+      if ( aInOutMap.IsBound (aHole))
+      {
+        const TopoDS_Shape & aSolid2 = aInOutMap( aHole );
+        if ( IsInside( aSolid, aSolid2 ))
+        {
+          aInOutMap.UnBind( aHole );
+          aInOutMap.Bind ( aHole, aSolid );
+        }
+      }
+      else
+        aInOutMap.Bind ( aHole, aSolid );
+    }
+
+    // add aHole to a solid
+    if (aInOutMap.IsBound( aHole ))
+      myBuilder.Add ( aInOutMap( aHole ), aHole );
+  }
+
+  theShellList.Clear();
+  theShellList.Append( aNewSolids );
+}
+ 
+//=======================================================================
+//function : FindFacesInside
+//purpose  : return compound of faces  of other shapes that are
+//           inside <theShape>. 
+//           <theShape> is an object shape.
+//           <CheckClosed> makes avoid faces that do not form a
+//           closed shell
+//           <All> makes return already added faces
+//=======================================================================
+
+TopoDS_Shape Partition_Spliter::FindFacesInside(const TopoDS_Shape& theShape,
+						const Standard_Boolean CheckClosed,
+						const Standard_Boolean All)
+{
+  // ================================================
+  // check if internal faces have been already found
+  // ================================================
+  TopExp_Explorer expl;
+  if (myInternalFaces.IsBound( theShape ))
+  {
+    TopoDS_Shape aIntFComp = myInternalFaces.Find ( theShape );
+    TopoDS_Shape aIntRemFComp = myIntNotClFaces.Find ( theShape );
+
+    expl.Init( aIntRemFComp, TopAbs_FACE);
+    if (CheckClosed || !expl.More())
+      return aIntFComp;
+
+    TopoDS_Compound C;
+    myBuilder.MakeCompound( C );
+    // add removed faces
+    for (; expl.More(); expl.Next())
+      myBuilder.Add( C, expl.Current() );
+    // add good internal faces
+    for (expl.Init( aIntFComp, TopAbs_FACE); expl.More(); expl.Next())
+      myBuilder.Add( C, expl.Current() );
+    return C;
+  }
+
+  // ===================================
+  // get data for internal faces search
+  // ===================================
+
+  // compound of split faces of theShape 
+  const TopoDS_Shape& CSF = myImageShape.Image(theShape).First();
+
+  TopTools_MapOfShape MSE, MFP;
+  TopTools_DataMapOfShapeListOfShape DMSEFP;
+  TopTools_MapIteratorOfMapOfShape itm;
+  TopTools_ListOfShape EmptyL;
+
+  // MSE filling: map of new section edges of CSF
+  for (expl.Init(CSF,TopAbs_EDGE); expl.More(); expl.Next()) {
+    const TopoDS_Shape & resE = expl.Current() ;
+    if (myNewSection.Contains( resE )) // only new edges
+      MSE.Add(resE);
+  }
+
+  // DMEF: map edge of CSF - faces of CSF
+  TopTools_IndexedDataMapOfShapeListOfShape DMEF;
+  TopExp::MapShapesAndAncestors(CSF, TopAbs_EDGE, TopAbs_FACE, DMEF);
+
+  // Fill
+  // 1.  MFP - a map of faces to process: map of resulting faces except
+  // those of theShape; we`ll add to C those of them which are inside CSF
+  // 2.  DMSEFP - edge of MSE => faces of MFP
+  TopTools_ListIteratorOfListOfShape itl;
+  for (itl.Initialize(myListShapes);itl.More();itl.Next()) {
+    const TopoDS_Shape& aShape = itl.Value();
+    if ( theShape.IsSame( aShape )) continue;
+    // fill maps
+    // iterate on split faces of aShape
+    TopoDS_Iterator itF ( myImageShape.Image(aShape).First() );
+    for ( ; itF.More(); itF.Next()) {
+      const TopoDS_Shape& sf = itF.Value();
+      MFP.Add(sf);
+      // iterate on edges of split faces of aShape,
+      // add to DMSEFP edges that are new
+      for (expl.Init( sf, TopAbs_EDGE ); expl.More(); expl.Next()) {
+	TopoDS_Shape se = expl.Current();
+	if ( MSE.Contains(se)) {// section edge
+	  if (!DMSEFP.IsBound(se)) 
+	    DMSEFP.Bind(se,EmptyL);
+	  DMSEFP(se).Append(sf);
+	}
+      }
+    }
+  }
+
+  // add tool faces having section edges on faces of theShape to MFP and DMSEFP;
+  // such tool faces need not to be reconstructed and so they are not in myListShapes
+  for (itm.Initialize(myMapTools); itm.More(); itm.Next())
+  {
+    const TopoDS_Shape & aToolFace = itm.Key();
+    if (myMapFaces.Contains( aToolFace ))
+      continue;
+    MFP.Add(aToolFace);
+    for (expl.Init( aToolFace, TopAbs_EDGE ); expl.More(); expl.Next()) {
+      TopoDS_Shape se = expl.Current();
+      if ( MSE.Contains( se )) {// section edge
+        if (!DMSEFP.IsBound( se )) 
+          DMSEFP.Bind( se, EmptyL );
+        DMSEFP( se ).Append( aToolFace );
+      }
+    }
+  }
+  
+
+  // ===========================
+  // find faces inside theShape
+  // ===========================
+
+  Standard_Boolean skipAlreadyAdded = Standard_False;
+  Standard_Boolean GoodOri, inside;
+  Standard_Real dot;
+  TopTools_ListOfShape KeepFaces;
+  TopTools_DataMapIteratorOfDataMapOfShapeListOfShape Mapit;
+
+  // iterate on section edges, check faces of other shapes
+  // sharing section edges and put internal faces to KeepFaces
+  for (Mapit.Initialize(DMSEFP); Mapit.More() ; Mapit.Next() ) {
+    // a new edge of theShape
+    const TopoDS_Edge& E = TopoDS::Edge (Mapit.Key());
+    // an original edge of which E is a split
+    const TopoDS_Edge& OrigE = TopoDS::Edge ( myImagesEdges.Root( E ));
+    // does OrigE itself splits a face
+    Standard_Boolean isSectionE = myInter3d.IsSectionEdge ( OrigE );
+
+    // split faces of other shapes sharing E
+    TopTools_ListOfShape& LSF = DMSEFP.ChangeFind(E);
+    itl.Initialize( LSF );
+    while (itl.More()) {
+      // a split faces of other shape
+      TopoDS_Face aFace1 = TopoDS::Face(itl.Value());
+      // remove aFace1 form DMSEFP and MFP
+      LSF.Remove( itl ); // == itl.Next();
+      if (!MFP.Remove( aFace1 ))
+	continue; // was not is MFP ( i.e already checked)
+      // check if aFace1 was already added to 2 shells
+      if (!All &&
+	  myAddedFacesMap.Contains( aFace1 ) &&
+	  myAddedFacesMap.Contains( aFace1.Reversed() )) {
+	skipAlreadyAdded = Standard_True;
+	continue;
+      }
+
+      // find another face which originates from the same face as aFace1:
+      // usually aFace2 is internal if aFace1 is not and vice versa
+
+      TopoDS_Shape anOrigFace = aFace1;
+      if (myImagesFaces.IsImage(aFace1))
+        anOrigFace = myImagesFaces.Root(aFace1);
+      TopoDS_Shape aFace2;
+      if ( !isSectionE ) {
+        while (itl.More()) {
+          aFace2 = itl.Value();
+          if (!MFP.Contains( aFace2 )) {
+            LSF.Remove( itl );
+            continue;
+          }
+          if (anOrigFace.IsSame( myImagesFaces.Root( aFace2 )))
+            break;
+          itl.Next();
+        }
+        if (itl.More()) { // aFace2 found, remove it from maps
+          LSF.Remove( itl );
+          MFP.Remove(aFace2);
+        }
+        else
+          aFace2.Nullify();
+        itl.Initialize( LSF );
+      }
+
+      // check that anOrigFace is not same domain with CSF faces it intersects
+
+      const TopTools_ListOfShape& FL = DMEF.FindFromKey(E); //faces of CSF sharing E
+      const TopoDS_Shape& origF1 = myImagesFaces.Root(FL.First());
+      const TopoDS_Shape& origF2 = myImagesFaces.Root(FL.Last());
+      Standard_Boolean sameDom1 = anOrigFace.IsSame( origF1 );
+      Standard_Boolean sameDom2 = anOrigFace.IsSame( origF2 );
+      if (!(sameDom1 || sameDom2) && myInter3d.HasSameDomainF( anOrigFace )) {
+	sameDom1 = myInter3d.IsSameDomainF( anOrigFace, origF1);
+        if (origF1 == origF2)
+          sameDom2 = sameDom1;
+        else
+          myInter3d.IsSameDomainF( anOrigFace, origF2);
+      }
+      if (sameDom1 && sameDom2)
+	continue;
+      if ((sameDom1 || sameDom2)) {
+	inside = Partition_Loop3d::IsInside (E,
+					     TopoDS::Face(FL.First()),
+					     TopoDS::Face(FL.Last()),
+					     1, dot, GoodOri);
+	if (inside || (dot + Precision::Angular() >= 1.0))
+	  continue; // E is convex between origF1 and origF2 or they are tangent
+      }
+
+
+      // keep one of found faces
+
+      //face of CSF sharing E
+      const TopoDS_Shape& aShapeFace = sameDom1 ? FL.Last() : FL.First();
+      // analyse aFace1 state
+      inside = Partition_Loop3d::IsInside (E, TopoDS::Face(aShapeFace), aFace1,
+					   1, dot, GoodOri);
+      if (inside && isSectionE)
+      {
+        // aFace1 must be tested with both adjacent faces of CSF
+        const TopoDS_Shape& aShapeFace2 = sameDom1 ? FL.First() : FL.Last();
+        if (aShapeFace2 != aShapeFace)
+          inside = Partition_Loop3d::IsInside (E, TopoDS::Face(aShapeFace2), aFace1,
+                                               1, dot, GoodOri);
+      }
+
+      // store internal face
+      if (inside)
+        KeepFaces.Append(aFace1);
+
+      else if (!aFace2.IsNull())
+      {
+        if (dot + Precision::Angular() >= 1.0)
+        {
+          // aFace2 state is not clear, it will be analysed alone,
+          // put it back to the maps
+          MFP.Add( aFace2 );
+          LSF.Append( aFace2 );
+        }
+        else
+          KeepFaces.Append(aFace2);
+      }
+    }
+  }
+
+  // ===================================================
+  // add not distributed faces connected with KeepFaces
+  // ===================================================
+
+  // ultimate list of internal faces
+  TopTools_ListOfShape KeptFaces;
+
+  // add to MFP not split tool faces as well, they may be connected with
+  // tool faces interfering with theShape
+  for ( itm.Initialize(myMapTools); itm.More(); itm.Next() ) {
+    const TopoDS_Shape& aToolFace = itm.Key();
+    if (!myImageShape.HasImage(aToolFace))
+      MFP.Add (aToolFace);
+  }
+
+  if (MFP.IsEmpty())
+    KeptFaces.Append (KeepFaces);
+
+  while (!KeepFaces.IsEmpty())
+  {
+    // KeepEdges : map of edges of faces kept last time
+    TopTools_IndexedMapOfShape KeepEdges;
+    for ( itl.Initialize(KeepFaces); itl.More(); itl.Next() ) {
+      TopExp::MapShapes( itl.Value(), TopAbs_EDGE, KeepEdges);
+      KeptFaces.Append( itl.Value() );
+    }
+
+    KeepFaces.Clear();
+
+    // keep faces connected with already kept faces by KeepEdges
+    for ( itm.Initialize(MFP); itm.More(); itm.Next() ) {
+      const TopoDS_Shape& FP = itm.Key();
+      for (expl.Init(FP,TopAbs_EDGE); expl.More(); expl.Next()) {
+        const TopoDS_Shape& se = expl.Current();
+        if (!MSE.Contains(se) && KeepEdges.Contains(se) ) {
+          KeepFaces.Append(FP);
+          MFP.Remove(FP);
+          break;
+        }
+      }
+    }
+  }
+
+  // ===============================================================
+  // here MFP contains faces outer of theShape and those of shapes
+  // which do not interfere with theShape at all and between which
+  // there may be those wrapped by theShape and whose faces may be
+  // needed to be returned as well
+  // ===============================================================
+
+  Standard_Boolean isSolid = (theShape.ShapeType() == TopAbs_SOLID);
+  if (All || isSolid)  // All is for sub-result removal
+  {
+    // loop on not used faces; checked faces will be removed from MFP
+    // during the loop
+    for ( itm.Initialize( MFP ); itm.More(); itm.Next() ) {
+      const TopoDS_Shape & aFace = itm.Key();
+
+      // a shape which aFace originates from
+      TopoDS_Shape anOrigShape = GetOriginalShape( aFace );
+
+      // find out if all split faces of anOrigShape are not in MFP
+      // and by the way remove them from MFP
+      Standard_Boolean isAllOut = Standard_True;
+      TopoDS_Shape aSplitFaces = anOrigShape;
+      if (myImageShape.HasImage(anOrigShape))
+        aSplitFaces = myImageShape.Image(anOrigShape).First();
+
+      TopTools_ListOfShape aSplitFaceL; // faces candidate to be kept
+      for (expl.Init( aSplitFaces, TopAbs_FACE ); expl.More(); expl.Next())
+      {
+        const TopoDS_Shape & aSpFace = expl.Current();
+        // a tool face which became object has image but the whole tool shape has not
+        if (myImageShape.HasImage( aSpFace ))
+        {
+          TopExp_Explorer exF (myImageShape.Image( aSpFace ).First(), TopAbs_FACE );
+          for ( ; exF.More(); exF.Next() )
+          {
+            aSplitFaceL.Append( exF.Current() );
+            if ( ! MFP.Remove( exF.Current() ) && isAllOut )
+              // a shared face might be removed from MFP during a prev loop
+              isAllOut = mySharedFaces.Contains( exF.Current() );
+          }
+        }
+        else
+        {
+          aSplitFaceL.Append( aSpFace );
+          if ( ! MFP.Remove( aSpFace ) && isAllOut)
+            // a shared face might be removed from MFP during a prev loop
+            isAllOut = mySharedFaces.Contains( aSpFace );
+        }
+      }
+      itm.Initialize( MFP ); // iterate remaining faces
+      if ( !isAllOut )
+        continue;
+
+      // classify anOrigShape against theShape
+      if (IsInside (anOrigShape, theShape))
+      {
+        if (isSolid && myClosedShapes.Contains( anOrigShape ))
+          // to make a special care at solid reconstruction
+          myWrappingSolid.Add ( theShape );
+
+        // keep faces of an internal shape anOrigShape
+        KeptFaces.Append( aSplitFaceL );
+      }
+    }
+  }
+
+  // ====================================================
+  // check if kept faces form a shell without free edges
+  // ====================================================
+
+  DMEF.Clear();  // edge - kept faces
+  MFP.Clear(); // reuse it for wrong faces
+  if (CheckClosed) {
+    for (itl.Initialize(KeptFaces); itl.More(); itl.Next() ) 
+      TopExp::MapShapesAndAncestors(itl.Value(), TopAbs_EDGE, TopAbs_FACE, DMEF);
+
+    Standard_Integer i, nb = DMEF.Extent();
+    Standard_Boolean isClosed = Standard_False;
+    while (!isClosed) {
+      isClosed = Standard_True;
+      for (i=1;  isClosed && i<=nb;  ++i) {
+        const TopoDS_Shape& E = DMEF.FindKey( i );
+        if (! BRep_Tool::Degenerated( TopoDS::Edge( E )) &&
+            ! MSE.Contains( E ))
+          isClosed = ( DMEF(i).Extent() != 1 );
+      }
+      if (!isClosed) {
+        const TopoDS_Shape& F = DMEF.FindFromIndex( i-1 ).First(); // bad face
+        MFP.Add( F ); 
+        // remove bad face from DMEF
+        for (expl.Init( F, TopAbs_EDGE); expl.More(); expl.Next()) {
+	  const TopoDS_Shape& E = expl.Current();
+          TopTools_ListOfShape& FL = DMEF.ChangeFromKey( E );
+          for (itl.Initialize( FL ); itl.More(); itl.Next() ) {
+            if ( F.IsSame( itl.Value() )) {
+              FL.Remove( itl );
+              break;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // ==============
+  // make a result
+  // ==============
+
+  TopoDS_Compound C;
+  // compound of removed internal faces
+  TopoDS_Compound CNotCl;
+
+  myBuilder.MakeCompound(C);
+  myBuilder.MakeCompound(CNotCl);
+
+  // add to compounds
+  for (itl.Initialize(KeptFaces); itl.More(); itl.Next() )
+  {
+    TopoDS_Shape & aIntFace = itl.Value();
+    if (! MFP.Contains( aIntFace ))
+      myBuilder.Add( C, aIntFace);
+    else
+      myBuilder.Add( CNotCl, aIntFace);
+  }
+
+  if (!skipAlreadyAdded && CheckClosed)
+  {
+    myInternalFaces.Bind( theShape, C );
+    myIntNotClFaces.Bind( theShape, CNotCl );
+  }
+
+  return C;
+}
+
+//=======================================================================
+//function : MakeShell
+//purpose  : split S into compound of shells
+//=======================================================================
+
+void Partition_Spliter::MakeShells(const TopoDS_Shape& S,
+                                   TopTools_ListOfShape& NS)
+{
+  Partition_Loop3d ShellMaker;
+  // get compound of split faces of S
+  const TopoDS_Shape& FacesComp = myImageShape.Image(S).First();
+  ShellMaker.AddConstFaces( FacesComp );
+  // add split faces inside S
+  if (myClosedShapes.Contains( S )) {
+    TopoDS_Shape InternalFacesComp = FindFacesInside(S, Standard_True);
+    ShellMaker.AddSectionFaces( InternalFacesComp );
+  }
+  
+  NS = ShellMaker.MakeShells( myAddedFacesMap );
+
+  // Add faces added to new shell to myAddedFacesMap:
+  // avoid rebuilding twice commont part of 2 solids.
+  TopTools_ListIteratorOfListOfShape itS(NS);
+  while ( itS.More()) {
+    TopExp_Explorer expF (itS.Value(), TopAbs_FACE);
+    for (; expF.More(); expF.Next())
+      myAddedFacesMap.Add (expF.Current());
+    
+    itS.Next();
+  }
+}
+
+//=======================================================================
+//function : findEqual
+//purpose  : compare edges of EL1 against edges of EL2,
+//           Result is in EMM binding EL1 edges to list of equal edges.
+//           Edges are considered equall only if they have same vertices.
+//           <addSame>==True makes consider same edges as equal
+//           Put in <AllEqMap> all equal edges
+//=======================================================================
+
+static void findEqual (const TopTools_ListOfShape& EL1,
+		       const TopTools_ListOfShape& EL2,
+		       const Standard_Boolean addSame,
+		       TopTools_DataMapOfShapeListOfShape& EEM,
+		       TopTools_MapOfShape& AllEqMap)
+{
+  // map vertices to edges for EL2
+  TopTools_DataMapOfShapeListOfShape VEM;
+  TopTools_ListIteratorOfListOfShape itE1, itE2(EL2);
+  TopoDS_Iterator itV;
+  TopTools_ListOfShape emptyL;
+  for (; itE2.More(); itE2.Next()) {
+    for (itV.Initialize( itE2.Value() ); itV.More(); itV.Next()) {
+      const TopoDS_Shape& V = itV.Value(); 
+      if (! VEM.IsBound( V ) )
+	VEM.Bind( V, emptyL);
+      VEM( V ).Append( itE2.Value());
+    }
+  }
+
+  gp_Vec D1, D2;
+  gp_Pnt P;
+  Standard_Real f,l,u,tol;
+  Handle(Geom_Curve) C1, C2;
+  Extrema_ExtPC Extrema;
+  TopoDS_Vertex V1, V2, V3, V4;
+
+  AllEqMap.Clear();
+  
+  for (itE1.Initialize(EL1); itE1.More(); itE1.Next()) {
+    const TopoDS_Edge& E1 = TopoDS::Edge( itE1.Value());
+    if (BRep_Tool::Degenerated( E1 ) || AllEqMap.Contains (E1))
+      continue;
+    TopExp::Vertices( E1, V1, V2 );
+
+    if (VEM.IsBound(V1))
+      itE2.Initialize( VEM(V1) );
+    for (; itE2.More(); itE2.Next()) {
+      const TopoDS_Edge& E2 = TopoDS::Edge( itE2.Value());
+      if (BRep_Tool::Degenerated( E2 ) || AllEqMap.Contains (E2))
+        continue;
+
+      if (E1.IsSame(E2)) {
+	if (!addSame)
+	  continue;
+      }
+      else {
+	TopExp::Vertices( E2, V3, V4);
+	if (!V2.IsSame(V4) && !V2.IsSame(V3))
+	  continue;
+	// E1 and E2 have same vertices
+	// check D1 at end points.
+        C2 = BRep_Tool::Curve( E2, f,l);
+        C1 = BRep_Tool::Curve( E1, f,l);
+	u = BRep_Tool::Parameter(V1,E1);
+        C1->D1(u, P, D1);
+	u = BRep_Tool::Parameter(V1.IsSame(V3) ? V3 : V4, E2);
+	C2->D1(u, P, D2);
+        D1.Normalize(); D2.Normalize();
+        if (Abs(D1*D2) + Precision::Angular() < 1.0)
+          continue;
+	if (! V1.IsSame(V2)) {
+	  u = BRep_Tool::Parameter(V2,E1);
+	  C1->D1(u, P, D1);
+	  u = BRep_Tool::Parameter(V2.IsSame(V3) ? V3 : V4, E2);
+	  C2->D1(u, P, D2);
+	  D1.Normalize(); D2.Normalize();
+	  if (Abs(D1*D2) + Precision::Angular() < 1.0)
+	    continue;
+	}
+        // check distance at a couple of internal points
+        tol = Max(BRep_Tool::Tolerance(E1),
+                  BRep_Tool::Tolerance(E2));
+        GeomAdaptor_Curve AC1(C1);
+        Extrema.Initialize(AC1,f,l);
+	Standard_Boolean ok = Standard_True, hasMin = Standard_False;
+	BRep_Tool::Range( E2, f, l);
+        Standard_Integer i=1, nbi=3;
+        for (; i<nbi && ok; ++i) {
+          Extrema.Perform( C2->Value( f+(l-f)*i/nbi ));
+          Standard_Integer j=1, nbj=Extrema.NbExt();
+          for (; j<=nbj && ok; ++j) {
+            if (Extrema.IsMin(j)) {
+	      hasMin = Standard_True;
+	      ok = Extrema.Value(j) <= tol;  // V6.3
+	      // ok = Extrema.SquareDistance(j) <= tol;  // V6.5
+	    }
+          }
+        }
+        if ( !hasMin || !ok)
+          continue;
+      }
+      // bind E2 to E1 in EEM
+      if (!EEM.IsBound(E1)) {
+        EEM.Bind (E1, emptyL);
+	AllEqMap.Add (E1);
+      }
+      EEM(E1).Append(E2);
+      AllEqMap.Add (E2);
+    }
+  }
+}
+
+//=======================================================================
+//function : MakeFaces
+//purpose  : split faces of S, return compound of new faces
+//=======================================================================
+
+TopoDS_Shape Partition_Spliter::MakeFaces (const TopoDS_Shape& S)
+{
+  TopoDS_Compound C;
+  myBuilder.MakeCompound(C);
+  
+  TopTools_ListIteratorOfListOfShape itl, itNE;
+  
+  TopExp_Explorer exp(S,TopAbs_FACE);
+  for (; exp.More(); exp.Next()) {
+
+    const TopoDS_Face& F = TopoDS::Face(exp.Current());
+
+    TopTools_ListOfShape LNF;
+    
+    if (myImagesFaces.HasImage( F )) {
+      myImagesFaces.LastImage( F, LNF );
+      TopAbs_Orientation oriF = F.Orientation();
+      for ( itl.Initialize( LNF ); itl.More(); itl.Next())
+	itl.Value().Orientation( oriF );
+    }
+    else {
+
+      Partition_Loop2d loops;
+      loops.Init(F);
+
+      TopTools_IndexedMapOfShape EM;
+      TopExp::MapShapes( F, TopAbs_EDGE, EM);
+
+      TopTools_MapOfShape AddedEqualM, EqualSeamM;
+      Standard_Boolean needRebuild = Standard_False;
+
+      // add splits to loops
+
+      // LE: old edges + new not splitted edges
+      const TopTools_ListOfShape& LE = myAsDes->Descendant(F);
+      for (itl.Initialize(LE); itl.More(); itl.Next()) {
+	const TopoDS_Edge& E = TopoDS::Edge( itl.Value() );
+
+	Standard_Boolean isSectionE = myInter3d.IsSectionEdge(E);
+	Standard_Boolean isNewE = !EM.Contains( E );
+
+	// LSE: list of split edges
+	TopTools_ListOfShape LSE;
+	myImagesEdges.LastImage(E,LSE); // splits of E or E itself
+
+	for (itNE.Initialize(LSE); itNE.More(); itNE.Next()) {
+
+	  TopoDS_Edge NE = TopoDS::Edge( itNE.Value() );
+	  Standard_Boolean isSameE = NE.IsSame ( E );
+	  
+	  if ( isNewE || isSectionE || !isSameE) {
+	    if (AddedEqualM.Contains( NE )) {
+              // a seam must be twice in a loop
+              if (!BRep_Tool::IsClosed( E, F ) || !EqualSeamM.Add( NE ))
+                continue;
+            }
+
+	    if (isNewE) {
+	      if (isSectionE) {
+		if ( ! myInter3d.IsSplitOn( NE, E, F) )
+		  continue;
+	      }
+	      else {
+		TopoDS_Vertex V1,V2;
+		TopExp::Vertices(NE,V1,V2);
+		const TopTools_ListOfShape& EL1 = myAsDes->Ascendant(V1);
+		const TopTools_ListOfShape& EL2 = myAsDes->Ascendant(V2);
+		if ( EL1.Extent() < 2 && EL2.Extent() < 2 )
+		  continue;
+	      }
+	    }
+	    else {
+	      NE.Orientation( E.Orientation());
+	      if (!isSameE) {
+		// orient NE because it may be a split of other edge
+		Standard_Real f,l,u;
+		Handle(Geom_Curve) C3d  = BRep_Tool::Curve( E,f,l );
+		Handle(Geom_Curve) NC3d = BRep_Tool::Curve( NE,f,l);
+		if ( C3d != NC3d) {
+		  gp_Vec D1, ND1;  gp_Pnt P;
+		  TopoDS_Vertex V = TopExp::FirstVertex(NE);
+		  u = BRep_Tool::Parameter(V,NE);
+		  NC3d->D1 (u, P, ND1);
+		  u = BRep_Tool::Parameter(V,E);
+		  C3d ->D1 (u, P, D1);
+		  if (ND1.Dot(D1) < 0)
+		    NE.Reverse();
+		}
+	      }
+	    }
+	    if (myEqualEdges.Contains( NE ))
+              AddedEqualM.Add( NE );
+
+	    needRebuild = Standard_True;
+	  }
+
+	  if (isNewE || isSectionE)
+	    myNewSection.Add( NE );
+
+	  if (isNewE) 
+	    loops.AddSectionEdge(NE);
+	  else
+	    loops.AddConstEdge(NE);
+	}
+      }
+
+      //-------------------
+      // Build the faces.
+      //-------------------
+      
+      if (needRebuild) {
+	
+        loops.Perform();
+        loops.WiresToFaces(myImagesEdges);
+
+        LNF = loops.NewFaces();
+
+        myImagesFaces.Bind(F,LNF);
+
+        // replace the result faces that have already been built
+        // during same domain faces reconstruction done earlier
+        if (myInter3d.HasSameDomainF( F ))
+        {
+          // build map edge to same domain faces: EFM
+          TopTools_IndexedDataMapOfShapeListOfShape EFM;
+          TopTools_MapOfShape SDFM; // avoid doubling
+          itl.Initialize( myInter3d.SameDomain( F ));
+          for (; itl.More(); itl.Next()) {
+            if ( !myImagesFaces.HasImage( itl.Value() ))
+              continue;
+            // loop on splits of a SD face
+            TopTools_ListIteratorOfListOfShape itNF;
+            itNF.Initialize (myImagesFaces.Image( itl.Value() ));
+            for ( ; itNF.More(); itNF.Next()) {
+              TopoDS_Shape SDF = itNF.Value();
+              if (myImagesFaces.HasImage( SDF )) // already replaced
+                SDF = myImagesFaces.Image( SDF ).First();
+              if (SDFM.Add (SDF))
+                TopExp::MapShapesAndAncestors(SDF, TopAbs_EDGE, TopAbs_FACE, EFM);
+            }
+          }
+          // do replace faces in the LNF
+          TopTools_ListOfShape LOF;
+          if ( !EFM.IsEmpty() )
+            itl.Initialize( LNF );
+          while (itl.More()) {
+            const TopoDS_Shape& NF = itl.Value();
+            TopExp_Explorer expE ( NF, TopAbs_EDGE );
+            const TopoDS_Edge& E  = TopoDS::Edge (expE.Current());
+            if (EFM.Contains( E )) {
+              const TopTools_ListOfShape& SDFL = EFM.FindFromKey( E );
+              TopoDS_Shape SDF = SDFL.First();
+              Standard_Boolean GoodOri;
+              Standard_Real dot;
+              Partition_Loop3d::IsInside (E, TopoDS::Face(NF), TopoDS::Face(SDF),
+                                          1, dot, GoodOri);
+              if (dot < 0)
+              {
+                // NF and SDF are on different side of E
+                if (SDFL.Extent() == 1) {
+                  itl.Next();
+                  continue;
+                }
+                else
+                  SDF = SDFL.Last(); // next face must be on the same side
+              }
+              gp_Vec V1 = Partition_Loop3d::Normal( E, TopoDS::Face( NF ));
+              gp_Vec V2 = Partition_Loop3d::Normal( E, TopoDS::Face( SDF ));
+              if (V1*V2 < 0)
+                SDF.Reverse();
+
+              if (!myImagesFaces.HasImage( NF ))
+                myImagesFaces.Bind( NF, SDF );
+
+              // mySharedFaces is used in FindFacesInside()
+              mySharedFaces.Add( SDF );
+
+              LOF.Prepend ( SDF );
+              LNF.Remove (itl);
+            }
+            else
+              itl.Next();
+          }
+
+          LNF.Append (LOF);
+        }
+      } // if (needRebuild)
+      
+      else {
+	LNF.Append( F );
+	myImagesFaces.Bind(F,LNF);
+      }
+    } // if (myImagesFaces.HasImage( F ))
+
+    // fill the resulting compound
+    for (itl.Initialize(LNF); itl.More(); itl.Next())
+      myBuilder.Add ( C, itl.Value());
+    
+  } // loop on faces of S
+
+  return C;
+}
+
+
+//=======================================================================
+//function : Tri
+//purpose  : 
+//=======================================================================
+
+static void Tri(const TopoDS_Edge&        E,
+		TopTools_SequenceOfShape& Seq,
+                const Partition_Inter3d & theInter3d)
+{
+  Standard_Boolean Invert   = Standard_True;
+  Standard_Real    U1,U2;
+  TopoDS_Vertex    V1,V2;
+
+  while (Invert) {
+    Invert = Standard_False;
+    for ( Standard_Integer i = 1; i < Seq.Length(); i++) {
+      
+      V1 = TopoDS::Vertex(Seq.Value(i));
+      V2 = TopoDS::Vertex(Seq.Value(i+1));
+      
+      V1.Orientation(TopAbs_INTERNAL);
+      V2.Orientation(TopAbs_INTERNAL);
+      
+      U1 = BRep_Tool::Parameter(V1,E);
+      U2 = BRep_Tool::Parameter(V2,E);
+      
+      if (IsEqual(U1,U2)) {
+        if (theInter3d.ReplaceSameDomainV( V1, E ).IsSame( V1 ))
+          Seq.Remove(i+1); // remove V2
+        else
+          Seq.Remove(i);
+	i--;
+	continue;
+      }
+      if (U2 < U1) {
+	Seq.Exchange(i,i+1);
+	Invert = Standard_True;
+      }
+    }
+  }
+}
+
+//=======================================================================
+//function : MakeEdges
+//purpose  : cut E by vertices VOnE, return list of new edges NE
+//=======================================================================
+
+void Partition_Spliter::MakeEdges (const TopoDS_Edge& E,
+                                   const TopTools_ListOfShape& VOnE,
+                                   TopTools_ListOfShape& NE   ) const
+{
+  TopoDS_Edge WE = E;
+  WE.Orientation(TopAbs_FORWARD);
+
+  Standard_Real    U1,U2, f, l;
+  TopoDS_Vertex    V1,V2,VF,VL;
+
+  BRep_Tool::Range(WE,f,l);
+  TopExp::Vertices(WE,VF,VL);
+
+  if (VOnE.Extent() < 3) { // do not rebuild not cut edge
+    if (( VF.IsSame( VOnE.First() ) && VL.IsSame( VOnE.Last() )) ||
+	VL.IsSame( VOnE.First() ) && VF.IsSame( VOnE.Last() )  ) {
+      NE.Append( E );
+      return;
+    }
+  }
+
+  TopTools_SequenceOfShape SV;
+  TopTools_ListIteratorOfListOfShape itv(VOnE);
+  TopTools_MapOfOrientedShape VM( VOnE.Extent() );
+  for (; itv.More(); itv.Next())
+    if ( VM.Add( itv.Value() ))
+      SV.Append(itv.Value());
+
+  Tri( WE, SV, myInter3d );
+
+  if (SV.Length() < 3) { // do not rebuild not cut edge
+    if (( VF.IsSame( SV.First() ) && VL.IsSame( SV.Last() )) ||
+	VL.IsSame( SV.First() ) && VF.IsSame( SV.Last() )  ) {
+      NE.Append( E );
+      return;
+    }
+  }
+
+  Standard_Integer iVer, NbVer = SV.Length();
+
+
+  //----------------------------------------------------------------
+  // Construction of the new edges .
+  //----------------------------------------------------------------
+
+  if (VF.IsSame(VL)) { // closed edge
+    if (NbVer==1) 
+      SV.Append( SV.First() );
+    else if (!SV.First().IsSame(SV.Last())) {
+      Standard_Boolean isFirst=0;
+      Standard_Real    minDU = 1.e10;
+      TopoDS_Vertex endV = Partition_Inter2d::FindEndVertex(VOnE, f,l, E, isFirst,minDU);
+      if (endV.IsSame(SV.First()))
+	SV.Append(endV);
+      else if (endV.IsSame(SV.Last()))
+	SV.Prepend(endV);
+      else
+	MESSAGE ("END VERTEX IS IN SEQUNCE MIDDLE");
+    }
+    NbVer = SV.Length();
+  }
+
+  for (iVer=1; iVer < NbVer; iVer++) {
+    V1  = TopoDS::Vertex(SV(iVer));
+    V2  = TopoDS::Vertex(SV(iVer+1));
+    
+    TopoDS_Shape NewEdge = WE.EmptyCopied();
+    V1.Orientation(TopAbs_FORWARD);
+    myBuilder.Add  (NewEdge,V1);
+    V2.Orientation(TopAbs_REVERSED);
+    myBuilder.Add  (NewEdge,V2);
+    
+    if (iVer==1)
+      U1 = f;
+    else 	{
+      V1.Orientation(TopAbs_INTERNAL);
+      U1=BRep_Tool::Parameter(V1,WE);
+    }
+    if (iVer+1 == NbVer)
+      U2 = l;
+    else	{
+      V2.Orientation(TopAbs_INTERNAL);
+      U2=BRep_Tool::Parameter(V2,WE);
+    }
+    if (Abs(U1-U2) <= Precision::PConfusion()) {
+      MESSAGE( "MakeEdges(), EQUAL PARAMETERS OF DIFFERENT VERTICES");
+      continue;
+    }
+    TopoDS_Edge EE=TopoDS::Edge(NewEdge);
+    myBuilder.Range (EE,U1,U2);
+
+    TopoDS_Edge NEdge = TopoDS::Edge(NewEdge);
+    myBuilder.SameParameter(NEdge,Standard_False);
+
+    Standard_Real tol = 1.0e-2;
+    Standard_Boolean flag = BRep_Tool::SameParameter(NEdge);
+    if (!flag) {
+      BRepLib::SameParameter(NEdge,tol);
+    }
+    NE.Append(NEdge.Oriented(E.Orientation()));
+  }
+}
+
+//=======================================================================
+//function : MergeEqualEdges
+//purpose  : find equal edges,  choose  ones  to  keep and make
+//           them have pcurves on all faces they are shared by
+//=======================================================================
+
+void Partition_Spliter::MergeEqualEdges (const TopTools_ListOfShape& LSE)
+{
+  // find equal edges
+  // map: edge - equal edges
+  TopTools_DataMapOfShapeListOfShape EEM( LSE.Extent() );
+  findEqual (LSE, LSE, 0, EEM, myEqualEdges);
+
+  TopTools_ListOfShape EEL; // list of equal edges
+  TopTools_DataMapIteratorOfDataMapOfShapeListOfShape itM (EEM);
+  for ( ; itM.More(); itM.Next()) {
+    EEL = itM.Value();
+    EEL.Append( itM.Key() );
+
+    // choose an edge to keep, section edges have priority
+    TopoDS_Edge EKeep;
+    TopTools_ListIteratorOfListOfShape itEE (EEL);
+    for (; itEE.More(); itEE.Next()) {
+      EKeep = TopoDS::Edge( itEE.Value() );
+      const TopoDS_Edge& EKeepOrig = TopoDS::Edge( myImagesEdges.Root( EKeep ));
+      if (myInter3d.IsSectionEdge( EKeepOrig ))
+        break;
+    }
+
+    // update edge images and build pcurves
+    Standard_Real f,l, tol;
+    for (itEE.Initialize (EEL); itEE.More(); itEE.Next()) {
+      const TopoDS_Edge& E = TopoDS::Edge( itEE.Value() );
+      if ( E.IsSame( EKeep )) 
+        continue;
+
+      // 1. build pcurves of the kept edge on faces where replaced edges exist
+      const TopoDS_Edge& EReplOrig = TopoDS::Edge( myImagesEdges.Root( E ));
+      TopTools_ListOfShape FL;
+      FL = myAsDes->Ascendant( EReplOrig );
+      Standard_Integer iFace, iFirstSectionFace = FL.Extent() + 1;
+      // add faces where the replaced edge is a section edge
+      if (myInter3d.IsSectionEdge( EReplOrig )) {
+        TopTools_ListIteratorOfListOfShape seIt;
+        seIt.Initialize( myInter3d.SectionEdgeFaces ( EReplOrig ));
+        for ( ; seIt.More(); seIt.Next())
+          FL.Append( seIt.Value() );
+      }
+      // loop on faces
+      TopTools_ListIteratorOfListOfShape itF (FL);
+      for ( iFace = 1 ; itF.More(); itF.Next(), ++iFace ) {
+        const TopoDS_Face& F = TopoDS::Face( itF.Value());
+
+        Handle(Geom2d_Curve) pc = BRep_Tool::CurveOnSurface( EKeep, F, f,l);
+        if (pc.IsNull()) {
+          Handle(Geom_Curve) C3d = BRep_Tool::Curve( EKeep, f, l);
+          C3d = new Geom_TrimmedCurve( C3d, f,l);
+          pc = TopOpeBRepTool_CurveTool::MakePCurveOnFace (F,C3d,tol);
+          if (pc.IsNull()) {
+            MESSAGE (" CANT BUILD PCURVE ");
+          }
+          myBuilder.UpdateEdge( EKeep, pc, F, tol);
+        }
+
+        if (iFace >= iFirstSectionFace ||
+            !BRep_Tool::IsClosed( EReplOrig, F ))
+          continue;
+
+        // build the second pcurve for a seam
+        TopoDS_Vertex V = TopExp::FirstVertex( EKeep );
+        Standard_Real Ukeep = BRep_Tool::Parameter( V, EKeep );
+        Standard_Real Urepl = BRep_Tool::Parameter( V, E );
+
+        TopoDS_Edge EReplRev = E;
+        EReplRev.Reverse();
+        Handle(Geom2d_Curve) pcRepl1 = BRep_Tool::CurveOnSurface( E, F, f,l);
+        Handle(Geom2d_Curve) pcRepl2 = BRep_Tool::CurveOnSurface( EReplRev, F, f,l);
+
+        gp_Pnt2d p1r, p2r, pk;
+        p1r = pcRepl1->Value( Urepl );
+        p2r = pcRepl2->Value( Urepl );
+        pk  = pc->Value( Ukeep );
+
+        // suppose that pk is equal to either p1r or p2r
+        Standard_Boolean isUPeriod =
+          ( Abs( p1r.X() - p2r.X() ) > Abs( p1r.Y() - p2r.Y() ));
+        Standard_Boolean is1Equal;
+        if (isUPeriod)
+          is1Equal = ( Abs( p1r.X() - pk.X() ) < Abs( p2r.X() - pk.X() ));
+        else
+          is1Equal = ( Abs( p1r.Y() - pk.Y() ) < Abs( p2r.Y() - pk.Y() ));
+
+        Handle(Geom2d_Curve) pc2 = Handle(Geom2d_Curve)::DownCast
+          ( pc->Translated( pk, is1Equal ? p2r : p1r ) );
+
+        if (E.Orientation() == TopAbs_REVERSED)
+          is1Equal = !is1Equal;
+
+        if (is1Equal)
+          myBuilder.UpdateEdge( EKeep, pc, pc2, F, tol);
+        else
+          myBuilder.UpdateEdge( EKeep, pc2, pc, F, tol);
+
+      } // loop on a Faces where a replaced edge exists
+
+
+      // 2. update edge images according to replacement
+      if (myImagesEdges.HasImage( E ))
+        myImagesEdges.Remove( E );
+      myImagesEdges.Bind( E, EKeep );
+
+    } // loop on a list of equal edges EEL
+  } // loop on a map of equal edges EEM
+}
+
+//=======================================================================
+//function : KeepShapesInside
+//purpose  : remove shapes that are outside of S from resul
+//=======================================================================
+
+void Partition_Spliter::KeepShapesInside (const TopoDS_Shape& S)
+{
+  TopoDS_Iterator it;
+  if (S.ShapeType() < TopAbs_SOLID) { // compound or compsolid
+    for (it.Initialize( S ); it.More(); it.Next())
+      KeepShapesInside( it.Value());
+    return;
+  }
+
+  Standard_Boolean isTool = Standard_False;
+  if (!myImageShape.HasImage( S )) {
+    isTool = CheckTool( S );
+    if (!isTool) return;
+  }
+
+  // build map of internal faces
+  TopTools_IndexedMapOfShape MIF;
+  TopoDS_Shape IntFacesComp = FindFacesInside( S, Standard_False, Standard_True);
+  TopExp::MapShapes( IntFacesComp, TopAbs_FACE, MIF );
+
+  TopoDS_Compound C;
+  myBuilder.MakeCompound(C);
+
+  TopAbs_ShapeEnum anInternalShapeType = TopAbs_SHAPE;
+  if (!MIF.IsEmpty())
+  {
+    // leave in the result only those shapes having a face in MIF
+    for (it.Initialize( myShape ); it.More(); it.Next()) {
+      const TopoDS_Shape & aResShape = it.Value();
+      TopExp_Explorer expResF( aResShape, TopAbs_FACE );
+      for (; expResF.More(); expResF.Next()) {
+        if ( MIF.Contains( expResF.Current())) {
+          myBuilder.Add( C, aResShape );
+          if (aResShape.ShapeType() < anInternalShapeType)
+            anInternalShapeType = aResShape.ShapeType();
+          break;
+        }
+      }
+    }
+  }
+
+  // may be S was not split by internal faces then it is missing
+  // in myShape, add it
+  if (!isTool &&
+      (anInternalShapeType > TopAbs_SOLID || S.ShapeType() > TopAbs_SOLID))
+  {
+    TopTools_IndexedMapOfShape MSF; // map of split faces of S
+    TopExp::MapShapes( myImageShape.Image(S).First(), TopAbs_FACE, MSF);
+
+    // find a shape having all faces in MSF
+    for (it.Initialize( myShape ); it.More(); it.Next()) {
+      TopExp_Explorer expResF( it.Value(), TopAbs_FACE );
+      for (; expResF.More(); expResF.Next()) {
+        if (! MSF.Contains( expResF.Current())) 
+          break;
+      }
+      if (! expResF.More()) {
+        myBuilder.Add( C, it.Value() );
+        break;
+      }
+    }
+  }
+
+  myShape = C;
+}
+
+//=======================================================================
+//function : RemoveShapesInside
+//purpose  : remove shapes that are inside S from resul
+//=======================================================================
+
+void Partition_Spliter::RemoveShapesInside (const TopoDS_Shape& S)
+{
+  TopoDS_Iterator it;
+  if (S.ShapeType() < TopAbs_SOLID) { // compound or compsolid
+    for (it.Initialize( S ); it.More(); it.Next())
+      RemoveShapesInside( it.Value());
+    return;
+  }
+  Standard_Boolean isTool = Standard_False;
+  if (!myImageShape.HasImage( S )) {
+    isTool = CheckTool( S );
+    if (!isTool) return;
+  }
+
+  TopoDS_Shape IntFacesComp = FindFacesInside( S, Standard_False, Standard_True);
+  TopTools_IndexedMapOfShape MIF; // map of internal faces
+  TopExp::MapShapes( IntFacesComp, TopAbs_FACE, MIF);
+
+  if (MIF.IsEmpty()) return;
+
+  // add to MIF split faces of S
+  if (myImageShape.HasImage(S))
+    TopExp::MapShapes( myImageShape.Image(S).First(), TopAbs_FACE, MIF);
+
+  // leave in the result only those shapes not having all face in MIF
+  
+  TopoDS_Compound C;
+  myBuilder.MakeCompound(C);
+
+  // RMF : faces of removed shapes that encounter once
+  TopTools_MapOfShape RFM;
+  
+  for (it.Initialize( myShape ); it.More(); it.Next()) {
+    
+    TopExp_Explorer expResF( it.Value(), TopAbs_FACE );
+    for (; expResF.More(); expResF.Next())
+      if (!MIF.Contains( expResF.Current()))
+	break;
+
+    if (expResF.More())
+      // add shape to result
+      myBuilder.Add( C, it.Value() );
+    else 
+      // add faces of a removed shape to RFM
+      for (expResF.ReInit(); expResF.More(); expResF.Next()) {
+	const TopoDS_Shape& F = expResF.Current();
+	if ( ! RFM.Remove ( F ))
+	  RFM.Add( F );
+      }
+  }
+
+  if (!isTool) {
+
+    // rebuild S, it must remain in the result
+
+    Standard_Boolean isClosed = Standard_False;
+    switch (S.ShapeType()) {
+    case TopAbs_SOLID :
+      isClosed = Standard_True; break;
+    case TopAbs_SHELL: {
+      TopTools_IndexedDataMapOfShapeListOfShape MEF;
+      TopExp::MapShapesAndAncestors(S, TopAbs_EDGE, TopAbs_FACE, MEF);
+      Standard_Integer i;
+      for (i=1;  isClosed && i<=MEF.Extent();  ++i) 
+        isClosed = ( MEF(i).Extent() != 1 );
+      break;
+    }
+    default:
+      isClosed = Standard_False;
+    }
+    if (isClosed) {
+
+      // add to a new shape external faces of removed shapes, ie those in RFM
+
+      TopoDS_Shell Shell;
+      myBuilder.MakeShell( Shell );
+
+      // exclude redundant internal face with edges encounterd only once
+      TopTools_IndexedDataMapOfShapeListOfShape MEF;
+      TopTools_MapIteratorOfMapOfShape itF (RFM);
+      for ( ; itF.More(); itF.Next()) 
+        TopExp::MapShapesAndAncestors(itF.Key(), TopAbs_EDGE, TopAbs_FACE, MEF);
+
+      // add only faces forming a closed shell
+      for (itF.Reset() ; itF.More(); itF.Next())
+      {
+        TopExp_Explorer expE (itF.Key(), TopAbs_EDGE);
+        for (; expE.More(); expE.Next())
+          if (MEF.FindFromKey(expE.Current()).Extent() == 1)
+            break;
+        if (!expE.More())
+          myBuilder.Add( Shell, itF.Key());
+      }
+
+      if (S.ShapeType() == TopAbs_SOLID) {
+        TopoDS_Solid Solid;
+        myBuilder.MakeSolid( Solid );
+        myBuilder.Add (Solid, Shell);
+        myBuilder.Add (C, Solid);
+      }
+      else
+        myBuilder.Add (C, Shell);
+    }
+    else {
+      if (myImageShape.HasImage( S )) {
+        for (it.Initialize( myImageShape.Image(S).First()); it.More(); it.Next())
+          myBuilder.Add (C, it.Value());
+      }
+    }
+  }
+  
+  myShape = C;
+}
+
+//=======================================================================
+//function : CheckTool
+//purpose  : Return True if <S>  is  a tool shape. Prepare tool
+//           faces of <S> for the search of internal faces.
+//=======================================================================
+
+Standard_Boolean Partition_Spliter::CheckTool(const TopoDS_Shape& S)
+{
+  // suppose S has not an image
+  
+  Standard_Boolean isTool = Standard_False;
+  TopoDS_Compound C;
+  myBuilder.MakeCompound( C );
+
+  TopExp_Explorer expF( S, TopAbs_FACE);
+  for (; expF.More(); expF.Next()) {
+
+    const TopoDS_Face& F = TopoDS::Face( expF.Current() );
+    if (myMapTools.Contains( F ))
+      isTool = Standard_True;
+    else
+      continue;
+
+    if (myImagesFaces.HasImage( F )) {
+      // F has been reconstructed
+      TopAbs_Orientation Fori = F.Orientation();
+      TopTools_ListOfShape LNF;
+      myImagesFaces.LastImage( F, LNF);
+      TopTools_ListIteratorOfListOfShape itF (LNF);
+      for ( ; itF.More(); itF.Next())
+	myBuilder.Add( C, itF.Value().Oriented(Fori) );
+      continue;
+    }
+    
+    Standard_Boolean hasSectionE = myInter3d.HasSectionEdge( F );
+    Standard_Boolean hasNewE     = myAsDes->HasDescendant( F );
+    if (!hasSectionE && !hasNewE)
+    {
+      // F intersects nothing
+      myBuilder.Add( C, F );
+      continue;
+    }
+    
+    // make an image for F
+    
+    TopoDS_Face NF = F;
+    NF.Orientation(TopAbs_FORWARD);
+    NF = TopoDS::Face( NF.EmptyCopied() ); // make a copy
+    TopoDS_Wire NW;
+    myBuilder.MakeWire( NW );
+
+    // add edges, as less as possible
+    TopTools_ListOfShape NEL;
+    TopTools_ListIteratorOfListOfShape itNE;
+    if (hasSectionE) {
+      // add section edges
+      TopExp_Explorer expE;
+      for ( ; expE.More(); expE.Next()) {
+	if (! myImagesEdges.HasImage( expE.Current() ))
+	  continue;
+	myImagesEdges.LastImage( expE.Current(), NEL );
+	for ( itNE.Initialize( NEL ); itNE.More(); itNE.Next())
+	  myBuilder.Add ( NW, itNE.Value());
+      }
+    }
+    if (hasNewE) {
+      // add new adges
+      NEL = myAsDes->Descendant( F );
+      for ( itNE.Initialize( NEL ); itNE.More(); itNE.Next()) {
+	TopTools_ListOfShape SEL; // splits
+	myImagesEdges.LastImage( itNE.Value(), SEL );
+	TopTools_ListIteratorOfListOfShape itSE (SEL);
+	for ( ; itSE.More(); itSE.Next()) 
+	  myBuilder.Add ( NW, itSE.Value());
+      }
+    }
+    myBuilder.Add( NF, NW );
+    myBuilder.Add (C, NF);
+    
+    NF.Orientation( F.Orientation() ); // NF is most probably invalid
+    myImagesFaces.Bind (F, NF);
+  }
+  if (isTool)
+    myImageShape.Bind (S, C);
+
+  return isTool;
+}
+
+//=======================================================================
+//function : IsInside
+//purpose  : Return True if the first vertex of S1 inside S2.
+//           If S1.IsNull(), check infinite point against S2.
+//=======================================================================
+
+Standard_Boolean Partition_Spliter::IsInside (const TopoDS_Shape& theS1,
+                                              const TopoDS_Shape& theS2)
+{
+  BRepClass3d_SolidClassifier aClassifier( theS2 );
+
+  TopExp_Explorer expl( theS1, TopAbs_VERTEX );
+  if (!expl.More())
+    aClassifier.PerformInfinitePoint( ::RealSmall());
+  else
+  {
+    const TopoDS_Vertex & aVertex = TopoDS::Vertex( expl.Current() );
+    aClassifier.Perform (BRep_Tool::Pnt( aVertex ),
+                         BRep_Tool::Tolerance( aVertex ));
+  }
+
+  return ( aClassifier.State() == TopAbs_IN );
+}
+
+//=======================================================================
+//function : GetOriginalShape
+//purpose  : Return the  shape  aShape  originates from. aShape
+//           should be a face or more complex result shape
+//=======================================================================
+
+TopoDS_Shape Partition_Spliter::GetOriginalShape(const TopoDS_Shape& theShape) const
+{
+  TopoDS_Shape anOrigShape;
+
+  TopExp_Explorer expl( theShape, TopAbs_FACE);
+  if (expl.More())
+  {
+
+    TopoDS_Shape aFace = expl.Current();
+    if (myImagesFaces.IsImage( aFace ))
+      aFace = myImagesFaces.Root( aFace );
+    anOrigShape = myFaceShapeMap.Find( aFace );
+  }
+  return anOrigShape;
+}
+
+//=======================================================================
+//function : FindToolsToReconstruct
+//purpose  : find and store  as  objects  tools which interfere
+//           with  solids   or   are   inside   solids  without
+//           an interference
+//=======================================================================
+
+void Partition_Spliter::FindToolsToReconstruct()
+{
+  if (myMapTools.IsEmpty())
+    return;
+
+  Standard_Integer nbFoundTools = 0;
+
+  // build edge - face map in order to detect interference with section edges
+  TopTools_IndexedDataMapOfShapeListOfShape EFM;
+  TopTools_MapIteratorOfMapOfShape aMapIt;
+  for (aMapIt.Initialize(myMapTools); aMapIt.More(); aMapIt.Next())
+    TopExp::MapShapesAndAncestors( aMapIt.Key(), TopAbs_EDGE, TopAbs_FACE, EFM);
+  for (aMapIt.Initialize(myMapFaces); aMapIt.More(); aMapIt.Next())
+    TopExp::MapShapesAndAncestors( aMapIt.Key(), TopAbs_EDGE, TopAbs_FACE, EFM);
+
+  TopTools_MapOfShape aCurrentSolids, aCheckedShapes;
+
+  // faces cut by new edges
+  TopTools_MapOfShape & aSectionFaces = myInter3d.TouchedFaces();
+
+  // keep solids interfering with each other in aCurrentSolids map
+  // and add tool faces intersecting solids as object shapes
+
+  TopTools_ListIteratorOfListOfShape itS, itF, itCF, itE;
+  for (itS.Initialize( myListShapes ); itS.More(); itS.Next()) {
+    TopExp_Explorer expSo (itS.Value(), TopAbs_SOLID);
+    for (; expSo.More(); expSo.Next()) {
+
+      // check if a solid has been already processed
+      const TopoDS_Shape & aSo = expSo.Current();
+      if (!aCheckedShapes.Add( aSo ))
+        continue;
+      aCurrentSolids.Add( aSo );
+
+      // faces to check
+      TopTools_ListOfShape aFacesToCheck;
+      TopExp_Explorer exp( aSo, TopAbs_FACE );
+      for ( ; exp.More(); exp.Next())
+        aFacesToCheck.Append ( exp.Current());
+
+      // add other shapes interefering with a solid.
+      // iterate faces to check while appending new ones
+      for (itCF.Initialize (aFacesToCheck) ; itCF.More(); itCF.Next())
+      {
+        const TopoDS_Shape& aCheckFace = itCF.Value();
+//         if (!aCheckedShapes.Add( aCheckFace ))
+//           continue;
+
+        // find faces interfering with aCheckFace
+        TopTools_ListOfShape anIntFaces;
+
+        // ** 1. faces intersecting aCheckFace with creation of new edges on it
+        if ( myAsDes->HasDescendant( aCheckFace ))
+        {
+          // new edges on aCheckFace
+          const TopTools_ListOfShape& NEL = myAsDes->Descendant( aCheckFace );
+          for (itE.Initialize( NEL); itE.More(); itE.Next())
+          {
+            const TopoDS_Shape & aNewEdge = itE.Value();
+            if (!aCheckedShapes.Add( aNewEdge ))
+              continue;
+
+            // faces interfering by aNewEdge
+            itF.Initialize (myAsDes->Ascendant( aNewEdge ));
+            for (; itF.More(); itF.Next())
+              if (aCheckFace != itF.Value())
+                anIntFaces.Append( itF.Value() );
+
+            // ** 2. faces having section edge aNewEdge on aFacesToCheck
+            if (EFM.Contains( aNewEdge))
+            {
+              itF.Initialize ( EFM.FindFromKey (itE.Value()));
+              for (; itF.More(); itF.Next())
+                if (aCheckFace != itF.Value())
+                  anIntFaces.Append( itF.Value() );
+            }
+          }
+        }
+
+        // ** 3. faces cut by edges of aCheckFace
+        TopExp_Explorer expE (aCheckFace, TopAbs_EDGE);
+        for ( ; expE.More(); expE.Next())
+        {
+          const TopoDS_Shape & aCheckEdge = expE.Current();
+          if (aCheckedShapes.Add( aCheckEdge ) &&
+              myInter3d.IsSectionEdge( TopoDS::Edge( aCheckEdge )))
+          {
+            itF.Initialize( myInter3d.SectionEdgeFaces( TopoDS::Edge( aCheckEdge )));
+            for (; itF.More(); itF.Next()) 
+              if (aCheckFace != itF.Value())
+                anIntFaces.Append( itF.Value() );
+          }
+        }
+
+        // process faces interfering with aCheckFace and shapes they
+        // belong to
+        for (itF.Initialize (anIntFaces); itF.More(); itF.Next())
+        {
+          const TopoDS_Shape & F = itF.Value();
+          if (! aCheckedShapes.Add( F ))
+            continue;
+
+          Standard_Boolean isTool = myMapTools.Contains( F );
+          if (isTool && 
+              myFaceShapeMap( aCheckFace ).ShapeType() == TopAbs_SOLID )
+          {
+            // a tool interfering with a solid
+            if (aSectionFaces.Contains( F ))
+              AddShape( F );
+            ++ nbFoundTools;
+            if (nbFoundTools == myMapTools.Extent())
+              return;
+          }
+
+          const TopoDS_Shape & S = myFaceShapeMap( F );
+          if (aCheckedShapes.Add( S ))
+          {
+            // a new shape interefering with aCurrentSolids is found
+            if (!isTool && S.ShapeType() == TopAbs_SOLID)
+              aCurrentSolids.Add ( S );
+            // add faces to aFacesToCheck list
+            for ( exp.Init( S, TopAbs_FACE ); exp.More(); exp.Next())
+              aFacesToCheck.Append ( exp.Current() );
+          }
+        }
+      } // loop on aFacesToCheck
+
+      // Here aCurrentSolids contains all solids interfering with each other.
+      // aCheckedShapes contains all faces belonging to shapes included
+      // in or interfering with aCurrentSolids or previously checked solids.
+      // Test if tool faces that do not interefere with other shapes are
+      // wrapped by any of aCurrentSolids
+
+      TopTools_MapIteratorOfMapOfShape aSolidIt (aCurrentSolids);
+      for ( ; aSolidIt.More(); aSolidIt.Next())
+      {
+        const TopoDS_Shape & aSolid = aSolidIt.Key();
+        TopTools_MapOfShape aCheckedTools( myMapTools.Extent() );
+
+        TopTools_MapIteratorOfMapOfShape aToolIt (myMapTools);
+        for ( ; aToolIt.More(); aToolIt.Next())
+        {
+          const TopoDS_Shape & aToolFace = aToolIt.Key();
+          if (aCheckedShapes.Contains( aToolFace ) || // already found
+              aCheckedTools.Contains( aToolFace ))    // checked against aSolid
+            continue;
+
+          const TopoDS_Shape & aToolShape = myFaceShapeMap( aToolFace );
+          TopExp_Explorer aToolFaceIt( aToolShape, TopAbs_FACE );
+          
+          Standard_Boolean isInside = IsInside( aToolShape, aSolid );
+          for ( ; aToolFaceIt.More(); aToolFaceIt.Next() )
+          {
+            const TopoDS_Shape & aTool = aToolFaceIt.Current();
+            aCheckedTools.Add( aTool );
+            if (isInside)
+            {
+              if (aSectionFaces.Contains( aTool ))
+                AddShape( aTool );
+              ++ nbFoundTools;
+              if (nbFoundTools == myMapTools.Extent())
+                return;
+              aCheckedShapes.Add( aTool );
+            }
+          }
+        }
+      }
+      
+    } // loop on solid shapes
+  }
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Spliter.hxx b/contrib/Netgen/libsrc/occ/Partition_Spliter.hxx
new file mode 100644
index 0000000000000000000000000000000000000000..f29917a3e144edcf2b8f03d7476b13cb86b3ecdd
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Spliter.hxx
@@ -0,0 +1,150 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  CEA/DEN, EDF R&D
+//
+//
+//
+//  File   : Partition_Spliter.hxx
+//  Module : GEOM
+
+#ifndef _Partition_Spliter_HeaderFile
+#define _Partition_Spliter_HeaderFile
+
+#ifndef _TopAbs_ShapeEnum_HeaderFile
+#include <TopAbs_ShapeEnum.hxx>
+#endif
+#ifndef _TopoDS_Compound_HeaderFile
+#include <TopoDS_Compound.hxx>
+#endif
+#ifndef _BRep_Builder_HeaderFile
+#include <BRep_Builder.hxx>
+#endif
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _TopTools_MapOfShape_HeaderFile
+#include <TopTools_MapOfShape.hxx>
+#endif
+#ifndef _TopTools_DataMapOfShapeShape_HeaderFile
+#include <TopTools_DataMapOfShapeShape.hxx>
+#endif
+#ifndef _Handle_BRepAlgo_AsDes_HeaderFile
+#include <Handle_BRepAlgo_AsDes.hxx>
+#endif
+#ifndef _BRepAlgo_Image_HeaderFile
+#include <BRepAlgo_Image.hxx>
+#endif
+#ifndef _Partition_Inter3d_HeaderFile
+#include "Partition_Inter3d.hxx"
+#endif
+#ifndef _TopTools_MapOfOrientedShape_HeaderFile
+#include <TopTools_MapOfOrientedShape.hxx>
+#endif
+#ifndef _Standard_Boolean_HeaderFile
+#include <Standard_Boolean.hxx>
+#endif
+class BRepAlgo_AsDes;
+class TopoDS_Shape;
+class TopTools_ListOfShape;
+class TopoDS_Edge;
+
+
+#ifndef _Standard_HeaderFile
+#include <Standard.hxx>
+#endif
+#ifndef _Standard_Macro_HeaderFile
+#include <Standard_Macro.hxx>
+#endif
+
+class Partition_Spliter  {
+
+public:
+
+   void* operator new(size_t,void* anAddress) 
+   {
+      return anAddress;
+   }
+   void* operator new(size_t size) 
+   { 
+      return Standard::Allocate(size); 
+   }
+   void  operator delete(void *anAddress) 
+   { 
+      if (anAddress) Standard::Free((Standard_Address&)anAddress); 
+   }
+   // Methods PUBLIC
+   // 
+   Partition_Spliter();
+   void AddShape(const TopoDS_Shape& S) ;
+   void AddTool(const TopoDS_Shape& S) ;
+   void Compute(const TopAbs_ShapeEnum Limit = TopAbs_SHAPE) ;
+   void KeepShapesInside(const TopoDS_Shape& S) ;
+   void RemoveShapesInside(const TopoDS_Shape& S) ;
+   TopoDS_Shape Shape() const;
+   void Clear() ;
+
+
+
+
+
+protected:
+
+   // Methods PROTECTED
+   // 
+
+
+   // Fields PROTECTED
+   //
+
+
+private: 
+
+   // Methods PRIVATE
+   // 
+   void MakeSolids(const TopoDS_Shape& Solid,TopTools_ListOfShape& Shells) ;
+   void MakeShells(const TopoDS_Shape& S,TopTools_ListOfShape& NS) ;
+   TopoDS_Shape MakeFaces(const TopoDS_Shape& S) ;
+   void MakeEdges(const TopoDS_Edge& E,const TopTools_ListOfShape& VOnE,TopTools_ListOfShape& NE) const;
+   TopoDS_Shape FindFacesInside(const TopoDS_Shape& S,const Standard_Boolean CheckClosed = Standard_False,const Standard_Boolean All = Standard_False) ;
+   Standard_Boolean CheckTool(const TopoDS_Shape& S) ;
+   void MergeEqualEdges(const TopTools_ListOfShape& LE) ;
+   static  Standard_Boolean IsInside(const TopoDS_Shape& S1,const TopoDS_Shape& S2) ;
+   TopoDS_Shape GetOriginalShape(const TopoDS_Shape& aShape) const;
+   void FindToolsToReconstruct() ;
+
+
+   // Fields PRIVATE
+   //
+   TopAbs_ShapeEnum myDoneStep;
+   TopoDS_Compound myShape;
+   BRep_Builder myBuilder;
+   TopTools_ListOfShape myListShapes;
+   TopTools_MapOfShape myMapFaces;
+   TopTools_MapOfShape myMapTools;
+   TopTools_MapOfShape myEqualEdges;
+   TopTools_MapOfShape myNewSection;
+   TopTools_MapOfShape myClosedShapes;
+   TopTools_MapOfShape mySharedFaces;
+   TopTools_MapOfShape myWrappingSolid;
+   TopTools_DataMapOfShapeShape myFaceShapeMap;
+   TopTools_DataMapOfShapeShape myInternalFaces;
+   TopTools_DataMapOfShapeShape myIntNotClFaces;
+   Handle_BRepAlgo_AsDes myAsDes;
+   BRepAlgo_Image myImagesFaces;
+   BRepAlgo_Image myImagesEdges;
+   BRepAlgo_Image myImageShape;
+   Partition_Inter3d myInter3d;
+   TopTools_MapOfOrientedShape myAddedFacesMap;
+
+
+};
+
+
+
+
+
+// other Inline functions and methods (like "C++: function call" methods)
+//
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/Partition_Spliter.ixx b/contrib/Netgen/libsrc/occ/Partition_Spliter.ixx
new file mode 100644
index 0000000000000000000000000000000000000000..ee825946855db633d7899ef2c280a9ef1858dab1
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Spliter.ixx
@@ -0,0 +1,31 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Spliter.ixx
+//  Module : GEOM
+
+#include "Partition_Spliter.jxx"
+
+ 
+
+
diff --git a/contrib/Netgen/libsrc/occ/Partition_Spliter.jxx b/contrib/Netgen/libsrc/occ/Partition_Spliter.jxx
new file mode 100644
index 0000000000000000000000000000000000000000..bf8622c93a6b74b3a14c6b91cf438e4d12cb347e
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Partition_Spliter.jxx
@@ -0,0 +1,41 @@
+//  GEOM PARTITION : partition algorithm
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : Partition_Spliter.jxx
+//  Module : GEOM
+
+#ifndef _BRepAlgo_AsDes_HeaderFile
+#include <BRepAlgo_AsDes.hxx>
+#endif
+#ifndef _TopoDS_Shape_HeaderFile
+#include <TopoDS_Shape.hxx>
+#endif
+#ifndef _TopTools_ListOfShape_HeaderFile
+#include <TopTools_ListOfShape.hxx>
+#endif
+#ifndef _TopoDS_Edge_HeaderFile
+#include <TopoDS_Edge.hxx>
+#endif
+#ifndef _Partition_Spliter_HeaderFile
+#include "Partition_Spliter.hxx"
+#endif
diff --git a/contrib/Netgen/libsrc/occ/occconstruction.cpp b/contrib/Netgen/libsrc/occ/occconstruction.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2945a1470adf99c1ec1a4f486122b172058b261f
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occconstruction.cpp
@@ -0,0 +1,157 @@
+
+#ifdef OCCGEOMETRY
+
+#include <mystdlib.h>
+#include <occgeom.hpp>  
+#include "ShapeAnalysis_ShapeTolerance.hxx"
+#include "ShapeAnalysis_ShapeContents.hxx"
+#include "ShapeAnalysis_CheckSmallFace.hxx"
+#include "ShapeAnalysis_DataMapOfShapeListOfReal.hxx"
+#include "BRepAlgoAPI_Fuse.hxx"
+#include "BRepCheck_Analyzer.hxx"
+#include "BRepLib.hxx"
+#include "ShapeBuild_ReShape.hxx"
+#include "ShapeFix.hxx"
+#include "ShapeFix_FixSmallFace.hxx"
+#include "Partition_Spliter.hxx"
+//#include "VrmlAPI.hxx"
+//#include "StlAPI.hxx"
+
+
+#include <GC_MakeSegment.hxx>
+#include <BRepBuilderAPI_MakeEdge.hxx>
+#include <BRepBuilderAPI_MakeWire.hxx>
+#include <BRepPrimAPI_MakeBox.hxx>
+// #include <BRep_Builder.hxx>
+#include <TopoDS_Builder.hxx>
+#include <BRepAlgoAPI_Cut.hxx>
+#include <BRepAlgoAPI_Common.hxx>
+#include <BRepAlgoAPI_Fuse.hxx>
+#include <BRepAlgoAPI_Section.hxx>
+#include <BRepOffsetAPI_Sewing.hxx>
+//#include <BRepAlgo_Sewing.hxx>
+#include <BRepOffsetAPI_MakeOffsetShape.hxx>
+#include <ShapeFix_Shape.hxx>
+namespace netgen
+{
+
+  void OCCConstructGeometry (OCCGeometry & geom)
+  {
+#ifdef NOTHING
+    cout << "OCC construction" << endl;
+
+    BRep_Builder builder;
+    BRepPrimAPI_MakeBox mbox(gp_Pnt(-10e5, -15e5, 0), gp_Pnt(20e5, 15e5, 10e5));
+
+
+    /*
+    TopoDS_Shape air = TopoDS_Solid (mbox);
+    air = BRepAlgoAPI_Cut (air, geom.somap(1));
+    air = BRepAlgoAPI_Cut (air, geom.somap(2));
+    air = BRepAlgoAPI_Cut (air, geom.somap(3));
+    air = BRepAlgoAPI_Cut (air, geom.somap(4));
+    air = BRepAlgoAPI_Cut (air, geom.somap(5));
+    air = BRepAlgoAPI_Cut (air, geom.somap(6));
+    air = BRepAlgoAPI_Cut (air, geom.somap(7));
+    // air = BRepAlgoAPI_Cut (air, geom.somap(8));
+    air = BRepAlgoAPI_Cut (air, geom.somap(9));
+    // air = BRepAlgoAPI_Cut (air, geom.somap(10));
+    */
+
+    /*
+    BRepOffsetAPI_MakeOffsetShape dom8plus (geom.somap(8), 1e4, 1e-6);
+    BRepOffsetAPI_MakeOffsetShape dom6plus (geom.somap(6), 1e4, 1e-6);
+    dom8plus.Build();
+    ShapeFix_Shape fixshape(dom8plus.Shape());
+    fixshape.Perform();
+    
+    ShapeFix_Shape fix_dom2(geom.somap(2));
+    fix_dom2.Perform();
+
+
+    BRepAlgoAPI_Cut dom2m8(fix_dom2.Shape(), fixshape.Shape());
+    ShapeFix_Shape fix_dom2m8 (dom2m8);
+    fix_dom2m8.Perform();
+
+    builder.Add (geom.shape, 
+		 BRepAlgoAPI_Cut 
+		 (BRepAlgoAPI_Cut (geom.somap(2), dom6plus),
+		  dom8plus));
+    // builder.Add (geom.shape, fix_dom2m8.Shape());
+    //     builder.Add (geom.shape, fixshape.Shape());
+    */
+
+    TopoDS_Shape my_fuse;
+    int cnt = 0;
+    for (TopExp_Explorer exp_solid(geom.shape, TopAbs_SOLID); exp_solid.More(); exp_solid.Next())
+      {
+	if (cnt == 0)
+	  my_fuse = exp_solid.Current();
+	else
+	  {
+	    cout << "fuse, cnt = " << cnt << endl;
+	    if (cnt != 7 && cnt != 9)
+	      my_fuse = BRepAlgoAPI_Fuse (my_fuse, exp_solid.Current());
+	  }
+	cnt++;
+      }
+    builder.Add (geom.shape, my_fuse);
+
+    /*
+    ShapeUpgrade_ShellSewing ss;
+    ss.ApplySewing(geom.shape,1e5);
+    */
+
+    /*
+    BRepAlgo_Sewing sewing(1.e5);
+    
+    int cnt = 0;
+    for (TopExp_Explorer exp_solid(geom.shape, TopAbs_SOLID); exp_solid.More(); exp_solid.Next())
+      {
+	cout << "swe, cnt = " << cnt << endl;
+	if (cnt != 7 && cnt != 9)
+	  sewing.Add (exp_solid.Current());
+	cnt++;
+      }
+
+    sewing.Perform();
+    builder.Add (geom.shape, sewing.SewedShape());
+    */
+
+
+    /*
+    cout << "build air domain" << endl;
+    TopoDS_Shape air = BRepAlgoAPI_Cut (TopoDS_Solid (mbox), my_fuse);
+
+    cnt = 0;
+    for (TopExp_Explorer exp_solid(geom.shape, TopAbs_SOLID); exp_solid.More(); exp_solid.Next())
+      {
+	cout << "section, cnt = " << cnt << endl;
+	if (cnt == 7)
+	  {
+	    builder.Add (geom.shape, 
+			 BRepAlgoAPI_Section (air, exp_solid.Current()));
+	  }
+	cnt++;
+      }
+    */
+
+
+
+    //    builder.Add (geom.shape, air);
+    for (int i = 1; i <= 10; i++)
+      builder.Remove (geom.shape, geom.somap(i));
+
+
+
+
+    geom.BuildFMap();
+    geom.BuildVisualizationMesh();
+    geom.changed = 1;
+#endif
+
+  }
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/occgenmesh.cpp b/contrib/Netgen/libsrc/occ/occgenmesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aefec844124f2522089ae55da4864a0c881aba99
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occgenmesh.cpp
@@ -0,0 +1,1460 @@
+#ifdef OCCGEOMETRY
+
+#include <mystdlib.h>
+#include <occgeom.hpp>
+#include <meshing.hpp>
+
+
+namespace netgen
+{
+
+#include "occmeshsurf.hpp"
+
+#define TCL_OK 0
+#define TCL_ERROR 1
+
+#define DIVIDEEDGESECTIONS 1000
+#define IGNORECURVELENGTH 1e-4
+#define VSMALL 1e-10
+
+
+   bool merge_solids = 1;
+
+
+  // can you please explain what you intend to compute here (JS) !!!
+   double Line :: Dist (Line l)
+   {
+      Vec<3> n = p1-p0;
+      Vec<3> q = l.p1-l.p0;
+      double nq = n*q;
+
+      Point<3> p = p0 + 0.5*n;
+      double lambda = (p-l.p0)*n / (nq + VSMALL);
+
+      if (lambda >= 0 && lambda <= 1)
+      {
+         double d = (p-l.p0-lambda*q).Length();
+         //        if (d < 1e-3) d = 1e99;
+         return d;
+      }
+      else
+         return 1e99;
+   }
+
+
+
+   double Line :: Length ()
+   {
+      return (p1-p0).Length();
+   }
+
+
+
+   inline Point<3> occ2ng (const gp_Pnt & p)
+   {
+      return  Point<3> (p.X(), p.Y(), p.Z());
+   }
+
+
+
+   double ComputeH (double kappa)
+   {
+      double hret;
+      kappa *= mparam.curvaturesafety;
+
+      if (mparam.maxh * kappa < 1)
+         hret = mparam.maxh;
+      else
+         hret = 1 / (kappa + VSMALL);
+
+      if (mparam.maxh < hret)
+         hret = mparam.maxh;
+
+      return (hret);
+   }
+
+
+
+
+   void RestrictHTriangle (gp_Pnt2d & par0, gp_Pnt2d & par1, gp_Pnt2d & par2,
+                           BRepLProp_SLProps * prop, Mesh & mesh, int depth, double h = 0)
+   {
+      int ls = -1;
+
+      gp_Pnt pnt0,pnt1,pnt2;
+
+      prop->SetParameters (par0.X(), par0.Y());
+      pnt0 = prop->Value();
+
+      prop->SetParameters (par1.X(), par1.Y());
+      pnt1 = prop->Value();
+
+      prop->SetParameters (par2.X(), par2.Y());
+      pnt2 = prop->Value();
+
+      double aux;
+      double maxside = pnt0.Distance(pnt1);
+      ls = 2;
+      aux = pnt1.Distance(pnt2);
+      if(aux > maxside)
+      {
+         maxside = aux;
+         ls = 0;
+      }
+      aux = pnt2.Distance(pnt0);
+      if(aux > maxside)
+      {
+         maxside = aux;
+         ls = 1;
+      }
+
+
+
+      gp_Pnt2d parmid;
+
+      parmid.SetX( (par0.X()+par1.X()+par2.X()) / 3 );
+      parmid.SetY( (par0.Y()+par1.Y()+par2.Y()) / 3 );
+
+      if (depth%3 == 0)
+      {
+         double curvature = 0;
+
+         prop->SetParameters (parmid.X(), parmid.Y());
+         if (!prop->IsCurvatureDefined())
+         {
+            (*testout) << "curvature not defined!" << endl;
+            return;
+         }
+         curvature = max(fabs(prop->MinCurvature()),
+            fabs(prop->MaxCurvature()));
+
+         prop->SetParameters (par0.X(), par0.Y());
+         if (!prop->IsCurvatureDefined())
+         {
+            (*testout) << "curvature not defined!" << endl;
+            return;
+         }
+         curvature = max(curvature,max(fabs(prop->MinCurvature()),
+            fabs(prop->MaxCurvature())));
+
+         prop->SetParameters (par1.X(), par1.Y());
+         if (!prop->IsCurvatureDefined())
+         {
+            (*testout) << "curvature not defined!" << endl;
+            return;
+         }
+         curvature = max(curvature,max(fabs(prop->MinCurvature()),
+            fabs(prop->MaxCurvature())));
+
+         prop->SetParameters (par2.X(), par2.Y());
+         if (!prop->IsCurvatureDefined())
+         {
+            (*testout) << "curvature not defined!" << endl;
+            return;
+         }
+         curvature = max(curvature,max(fabs(prop->MinCurvature()),
+            fabs(prop->MaxCurvature())));
+
+         //(*testout) << "curvature " << curvature << endl;
+
+         if (curvature < 1e-3)
+         {
+            //(*testout) << "curvature too small (" << curvature << ")!" << endl;
+            return;
+            // return war bis 10.2.05 auskommentiert
+         }
+
+
+
+         h = ComputeH (curvature+1e-10);
+
+         if(h < 1e-4*maxside)
+            return;
+
+
+         if (h > 30) return;
+      }
+
+      if (h < maxside && depth < 10)
+      {
+         //cout << "\r h " << h << flush;
+         gp_Pnt2d pm;
+
+         //cout << "h " << h << " maxside " << maxside << " depth " << depth << endl;
+         //cout << "par0 " << par0.X() << " " << par0.Y()
+         //<< " par1 " << par1.X() << " " << par1.Y()
+         //   << " par2 " << par2.X() << " " << par2.Y()<< endl;
+
+         if(ls == 0)
+         {
+            pm.SetX(0.5*(par1.X()+par2.X())); pm.SetY(0.5*(par1.Y()+par2.Y()));
+            RestrictHTriangle(pm, par2, par0, prop, mesh, depth+1, h);
+            RestrictHTriangle(pm, par0, par1, prop, mesh, depth+1, h);
+         }
+         else if(ls == 1)
+         {
+            pm.SetX(0.5*(par0.X()+par2.X())); pm.SetY(0.5*(par0.Y()+par2.Y()));
+            RestrictHTriangle(pm, par1, par2, prop, mesh, depth+1, h);
+            RestrictHTriangle(pm, par0, par1, prop, mesh, depth+1, h);
+         }
+         else if(ls == 2)
+         {
+            pm.SetX(0.5*(par0.X()+par1.X())); pm.SetY(0.5*(par0.Y()+par1.Y()));
+            RestrictHTriangle(pm, par1, par2, prop, mesh, depth+1, h);
+            RestrictHTriangle(pm, par2, par0, prop, mesh, depth+1, h);
+         }
+
+      }
+      else
+      {
+         gp_Pnt pnt;
+         Point3d p3d;
+
+         prop->SetParameters (parmid.X(), parmid.Y());
+         pnt = prop->Value();
+         p3d = Point3d(pnt.X(), pnt.Y(), pnt.Z());
+         mesh.RestrictLocalH (p3d, h);
+
+         p3d = Point3d(pnt0.X(), pnt0.Y(), pnt0.Z());
+         mesh.RestrictLocalH (p3d, h);
+
+         p3d = Point3d(pnt1.X(), pnt1.Y(), pnt1.Z());
+         mesh.RestrictLocalH (p3d, h);
+
+         p3d = Point3d(pnt2.X(), pnt2.Y(), pnt2.Z());
+         mesh.RestrictLocalH (p3d, h);
+
+         //(*testout) << "p = " << p3d << ", h = " << h << ", maxside = " << maxside << endl;
+
+      }
+   }
+
+
+
+   void DivideEdge (TopoDS_Edge & edge, Array<MeshPoint> & ps,
+                    Array<double> & params, Mesh & mesh)
+   {
+      double s0, s1;
+      double maxh = mparam.maxh;
+      int nsubedges = 1;
+      gp_Pnt pnt, oldpnt;
+      double svalue[DIVIDEEDGESECTIONS];
+
+      GProp_GProps system;
+      BRepGProp::LinearProperties(edge, system);
+      double L = system.Mass();
+
+      Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1);
+
+      double hvalue[DIVIDEEDGESECTIONS+1];
+      hvalue[0] = 0;
+      pnt = c->Value(s0);
+
+      double olddist = 0;
+      double dist = 0;
+
+      int tmpVal = (int)(DIVIDEEDGESECTIONS);
+
+      for (int i = 1; i <= tmpVal; i++)
+      {
+         oldpnt = pnt;
+         pnt = c->Value(s0+(i/double(DIVIDEEDGESECTIONS))*(s1-s0));
+         hvalue[i] = hvalue[i-1] +
+            1.0/mesh.GetH(Point3d(pnt.X(), pnt.Y(), pnt.Z()))*
+            pnt.Distance(oldpnt);
+
+         //(*testout) << "mesh.GetH(Point3d(pnt.X(), pnt.Y(), pnt.Z())) " << mesh.GetH(Point3d(pnt.X(), pnt.Y(), pnt.Z()))
+         //	   <<  " pnt.Distance(oldpnt) " << pnt.Distance(oldpnt) << endl;
+
+         olddist = dist;
+         dist = pnt.Distance(oldpnt);
+      }
+
+      //  nsubedges = int(ceil(hvalue[DIVIDEEDGESECTIONS]));
+      nsubedges = max (1, int(floor(hvalue[DIVIDEEDGESECTIONS]+0.5)));
+
+      ps.SetSize(nsubedges-1);
+      params.SetSize(nsubedges+1);
+
+      int i = 1;
+      int i1 = 0;
+      do
+      {
+         if (hvalue[i1]/hvalue[DIVIDEEDGESECTIONS]*nsubedges >= i)
+         {
+            params[i] = s0+(i1/double(DIVIDEEDGESECTIONS))*(s1-s0);
+            pnt = c->Value(params[i]);
+            ps[i-1] = MeshPoint (Point3d(pnt.X(), pnt.Y(), pnt.Z()));
+            i++;
+         }
+         i1++;
+         if (i1 > DIVIDEEDGESECTIONS)
+         {
+            nsubedges = i;
+            ps.SetSize(nsubedges-1);
+            params.SetSize(nsubedges+1);
+            cout << "divide edge: local h too small" << endl;
+         }
+      } while (i < nsubedges);
+
+      params[0] = s0;
+      params[nsubedges] = s1;
+
+      if (params[nsubedges] <= params[nsubedges-1])
+      {
+         cout << "CORRECTED" << endl;
+         ps.SetSize (nsubedges-2);
+         params.SetSize (nsubedges);
+         params[nsubedges] = s1;
+      }
+   }
+
+
+
+
+   void OCCFindEdges (OCCGeometry & geom, Mesh & mesh)
+   {
+      const char * savetask = multithread.task;
+      multithread.task = "Edge meshing";
+
+      (*testout) << "edge meshing" << endl;
+
+      int nvertices = geom.vmap.Extent();
+      int nedges = geom.emap.Extent();
+
+      (*testout) << "nvertices = " << nvertices << endl;
+      (*testout) << "nedges = " << nedges << endl;
+
+      double eps = 1e-6 * geom.GetBoundingBox().Diam();
+
+      for (int i = 1; i <= nvertices; i++)
+      {
+         gp_Pnt pnt = BRep_Tool::Pnt (TopoDS::Vertex(geom.vmap(i)));
+         MeshPoint mp( Point<3>(pnt.X(), pnt.Y(), pnt.Z()) );
+
+         bool exists = 0;
+         if (merge_solids)
+            for (PointIndex pi = 1; pi <= mesh.GetNP(); pi++)
+               if ( Dist2 (mesh[pi], Point<3>(mp)) < eps*eps)
+               {
+                  exists = 1;
+                  break;
+               }
+
+               if (!exists)
+                  mesh.AddPoint (mp);
+      }
+
+      (*testout) << "different vertices = " << mesh.GetNP() << endl;
+
+
+      int first_ep = mesh.GetNP()+1;
+
+      Array<int> face2solid[2];
+      for (int i = 0; i<2; i++)
+      {
+         face2solid[i].SetSize (geom.fmap.Extent());
+         face2solid[i] = 0;
+      }
+
+      int solidnr = 0;
+      for (TopExp_Explorer exp0(geom.shape, TopAbs_SOLID); exp0.More(); exp0.Next())
+      {
+         solidnr++;
+         for (TopExp_Explorer exp1(exp0.Current(), TopAbs_FACE); exp1.More(); exp1.Next())
+         {
+            TopoDS_Face face = TopoDS::Face(exp1.Current());
+            int facenr = geom.fmap.FindIndex(face);
+
+            if (face2solid[0][facenr-1] == 0)
+               face2solid[0][facenr-1] = solidnr;
+            else
+               face2solid[1][facenr-1] = solidnr;
+         }
+      }
+
+
+      int total = 0;
+      for (int i3 = 1; i3 <= geom.fmap.Extent(); i3++)
+         for (TopExp_Explorer exp2(geom.fmap(i3), TopAbs_WIRE); exp2.More(); exp2.Next())
+            for (TopExp_Explorer exp3(exp2.Current(), TopAbs_EDGE); exp3.More(); exp3.Next())
+               total++;
+
+
+      int facenr = 0;
+      int edgenr = 0;
+
+
+      (*testout) << "faces = " << geom.fmap.Extent() << endl;
+      int curr = 0;
+
+      for (int i3 = 1; i3 <= geom.fmap.Extent(); i3++)
+      {
+         TopoDS_Face face = TopoDS::Face(geom.fmap(i3));
+         facenr = geom.fmap.FindIndex (face);       // sollte doch immer == i3 sein ??? JS
+
+         int solidnr0 = face2solid[0][i3-1];
+         int solidnr1 = face2solid[1][i3-1];
+
+         /* auskommentiert am 3.3.05 von robert
+         for (exp2.Init (geom.somap(solidnr0), TopAbs_FACE); exp2.More(); exp2.Next())
+         {
+         TopoDS_Face face2 = TopoDS::Face(exp2.Current());
+         if (geom.fmap.FindIndex(face2) == facenr)
+         {
+         //		      if (face.Orientation() != face2.Orientation()) swap (solidnr0, solidnr1);
+         }
+         }
+         */
+
+         mesh.AddFaceDescriptor (FaceDescriptor(facenr, solidnr0, solidnr1, 0));
+
+         // Philippose - 06/07/2009
+         // Add the face colour to the mesh data
+         Quantity_Color face_colour;
+
+         if(!(geom.face_colours.IsNull())
+            && (geom.face_colours->GetColor(face,XCAFDoc_ColorSurf,face_colour)))
+         {
+            mesh.GetFaceDescriptor(facenr).SetSurfColour(Vec3d(face_colour.Red(),face_colour.Green(),face_colour.Blue()));
+         }
+         else
+         {
+            mesh.GetFaceDescriptor(facenr).SetSurfColour(Vec3d(0.0,1.0,0.0));
+         }
+         // ACHTUNG! STIMMT NICHT ALLGEMEIN (RG)
+
+
+         Handle(Geom_Surface) occface = BRep_Tool::Surface(face);
+
+         for (TopExp_Explorer exp2 (face, TopAbs_WIRE); exp2.More(); exp2.Next())
+         {
+            TopoDS_Shape wire = exp2.Current();
+
+            for (TopExp_Explorer exp3 (wire, TopAbs_EDGE); exp3.More(); exp3.Next())
+            {
+               curr++;
+               (*testout) << "edge nr " << curr << endl;
+
+               multithread.percent = 100 * curr / double (total);
+               if (multithread.terminate) return;
+
+               TopoDS_Edge edge = TopoDS::Edge (exp3.Current());
+               if (BRep_Tool::Degenerated(edge))
+               {
+                  //(*testout) << "ignoring degenerated edge" << endl;
+                  continue;
+               }
+
+               if (geom.vmap.FindIndex(TopExp::FirstVertex (edge)) ==
+                  geom.vmap.FindIndex(TopExp::LastVertex (edge)))
+               {
+                  GProp_GProps system;
+                  BRepGProp::LinearProperties(edge, system);
+
+                  if (system.Mass() < eps)
+                  {
+                     cout << "ignoring edge " << geom.emap.FindIndex (edge)
+                        << ". closed edge with length < " << eps << endl;
+                     continue;
+                  }
+               }
+
+
+               Handle(Geom2d_Curve) cof;
+               double s0, s1;
+               cof = BRep_Tool::CurveOnSurface (edge, face, s0, s1);
+
+               int geomedgenr = geom.emap.FindIndex(edge);
+
+               Array <MeshPoint> mp;
+               Array <double> params;
+
+               DivideEdge (edge, mp, params, mesh);
+ 
+               Array <int> pnums;
+               pnums.SetSize (mp.Size()+2);
+
+               if (!merge_solids)
+               {
+                  pnums[0] = geom.vmap.FindIndex (TopExp::FirstVertex (edge));
+                  pnums[pnums.Size()-1] = geom.vmap.FindIndex (TopExp::LastVertex (edge));
+               }
+               else
+               {
+                  Point<3> fp = occ2ng (BRep_Tool::Pnt (TopExp::FirstVertex (edge)));
+                  Point<3> lp = occ2ng (BRep_Tool::Pnt (TopExp::LastVertex (edge)));
+
+                  pnums[0] = -1;
+                  pnums.Last() = -1;
+                  for (PointIndex pi = 1; pi < first_ep; pi++)
+                  {
+                     if (Dist2 (mesh[pi], fp) < eps*eps) pnums[0] = pi;
+                     if (Dist2 (mesh[pi], lp) < eps*eps) pnums.Last() = pi;
+                  }
+               }
+
+
+               for (int i = 1; i <= mp.Size(); i++)
+               {
+                  bool exists = 0;
+                  int j;
+                  for (j = first_ep; j <= mesh.GetNP(); j++)
+                     if ((mesh.Point(j)-Point<3>(mp[i-1])).Length() < eps)
+                     {
+                        exists = 1;
+                        break;
+                     }
+
+                     if (exists)
+                        pnums[i] = j;
+                     else
+                     {
+                        mesh.AddPoint (mp[i-1]);
+                        (*testout) << "add meshpoint " << mp[i-1] << endl;
+                        pnums[i] = mesh.GetNP();
+                     }
+               }
+               (*testout) << "NP = " << mesh.GetNP() << endl;
+
+               //(*testout) << pnums[pnums.Size()-1] << endl;
+
+               for (int i = 1; i <= mp.Size()+1; i++)
+               {
+                  edgenr++;
+                  Segment seg;
+
+                  seg[0] = pnums[i-1];
+                  seg[1] = pnums[i];
+                  seg.edgenr = edgenr;
+                  seg.si = facenr;
+                  seg.epgeominfo[0].dist = params[i-1];
+                  seg.epgeominfo[1].dist = params[i];
+                  seg.epgeominfo[0].edgenr = geomedgenr;
+                  seg.epgeominfo[1].edgenr = geomedgenr;
+
+                  gp_Pnt2d p2d;
+                  p2d = cof->Value(params[i-1]);
+                  //			if (i == 1) p2d = cof->Value(s0);
+                  seg.epgeominfo[0].u = p2d.X();
+                  seg.epgeominfo[0].v = p2d.Y();
+                  p2d = cof->Value(params[i]);
+                  //			if (i == mp.Size()+1) p2d = cof -> Value(s1);
+                  seg.epgeominfo[1].u = p2d.X();
+                  seg.epgeominfo[1].v = p2d.Y();
+
+                  /*
+                  if (occface->IsUPeriodic())
+                  {
+                  cout << "U Periodic" << endl;
+                  if (fabs(seg.epgeominfo[1].u-seg.epgeominfo[0].u) >
+                  fabs(seg.epgeominfo[1].u-
+                  (seg.epgeominfo[0].u-occface->UPeriod())))
+                  seg.epgeominfo[0].u = p2d.X()+occface->UPeriod();
+
+                  if (fabs(seg.epgeominfo[1].u-seg.epgeominfo[0].u) >
+                  fabs(seg.epgeominfo[1].u-
+                  (seg.epgeominfo[0].u+occface->UPeriod())))
+                  seg.epgeominfo[0].u = p2d.X()-occface->UPeriod();
+                  }
+
+                  if (occface->IsVPeriodic())
+                  {
+                  cout << "V Periodic" << endl;
+                  if (fabs(seg.epgeominfo[1].v-seg.epgeominfo[0].v) >
+                  fabs(seg.epgeominfo[1].v-
+                  (seg.epgeominfo[0].v-occface->VPeriod())))
+                  seg.epgeominfo[0].v = p2d.Y()+occface->VPeriod();
+
+                  if (fabs(seg.epgeominfo[1].v-seg.epgeominfo[0].v) >
+                  fabs(seg.epgeominfo[1].v-
+                  (seg.epgeominfo[0].v+occface->VPeriod())))
+                  seg.epgeominfo[0].v = p2d.Y()-occface->VPeriod();
+                  }
+                  */
+
+                  if (edge.Orientation() == TopAbs_REVERSED)
+                  {
+                     swap (seg[0], seg[1]);
+                     swap (seg.epgeominfo[0].dist, seg.epgeominfo[1].dist);
+                     swap (seg.epgeominfo[0].u, seg.epgeominfo[1].u);
+                     swap (seg.epgeominfo[0].v, seg.epgeominfo[1].v);
+                  }
+
+                  mesh.AddSegment (seg);
+
+                  //edgesegments[geomedgenr-1]->Append(mesh.GetNSeg());
+
+               }
+            }
+         }
+      }
+
+      //	for(i=1; i<=mesh.GetNSeg(); i++)
+      //		(*testout) << "edge " << mesh.LineSegment(i).edgenr << " face " << mesh.LineSegment(i).si
+      //				<< " p1 " << mesh.LineSegment(i)[0] << " p2 " << mesh.LineSegment(i)[1] << endl;
+      //	exit(10);
+
+      mesh.CalcSurfacesOfNode();
+      multithread.task = savetask;
+   }
+
+
+
+
+   void OCCMeshSurface (OCCGeometry & geom, Mesh & mesh, int perfstepsend)
+   {
+      int i, j, k;
+      int changed;
+
+      const char * savetask = multithread.task;
+      multithread.task = "Surface meshing";
+
+      geom.facemeshstatus = 0;
+
+      int noldp = mesh.GetNP();
+
+      double starttime = GetTime();
+
+      Array<int> glob2loc(noldp);
+
+      //int projecttype = PARAMETERSPACE;
+
+      int projecttype = PARAMETERSPACE;
+
+      int notrys = 1;
+
+      int surfmesherror = 0;
+
+      for (k = 1; k <= mesh.GetNFD(); k++)
+      {
+         if(1==0 && !geom.fvispar[k-1].IsDrawable())
+         {
+            (*testout) << "ignoring face " << k << endl;
+            cout << "ignoring face " << k << endl;
+            continue;
+         }
+
+         (*testout) << "mesh face " << k << endl;
+         multithread.percent = 100 * k / (mesh.GetNFD() + VSMALL);
+         geom.facemeshstatus[k-1] = -1;
+
+
+         /*
+         if (k != 42)
+         {
+         cout << "skipped" << endl;
+         continue;
+         }
+         */
+
+
+         FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+
+         int oldnf = mesh.GetNSE();
+
+         Box<3> bb = geom.GetBoundingBox();
+
+         //      int projecttype = PLANESPACE;
+
+         Meshing2OCCSurfaces meshing(TopoDS::Face(geom.fmap(k)), bb, projecttype);
+
+         if (meshing.GetProjectionType() == PLANESPACE)
+            PrintMessage (2, "Face ", k, " / ", mesh.GetNFD(), " (plane space projection)");
+         else
+            PrintMessage (2, "Face ", k, " / ", mesh.GetNFD(), " (parameter space projection)");
+
+         if (surfmesherror)
+            cout << "Surface meshing error occured before (in " << surfmesherror << " faces)" << endl;
+
+         //      Meshing2OCCSurfaces meshing(f2, bb);
+         meshing.SetStartTime (starttime);
+
+         //(*testout) << "Face " << k << endl << endl;
+
+
+         if (meshing.GetProjectionType() == PLANESPACE)
+         {
+            int cntp = 0;
+            glob2loc = 0;
+            for (i = 1; i <= mesh.GetNSeg(); i++)
+            {
+               Segment & seg = mesh.LineSegment(i);
+               if (seg.si == k)
+               {
+                  for (j = 1; j <= 2; j++)
+                  {
+                     int pi = (j == 1) ? seg[0] : seg[1];
+                     if (!glob2loc.Get(pi))
+                     {
+                        meshing.AddPoint (mesh.Point(pi), pi);
+                        cntp++;
+                        glob2loc.Elem(pi) = cntp;
+                     }
+                  }
+               }
+            }
+
+            for (i = 1; i <= mesh.GetNSeg(); i++)
+            {
+               Segment & seg = mesh.LineSegment(i);
+               if (seg.si == k)
+               {
+                  PointGeomInfo gi0, gi1;
+                  gi0.trignum = gi1.trignum = k;
+                  gi0.u = seg.epgeominfo[0].u;
+                  gi0.v = seg.epgeominfo[0].v;
+                  gi1.u = seg.epgeominfo[1].u;
+                  gi1.v = seg.epgeominfo[1].v;
+
+                  meshing.AddBoundaryElement (glob2loc.Get(seg[0]), glob2loc.Get(seg[1]), gi0, gi1);
+                  //(*testout) << gi0.u << " " << gi0.v << endl;
+                  //(*testout) << gi1.u << " " << gi1.v << endl;
+               }
+            }
+         }
+         else
+         {
+            int cntp = 0;
+
+            for (i = 1; i <= mesh.GetNSeg(); i++)
+               if (mesh.LineSegment(i).si == k)
+                  cntp+=2;
+
+
+            Array< PointGeomInfo > gis;
+
+            gis.SetAllocSize (cntp);
+            gis.SetSize (0);
+
+            for (i = 1; i <= mesh.GetNSeg(); i++)
+            {
+               Segment & seg = mesh.LineSegment(i);
+               if (seg.si == k)
+               {
+                  PointGeomInfo gi0, gi1;
+                  gi0.trignum = gi1.trignum = k;
+                  gi0.u = seg.epgeominfo[0].u;
+                  gi0.v = seg.epgeominfo[0].v;
+                  gi1.u = seg.epgeominfo[1].u;
+                  gi1.v = seg.epgeominfo[1].v;
+
+                  int locpnum[2] = {0, 0};
+
+                  for (j = 0; j < 2; j++)
+                  {
+                     PointGeomInfo gi = (j == 0) ? gi0 : gi1;
+
+                     int l;
+                     for (l = 0; l < gis.Size() && locpnum[j] == 0; l++)
+                     {
+                        double dist = sqr (gis[l].u-gi.u)+sqr(gis[l].v-gi.v);
+
+                        if (dist < 1e-10)
+                           locpnum[j] = l+1;
+                     }
+
+                     if (locpnum[j] == 0)
+                     {
+                        int pi = (j == 0) ? seg[0] : seg[1];
+                        meshing.AddPoint (mesh.Point(pi), pi);
+
+                        gis.SetSize (gis.Size()+1);
+                        gis[l] = gi;
+                        locpnum[j] = l+1;
+                     }
+                  }
+
+                  meshing.AddBoundaryElement (locpnum[0], locpnum[1], gi0, gi1);
+                  //(*testout) << gi0.u << " " << gi0.v << endl;
+                  //(*testout) << gi1.u << " " << gi1.v << endl;
+
+               }
+            }
+         }
+
+
+
+
+
+         // Philippose - 15/01/2009
+         double maxh = geom.face_maxh[k-1];
+         //double maxh = mparam.maxh;
+         mparam.checkoverlap = 0;
+         //      int noldpoints = mesh->GetNP();
+         int noldsurfel = mesh.GetNSE();
+
+         GProp_GProps sprops;
+         BRepGProp::SurfaceProperties(TopoDS::Face(geom.fmap(k)),sprops);
+         meshing.SetMaxArea(2.*sprops.Mass());
+
+         MESHING2_RESULT res;
+
+         try {
+	   res = meshing.GenerateMesh (mesh, mparam, maxh, k);
+         }
+
+         catch (SingularMatrixException)
+         {
+            (*myerr) << "Singular Matrix" << endl;
+            res = MESHING2_GIVEUP;
+         }
+
+         catch (UVBoundsException)
+         {
+            (*myerr) << "UV bounds exceeded" << endl;
+            res = MESHING2_GIVEUP;
+         }
+
+         projecttype = PARAMETERSPACE;
+
+         if (res != MESHING2_OK)
+         {
+            if (notrys == 1)
+            {
+               for (int i = noldsurfel+1; i <= mesh.GetNSE(); i++)
+                  mesh.DeleteSurfaceElement (i);
+
+               mesh.Compress();
+
+               cout << "retry Surface " << k << endl;
+
+               k--;
+               projecttype*=-1;
+               notrys++;
+               continue;
+            }
+            else
+            {
+               geom.facemeshstatus[k-1] = -1;
+               PrintError ("Problem in Surface mesh generation");
+               surfmesherror++;
+               //	      throw NgException ("Problem in Surface mesh generation");
+            }
+         }
+         else
+         {
+            geom.facemeshstatus[k-1] = 1;
+         }
+
+         notrys = 1;
+
+         for (i = oldnf+1; i <= mesh.GetNSE(); i++)
+            mesh.SurfaceElement(i).SetIndex (k);
+
+      }
+
+//      ofstream problemfile("occmesh.rep");
+
+//      problemfile << "SURFACEMESHING" << endl << endl;
+
+      if (surfmesherror)
+      {
+         cout << "WARNING! NOT ALL FACES HAVE BEEN MESHED" << endl;
+         cout << "SURFACE MESHING ERROR OCCURED IN " << surfmesherror << " FACES:" << endl;
+         for (int i = 1; i <= geom.fmap.Extent(); i++)
+            if (geom.facemeshstatus[i-1] == -1)
+            {
+               cout << "Face " << i << endl;
+//               problemfile << "problem with face " << i << endl;
+//               problemfile << "vertices: " << endl;
+               TopExp_Explorer exp0,exp1,exp2;
+               for ( exp0.Init(TopoDS::Face (geom.fmap(i)), TopAbs_WIRE); exp0.More(); exp0.Next() )
+               {
+                  TopoDS_Wire wire = TopoDS::Wire(exp0.Current());
+                  for ( exp1.Init(wire,TopAbs_EDGE); exp1.More(); exp1.Next() )
+                  {
+                     TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+                     for ( exp2.Init(edge,TopAbs_VERTEX); exp2.More(); exp2.Next() )
+                     {
+                        TopoDS_Vertex vertex = TopoDS::Vertex(exp2.Current());
+                        gp_Pnt point = BRep_Tool::Pnt(vertex);
+//                        problemfile << point.X() << " " << point.Y() << " " << point.Z() << endl;
+                     }
+                  }
+               }
+//               problemfile << endl;
+
+            }
+            cout << endl << endl;
+            cout << "for more information open IGES/STEP Topology Explorer" << endl;
+//            problemfile.close();
+            throw NgException ("Problem in Surface mesh generation");
+      }
+      else
+      {
+//         problemfile << "OK" << endl << endl;
+//         problemfile.close();
+      }
+
+
+
+
+      if (multithread.terminate || perfstepsend < MESHCONST_OPTSURFACE)
+         return;
+
+      multithread.task = "Optimizing surface";
+
+      static int timer_opt2d = NgProfiler::CreateTimer ("Optimization 2D");
+      NgProfiler::StartTimer (timer_opt2d);
+
+      for (k = 1; k <= mesh.GetNFD(); k++)
+      {
+         //      if (k != 42) continue;
+         //      if (k != 36) continue;
+
+         //      (*testout) << "optimize face " << k << endl;
+         multithread.percent = 100 * k / (mesh.GetNFD() + VSMALL);
+
+         FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+
+         PrintMessage (1, "Optimize Surface ", k);
+         for (i = 1; i <= mparam.optsteps2d; i++)
+         {
+            //	  (*testout) << "optstep " << i << endl;
+            if (multithread.terminate) return;
+
+            {
+               MeshOptimize2dOCCSurfaces meshopt(geom);
+               meshopt.SetFaceIndex (k);
+               meshopt.SetImproveEdges (0);
+               meshopt.SetMetricWeight (mparam.elsizeweight);
+               //meshopt.SetMetricWeight (0.2);
+               meshopt.SetWriteStatus (0);
+
+               //	    (*testout) << "EdgeSwapping (mesh, (i > mparam.optsteps2d/2))" << endl;
+               meshopt.EdgeSwapping (mesh, (i > mparam.optsteps2d/2));
+            }
+
+            if (multithread.terminate) return;
+            {
+               MeshOptimize2dOCCSurfaces meshopt(geom);
+               meshopt.SetFaceIndex (k);
+               meshopt.SetImproveEdges (0);
+               //meshopt.SetMetricWeight (0.2);
+               meshopt.SetMetricWeight (mparam.elsizeweight);
+               meshopt.SetWriteStatus (0);
+
+               //	    (*testout) << "ImproveMesh (mesh)" << endl;
+               meshopt.ImproveMesh (mesh, mparam);
+            }
+
+            {
+               MeshOptimize2dOCCSurfaces meshopt(geom);
+               meshopt.SetFaceIndex (k);
+               meshopt.SetImproveEdges (0);
+               //meshopt.SetMetricWeight (0.2);
+               meshopt.SetMetricWeight (mparam.elsizeweight);
+               meshopt.SetWriteStatus (0);
+
+               //	    (*testout) << "CombineImprove (mesh)" << endl;
+               meshopt.CombineImprove (mesh);
+            }
+
+            if (multithread.terminate) return;
+            {
+               MeshOptimize2dOCCSurfaces meshopt(geom);
+               meshopt.SetFaceIndex (k);
+               meshopt.SetImproveEdges (0);
+               //meshopt.SetMetricWeight (0.2);
+               meshopt.SetMetricWeight (mparam.elsizeweight);
+               meshopt.SetWriteStatus (0);
+
+               //	    (*testout) << "ImproveMesh (mesh)" << endl;
+               meshopt.ImproveMesh (mesh, mparam);
+            }
+         }
+
+      }
+
+
+      mesh.CalcSurfacesOfNode();
+      mesh.Compress();
+
+      NgProfiler::StopTimer (timer_opt2d);
+
+      multithread.task = savetask;
+   }
+
+
+
+   void OCCSetLocalMeshSize(OCCGeometry & geom, Mesh & mesh)
+   {
+      mesh.SetGlobalH (mparam.maxh);
+      mesh.SetMinimalH (mparam.minh);
+
+      Array<double> maxhdom;
+      maxhdom.SetSize (geom.NrSolids());
+      maxhdom = mparam.maxh;
+
+      mesh.SetMaxHDomain (maxhdom);
+
+      Box<3> bb = geom.GetBoundingBox();
+      bb.Increase (bb.Diam()/10);
+
+      mesh.SetLocalH (bb.PMin(), bb.PMax(), 0.5);
+
+      if (mparam.uselocalh)
+      {
+         const char * savetask = multithread.task;
+         multithread.percent = 0;
+
+         mesh.SetLocalH (bb.PMin(), bb.PMax(), mparam.grading);
+
+         int nedges = geom.emap.Extent();
+
+         double maxedgelen = 0;
+         double minedgelen = 1e99;
+
+         multithread.task = "Setting local mesh size (elements per edge)";
+
+         // setting elements per edge
+
+         for (int i = 1; i <= nedges && !multithread.terminate; i++)
+         {
+            TopoDS_Edge e = TopoDS::Edge (geom.emap(i));
+            multithread.percent = 100 * (i-1)/double(nedges);
+            if (BRep_Tool::Degenerated(e)) continue;
+
+            GProp_GProps system;
+            BRepGProp::LinearProperties(e, system);
+            double len = system.Mass();
+
+            if (len < IGNORECURVELENGTH)
+            {
+               (*testout) << "ignored" << endl;
+               continue;
+            }
+
+            double localh = len/mparam.segmentsperedge;
+            double s0, s1;
+
+            // Philippose - 23/01/2009
+            // Find all the parent faces of a given edge
+            // and limit the mesh size of the edge based on the
+            // mesh size limit of the face
+            TopTools_IndexedDataMapOfShapeListOfShape edge_face_map;
+            edge_face_map.Clear();
+
+            TopExp::MapShapesAndAncestors(geom.shape, TopAbs_EDGE, TopAbs_FACE, edge_face_map);
+            const TopTools_ListOfShape& parent_faces = edge_face_map.FindFromKey(e);
+
+            TopTools_ListIteratorOfListOfShape parent_face_list;
+
+            for(parent_face_list.Initialize(parent_faces); parent_face_list.More(); parent_face_list.Next())
+            {
+               TopoDS_Face parent_face = TopoDS::Face(parent_face_list.Value());
+
+               int face_index = geom.fmap.FindIndex(parent_face);
+
+               if(face_index >= 1) localh = min(localh,geom.face_maxh[face_index - 1]);
+            }
+
+            Handle(Geom_Curve) c = BRep_Tool::Curve(e, s0, s1);
+
+            maxedgelen = max (maxedgelen, len);
+            minedgelen = min (minedgelen, len);
+
+            // Philippose - 23/01/2009
+            // Modified the calculation of maxj, because the
+            // method used so far always results in maxj = 2,
+            // which causes the localh to be set only at the
+            // starting, mid and end of the edge.
+            // Old Algorithm:
+            // int maxj = 2 * (int) ceil (localh/len);
+            int maxj = max((int) ceil(len/localh), 2);
+
+            for (int j = 0; j <= maxj; j++)
+            {
+               gp_Pnt pnt = c->Value (s0+double(j)/maxj*(s1-s0));
+               mesh.RestrictLocalH (Point3d(pnt.X(), pnt.Y(), pnt.Z()), localh);
+            }
+         }
+
+         multithread.task = "Setting local mesh size (edge curvature)";
+
+         // setting edge curvature
+
+         int nsections = 20;
+
+         for (int i = 1; i <= nedges && !multithread.terminate; i++)
+         {
+            double maxcur = 0;
+            multithread.percent = 100 * (i-1)/double(nedges);
+            TopoDS_Edge edge = TopoDS::Edge (geom.emap(i));
+            if (BRep_Tool::Degenerated(edge)) continue;
+            double s0, s1;
+            Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1);
+            BRepAdaptor_Curve brepc(edge);
+            BRepLProp_CLProps prop(brepc, 2, 1e-5);
+
+            for (int j = 1; j <= nsections; j++)
+            {
+               double s = s0 + j/(double) nsections * (s1-s0);
+               prop.SetParameter (s);
+               double curvature = prop.Curvature();
+               if(curvature> maxcur) maxcur = curvature;
+
+               if (curvature >= 1e99)
+                  continue;
+
+               gp_Pnt pnt = c->Value (s);
+
+               mesh.RestrictLocalH (Point3d(pnt.X(), pnt.Y(), pnt.Z()), ComputeH (fabs(curvature)));
+            }
+            // (*testout) << "edge " << i << " max. curvature: " << maxcur << endl;
+         }
+
+         multithread.task = "Setting local mesh size (face curvature)";
+
+         // setting face curvature
+
+         int nfaces = geom.fmap.Extent();
+
+         for (int i = 1; i <= nfaces && !multithread.terminate; i++)
+         {
+            multithread.percent = 100 * (i-1)/double(nfaces);
+            TopoDS_Face face = TopoDS::Face(geom.fmap(i));
+            TopLoc_Location loc;
+            Handle(Geom_Surface) surf = BRep_Tool::Surface (face);
+            Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc);
+
+            if (triangulation.IsNull()) continue;
+
+            BRepAdaptor_Surface sf(face, Standard_True);
+            BRepLProp_SLProps prop(sf, 2, 1e-5);
+
+            int ntriangles = triangulation -> NbTriangles();
+            for (int j = 1; j <= ntriangles; j++)
+            {
+               gp_Pnt p[3];
+               gp_Pnt2d par[3];
+
+               for (int k = 1; k <=3; k++)
+               {
+                  int n = triangulation->Triangles()(j)(k);
+                  p[k-1] = triangulation->Nodes()(n).Transformed(loc);
+                  par[k-1] = triangulation->UVNodes()(n);
+               }
+
+               //double maxside = 0;
+               //maxside = max (maxside, p[0].Distance(p[1]));
+               //maxside = max (maxside, p[0].Distance(p[2]));
+               //maxside = max (maxside, p[1].Distance(p[2]));
+               //cout << "\rFace " << i << " pos11 ntriangles " << ntriangles << " maxside " << maxside << flush;
+
+               RestrictHTriangle (par[0], par[1], par[2], &prop, mesh, 0);
+               //cout << "\rFace " << i << " pos12 ntriangles " << ntriangles << flush;
+            }
+         }
+
+         // setting close edges
+
+         if (occparam.resthcloseedgeenable)
+         {
+            multithread.task = "Setting local mesh size (close edges)";
+
+            int sections = 100;
+
+            Array<Line> lines(sections*nedges);
+
+            Box3dTree* searchtree =
+               new Box3dTree (bb.PMin(), bb.PMax());
+
+            int nlines = 0;
+            for (int i = 1; i <= nedges && !multithread.terminate; i++)
+            {
+               TopoDS_Edge edge = TopoDS::Edge (geom.emap(i));
+               if (BRep_Tool::Degenerated(edge)) continue;
+
+               double s0, s1;
+               Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1);
+               BRepAdaptor_Curve brepc(edge);
+               BRepLProp_CLProps prop(brepc, 1, 1e-5);
+               prop.SetParameter (s0);
+
+               gp_Vec d0 = prop.D1().Normalized();
+               double s_start = s0;
+               int count = 0;
+               for (int j = 1; j <= sections; j++)
+               {
+                  double s = s0 + (s1-s0)*(double)j/(double)sections;
+                  prop.SetParameter (s);
+                  gp_Vec d1 = prop.D1().Normalized();
+                  double cosalpha = fabs(d0*d1);
+                  if ((j == sections) || (cosalpha < cos(10.0/180.0*M_PI)))
+                  {
+                     count++;
+                     gp_Pnt p0 = c->Value (s_start);
+                     gp_Pnt p1 = c->Value (s);
+                     lines[nlines].p0 = Point<3> (p0.X(), p0.Y(), p0.Z());
+                     lines[nlines].p1 = Point<3> (p1.X(), p1.Y(), p1.Z());
+
+                     Box3d box;
+                     box.SetPoint (Point3d(lines[nlines].p0));
+                     box.AddPoint (Point3d(lines[nlines].p1));
+
+                     searchtree->Insert (box.PMin(), box.PMax(), nlines+1);
+                     nlines++;
+
+                     s_start = s;
+                     d0 = d1;
+                  }
+               }
+            }
+
+            Array<int> linenums;
+
+            for (int i = 0; i < nlines; i++)
+            {
+               multithread.percent = (100*i)/double(nlines);
+               Line & line = lines[i];
+
+               Box3d box;
+               box.SetPoint (Point3d(line.p0));
+               box.AddPoint (Point3d(line.p1));
+               double maxhline = max (mesh.GetH(box.PMin()),
+                  mesh.GetH(box.PMax()));
+               box.Increase(maxhline);
+
+               double mindist = 1e99;
+               linenums.SetSize(0);
+               searchtree->GetIntersecting(box.PMin(),box.PMax(),linenums);
+
+               for (int j = 0; j < linenums.Size(); j++)
+               {
+                  int num = linenums[j]-1;
+                  if (i == num) continue;
+                  if ((line.p0-lines[num].p0).Length2() < 1e-15) continue;
+                  if ((line.p0-lines[num].p1).Length2() < 1e-15) continue;
+                  if ((line.p1-lines[num].p0).Length2() < 1e-15) continue;
+                  if ((line.p1-lines[num].p1).Length2() < 1e-15) continue;
+                  mindist = min (mindist, line.Dist(lines[num]));
+               }
+
+               mindist /= (occparam.resthcloseedgefac + VSMALL);
+
+               if (mindist < 1e-3)
+               {
+                  (*testout) << "extremely small local h: " << mindist
+                     << " --> setting to 1e-3" << endl;
+                  (*testout) << "somewhere near " << line.p0 << " - " << line.p1 << endl;
+                  mindist = 1e-3;
+               }
+
+               mesh.RestrictLocalHLine(line.p0, line.p1, mindist);
+            }
+         }
+
+         multithread.task = savetask;
+
+      }
+
+      // Philippose - 09/03/2009
+      // Added the capability to load the mesh size from a 
+      // file also for OpenCascade Geometry
+      // Note: 
+      // ** If the "uselocalh" option is ticked in 
+      // the "mesh options...insider" menu, the mesh 
+      // size will be further modified by the topology 
+      // analysis routines.
+      // ** To use the mesh size file as the sole source 
+      // for defining the mesh size, uncheck the "uselocalh"
+      // option.
+      mesh.LoadLocalMeshSize (mparam.meshsizefilename);
+   }
+
+
+
+  int OCCGenerateMesh (OCCGeometry & geom, Mesh *& mesh, MeshingParameters & mparam,
+		       int perfstepsstart, int perfstepsend)
+   {
+      multithread.percent = 0;
+
+      if (perfstepsstart <= MESHCONST_ANALYSE)
+      {
+         delete mesh;
+         mesh = new Mesh();
+         mesh->geomtype = Mesh::GEOM_OCC;
+
+         OCCSetLocalMeshSize(geom,*mesh);
+      }
+
+      if (multithread.terminate || perfstepsend <= MESHCONST_ANALYSE)
+         return TCL_OK;
+
+      if (perfstepsstart <= MESHCONST_MESHEDGES)
+      {
+         OCCFindEdges (geom, *mesh);
+
+         /*
+         cout << "Removing redundant points" << endl;
+
+         int i, j;
+         int np = mesh->GetNP();
+         Array<int> equalto;
+
+         equalto.SetSize (np);
+         equalto = 0;
+
+         for (i = 1; i <= np; i++)
+         {
+         for (j = i+1; j <= np; j++)
+         {
+         if (!equalto[j-1] && (Dist2 (mesh->Point(i), mesh->Point(j)) < 1e-12))
+         equalto[j-1] = i;
+         }
+         }
+
+         for (i = 1; i <= np; i++)
+         if (equalto[i-1])
+         {
+         cout << "Point " << i << " is equal to Point " << equalto[i-1] << endl;
+         for (j = 1; j <= mesh->GetNSeg(); j++)
+         {
+         Segment & seg = mesh->LineSegment(j);
+         if (seg[0] == i) seg[0] = equalto[i-1];
+         if (seg[1] == i) seg[1] = equalto[i-1];
+         }
+         }
+
+         cout << "Removing degenerated segments" << endl;
+         for (j = 1; j <= mesh->GetNSeg(); j++)
+         {
+         Segment & seg = mesh->LineSegment(j);
+         if (seg[0] == seg[1])
+         {
+         mesh->DeleteSegment(j);
+         cout << "Deleting Segment " << j << endl;
+         }
+         }
+
+         mesh->Compress();
+         */
+
+         /*
+         for (int i = 1; i <= geom.fmap.Extent(); i++)
+         {
+         Handle(Geom_Surface) hf1 =
+         BRep_Tool::Surface(TopoDS::Face(geom.fmap(i)));
+         for (int j = i+1; j <= geom.fmap.Extent(); j++)
+         {
+         Handle(Geom_Surface) hf2 =
+         BRep_Tool::Surface(TopoDS::Face(geom.fmap(j)));
+         if (hf1 == hf2) cout << "face " << i << " and face " << j << " lie on same surface" << endl;
+         }
+         }
+         */
+
+#ifdef LOG_STREAM
+         (*logout) << "Edges meshed" << endl
+            << "time = " << GetTime() << " sec" << endl
+            << "points: " << mesh->GetNP() << endl;
+#endif
+      }
+
+      if (multithread.terminate || perfstepsend <= MESHCONST_MESHEDGES)
+         return TCL_OK;
+
+      if (perfstepsstart <= MESHCONST_MESHSURFACE)
+      {
+         OCCMeshSurface (geom, *mesh, perfstepsend);
+         if (multithread.terminate) return TCL_OK;
+
+#ifdef LOG_STREAM
+         (*logout) << "Surfaces meshed" << endl
+            << "time = " << GetTime() << " sec" << endl
+            << "points: " << mesh->GetNP() << endl;
+#endif
+
+#ifdef STAT_STREAM
+         (*statout) << mesh->GetNSeg() << " & "
+            << mesh->GetNSE() << " & - &"
+            << GetTime() << " & " << endl;
+#endif
+
+         //      MeshQuality2d (*mesh);
+         mesh->CalcSurfacesOfNode();
+      }
+
+      if (multithread.terminate || perfstepsend <= MESHCONST_OPTSURFACE)
+         return TCL_OK;
+
+      if (perfstepsstart <= MESHCONST_MESHVOLUME)
+      {
+         multithread.task = "Volume meshing";
+
+         MESHING3_RESULT res = MeshVolume (mparam, *mesh);
+
+/*
+         ofstream problemfile("occmesh.rep",ios_base::app);
+
+         problemfile << "VOLUMEMESHING" << endl << endl;
+         if(res != MESHING3_OK)
+            problemfile << "ERROR" << endl << endl;
+         else
+            problemfile << "OK" << endl
+            << mesh->GetNE() << " elements" << endl << endl;
+
+         problemfile.close();
+*/
+
+         if (res != MESHING3_OK) return TCL_ERROR;
+
+         if (multithread.terminate) return TCL_OK;
+
+         RemoveIllegalElements (*mesh);
+         if (multithread.terminate) return TCL_OK;
+
+         MeshQuality3d (*mesh);
+
+#ifdef STAT_STREAM
+         (*statout) << GetTime() << " & ";
+#endif
+
+#ifdef LOG_STREAM
+         (*logout) << "Volume meshed" << endl
+            << "time = " << GetTime() << " sec" << endl
+            << "points: " << mesh->GetNP() << endl;
+#endif
+      }
+
+      if (multithread.terminate || perfstepsend <= MESHCONST_MESHVOLUME)
+         return TCL_OK;
+
+      if (perfstepsstart <= MESHCONST_OPTVOLUME)
+      {
+         multithread.task = "Volume optimization";
+
+         OptimizeVolume (mparam, *mesh);
+         if (multithread.terminate) return TCL_OK;
+
+#ifdef STAT_STREAM
+         (*statout) << GetTime() << " & "
+            << mesh->GetNE() << " & "
+            << mesh->GetNP() << " " << '\\' << '\\' << " \\" << "hline" << endl;
+#endif
+
+#ifdef LOG_STREAM
+         (*logout) << "Volume optimized" << endl
+            << "time = " << GetTime() << " sec" << endl
+            << "points: " << mesh->GetNP() << endl;
+#endif
+
+         // cout << "Optimization complete" << endl;
+
+      }
+
+      (*testout) << "NP: " << mesh->GetNP() << endl;
+      for (int i = 1; i <= mesh->GetNP(); i++)
+         (*testout) << mesh->Point(i) << endl;
+
+      (*testout) << endl << "NSegments: " << mesh->GetNSeg() << endl;
+      for (int i = 1; i <= mesh->GetNSeg(); i++)
+         (*testout) << mesh->LineSegment(i) << endl;
+
+      return TCL_OK;
+   }
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/occgeom.cpp b/contrib/Netgen/libsrc/occ/occgeom.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..964688bd65195309259977a4a2d4102017fec138
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occgeom.cpp
@@ -0,0 +1,1608 @@
+
+#ifdef OCCGEOMETRY
+
+#include <mystdlib.h>
+#include <occgeom.hpp>
+#include "ShapeAnalysis_ShapeTolerance.hxx"
+#include "ShapeAnalysis_ShapeContents.hxx"
+#include "ShapeAnalysis_CheckSmallFace.hxx"
+#include "ShapeAnalysis_DataMapOfShapeListOfReal.hxx"
+#include "ShapeAnalysis_Surface.hxx"
+#include "BRepAlgoAPI_Fuse.hxx"
+#include "BRepCheck_Analyzer.hxx"
+#include "BRepLib.hxx"
+#include "ShapeBuild_ReShape.hxx"
+#include "ShapeFix.hxx"
+#include "ShapeFix_FixSmallFace.hxx"
+#include "Partition_Spliter.hxx"
+
+
+namespace netgen
+{
+   void OCCGeometry :: PrintNrShapes ()
+   {
+      TopExp_Explorer e;
+      int count = 0;
+      for (e.Init(shape, TopAbs_COMPSOLID); e.More(); e.Next()) count++;
+      cout << "CompSolids: " << count << endl;
+
+      cout << "Solids    : " << somap.Extent() << endl;
+      cout << "Shells    : " << shmap.Extent() << endl;
+      cout << "Faces     : " << fmap.Extent() << endl;
+      cout << "Edges     : " << emap.Extent() << endl;
+      cout << "Vertices  : " << vmap.Extent() << endl;
+   }
+
+
+
+
+   void PrintContents (OCCGeometry * geom)
+   {
+      ShapeAnalysis_ShapeContents cont;
+      cont.Clear();
+      cont.Perform(geom->shape);
+
+      (*testout) << "OCC CONTENTS" << endl;
+      (*testout) << "============" << endl;
+      (*testout) << "SOLIDS   : " << cont.NbSolids() << endl;
+      (*testout) << "SHELLS   : " << cont.NbShells() << endl;
+      (*testout) << "FACES    : " << cont.NbFaces() << endl;
+      (*testout) << "WIRES    : " << cont.NbWires() << endl;
+      (*testout) << "EDGES    : " << cont.NbEdges() << endl;
+      (*testout) << "VERTICES : " << cont.NbVertices() << endl;
+
+      TopExp_Explorer e;
+      int count = 0;
+      for (e.Init(geom->shape, TopAbs_COMPOUND); e.More(); e.Next())
+         count++;
+      (*testout) << "Compounds: " << count << endl;
+
+      count = 0;
+      for (e.Init(geom->shape, TopAbs_COMPSOLID); e.More(); e.Next())
+         count++;
+      (*testout) << "CompSolids: " << count << endl;
+
+      (*testout) << endl;
+
+      cout << "Highest entry in topology hierarchy: " << endl;
+      if (count)
+         cout << count << " composite solid(s)" << endl;
+      else
+         if (geom->somap.Extent())
+            cout << geom->somap.Extent() << " solid(s)" << endl;
+         else
+            if (geom->shmap.Extent())
+               cout << geom->shmap.Extent() << " shells(s)" << endl;
+            else
+               if (geom->fmap.Extent())
+                  cout << geom->fmap.Extent() << " face(s)" << endl;
+               else
+                  if (geom->wmap.Extent())
+                     cout << geom->wmap.Extent() << " wire(s)" << endl;
+                  else
+                     if (geom->emap.Extent())
+                        cout << geom->emap.Extent() << " edge(s)" << endl;
+                     else
+                        if (geom->vmap.Extent())
+                           cout << geom->vmap.Extent() << " vertices(s)" << endl;
+                        else
+                           cout << "no entities" << endl;
+
+   }
+
+
+
+   void OCCGeometry :: HealGeometry ()
+   {
+      int nrc = 0, nrcs = 0,
+         nrso = somap.Extent(),
+         nrsh = shmap.Extent(),
+         nrf = fmap.Extent(),
+         nrw = wmap.Extent(),
+         nre = emap.Extent(),
+         nrv = vmap.Extent();
+
+      TopExp_Explorer exp0;
+      TopExp_Explorer exp1;
+
+
+      for (exp0.Init(shape, TopAbs_COMPOUND); exp0.More(); exp0.Next()) nrc++;
+      for (exp0.Init(shape, TopAbs_COMPSOLID); exp0.More(); exp0.Next()) nrcs++;
+
+      double surfacecont = 0;
+
+      {
+         Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+         rebuild->Apply(shape);
+         for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next())
+         {
+            TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+            if ( BRep_Tool::Degenerated(edge) )
+               rebuild->Remove(edge, false);
+         }
+         shape = rebuild->Apply(shape);
+      }
+
+      BuildFMap();
+
+
+      for (exp0.Init (shape, TopAbs_FACE); exp0.More(); exp0.Next())
+      {
+         TopoDS_Face face = TopoDS::Face(exp0.Current());
+
+         GProp_GProps system;
+         BRepGProp::SurfaceProperties(face, system);
+         surfacecont += system.Mass();
+      }
+
+
+      cout << "Starting geometry healing procedure (tolerance: " << tolerance << ")" << endl
+         << "-----------------------------------" << endl;
+
+      {
+         cout << endl << "- repairing faces" << endl;
+
+         Handle(ShapeFix_Face) sff;
+         Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+         rebuild->Apply(shape);
+
+
+         for (exp0.Init (shape, TopAbs_FACE); exp0.More(); exp0.Next())
+         {
+            // Variable to hold the colour (if there exists one) of 
+            // the current face being processed
+            Quantity_Color face_colour;
+
+            TopoDS_Face face = TopoDS::Face (exp0.Current());
+
+            if(face_colours.IsNull()
+               || (!(face_colours->GetColor(face,XCAFDoc_ColorSurf,face_colour))))
+            {
+               // Set the default face colour to green (Netgen Standard)
+               // if no colour has been defined for the face
+               face_colour = Quantity_Color(0.0,1.0,0.0,Quantity_TOC_RGB);
+            }
+
+            sff = new ShapeFix_Face (face);
+            sff->FixAddNaturalBoundMode() = Standard_True;
+            sff->FixSmallAreaWireMode() = Standard_True;
+            sff->Perform();
+
+            if(sff->Status(ShapeExtend_DONE1) ||
+               sff->Status(ShapeExtend_DONE2) ||
+               sff->Status(ShapeExtend_DONE3) ||
+               sff->Status(ShapeExtend_DONE4) ||
+               sff->Status(ShapeExtend_DONE5))
+            {
+               cout << "repaired face " << fmap.FindIndex(face) << " ";
+               if(sff->Status(ShapeExtend_DONE1))
+                  cout << "(some wires are fixed)" <<endl;
+               else if(sff->Status(ShapeExtend_DONE2))
+                  cout << "(orientation of wires fixed)" <<endl;
+               else if(sff->Status(ShapeExtend_DONE3))
+                  cout << "(missing seam added)" <<endl;
+               else if(sff->Status(ShapeExtend_DONE4))
+                  cout << "(small area wire removed)" <<endl;
+               else if(sff->Status(ShapeExtend_DONE5))
+                  cout << "(natural bounds added)" <<endl;
+               TopoDS_Face newface = sff->Face();
+
+               rebuild->Replace(face, newface, Standard_False);
+            }
+
+            // Set the original colour of the face to the newly created 
+            // face (after the healing process)
+            face = TopoDS::Face (exp0.Current());
+            face_colours->SetColor(face,face_colour,XCAFDoc_ColorSurf);
+         }
+         shape = rebuild->Apply(shape);
+      }
+
+
+      {
+         Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+         rebuild->Apply(shape);
+         for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next())
+         {
+            TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+            if ( BRep_Tool::Degenerated(edge) )
+               rebuild->Remove(edge, false);
+         }
+         shape = rebuild->Apply(shape);
+      }
+
+
+      if (fixsmalledges)
+      {
+         cout << endl << "- fixing small edges" << endl;
+
+         Handle(ShapeFix_Wire) sfw;
+         Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+         rebuild->Apply(shape);
+
+
+         for (exp0.Init (shape, TopAbs_FACE); exp0.More(); exp0.Next())
+         {
+            TopoDS_Face face = TopoDS::Face(exp0.Current());
+
+            for (exp1.Init (face, TopAbs_WIRE); exp1.More(); exp1.Next())
+            {
+               TopoDS_Wire oldwire = TopoDS::Wire(exp1.Current());
+               sfw = new ShapeFix_Wire (oldwire, face ,tolerance);
+               sfw->ModifyTopologyMode() = Standard_True;
+
+               sfw->ClosedWireMode() = Standard_True;
+
+               bool replace = false;
+
+               replace = sfw->FixReorder() || replace;
+
+               replace = sfw->FixConnected() || replace;
+
+
+
+               if (sfw->FixSmall (Standard_False, tolerance) && ! (sfw->StatusSmall(ShapeExtend_FAIL1) ||
+                  sfw->StatusSmall(ShapeExtend_FAIL2) ||
+                  sfw->StatusSmall(ShapeExtend_FAIL3)))
+               {
+                  cout << "Fixed small edge in wire " << wmap.FindIndex (oldwire) << endl;
+                  replace = true;
+
+               }
+               else if (sfw->StatusSmall(ShapeExtend_FAIL1))
+                  cerr << "Failed to fix small edge in wire " << wmap.FindIndex (oldwire)
+                  << ", edge cannot be checked (no 3d curve and no pcurve)" << endl;
+               else if (sfw->StatusSmall(ShapeExtend_FAIL2))
+                  cerr << "Failed to fix small edge in wire " << wmap.FindIndex (oldwire)
+                  << ", edge is null-length and has different vertives at begin and end, and lockvtx is True or ModifiyTopologyMode is False" << endl;
+               else if (sfw->StatusSmall(ShapeExtend_FAIL3))
+                  cerr << "Failed to fix small edge in wire " << wmap.FindIndex (oldwire)
+                  << ", CheckConnected has failed" << endl;
+
+               replace = sfw->FixEdgeCurves() || replace;
+
+               replace = sfw->FixDegenerated() || replace;
+
+               replace = sfw->FixSelfIntersection() || replace;
+
+               replace = sfw->FixLacking(Standard_True) || replace;
+
+               if(replace)
+               {
+                  TopoDS_Wire newwire = sfw->Wire();
+                  rebuild->Replace(oldwire, newwire, Standard_False);
+               }
+
+               //delete sfw; sfw = NULL;
+
+            }
+         }
+
+         shape = rebuild->Apply(shape);
+
+
+
+         {
+            BuildFMap();
+            Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+            rebuild->Apply(shape);
+
+            for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next())
+            {
+               TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+               if (vmap.FindIndex(TopExp::FirstVertex (edge)) ==
+                  vmap.FindIndex(TopExp::LastVertex (edge)))
+               {
+                  GProp_GProps system;
+                  BRepGProp::LinearProperties(edge, system);
+                  if (system.Mass() < tolerance)
+                  {
+                     cout << "removing degenerated edge " << emap.FindIndex(edge)
+                        << " from vertex " << vmap.FindIndex(TopExp::FirstVertex (edge))
+                        << " to vertex " << vmap.FindIndex(TopExp::LastVertex (edge)) << endl;
+                     rebuild->Remove(edge, false);
+                  }
+               }
+            }
+            shape = rebuild->Apply(shape);
+
+            //delete rebuild; rebuild = NULL;
+         }
+
+
+
+         {
+            Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+            rebuild->Apply(shape);
+            for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next())
+            {
+               TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+               if ( BRep_Tool::Degenerated(edge) )
+                  rebuild->Remove(edge, false);
+            }
+            shape = rebuild->Apply(shape);
+         }
+
+
+
+
+         Handle(ShapeFix_Wireframe) sfwf = new ShapeFix_Wireframe;
+         sfwf->SetPrecision(tolerance);
+         sfwf->Load (shape);
+         sfwf->ModeDropSmallEdges() = Standard_True;
+
+         sfwf->SetPrecision(boundingbox.Diam());
+
+         if (sfwf->FixWireGaps())
+         {
+            cout << endl << "- fixing wire gaps" << endl;
+            if (sfwf->StatusWireGaps(ShapeExtend_OK)) cout << "no gaps found" << endl;
+            if (sfwf->StatusWireGaps(ShapeExtend_DONE1)) cout << "some 2D gaps fixed" << endl;
+            if (sfwf->StatusWireGaps(ShapeExtend_DONE2)) cout << "some 3D gaps fixed" << endl;
+            if (sfwf->StatusWireGaps(ShapeExtend_FAIL1)) cout << "failed to fix some 2D gaps" << endl;
+            if (sfwf->StatusWireGaps(ShapeExtend_FAIL2)) cout << "failed to fix some 3D gaps" << endl;
+         }
+
+         sfwf->SetPrecision(tolerance);
+
+
+         {
+            for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next())
+            {
+               TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+               if ( BRep_Tool::Degenerated(edge) )
+                  cout << "degenerated edge at position 4" << endl;
+            }
+         }
+
+
+
+         if (sfwf->FixSmallEdges())
+         {
+            cout << endl << "- fixing wire frames" << endl;
+            if (sfwf->StatusSmallEdges(ShapeExtend_OK)) cout << "no small edges found" << endl;
+            if (sfwf->StatusSmallEdges(ShapeExtend_DONE1)) cout << "some small edges fixed" << endl;
+            if (sfwf->StatusSmallEdges(ShapeExtend_FAIL1)) cout << "failed to fix some small edges" << endl;
+         }
+
+
+
+         shape = sfwf->Shape();
+
+         //delete sfwf; sfwf = NULL;
+         //delete rebuild; rebuild = NULL;
+
+      }
+
+
+
+
+
+      {
+         for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next())
+         {
+            TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+            if ( BRep_Tool::Degenerated(edge) )
+               cout << "degenerated edge at position 5" << endl;
+         }
+      }
+
+
+
+
+      if (fixspotstripfaces)
+      {
+
+         cout << endl << "- fixing spot and strip faces" << endl;
+         Handle(ShapeFix_FixSmallFace) sffsm = new ShapeFix_FixSmallFace();
+         sffsm -> Init (shape);
+         sffsm -> SetPrecision (tolerance);
+         sffsm -> Perform();
+
+         shape = sffsm -> FixShape();
+         //delete sffsm; sffsm = NULL;
+      }
+
+
+      {
+         for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next())
+         {
+            TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+            if ( BRep_Tool::Degenerated(edge) )
+               cout << "degenerated edge at position 6" << endl;
+         }
+      }
+
+
+
+      if (sewfaces)
+      {
+         cout << endl << "- sewing faces" << endl;
+
+         BRepOffsetAPI_Sewing sewedObj(tolerance);
+
+         for (exp0.Init (shape, TopAbs_FACE); exp0.More(); exp0.Next())
+         {
+            TopoDS_Face face = TopoDS::Face (exp0.Current());
+            sewedObj.Add (face);
+         }
+
+         sewedObj.Perform();
+
+         if (!sewedObj.SewedShape().IsNull())
+            shape = sewedObj.SewedShape();
+         else
+            cout << " not possible";
+      }
+
+
+
+      {
+         Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+         rebuild->Apply(shape);
+         for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next())
+         {
+            TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+            if ( BRep_Tool::Degenerated(edge) )
+               rebuild->Remove(edge, false);
+         }
+         shape = rebuild->Apply(shape);
+      }
+
+
+      if (makesolids)
+      {
+         cout << endl << "- making solids" << endl;
+
+         BRepBuilderAPI_MakeSolid ms;
+         int count = 0;
+         for (exp0.Init(shape, TopAbs_SHELL); exp0.More(); exp0.Next())
+         {
+            count++;
+            ms.Add (TopoDS::Shell(exp0.Current()));
+         }
+
+         if (!count)
+         {
+            cout << " not possible (no shells)" << endl;
+         }
+         else
+         {
+            BRepCheck_Analyzer ba(ms);
+            if (ba.IsValid ())
+            {
+               Handle(ShapeFix_Shape) sfs = new ShapeFix_Shape;
+               sfs->Init (ms);
+               sfs->SetPrecision(tolerance);
+               sfs->SetMaxTolerance(tolerance);
+               sfs->Perform();
+               shape = sfs->Shape();
+
+               for (exp0.Init(shape, TopAbs_SOLID); exp0.More(); exp0.Next())
+               {
+                  TopoDS_Solid solid = TopoDS::Solid(exp0.Current());
+                  TopoDS_Solid newsolid = solid;
+                  BRepLib::OrientClosedSolid (newsolid);
+                  Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+                  //		  rebuild->Apply(shape);
+                  rebuild->Replace(solid, newsolid, Standard_False);
+                  TopoDS_Shape newshape = rebuild->Apply(shape, TopAbs_COMPSOLID);//, 1);
+                  //		  TopoDS_Shape newshape = rebuild->Apply(shape);
+                  shape = newshape;
+               }
+
+               //delete sfs; sfs = NULL;
+            }
+            else
+               cout << " not possible" << endl;
+         }
+      }
+
+
+
+      if (splitpartitions)
+      {
+         cout << "- running SALOME partition splitter" << endl;
+
+         TopExp_Explorer e2;
+         Partition_Spliter ps;
+         int count = 0;
+
+         for (e2.Init (shape, TopAbs_SOLID);
+            e2.More(); e2.Next())
+         {
+            count++;
+            ps.AddShape (e2.Current());
+         }
+
+         ps.Compute();
+         shape = ps.Shape();
+
+         cout << " before: " << count << " solids" << endl;
+
+         count = 0;
+         for (e2.Init (shape, TopAbs_SOLID);
+            e2.More(); e2.Next()) count++;
+
+            cout << " after : " << count << " solids" << endl;
+      }
+
+      BuildFMap();
+
+
+
+      {
+         for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next())
+         {
+            TopoDS_Edge edge = TopoDS::Edge(exp1.Current());
+            if ( BRep_Tool::Degenerated(edge) )
+               cout << "degenerated edge at position 8" << endl;
+         }
+      }
+
+
+      double newsurfacecont = 0;
+
+
+      for (exp0.Init (shape, TopAbs_FACE); exp0.More(); exp0.Next())
+      {
+         TopoDS_Face face = TopoDS::Face(exp0.Current());
+         GProp_GProps system;
+         BRepGProp::SurfaceProperties(face, system);
+         newsurfacecont += system.Mass();
+      }
+
+
+      int nnrc = 0, nnrcs = 0,
+         nnrso = somap.Extent(),
+         nnrsh = shmap.Extent(),
+         nnrf = fmap.Extent(),
+         nnrw = wmap.Extent(),
+         nnre = emap.Extent(),
+         nnrv = vmap.Extent();
+
+      for (exp0.Init(shape, TopAbs_COMPOUND); exp0.More(); exp0.Next()) nnrc++;
+      for (exp0.Init(shape, TopAbs_COMPSOLID); exp0.More(); exp0.Next()) nnrcs++;
+
+      cout << "-----------------------------------" << endl;
+      cout << "Compounds       : " << nnrc << " (" << nrc << ")" << endl;
+      cout << "Composite solids: " << nnrcs << " (" << nrcs << ")" << endl;
+      cout << "Solids          : " << nnrso << " (" << nrso << ")" << endl;
+      cout << "Shells          : " << nnrsh << " (" << nrsh << ")" << endl;
+      cout << "Wires           : " << nnrw << " (" << nrw << ")" << endl;
+      cout << "Faces           : " << nnrf << " (" << nrf << ")" << endl;
+      cout << "Edges           : " << nnre << " (" << nre << ")" << endl;
+      cout << "Vertices        : " << nnrv << " (" << nrv << ")" << endl;
+      cout << endl;
+      cout << "Total surface area : " << newsurfacecont << " (" << surfacecont << ")" << endl;
+      cout << endl;
+   }
+
+
+
+
+   void OCCGeometry :: BuildFMap()
+   {
+      somap.Clear();
+      shmap.Clear();
+      fmap.Clear();
+      wmap.Clear();
+      emap.Clear();
+      vmap.Clear();
+
+      TopExp_Explorer exp0, exp1, exp2, exp3, exp4, exp5;
+
+      for (exp0.Init(shape, TopAbs_COMPOUND);
+         exp0.More(); exp0.Next())
+      {
+         TopoDS_Compound compound = TopoDS::Compound (exp0.Current());
+         (*testout) << "compound" << endl;
+         int i = 0;
+         for (exp1.Init(compound, TopAbs_SHELL);
+            exp1.More(); exp1.Next())
+         {
+            (*testout) << "shell " << ++i << endl;
+         }
+      }
+
+      for (exp0.Init(shape, TopAbs_SOLID);
+         exp0.More(); exp0.Next())
+      {
+         TopoDS_Solid solid = TopoDS::Solid (exp0.Current());
+
+         if (somap.FindIndex(solid) < 1)
+         {
+            somap.Add (solid);
+
+            for (exp1.Init(solid, TopAbs_SHELL);
+               exp1.More(); exp1.Next())
+            {
+               TopoDS_Shell shell = TopoDS::Shell (exp1.Current());
+               if (shmap.FindIndex(shell) < 1)
+               {
+                  shmap.Add (shell);
+
+                  for (exp2.Init(shell, TopAbs_FACE);
+                     exp2.More(); exp2.Next())
+                  {
+                     TopoDS_Face face = TopoDS::Face(exp2.Current());
+                     if (fmap.FindIndex(face) < 1)
+                     {
+                        fmap.Add (face);
+                        (*testout) << "face " << fmap.FindIndex(face) << " ";
+                        (*testout) << ((face.Orientation() == TopAbs_REVERSED) ? "-" : "+") << ", ";
+                        (*testout) << ((exp2.Current().Orientation() == TopAbs_REVERSED) ? "-" : "+") << endl;
+                        for (exp3.Init(exp2.Current(), TopAbs_WIRE);
+                           exp3.More(); exp3.Next())
+                        {
+                           TopoDS_Wire wire = TopoDS::Wire (exp3.Current());
+                           if (wmap.FindIndex(wire) < 1)
+                           {
+                              wmap.Add (wire);
+
+                              for (exp4.Init(exp3.Current(), TopAbs_EDGE);
+                                 exp4.More(); exp4.Next())
+                              {
+                                 TopoDS_Edge edge = TopoDS::Edge(exp4.Current());
+                                 if (emap.FindIndex(edge) < 1)
+                                 {
+                                    emap.Add (edge);
+                                    for (exp5.Init(exp4.Current(), TopAbs_VERTEX);
+                                       exp5.More(); exp5.Next())
+                                    {
+                                       TopoDS_Vertex vertex = TopoDS::Vertex(exp5.Current());
+                                       if (vmap.FindIndex(vertex) < 1)
+                                          vmap.Add (vertex);
+                                    }
+                                 }
+                              }
+                           }
+                        }
+                     }
+                  }
+               }
+            }
+         }
+      }
+
+      // Free Shells
+      for (exp1.Init(shape, TopAbs_SHELL, TopAbs_SOLID); exp1.More(); exp1.Next())
+      {
+         TopoDS_Shell shell = TopoDS::Shell(exp1.Current());
+         if (shmap.FindIndex(shell) < 1)
+         {
+            shmap.Add (shell);
+
+            (*testout) << "shell " << shmap.FindIndex(shell) << " ";
+            (*testout) << ((shell.Orientation() == TopAbs_REVERSED) ? "-" : "+") << ", ";
+            (*testout) << ((exp1.Current().Orientation() == TopAbs_REVERSED) ? "-" : "+") << endl;
+
+            for (exp2.Init(shell, TopAbs_FACE); exp2.More(); exp2.Next())
+            {
+               TopoDS_Face face = TopoDS::Face(exp2.Current());
+               if (fmap.FindIndex(face) < 1)
+               {
+                  fmap.Add (face);
+
+                  for (exp3.Init(face, TopAbs_WIRE); exp3.More(); exp3.Next())
+                  {
+                     TopoDS_Wire wire = TopoDS::Wire (exp3.Current());
+                     if (wmap.FindIndex(wire) < 1)
+                     {
+                        wmap.Add (wire);
+
+                        for (exp4.Init(wire, TopAbs_EDGE); exp4.More(); exp4.Next())
+                        {
+                           TopoDS_Edge edge = TopoDS::Edge(exp4.Current());
+                           if (emap.FindIndex(edge) < 1)
+                           {
+                              emap.Add (edge);
+                              for (exp5.Init(edge, TopAbs_VERTEX); exp5.More(); exp5.Next())
+                              {
+                                 TopoDS_Vertex vertex = TopoDS::Vertex(exp5.Current());
+                                 if (vmap.FindIndex(vertex) < 1)
+                                    vmap.Add (vertex);
+                              }
+                           }
+                        }
+                     }
+                  }
+               }
+            }
+         }
+      }
+
+
+      // Free Faces
+
+      for (exp2.Init(shape, TopAbs_FACE, TopAbs_SHELL); exp2.More(); exp2.Next())
+      {
+         TopoDS_Face face = TopoDS::Face(exp2.Current());
+         if (fmap.FindIndex(face) < 1)
+         {
+            fmap.Add (face);
+
+            for (exp3.Init(exp2.Current(), TopAbs_WIRE); exp3.More(); exp3.Next())
+            {
+               TopoDS_Wire wire = TopoDS::Wire (exp3.Current());
+               if (wmap.FindIndex(wire) < 1)
+               {
+                  wmap.Add (wire);
+
+                  for (exp4.Init(exp3.Current(), TopAbs_EDGE); exp4.More(); exp4.Next())
+                  {
+                     TopoDS_Edge edge = TopoDS::Edge(exp4.Current());
+                     if (emap.FindIndex(edge) < 1)
+                     {
+                        emap.Add (edge);
+                        for (exp5.Init(exp4.Current(), TopAbs_VERTEX); exp5.More(); exp5.Next())
+                        {
+                           TopoDS_Vertex vertex = TopoDS::Vertex(exp5.Current());
+                           if (vmap.FindIndex(vertex) < 1)
+                              vmap.Add (vertex);
+                        }
+                     }
+                  }
+               }
+            }
+         }
+      }
+
+
+      // Free Wires
+
+      for (exp3.Init(shape, TopAbs_WIRE, TopAbs_FACE); exp3.More(); exp3.Next())
+      {
+         TopoDS_Wire wire = TopoDS::Wire (exp3.Current());
+         if (wmap.FindIndex(wire) < 1)
+         {
+            wmap.Add (wire);
+
+            for (exp4.Init(exp3.Current(), TopAbs_EDGE); exp4.More(); exp4.Next())
+            {
+               TopoDS_Edge edge = TopoDS::Edge(exp4.Current());
+               if (emap.FindIndex(edge) < 1)
+               {
+                  emap.Add (edge);
+                  for (exp5.Init(exp4.Current(), TopAbs_VERTEX); exp5.More(); exp5.Next())
+                  {
+                     TopoDS_Vertex vertex = TopoDS::Vertex(exp5.Current());
+                     if (vmap.FindIndex(vertex) < 1)
+                        vmap.Add (vertex);
+                  }
+               }
+            }
+         }
+      }
+
+
+      // Free Edges
+
+      for (exp4.Init(shape, TopAbs_EDGE, TopAbs_WIRE); exp4.More(); exp4.Next())
+      {
+         TopoDS_Edge edge = TopoDS::Edge(exp4.Current());
+         if (emap.FindIndex(edge) < 1)
+         {
+            emap.Add (edge);
+            for (exp5.Init(exp4.Current(), TopAbs_VERTEX); exp5.More(); exp5.Next())
+            {
+               TopoDS_Vertex vertex = TopoDS::Vertex(exp5.Current());
+               if (vmap.FindIndex(vertex) < 1)
+                  vmap.Add (vertex);
+            }
+         }
+      }
+
+
+      // Free Vertices
+
+      for (exp5.Init(shape, TopAbs_VERTEX, TopAbs_EDGE); exp5.More(); exp5.Next())
+      {
+         TopoDS_Vertex vertex = TopoDS::Vertex(exp5.Current());
+         if (vmap.FindIndex(vertex) < 1)
+            vmap.Add (vertex);
+      }
+
+
+
+
+      facemeshstatus.DeleteAll();
+      facemeshstatus.SetSize (fmap.Extent());
+      facemeshstatus = 0;
+
+      // Philippose - 15/01/2009
+      face_maxh.DeleteAll();
+      face_maxh.SetSize (fmap.Extent());
+      face_maxh = mparam.maxh;
+
+      // Philippose - 15/01/2010      
+      face_maxh_modified.DeleteAll();      
+      face_maxh_modified.SetSize(fmap.Extent());      
+      face_maxh_modified = 0;
+      
+
+      // Philippose - 17/01/2009
+      face_sel_status.DeleteAll();
+      face_sel_status.SetSize (fmap.Extent());
+      face_sel_status = 0;
+
+      fvispar.SetSize (fmap.Extent());
+      evispar.SetSize (emap.Extent());
+      vvispar.SetSize (vmap.Extent());
+
+      fsingular.SetSize (fmap.Extent());
+      esingular.SetSize (emap.Extent());
+      vsingular.SetSize (vmap.Extent());
+
+      fsingular = esingular = vsingular = false;
+   }
+
+
+
+   void OCCGeometry :: SewFaces ()
+   {
+      (*testout) << "Trying to sew faces ..." << endl;
+      cout << "Trying to sew faces ..." << flush;
+
+      BRepOffsetAPI_Sewing sewedObj(1);
+ 
+      for (int i = 1; i <= fmap.Extent(); i++)
+      {
+         TopoDS_Face face = TopoDS::Face (fmap(i));
+         sewedObj.Add (face);
+      }
+
+      sewedObj.Perform();
+
+      if (!sewedObj.SewedShape().IsNull())
+      {
+         shape = sewedObj.SewedShape();
+         cout << " done" << endl;
+      }
+      else
+         cout << " not possible";
+   }
+
+
+
+
+
+   void OCCGeometry :: MakeSolid ()
+   {
+      TopExp_Explorer exp0;
+
+      (*testout) << "Trying to build solids ..." << endl;
+      cout << "Trying to build solids ..." << flush;
+
+      BRepBuilderAPI_MakeSolid ms;
+      int count = 0;
+      for (exp0.Init(shape, TopAbs_SHELL); exp0.More(); exp0.Next())
+      {
+         count++;
+         ms.Add (TopoDS::Shell(exp0.Current()));
+      }
+
+      if (!count)
+      {
+         cout << " not possible (no shells)" << endl;
+         return;
+      }
+
+      BRepCheck_Analyzer ba(ms);
+      if (ba.IsValid ())
+      {
+         Handle(ShapeFix_Shape) sfs = new ShapeFix_Shape;
+         sfs->Init (ms);
+
+         sfs->SetPrecision(1e-5);
+         sfs->SetMaxTolerance(1e-5);
+
+         sfs->Perform();
+
+         shape = sfs->Shape();
+
+         for (exp0.Init(shape, TopAbs_SOLID); exp0.More(); exp0.Next())
+         {
+            TopoDS_Solid solid = TopoDS::Solid(exp0.Current());
+            TopoDS_Solid newsolid = solid;
+            BRepLib::OrientClosedSolid (newsolid);
+            Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+            rebuild->Replace(solid, newsolid, Standard_False);
+
+            TopoDS_Shape newshape = rebuild->Apply(shape, TopAbs_SHAPE, 1);
+            shape = newshape;
+         }
+
+         cout << " done" << endl;
+      }
+      else
+         cout << " not possible" << endl;
+   }
+
+
+
+
+   void OCCGeometry :: BuildVisualizationMesh (double deflection)
+   {
+      cout << "Preparing visualization (deflection = " << deflection << ") ... " << flush;
+
+      BRepTools::Clean (shape);
+      // BRepMesh_IncrementalMesh::
+      BRepMesh_IncrementalMesh (shape, deflection, true);
+      cout << "done" << endl;
+   }
+
+
+
+
+   void OCCGeometry :: CalcBoundingBox ()
+   {
+      Bnd_Box bb;
+      BRepBndLib::Add (shape, bb);
+
+      double x1,y1,z1,x2,y2,z2;
+      bb.Get (x1,y1,z1,x2,y2,z2);
+      Point<3> p1 = Point<3> (x1,y1,z1);
+      Point<3> p2 = Point<3> (x2,y2,z2);
+
+      (*testout) << "Bounding Box = [" << p1 << " - " << p2 << "]" << endl;
+      boundingbox = Box<3> (p1,p2);
+      SetCenter();
+   }
+
+
+
+
+   void OCCGeometry :: Project (int surfi, Point<3> & p) const
+   {
+      static int cnt = 0;
+      if (++cnt % 1000 == 0) cout << "Project cnt = " << cnt << endl;
+
+      gp_Pnt pnt(p(0), p(1), p(2));
+
+      double u,v;
+      Handle( Geom_Surface ) thesurf = BRep_Tool::Surface(TopoDS::Face(fmap(surfi)));
+      Handle( ShapeAnalysis_Surface ) su = new ShapeAnalysis_Surface( thesurf );
+      gp_Pnt2d suval = su->ValueOfUV ( pnt, BRep_Tool::Tolerance( TopoDS::Face(fmap(surfi)) ) );
+      suval.Coord( u, v);
+      pnt = thesurf->Value( u, v );
+
+
+      p = Point<3> (pnt.X(), pnt.Y(), pnt.Z());
+
+   }
+
+
+
+
+   bool OCCGeometry :: FastProject (int surfi, Point<3> & ap, double& u, double& v) const
+   {
+      gp_Pnt p(ap(0), ap(1), ap(2));
+
+      Handle(Geom_Surface) surface = BRep_Tool::Surface(TopoDS::Face(fmap(surfi)));
+
+      gp_Pnt x = surface->Value (u,v);
+
+      if (p.SquareDistance(x) <= sqr(PROJECTION_TOLERANCE)) return true;
+
+      gp_Vec du, dv;
+
+      surface->D1(u,v,x,du,dv);
+
+      int count = 0;
+
+      gp_Pnt xold;
+      gp_Vec n;
+      double det, lambda, mu;
+
+      do {
+         count++;
+
+         n = du^dv;
+
+         det = Det3 (n.X(), du.X(), dv.X(),
+            n.Y(), du.Y(), dv.Y(),
+            n.Z(), du.Z(), dv.Z());
+
+         if (det < 1e-15) return false;
+
+         lambda = Det3 (n.X(), p.X()-x.X(), dv.X(),
+            n.Y(), p.Y()-x.Y(), dv.Y(),
+            n.Z(), p.Z()-x.Z(), dv.Z())/det;
+
+         mu     = Det3 (n.X(), du.X(), p.X()-x.X(),
+            n.Y(), du.Y(), p.Y()-x.Y(),
+            n.Z(), du.Z(), p.Z()-x.Z())/det;
+
+         u += lambda;
+         v += mu;
+
+         xold = x;
+         surface->D1(u,v,x,du,dv);
+
+      } while (xold.SquareDistance(x) > sqr(PROJECTION_TOLERANCE) && count < 50);
+
+      //    (*testout) << "FastProject count: " << count << endl;
+
+      if (count == 50) return false;
+
+      ap = Point<3> (x.X(), x.Y(), x.Z());
+
+      return true;
+   }
+
+
+
+
+   void OCCGeometry :: WriteOCC_STL(char * filename)
+   {
+      cout << "writing stl..."; cout.flush();
+      StlAPI_Writer writer;
+      writer.RelativeMode() = Standard_False;
+
+      writer.SetDeflection(0.02);
+      writer.Write(shape,filename);
+
+      cout << "done" << endl;
+   }
+
+
+
+   // Philippose - 23/02/2009
+   /* Special IGES File load function including the ability
+   to extract individual surface colours via the extended
+   OpenCascade XDE and XCAF Feature set.
+   */
+   OCCGeometry *LoadOCC_IGES(const char *filename)
+   {
+      OCCGeometry *occgeo;
+      occgeo = new OCCGeometry;
+
+      // Initiate a dummy XCAF Application to handle the IGES XCAF Document
+      static Handle_XCAFApp_Application dummy_app = XCAFApp_Application::GetApplication();
+
+      // Create an XCAF Document to contain the IGES file itself
+      Handle_TDocStd_Document iges_doc;
+
+      // Check if a IGES File is already open under this handle, if so, close it to prevent
+      // Segmentation Faults when trying to create a new document
+      if(dummy_app->NbDocuments() > 0)
+      {
+         dummy_app->GetDocument(1,iges_doc);
+         dummy_app->Close(iges_doc);
+      }
+      dummy_app->NewDocument ("IGES-XCAF",iges_doc);
+
+      IGESCAFControl_Reader reader;
+
+      Standard_Integer stat = reader.ReadFile((char*)filename);
+
+      if(stat != IFSelect_RetDone)
+      {
+         delete occgeo;
+         return NULL;
+      }
+
+      // Enable transfer of colours
+      reader.SetColorMode(Standard_True);
+
+      reader.Transfer(iges_doc);
+
+      // Read in the shape(s) and the colours present in the IGES File
+      Handle_XCAFDoc_ShapeTool iges_shape_contents = XCAFDoc_DocumentTool::ShapeTool(iges_doc->Main());
+      Handle_XCAFDoc_ColorTool iges_colour_contents = XCAFDoc_DocumentTool::ColorTool(iges_doc->Main());
+
+      TDF_LabelSequence iges_shapes;
+      iges_shape_contents->GetShapes(iges_shapes);
+
+      // List out the available colours in the IGES File as Colour Names
+      TDF_LabelSequence all_colours;
+      iges_colour_contents->GetColors(all_colours);
+      PrintMessage(1,"Number of colours in IGES File: ",all_colours.Length());
+      for(int i = 1; i <= all_colours.Length(); i++)
+      {
+         Quantity_Color col;
+         stringstream col_rgb;
+         iges_colour_contents->GetColor(all_colours.Value(i),col);
+         col_rgb << " : (" << col.Red() << "," << col.Green() << "," << col.Blue() << ")";
+         PrintMessage(1, "Colour [", i, "] = ",col.StringName(col.Name()),col_rgb.str());
+      }
+
+
+      // For the IGES Reader, all the shapes can be exported as one compund shape 
+      // using the "OneShape" member
+      occgeo->shape = reader.OneShape();
+      occgeo->face_colours = iges_colour_contents;
+      occgeo->changed = 1;
+      occgeo->BuildFMap();
+
+      occgeo->CalcBoundingBox();
+      PrintContents (occgeo);
+
+      return occgeo;
+   }
+
+
+
+
+
+   // Philippose - 29/01/2009
+   /* Special STEP File load function including the ability
+   to extract individual surface colours via the extended
+   OpenCascade XDE and XCAF Feature set.
+   */
+   OCCGeometry * LoadOCC_STEP (const char * filename)
+   {
+      OCCGeometry * occgeo;
+      occgeo = new OCCGeometry;
+
+      // Initiate a dummy XCAF Application to handle the STEP XCAF Document
+      static Handle_XCAFApp_Application dummy_app = XCAFApp_Application::GetApplication();
+
+      // Create an XCAF Document to contain the STEP file itself
+      Handle_TDocStd_Document step_doc;
+
+      // Check if a STEP File is already open under this handle, if so, close it to prevent
+      // Segmentation Faults when trying to create a new document
+      if(dummy_app->NbDocuments() > 0)
+      {
+         dummy_app->GetDocument(1,step_doc);
+         dummy_app->Close(step_doc);
+      }
+      dummy_app->NewDocument ("STEP-XCAF",step_doc);
+
+      STEPCAFControl_Reader reader;
+
+      // Enable transfer of colours
+      reader.SetColorMode(Standard_True);
+
+      Standard_Integer stat = reader.ReadFile((char*)filename);
+
+      if(stat != IFSelect_RetDone)
+      {
+         delete occgeo;
+         return NULL;
+      }
+
+      reader.Transfer(step_doc);
+
+      // Read in the shape(s) and the colours present in the STEP File
+      Handle_XCAFDoc_ShapeTool step_shape_contents = XCAFDoc_DocumentTool::ShapeTool(step_doc->Main());
+      Handle_XCAFDoc_ColorTool step_colour_contents = XCAFDoc_DocumentTool::ColorTool(step_doc->Main());
+
+      TDF_LabelSequence step_shapes;
+      step_shape_contents->GetShapes(step_shapes);
+
+      // List out the available colours in the STEP File as Colour Names
+      TDF_LabelSequence all_colours;
+      step_colour_contents->GetColors(all_colours);
+      PrintMessage(1,"Number of colours in STEP File: ",all_colours.Length());
+      for(int i = 1; i <= all_colours.Length(); i++)
+      {
+         Quantity_Color col;
+         stringstream col_rgb;
+         step_colour_contents->GetColor(all_colours.Value(i),col);
+         col_rgb << " : (" << col.Red() << "," << col.Green() << "," << col.Blue() << ")";
+         PrintMessage(1, "Colour [", i, "] = ",col.StringName(col.Name()),col_rgb.str());
+      }
+
+
+      // For the STEP File Reader in OCC, the 1st Shape contains the entire 
+      // compound geometry as one shape
+      occgeo->shape = step_shape_contents->GetShape(step_shapes.Value(1));
+      occgeo->face_colours = step_colour_contents;
+      occgeo->changed = 1;
+      occgeo->BuildFMap();
+
+      occgeo->CalcBoundingBox();
+      PrintContents (occgeo);
+
+      return occgeo;
+   }
+
+
+
+
+   OCCGeometry *LoadOCC_BREP (const char *filename)
+   {
+      OCCGeometry * occgeo;
+      occgeo = new OCCGeometry;
+
+      BRep_Builder aBuilder;
+      Standard_Boolean result = BRepTools::Read(occgeo->shape, const_cast<char*> (filename),aBuilder);
+
+      if(!result)
+      {
+         delete occgeo;
+         return NULL;
+      }
+
+      // Philippose - 23/02/2009
+      // Fixed a bug in the OpenCascade XDE Colour handling when 
+      // opening BREP Files, since BREP Files have no colour data.
+      // Hence, the face_colours Handle needs to be created as a NULL handle.
+      occgeo->face_colours = Handle_XCAFDoc_ColorTool();
+      occgeo->face_colours.Nullify();
+      occgeo->changed = 1;
+      occgeo->BuildFMap();
+
+      occgeo->CalcBoundingBox();
+      PrintContents (occgeo);
+
+      return occgeo;
+   }
+
+
+  void OCCGeometry :: Save (string sfilename) const
+  {
+    const char * filename = sfilename.c_str();
+    if (strlen(filename) < 4) 
+      throw NgException ("illegal filename");
+    
+    if (strcmp (&filename[strlen(filename)-3], "igs") == 0)
+      {
+	IGESControl_Writer writer("millimeters", 1);
+	writer.AddShape (shape);
+	writer.Write (filename);
+      }
+    else if (strcmp (&filename[strlen(filename)-3], "stp") == 0)
+      {
+	STEPControl_Writer writer;
+	writer.Transfer (shape, STEPControl_AsIs);
+	writer.Write (filename);
+      }
+    else if (strcmp (&filename[strlen(filename)-3], "stl") == 0)
+      {
+	StlAPI_Writer writer;
+	writer.ASCIIMode() = Standard_True;
+	writer.Write (shape, filename);
+      }
+    else if (strcmp (&filename[strlen(filename)-4], "stlb") == 0)
+      {
+	StlAPI_Writer writer;
+	writer.ASCIIMode() = Standard_False;
+	writer.Write (shape, filename);
+      }
+  }
+
+
+
+  const char * shapesname[] =
+   {" ", "CompSolids", "Solids", "Shells",
+
+   "Faces", "Wires", "Edges", "Vertices"};
+
+  const char * shapename[] =
+   {" ", "CompSolid", "Solid", "Shell",
+   "Face", "Wire", "Edge", "Vertex"};
+
+  const char * orientationstring[] =
+     {"+", "-"};
+
+
+
+
+   void OCCGeometry :: RecursiveTopologyTree (const TopoDS_Shape & sh,
+      stringstream & str,
+      TopAbs_ShapeEnum l,
+      bool isfree,
+      const char * lname)
+   {
+      if (l > TopAbs_VERTEX) return;
+
+      TopExp_Explorer e;
+      int count = 0;
+      int count2 = 0;
+
+      if (isfree)
+         e.Init(sh, l, TopAbs_ShapeEnum(l-1));
+      else
+         e.Init(sh, l);
+
+      for (; e.More(); e.Next())
+      {
+         count++;
+
+         stringstream lname2;
+         lname2 << lname << "/" << shapename[l] << count;
+         str << lname2.str() << " ";
+
+         switch (e.Current().ShapeType())
+	   {
+	   case TopAbs_SOLID:
+	     count2 = somap.FindIndex(TopoDS::Solid(e.Current())); break;
+	   case TopAbs_SHELL:
+	     count2 = shmap.FindIndex(TopoDS::Shell(e.Current())); break;
+	   case TopAbs_FACE:
+	     count2 = fmap.FindIndex(TopoDS::Face(e.Current())); break;
+	   case TopAbs_WIRE:
+	     count2 = wmap.FindIndex(TopoDS::Wire(e.Current())); break;
+	   case TopAbs_EDGE:
+	     count2 = emap.FindIndex(TopoDS::Edge(e.Current())); break;
+	   case TopAbs_VERTEX:
+	     count2 = vmap.FindIndex(TopoDS::Vertex(e.Current())); break;
+	   default:
+	     cout << "RecursiveTopologyTree: Case " << e.Current().ShapeType() << " not handeled" << endl;
+         }
+
+         int nrsubshapes = 0;
+
+         if (l <= TopAbs_WIRE)
+         {
+            TopExp_Explorer e2;
+            for (e2.Init (e.Current(), TopAbs_ShapeEnum (l+1));
+               e2.More(); e2.Next())
+               nrsubshapes++;
+         }
+
+         str << "{" << shapename[l] << " " << count2;
+
+         if (l <= TopAbs_EDGE)
+         {
+            str << " (" << orientationstring[e.Current().Orientation()];
+            if (nrsubshapes != 0) str << ", " << nrsubshapes;
+            str << ") } ";
+         }
+         else
+            str << " } ";
+
+         RecursiveTopologyTree (e.Current(), str, TopAbs_ShapeEnum (l+1),
+            false, (char*)lname2.str().c_str());
+
+      }
+   }
+
+
+
+
+   void OCCGeometry :: GetTopologyTree (stringstream & str)
+   {
+      cout << "Building topology tree ... " << flush;
+      RecursiveTopologyTree (shape, str, TopAbs_COMPSOLID, false, "CompSolids");
+      RecursiveTopologyTree (shape, str, TopAbs_SOLID, true, "FreeSolids");
+      RecursiveTopologyTree (shape, str, TopAbs_SHELL, true, "FreeShells");
+      RecursiveTopologyTree (shape, str, TopAbs_FACE, true, "FreeFaces");
+      RecursiveTopologyTree (shape, str, TopAbs_WIRE, true, "FreeWires");
+      RecursiveTopologyTree (shape, str, TopAbs_EDGE, true, "FreeEdges");
+      RecursiveTopologyTree (shape, str, TopAbs_VERTEX, true, "FreeVertices");
+      str << flush;
+      //  cout << "done" << endl;
+   }
+
+
+
+
+   void OCCGeometry :: CheckIrregularEntities(stringstream & str)
+   {
+      ShapeAnalysis_CheckSmallFace csm;
+
+      csm.SetTolerance (1e-6);
+
+      TopTools_DataMapOfShapeListOfShape mapEdges;
+      ShapeAnalysis_DataMapOfShapeListOfReal mapParam;
+      TopoDS_Compound theAllVert;
+
+      int spotfaces = 0;
+      int stripsupportfaces = 0;
+      int singlestripfaces = 0;
+      int stripfaces = 0;
+      int facessplitbyvertices = 0;
+      int stretchedpinfaces = 0;
+      int smoothpinfaces = 0;
+      int twistedfaces = 0;
+      // int edgessamebutnotidentified = 0;
+
+      cout << "checking faces ... " << flush;
+
+      int i;
+      for (i = 1; i <= fmap.Extent(); i++)
+      {
+         TopoDS_Face face = TopoDS::Face (fmap(i));
+         TopoDS_Edge e1, e2;
+
+         if (csm.CheckSpotFace (face))
+         {
+            if (!spotfaces++)
+               str << "SpotFace {Spot face} ";
+
+            (*testout) << "Face " << i << " is a spot face" << endl;
+            str << "SpotFace/Face" << i << " ";
+            str << "{Face " << i << " } ";
+         }
+
+         if (csm.IsStripSupport (face))
+         {
+            if (!stripsupportfaces++)
+               str << "StripSupportFace {Strip support face} ";
+
+            (*testout) << "Face " << i << " has strip support" << endl;
+            str << "StripSupportFace/Face" << i << " ";
+            str << "{Face " << i << " } ";
+         }
+
+         if (csm.CheckSingleStrip(face, e1, e2))
+         {
+            if (!singlestripfaces++)
+               str << "SingleStripFace {Single strip face} ";
+
+            (*testout) << "Face " << i << " is a single strip (edge " << emap.FindIndex(e1)
+               << " and edge " << emap.FindIndex(e2) << " are identical)" << endl;
+            str << "SingleStripFace/Face" << i << " ";
+            str << "{Face " << i << " (edge " << emap.FindIndex(e1)
+               << " and edge " << emap.FindIndex(e2) << " are identical)} ";
+         }
+
+         if (csm.CheckStripFace(face, e1, e2))
+         {
+            if (!stripfaces++)
+               str << "StripFace {Strip face} ";
+
+            (*testout) << "Face " << i << " is a strip (edge " << emap.FindIndex(e1)
+               << " and edge " << emap.FindIndex(e2)
+               << " are identical)" << endl;
+            str << "StripFace/Face" << i << " ";
+            str << "{Face " << i << " (edge " << emap.FindIndex(e1)
+               << " and edge " << emap.FindIndex(e2) << " are identical)} ";
+         }
+
+         if (int count = csm.CheckSplittingVertices(face, mapEdges, mapParam, theAllVert))
+         {
+            if (!facessplitbyvertices++)
+               str << "FaceSplitByVertices {Face split by vertices} ";
+
+            (*testout) << "Face " << i << " is split by " << count
+               << " vertex/vertices " << endl;
+            str << "FaceSplitByVertices/Face" << i << " ";
+            str << "{Face " << i << " (split by " << count << "vertex/vertices)} ";
+         }
+
+         int whatrow, sens;
+         if (int type = csm.CheckPin (face, whatrow, sens))
+         {
+            if (type == 1)
+            {
+               if (!smoothpinfaces++)
+                  str << "SmoothPinFace {Smooth pin face} ";
+
+               (*testout) << "Face " << i << " is a smooth pin" << endl;
+               str << "SmoothPinFace/Face" << i << " ";
+               str << "{Face " << i << " } ";
+            }
+            else
+            {
+               if (!stretchedpinfaces++)
+                  str << "StretchedPinFace {Stretched pin face} ";
+
+               (*testout) << "Face " << i << " is a streched pin" << endl;
+               str << "StretchedPinFace/Face" << i << " ";
+               str << "{Face " << i << " } ";
+            }
+         }
+
+         double paramu, paramv;
+         if (csm.CheckTwisted (face, paramu, paramv))
+         {
+            if (!twistedfaces++)
+               str << "TwistedFace {Twisted face} ";
+
+            (*testout) << "Face " << i << " is twisted" << endl;
+            str << "TwistedFace/Face" << i << " ";
+            str << "{Face " << i << " } ";
+         }
+      }
+
+      cout << "done" << endl;
+      cout << "checking edges ... " << flush;
+
+      // double dmax;
+      // int cnt = 0;
+      Array <double> edgeLengths;
+      Array <int> order;
+      edgeLengths.SetSize (emap.Extent());
+      order.SetSize (emap.Extent());
+
+      for (i = 1; i <= emap.Extent(); i++)
+      {
+         TopoDS_Edge edge1 = TopoDS::Edge (emap(i));
+         GProp_GProps system;
+         BRepGProp::LinearProperties(edge1, system);
+         edgeLengths[i-1] = system.Mass();
+      }
+
+      Sort (edgeLengths, order);
+
+      str << "ShortestEdges {Shortest edges} ";
+      for (i = 1; i <= min(20, emap.Extent()); i++)
+      {
+         str << "ShortestEdges/Edge" << i;
+         str << " {Edge " << order[i-1] << " (L=" << edgeLengths[order[i-1]-1] << ")} ";
+      }
+
+      str << flush;
+
+      cout << "done" << endl;
+   }
+
+
+
+
+   void OCCGeometry :: GetUnmeshedFaceInfo (stringstream & str)
+   {
+      for (int i = 1; i <= fmap.Extent(); i++)
+      {
+         if (facemeshstatus[i-1] == -1)
+            str << "Face" << i << " {Face " << i << " } ";
+      }
+      str << flush;
+   }
+
+
+
+
+   void OCCGeometry :: GetNotDrawableFaces (stringstream & str)
+   {
+      for (int i = 1; i <= fmap.Extent(); i++)
+      {
+         if (!fvispar[i-1].IsDrawable())
+            str << "Face" << i << " {Face " << i << " } ";
+      }
+      str << flush;
+   }
+
+
+
+
+   bool OCCGeometry :: ErrorInSurfaceMeshing ()
+   {
+      for (int i = 1; i <= fmap.Extent(); i++)
+         if (facemeshstatus[i-1] == -1)
+            return true;
+
+      return false;
+   }
+
+
+
+
+  int OCCGeometry :: GenerateMesh (Mesh*& mesh, MeshingParameters & mparam,
+      int perfstepsstart, int perfstepsend)
+   {
+     return OCCGenerateMesh (*this, mesh, mparam, perfstepsstart, perfstepsend);
+   }
+
+
+
+
+   const Refinement & OCCGeometry :: GetRefinement () const
+   {
+      return * new OCCRefinementSurfaces (*this);
+   }
+
+
+
+
+   OCCParameters :: OCCParameters()
+   {
+      resthcloseedgefac = 1;
+      resthcloseedgeenable = 1;
+   }
+
+
+
+
+   void OCCParameters :: Print(ostream & ost) const
+   {
+      ost << "OCC Parameters:" << endl
+         << "close edges: " << resthcloseedgeenable
+         << ", fac = " << resthcloseedgefac << endl;
+   }
+
+
+
+
+   OCCParameters occparam;
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/occgeom.hpp b/contrib/Netgen/libsrc/occ/occgeom.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..16b2f761fcb00729d3f29d2f477ba79c77f9cc0a
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occgeom.hpp
@@ -0,0 +1,451 @@
+#ifndef FILE_OCCGEOM
+#define FILE_OCCGEOM
+
+/* *************************************************************************/
+/* File:   occgeom.hpp                                                     */
+/* Author: Robert Gaisbauer                                                */
+/* Date:   26. May  03                                                     */
+/* *************************************************************************/
+
+#ifdef OCCGEOMETRY
+
+#include <meshing.hpp>
+
+#include "BRep_Tool.hxx"
+#include "Geom_Curve.hxx"
+#include "Geom2d_Curve.hxx"
+#include "Geom_Surface.hxx"
+#include "GeomAPI_ProjectPointOnSurf.hxx"
+#include "GeomAPI_ProjectPointOnCurve.hxx"
+#include "BRepTools.hxx"
+#include "TopExp.hxx"
+#include "BRepBuilderAPI_MakeVertex.hxx"
+#include "BRepBuilderAPI_MakeShell.hxx"
+#include "BRepBuilderAPI_MakeSolid.hxx"
+#include "BRepOffsetAPI_Sewing.hxx"
+#include "BRepLProp_SLProps.hxx"
+#include "BRepAdaptor_Surface.hxx"
+#include "Poly_Triangulation.hxx"
+#include "Poly_Array1OfTriangle.hxx"
+#include "TColgp_Array1OfPnt2d.hxx"
+#include "Poly_Triangle.hxx"
+#include "GProp_GProps.hxx"
+#include "BRepGProp.hxx"
+#include "Geom_Surface.hxx"
+#include "TopExp.hxx"
+#include "gp_Pnt.hxx"
+#include "TopoDS.hxx"
+#include "TopoDS_Solid.hxx"
+#include "TopExp_Explorer.hxx"
+#include "TopTools_ListIteratorOfListOfShape.hxx"
+#include "BRep_Tool.hxx"
+#include "Geom_Curve.hxx"
+#include "Geom2d_Curve.hxx"
+#include "Geom_Surface.hxx"
+#include "GeomAPI_ProjectPointOnSurf.hxx"
+#include "GeomAPI_ProjectPointOnCurve.hxx"
+#include "TopoDS_Wire.hxx"
+#include "BRepTools_WireExplorer.hxx"
+#include "BRepTools.hxx"
+#include "TopTools_IndexedMapOfShape.hxx"
+#include "TopExp.hxx"
+#include "BRepBuilderAPI_MakeVertex.hxx"
+#include "BRepBuilderAPI_MakeShell.hxx"
+#include "BRepBuilderAPI_MakeSolid.hxx"
+#include "BRepOffsetAPI_Sewing.hxx"
+#include "BRepLProp_CLProps.hxx"
+#include "BRepLProp_SLProps.hxx"
+#include "BRepAdaptor_Surface.hxx"
+#include "BRepAdaptor_Curve.hxx"
+#include "Poly_Triangulation.hxx"
+#include "Poly_Array1OfTriangle.hxx"
+#include "TColgp_Array1OfPnt2d.hxx"
+#include "Poly_Triangle.hxx"
+#include "GProp_GProps.hxx"
+#include "BRepGProp.hxx"
+#include "TopoDS_Shape.hxx"
+#include "TopoDS_Face.hxx"
+#include "IGESToBRep_Reader.hxx"
+#include "Interface_Static.hxx"
+#include "GeomAPI_ExtremaCurveCurve.hxx"
+#include "Standard_ErrorHandler.hxx"
+#include "Standard_Failure.hxx"
+#include "ShapeUpgrade_ShellSewing.hxx"
+#include "ShapeFix_Shape.hxx"
+#include "ShapeFix_Wireframe.hxx"
+#include "BRepMesh.hxx"
+#include "BRepMesh_IncrementalMesh.hxx"
+#include "BRepBndLib.hxx"
+#include "Bnd_Box.hxx"
+#include "ShapeAnalysis.hxx"
+#include "ShapeBuild_ReShape.hxx"
+
+
+// Philippose - 29/01/2009
+// OpenCascade XDE Support
+// Include support for OpenCascade XDE Features
+#include "TDocStd_Document.hxx"
+#include "Quantity_Color.hxx"
+#include "XCAFApp_Application.hxx"
+#include "XCAFDoc_ShapeTool.hxx"
+#include "XCAFDoc_Color.hxx"
+#include "XCAFDoc_ColorTool.hxx"
+#include "XCAFDoc_ColorType.hxx"
+#include "XCAFDoc_LayerTool.hxx"
+#include "XCAFDoc_DimTolTool.hxx"
+#include "XCAFDoc_MaterialTool.hxx"
+#include "XCAFDoc_DocumentTool.hxx"
+#include "TDF_Label.hxx"
+#include "TDF_LabelSequence.hxx"
+#include "STEPCAFControl_Reader.hxx"
+#include "STEPCAFControl_Writer.hxx"
+#include "IGESCAFControl_Reader.hxx"
+#include "IGESCAFControl_Writer.hxx"
+
+#include "IGESControl_Reader.hxx"
+#include "STEPControl_Reader.hxx"
+#include "IGESControl_Writer.hxx"
+#include "STEPControl_Writer.hxx"
+
+#include "StlAPI_Writer.hxx"
+#include "STEPControl_StepModelType.hxx"
+
+namespace netgen
+{
+#include "occmeshsurf.hpp"
+
+  extern DLL_HEADER MeshingParameters mparam;
+
+#define PROJECTION_TOLERANCE 1e-10
+
+#define ENTITYISVISIBLE 1
+#define ENTITYISHIGHLIGHTED 2
+#define ENTITYISDRAWABLE 4
+
+#define OCCGEOMETRYVISUALIZATIONNOCHANGE   0
+#define OCCGEOMETRYVISUALIZATIONFULLCHANGE 1  // Compute transformation matrices and redraw
+#define OCCGEOMETRYVISUALIZATIONHALFCHANGE 2  // Redraw
+
+
+
+   class EntityVisualizationCode
+   {
+      int code;
+
+   public:
+
+      EntityVisualizationCode()
+      {  code = ENTITYISVISIBLE + !ENTITYISHIGHLIGHTED + ENTITYISDRAWABLE;}
+
+      int IsVisible ()
+      {  return code & ENTITYISVISIBLE;}
+
+      int IsHighlighted ()
+      {  return code & ENTITYISHIGHLIGHTED;}
+
+      int IsDrawable ()
+      {  return code & ENTITYISDRAWABLE;}
+
+      void Show ()
+      {  code |= ENTITYISVISIBLE;}
+
+      void Hide ()
+      {  code &= ~ENTITYISVISIBLE;}
+
+      void Highlight ()
+      {  code |= ENTITYISHIGHLIGHTED;}
+
+      void Lowlight ()
+      {  code &= ~ENTITYISHIGHLIGHTED;}
+
+      void SetDrawable ()
+      {  code |= ENTITYISDRAWABLE;}
+
+      void SetNotDrawable ()
+      {  code &= ~ENTITYISDRAWABLE;}
+   };
+
+
+
+   class Line
+   {
+   public:
+      Point<3> p0, p1;
+
+      double Dist (Line l);
+
+      double Length ();
+   };
+
+
+
+   inline double Det3 (double a00, double a01, double a02,
+      double a10, double a11, double a12,
+      double a20, double a21, double a22)
+   {
+      return a00*a11*a22 + a01*a12*a20 + a10*a21*a02 - a20*a11*a02 - a10*a01*a22 - a21*a12*a00;
+   }
+
+
+
+
+   class OCCGeometry : public NetgenGeometry
+   {
+      Point<3> center;
+
+   public:
+      TopoDS_Shape shape;
+      TopTools_IndexedMapOfShape fmap, emap, vmap, somap, shmap, wmap;
+      Array<bool> fsingular, esingular, vsingular;
+      Box<3> boundingbox;
+
+      // Philippose - 29/01/2009
+      // OpenCascade XDE Support
+      // XCAF Handle to make the face colours available to the rest of
+      // the system
+      Handle_XCAFDoc_ColorTool face_colours;
+
+     mutable int changed;
+      Array<int> facemeshstatus;
+
+      // Philippose - 15/01/2009
+      // Maximum mesh size for a given face
+      // (Used to explicitly define mesh size limits on individual faces)
+      Array<double> face_maxh;
+      
+      // Philippose - 14/01/2010
+      // Boolean array to detect whether a face has been explicitly modified 
+      // by the user or not
+      Array<bool> face_maxh_modified;
+
+      // Philippose - 15/01/2009
+      // Indicates which faces have been selected by the user in geometry mode
+      // (Currently handles only selection of one face at a time, but an array would
+      //  help to extend this to multiple faces)
+      Array<bool> face_sel_status;
+
+      Array<EntityVisualizationCode> fvispar, evispar, vvispar;
+
+      double tolerance;
+      bool fixsmalledges;
+      bool fixspotstripfaces;
+      bool sewfaces;
+      bool makesolids;
+      bool splitpartitions;
+
+      OCCGeometry()
+      {
+         somap.Clear();
+         shmap.Clear();
+         fmap.Clear();
+         wmap.Clear();
+         emap.Clear();
+         vmap.Clear();
+      }
+
+
+     virtual void Save (string filename) const;
+
+
+      void BuildFMap();
+
+      Box<3> GetBoundingBox()
+      {  return boundingbox;}
+
+      int NrSolids()
+      {  return somap.Extent();}
+
+      // Philippose - 17/01/2009
+      // Total number of faces in the geometry
+      int NrFaces()
+      {  return fmap.Extent();}
+
+      void SetCenter()
+      {  center = boundingbox.Center();}
+
+      Point<3> Center()
+      {  return center;}
+
+      void Project (int surfi, Point<3> & p) const;
+      bool FastProject (int surfi, Point<3> & ap, double& u, double& v) const;
+
+      OCCSurface GetSurface (int surfi)
+      {
+         cout << "OCCGeometry::GetSurface using PLANESPACE" << endl;
+         return OCCSurface (TopoDS::Face(fmap(surfi)), PLANESPACE);
+      }
+
+      void CalcBoundingBox ();
+      void BuildVisualizationMesh (double deflection);
+
+      void RecursiveTopologyTree (const TopoDS_Shape & sh,
+         stringstream & str,
+         TopAbs_ShapeEnum l,
+         bool free,
+         const char * lname);
+
+      void GetTopologyTree (stringstream & str);
+
+      void PrintNrShapes ();
+
+      void CheckIrregularEntities (stringstream & str);
+
+      void SewFaces();
+
+      void MakeSolid();
+
+      void HealGeometry();
+
+      // Philippose - 15/01/2009
+      // Sets the maximum mesh size for a given face
+      // (Note: Local mesh size limited by the global max mesh size)
+      void SetFaceMaxH(int facenr, double faceh)
+      {
+         if((facenr> 0) && (facenr <= fmap.Extent()))
+         {
+	   face_maxh[facenr-1] = min(mparam.maxh,faceh);
+            
+            // Philippose - 14/01/2010
+            // If the face maxh is greater than or equal to the 
+            // current global maximum, then identify the face as 
+            // not explicitly controlled by the user any more
+            if(faceh >= mparam.maxh)
+            {
+               face_maxh_modified[facenr-1] = 0;
+            }
+            else
+            {
+               face_maxh_modified[facenr-1] = 1;
+            }
+         }
+      }
+
+      // Philippose - 15/01/2009
+      // Returns the local mesh size of a given face
+      double GetFaceMaxH(int facenr)
+      {
+         if((facenr> 0) && (facenr <= fmap.Extent()))
+         {
+            return face_maxh[facenr-1];
+         }
+         else
+         {
+            return 0.0;
+         }
+      }
+      
+      // Philippose - 14/01/2010
+      // Returns the flag whether the given face 
+      // has a mesh size controlled by the user or not
+      bool GetFaceMaxhModified(int facenr)
+      {
+         return face_maxh_modified[facenr-1];
+      }
+      
+      // Philippose - 17/01/2009
+      // Returns the index of the currently selected face
+      int SelectedFace()
+      {
+         int i;
+
+         for(i = 1; i <= fmap.Extent(); i++)
+         {
+            if(face_sel_status[i-1])
+            {
+               return i;
+            }
+         }
+
+         return 0;
+      }
+
+      // Philippose - 17/01/2009
+      // Sets the currently selected face
+      void SetSelectedFace(int facenr)
+      {
+         face_sel_status = 0;
+
+         if((facenr >= 1) && (facenr <= fmap.Extent()))
+         {
+            face_sel_status[facenr-1] = 1;
+         }
+      }
+
+      void LowLightAll()
+      {
+         for (int i = 1; i <= fmap.Extent(); i++)
+            fvispar[i-1].Lowlight();
+         for (int i = 1; i <= emap.Extent(); i++)
+            evispar[i-1].Lowlight();
+         for (int i = 1; i <= vmap.Extent(); i++)
+            vvispar[i-1].Lowlight();
+      }
+
+      void GetUnmeshedFaceInfo (stringstream & str);
+      void GetNotDrawableFaces (stringstream & str);
+      bool ErrorInSurfaceMeshing ();
+
+     void WriteOCC_STL(char * filename);
+
+     virtual int GenerateMesh (Mesh*& mesh, MeshingParameters & mparam, 
+         int perfstepsstart, int perfstepsend);
+
+      virtual const Refinement & GetRefinement () const;
+   };
+
+
+
+   class OCCParameters
+   {
+   public:
+
+      /// Factor for meshing close edges 
+      double resthcloseedgefac;
+
+
+      /// Enable / Disable detection of close edges
+      int resthcloseedgeenable;
+
+
+
+      /*!
+         Default Constructor for the OpenCascade
+         Mesh generation parameter set
+      */
+      OCCParameters();
+
+
+      /*!
+         Dump all the OpenCascade specific meshing parameters 
+         to console
+      */
+      void Print (ostream & ost) const;
+   };
+   
+
+   void PrintContents (OCCGeometry * geom);
+
+   OCCGeometry * LoadOCC_IGES (const char * filename);
+   OCCGeometry * LoadOCC_STEP (const char * filename);
+   OCCGeometry * LoadOCC_BREP (const char * filename);
+
+   extern OCCParameters occparam;
+
+
+   // Philippose - 31.09.2009
+   // External access to the mesh generation functions within the OCC
+   // subsystem (Not sure if this is the best way to implement this....!!)
+   extern int OCCGenerateMesh (OCCGeometry & occgeometry, Mesh*& mesh,
+			       MeshingParameters & mparam,
+			       int perfstepsstart, int perfstepsend);
+
+  extern void OCCSetLocalMeshSize(OCCGeometry & geom, Mesh & mesh);
+
+   extern void OCCMeshSurface (OCCGeometry & geom, Mesh & mesh, int perfstepsend);
+
+   extern void OCCFindEdges (OCCGeometry & geom, Mesh & mesh);
+}
+
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/occmeshsurf.cpp b/contrib/Netgen/libsrc/occ/occmeshsurf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a1f60db6781d59c469ef49bdbacf7cd7894e9d45
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occmeshsurf.cpp
@@ -0,0 +1,735 @@
+#ifdef OCCGEOMETRY
+
+#include <mystdlib.h>
+
+#include <occgeom.hpp>
+#include <meshing.hpp>
+#include <GeomLProp_SLProps.hxx>
+#include <ShapeAnalysis_Surface.hxx>
+
+
+namespace netgen
+{
+#include "occmeshsurf.hpp"
+
+
+  bool glob_testout(false);
+
+  void OCCSurface :: GetNormalVector (const Point<3> & p, 
+				      const PointGeomInfo & geominfo,
+				      Vec<3> & n) const
+  {
+    gp_Pnt pnt;
+    gp_Vec du, dv;
+
+    /*
+      double gu = geominfo.u;
+      double gv = geominfo.v;
+
+      if (fabs (gu) < 1e-3) gu = 0;
+      if (fabs (gv) < 1e-3) gv = 0;
+
+      occface->D1(gu,gv,pnt,du,dv);
+    */
+
+    /*
+      occface->D1(geominfo.u,geominfo.v,pnt,du,dv);
+
+      n = Cross (Vec<3>(du.X(), du.Y(), du.Z()),
+      Vec<3>(dv.X(), dv.Y(), dv.Z()));
+      n.Normalize();
+    */
+
+
+
+    GeomLProp_SLProps lprop(occface,geominfo.u,geominfo.v,1,1e-5);
+    double setu=geominfo.u,setv=geominfo.v;
+
+    if(lprop.D1U().Magnitude() < 1e-5 || lprop.D1V().Magnitude() < 1e-5)
+      {
+	double ustep = 0.01*(umax-umin);
+	double vstep = 0.01*(vmax-vmin);
+
+	n=0;
+
+	while(setu < umax && (lprop.D1U().Magnitude() < 1e-5 || lprop.D1V().Magnitude() < 1e-5))
+	  setu += ustep;
+	if(setu < umax)
+	  {
+	    lprop.SetParameters(setu,setv);
+	    n(0)+=lprop.Normal().X();
+	    n(1)+=lprop.Normal().Y();
+	    n(2)+=lprop.Normal().Z();
+	  }
+	setu = geominfo.u;
+	while(setu > umin && (lprop.D1U().Magnitude() < 1e-5 || lprop.D1V().Magnitude() < 1e-5))
+	  setu -= ustep;
+	if(setu > umin)
+	  {
+	    lprop.SetParameters(setu,setv);
+	    n(0)+=lprop.Normal().X();
+	    n(1)+=lprop.Normal().Y();
+	    n(2)+=lprop.Normal().Z();
+	  }
+	setu = geominfo.u;
+
+	while(setv < vmax && (lprop.D1U().Magnitude() < 1e-5 || lprop.D1V().Magnitude() < 1e-5))
+	  setv += ustep;
+	if(setv < vmax)
+	  {
+	    lprop.SetParameters(setu,setv);
+	    n(0)+=lprop.Normal().X();
+	    n(1)+=lprop.Normal().Y();
+	    n(2)+=lprop.Normal().Z();
+	  }
+	setv = geominfo.v;
+	while(setv > vmin && (lprop.D1U().Magnitude() < 1e-5 || lprop.D1V().Magnitude() < 1e-5))
+	  setv -= ustep;
+	if(setv > vmin)
+	  {
+	    lprop.SetParameters(setu,setv);
+	    n(0)+=lprop.Normal().X();
+	    n(1)+=lprop.Normal().Y();
+	    n(2)+=lprop.Normal().Z();
+	  }
+	setv = geominfo.v;
+
+	n.Normalize();
+      }
+    else
+      {
+	n(0)=lprop.Normal().X();
+	n(1)=lprop.Normal().Y();
+	n(2)=lprop.Normal().Z();
+      }
+
+    if(glob_testout)
+      {
+	(*testout) << "u " << geominfo.u << " v " << geominfo.v 
+		   << " du " << lprop.D1U().X() << " "<< lprop.D1U().Y() << " "<< lprop.D1U().Z()
+		   << " dv " << lprop.D1V().X() << " "<< lprop.D1V().Y() << " "<< lprop.D1V().Z() << endl;
+      }
+
+
+
+    if (orient == TopAbs_REVERSED) n = -1*n;
+    //  (*testout) << "GetNormalVector" << endl;
+  }
+
+
+  void OCCSurface :: DefineTangentialPlane (const Point<3> & ap1,
+					    const PointGeomInfo & geominfo1,
+					    const Point<3> & ap2,
+					    const PointGeomInfo & geominfo2)
+  {
+    if (projecttype == PLANESPACE)
+      {
+	p1 = ap1; p2 = ap2;
+
+	//cout << "p1 = " << p1 << endl;
+	//cout << "p2 = " << p2 << endl;
+      
+	GetNormalVector (p1, geominfo1, ez);
+      
+	ex = p2 - p1;
+	ex -= (ex * ez) * ez;
+	ex.Normalize();
+	ey = Cross (ez, ex); 
+
+	GetNormalVector (p2, geominfo2, n2);
+  
+	nmid = 0.5*(n2+ez);
+      
+	ez = nmid;
+	ez.Normalize(); 
+      
+	ex = (p2 - p1).Normalize();
+	ez -= (ez * ex) * ex;
+	ez.Normalize();
+	ey = Cross (ez, ex);
+	nmid = ez;
+	//cout << "ex " << ex << " ey " << ey << " ez " << ez << endl;
+      }
+    else
+      {
+	if ( (geominfo1.u < umin) ||
+	     (geominfo1.u > umax) ||
+	     (geominfo2.u < umin) ||
+	     (geominfo2.u > umax) ||
+	     (geominfo1.v < vmin) ||
+	     (geominfo1.v > vmax) ||
+	     (geominfo2.v < vmin) ||
+	     (geominfo2.v > vmax) ) throw UVBoundsException();
+	  
+
+	p1 = ap1; p2 = ap2;
+	psp1 = Point<2>(geominfo1.u, geominfo1.v);
+	psp2 = Point<2>(geominfo2.u, geominfo2.v);
+      
+	Vec<3> n;
+	GetNormalVector (p1, geominfo1, n);
+
+	gp_Pnt pnt;
+	gp_Vec du, dv;
+	occface->D1 (geominfo1.u, geominfo1.v, pnt, du, dv);
+
+	DenseMatrix D1(3,2), D1T(2,3), DDTinv(2,2);
+	D1(0,0) = du.X(); D1(1,0) = du.Y(); D1(2,0) = du.Z();
+	D1(0,1) = dv.X(); D1(1,1) = dv.Y(); D1(2,1) = dv.Z();
+
+	/*
+	  (*testout) << "DefineTangentialPlane" << endl
+	  << "---------------------" << endl;
+	  (*testout) << "D1 = " << endl << D1 << endl;
+	*/
+
+	Transpose (D1, D1T);
+	DenseMatrix D1TD1(3,3);
+
+	D1TD1 = D1T*D1;
+	if (D1TD1.Det() == 0) throw SingularMatrixException();
+      
+	CalcInverse (D1TD1, DDTinv);
+	DenseMatrix Y(3,2);
+	Vec<3> y1 = (ap2-ap1).Normalize();
+	Vec<3> y2 = Cross(n, y1).Normalize();
+	for (int i = 0; i < 3; i++)
+	  {
+	    Y(i,0) = y1(i);
+	    Y(i,1) = y2(i);
+	  }
+
+	DenseMatrix A(2,2);
+	A = DDTinv * D1T * Y;
+	DenseMatrix Ainv(2,2);
+
+	if (A.Det() == 0) throw SingularMatrixException();
+
+	CalcInverse (A, Ainv);
+
+	for (int i = 0; i < 2; i++)
+	  for (int j = 0; j < 2; j++)
+	    {
+	      Amat(i,j) = A(i,j);
+	      Amatinv(i,j) = Ainv(i,j);
+	    }
+
+	Vec<2> temp = Amatinv * (psp2-psp1);
+      
+
+	double r = temp.Length();
+	//      double alpha = -acos (temp(0)/r);
+	double alpha = -atan2 (temp(1),temp(0));
+	DenseMatrix R(2,2);
+	R(0,0) = cos (alpha);
+	R(1,0) = -sin (alpha);
+	R(0,1) = sin (alpha);
+	R(1,1) = cos (alpha);
+
+
+	A = A*R;
+
+	if (A.Det() == 0) throw SingularMatrixException();
+
+	CalcInverse (A, Ainv);
+    
+
+	for (int i = 0; i < 2; i++)
+	  for (int j = 0; j < 2; j++)
+	    {
+	      Amat(i,j) = A(i,j);
+	      Amatinv(i,j) = Ainv(i,j);
+	    }
+
+	temp = Amatinv * (psp2-psp1);
+      
+      };
+ 
+  }
+
+
+  void OCCSurface :: ToPlane (const Point<3> & p3d,
+			      const PointGeomInfo & geominfo,
+			      Point<2> & pplane, 
+			      double h, int & zone) const
+  {
+    if (projecttype == PLANESPACE)
+      {
+	Vec<3> p1p, n;
+	GetNormalVector (p3d, geominfo, n);
+      
+	p1p = p3d - p1;
+	pplane(0) = (p1p * ex) / h;
+	pplane(1) = (p1p * ey) / h;
+      
+	if (n * nmid < 0)
+	  zone = -1;
+	else
+	  zone = 0;
+
+	/*
+	  if(zone == -1)
+	  {
+	  (*testout) << "zone = -1 for " << p3d << " 2D: " << pplane << " n " << n << " nmid " << nmid << endl;
+	  glob_testout = true;
+	  GetNormalVector (p3d, geominfo, n);
+	  glob_testout = false;
+	  }
+	*/
+      }
+    else
+      {
+	pplane = Point<2>(geominfo.u, geominfo.v);
+	//      (*testout) << "(u,v) = " << geominfo.u << ", " << geominfo.v << endl;
+	pplane = Point<2> (1/h * (Amatinv * (pplane-psp1)));
+	//      pplane = Point<2> (h * (Amatinv * (pplane-psp1)));
+	//      pplane = Point<2> (1/h * ((pplane-psp1)));
+
+	zone = 0;
+      };
+  }	
+
+
+  void OCCSurface :: FromPlane (const Point<2> & pplane, 
+				Point<3> & p3d,
+				PointGeomInfo & gi,
+				double h) 
+  { 
+    if (projecttype == PLANESPACE)
+      {
+	//      cout << "2d   : " << pplane << endl;
+	p3d = p1 + (h * pplane(0)) * ex + (h * pplane(1)) * ey;
+	//      cout << "3d   : " << p3d << endl;
+	Project (p3d, gi);  
+	//      cout << "proj : " << p3d << endl;
+      }
+    else
+      {
+	//      Point<2> pspnew = Point<2>(1/h * (Amat * Vec<2>(pplane)) + Vec<2>(psp1));
+	Point<2> pspnew = Point<2>(h * (Amat * Vec<2>(pplane)) + Vec<2>(psp1));
+	//      Point<2> pspnew = Point<2>(h * (Vec<2>(pplane)) + Vec<2>(psp1));
+	gi.u = pspnew(0);
+	gi.v = pspnew(1);
+	gi.trignum = 1;
+	gp_Pnt val = occface->Value (gi.u, gi.v);
+	p3d = Point<3> (val.X(), val.Y(), val.Z());
+      };
+  }
+
+
+
+  void OCCSurface :: Project (Point<3> & p, PointGeomInfo & gi)
+  {
+    //   static int cnt = 0;
+    //  if (cnt++ % 1000 == 0) cout << "********************************************** OCCSurfce :: Project, cnt = " << cnt << endl;
+  
+    gp_Pnt pnt(p(0), p(1), p(2));
+
+    //(*testout) << "pnt = " << pnt.X() << ", " << pnt.Y() << ", " << pnt.Z() << endl;
+
+
+    /*
+    GeomAPI_ProjectPointOnSurf proj(pnt, occface, umin, umax, vmin, vmax);
+
+    if (!proj.NbPoints())
+      {
+	cout << "Project Point on Surface FAIL" << endl;
+	throw UVBoundsException();
+      }
+    */
+
+    
+
+
+
+    /*
+      cout << "NP = " << proj.NbPoints() << endl;
+
+      for (int i = 1; i <= proj.NbPoints(); i++)
+      {
+      gp_Pnt pnt2 = proj.Point(i);
+      Point<3> p2 = Point<3> (pnt2.X(), pnt2.Y(), pnt2.Z());
+      cout << i << ". p = " << p2 << ", dist = " << (p2-p).Length() << endl;
+      }
+    */
+
+    /*
+    pnt = proj.NearestPoint();
+    proj.LowerDistanceParameters (gi.u, gi.v);
+    */
+
+    double u,v;
+    Handle( ShapeAnalysis_Surface ) su = new ShapeAnalysis_Surface( occface );
+    gp_Pnt2d suval = su->ValueOfUV ( pnt, BRep_Tool::Tolerance( topods_face ) );
+    suval.Coord( u, v);
+    pnt = occface->Value( u, v );
+    
+    //(*testout) << "pnt(proj) = " << pnt.X() << ", " << pnt.Y() << ", " << pnt.Z() << endl;
+    gi.u = u;
+    gi.v = v;
+    
+
+    gi.trignum = 1;
+
+    p = Point<3> (pnt.X(), pnt.Y(), pnt.Z());
+  } 
+
+
+  Meshing2OCCSurfaces :: Meshing2OCCSurfaces (const TopoDS_Shape & asurf,
+					      const Box<3> & abb, int aprojecttype)
+    : Meshing2(mparam, Box<3>(abb.PMin(), abb.PMax())), surface(TopoDS::Face(asurf), aprojecttype)
+  {
+    ;
+  }
+
+
+  void Meshing2OCCSurfaces :: DefineTransformation (const Point3d & p1, const Point3d & p2,
+						    const PointGeomInfo * geominfo1,
+						    const PointGeomInfo * geominfo2)
+  {
+    ((OCCSurface&)surface).DefineTangentialPlane (p1, *geominfo1, p2, *geominfo2);
+  }
+ 
+  void Meshing2OCCSurfaces :: TransformToPlain (const Point3d & locpoint, 
+						const MultiPointGeomInfo & geominfo,
+						Point2d & planepoint, 
+						double h, int & zone)
+  {
+    Point<2> hp;
+    surface.ToPlane (locpoint, geominfo.GetPGI(1), hp, h, zone);
+    planepoint.X() = hp(0);
+    planepoint.Y() = hp(1);
+  }
+
+  int Meshing2OCCSurfaces :: TransformFromPlain (Point2d & planepoint,
+						 Point3d & locpoint,
+						 PointGeomInfo & gi,
+						 double h)
+  {
+    Point<3> hp;
+    Point<2> hp2 (planepoint.X(), planepoint.Y());
+    surface.FromPlane (hp2, hp, gi, h);
+    locpoint = hp;
+    return 0;
+  }
+
+
+
+  double Meshing2OCCSurfaces :: CalcLocalH (const Point3d & p, double gh) const
+  {
+    return gh;
+  }
+
+
+
+
+
+
+  MeshOptimize2dOCCSurfaces :: MeshOptimize2dOCCSurfaces (const OCCGeometry & ageometry)
+    : MeshOptimize2d(), geometry(ageometry)
+  {
+    ;
+  }
+
+
+  void MeshOptimize2dOCCSurfaces :: ProjectPoint (INDEX surfind, Point<3> & p) const
+  {
+    geometry.Project (surfind, p);
+  }
+
+
+  int MeshOptimize2dOCCSurfaces :: ProjectPointGI (INDEX surfind, Point<3> & p, PointGeomInfo & gi) const
+  {
+    double u = gi.u;
+    double v = gi.v;
+
+    Point<3> hp = p;
+    if (geometry.FastProject (surfind, hp, u, v))
+      {
+	p = hp;
+	return 1;
+      }
+    ProjectPoint (surfind, p); 
+    return CalcPointGeomInfo (surfind, gi, p); 
+  }
+
+
+  void MeshOptimize2dOCCSurfaces :: ProjectPoint2 (INDEX surfind, INDEX surfind2, 
+						   Point<3> & p) const
+  {
+    TopExp_Explorer exp0, exp1;
+    bool done = false;
+    Handle(Geom_Curve) c;
+
+    for (exp0.Init(geometry.fmap(surfind), TopAbs_EDGE); !done && exp0.More(); exp0.Next())
+      for (exp1.Init(geometry.fmap(surfind2), TopAbs_EDGE); !done && exp1.More(); exp1.Next())
+	{
+	  if (TopoDS::Edge(exp0.Current()).IsSame(TopoDS::Edge(exp1.Current())))
+	    {
+	      done = true;
+	      double s0, s1;
+	      c = BRep_Tool::Curve(TopoDS::Edge(exp0.Current()), s0, s1);
+	    }
+	}
+  
+    gp_Pnt pnt(p(0), p(1), p(2));
+    GeomAPI_ProjectPointOnCurve proj(pnt, c);
+    pnt = proj.NearestPoint();  
+    p(0) = pnt.X();
+    p(1) = pnt.Y();
+    p(2) = pnt.Z();
+	
+  }
+
+  void MeshOptimize2dOCCSurfaces :: 
+  GetNormalVector(INDEX surfind, const Point<3> & p, PointGeomInfo & geominfo, Vec<3> & n) const
+  {
+    gp_Pnt pnt;
+    gp_Vec du, dv;
+
+    Handle(Geom_Surface) occface;
+    occface = BRep_Tool::Surface(TopoDS::Face(geometry.fmap(surfind)));
+
+    occface->D1(geominfo.u,geominfo.v,pnt,du,dv);
+
+    n = Cross (Vec<3>(du.X(), du.Y(), du.Z()),
+	       Vec<3>(dv.X(), dv.Y(), dv.Z()));
+    n.Normalize();
+
+    if (geometry.fmap(surfind).Orientation() == TopAbs_REVERSED) n = -1*n;  
+  
+    //  GetNormalVector (surfind, p, n);
+  }
+
+
+  void MeshOptimize2dOCCSurfaces :: 
+  GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const
+  {
+    //  static int cnt = 0;
+    //  if (cnt++ % 1000 == 0) cout << "GetNV cnt = " << cnt << endl;
+    Standard_Real u,v;
+
+    gp_Pnt pnt(p(0), p(1), p(2));
+
+    Handle(Geom_Surface) occface;
+    occface = BRep_Tool::Surface(TopoDS::Face(geometry.fmap(surfind)));
+
+    /*
+    GeomAPI_ProjectPointOnSurf proj(pnt, occface);
+
+    if (proj.NbPoints() < 1)
+      {
+	cout << "ERROR: OCCSurface :: GetNormalVector: GeomAPI_ProjectPointOnSurf failed!"
+	     << endl;
+	cout << p << endl;
+	return;
+      }
+ 
+    proj.LowerDistanceParameters (u, v);
+    */
+    
+    Handle( ShapeAnalysis_Surface ) su = new ShapeAnalysis_Surface( occface );
+    gp_Pnt2d suval = su->ValueOfUV ( pnt, BRep_Tool::Tolerance( TopoDS::Face(geometry.fmap(surfind)) ) );
+    suval.Coord( u, v);
+    pnt = occface->Value( u, v );
+    
+    
+
+    gp_Vec du, dv;
+    occface->D1(u,v,pnt,du,dv);
+
+    /*
+      if (!occface->IsCNu (1) || !occface->IsCNv (1))
+      (*testout) << "SurfOpt: Differentiation FAIL" << endl;
+    */
+
+    n = Cross (Vec3d(du.X(), du.Y(), du.Z()),
+	       Vec3d(dv.X(), dv.Y(), dv.Z()));
+    n.Normalize();
+
+    if (geometry.fmap(surfind).Orientation() == TopAbs_REVERSED) n = -1*n;  
+  }
+
+
+  int MeshOptimize2dOCCSurfaces :: 
+  CalcPointGeomInfo(int surfind, PointGeomInfo& gi, const Point<3> & p) const
+  {
+    Standard_Real u,v;
+
+    gp_Pnt pnt(p(0), p(1), p(2));
+
+    Handle(Geom_Surface) occface;
+    occface = BRep_Tool::Surface(TopoDS::Face(geometry.fmap(surfind)));
+
+    /*
+    GeomAPI_ProjectPointOnSurf proj(pnt, occface);
+
+    if (proj.NbPoints() < 1)
+      {
+	cout << "ERROR: OCCSurface :: GetNormalVector: GeomAPI_ProjectPointOnSurf failed!"
+	     << endl;
+	cout << p << endl;
+	return 0;
+      }
+ 
+    proj.LowerDistanceParameters (u, v);  
+    */
+
+    Handle( ShapeAnalysis_Surface ) su = new ShapeAnalysis_Surface( occface );
+    gp_Pnt2d suval = su->ValueOfUV ( pnt, BRep_Tool::Tolerance( TopoDS::Face(geometry.fmap(surfind)) ) );
+    suval.Coord( u, v);
+    //pnt = occface->Value( u, v );
+    
+
+    gi.u = u;
+    gi.v = v;
+    return 1;
+  }
+
+
+
+
+
+
+  OCCRefinementSurfaces :: OCCRefinementSurfaces (const OCCGeometry & ageometry)
+    : Refinement(), geometry(ageometry)
+  {
+    ;
+  }
+
+  OCCRefinementSurfaces :: ~OCCRefinementSurfaces ()
+  {
+    ;
+  }
+
+  /*
+    inline double Det3 (double a00, double a01, double a02,
+    double a10, double a11, double a12,
+    double a20, double a21, double a22)
+    {
+    return a00*a11*a22 + a01*a12*a20 + a10*a21*a02 - a20*a11*a02 - a10*a01*a22 - a21*a12*a00;
+    }
+
+    bool ProjectToSurface (gp_Pnt & p, Handle(Geom_Surface) surface, double& u, double& v)
+    {
+    gp_Pnt x = surface->Value (u,v);
+
+    if (p.SquareDistance(x) <= sqr(PROJECTION_TOLERANCE)) return true;
+
+    gp_Vec du, dv;
+
+    surface->D1(u,v,x,du,dv);
+
+    int count = 0;
+
+    gp_Pnt xold;
+    gp_Vec n;
+    double det, lambda, mu;
+
+    do {
+    count++;
+
+    n = du^dv;
+
+    det = Det3 (n.X(), du.X(), dv.X(),
+    n.Y(), du.Y(), dv.Y(),
+    n.Z(), du.Z(), dv.Z());
+
+    if (det < 1e-15) return false; 
+
+    lambda = Det3 (n.X(), p.X()-x.X(), dv.X(),
+    n.Y(), p.Y()-x.Y(), dv.Y(),
+    n.Z(), p.Z()-x.Z(), dv.Z())/det;
+
+    mu     = Det3 (n.X(), du.X(), p.X()-x.X(),
+    n.Y(), du.Y(), p.Y()-x.Y(),
+    n.Z(), du.Z(), p.Z()-x.Z())/det;
+  
+    u += lambda;
+    v += mu;
+
+    xold = x;
+    surface->D1(u,v,x,du,dv);
+
+    } while (xold.SquareDistance(x) > sqr(PROJECTION_TOLERANCE) || count > 50);
+
+    if (count > 50) return false;
+
+    p = x;
+
+    return true;
+    }
+  */
+
+  void OCCRefinementSurfaces :: 
+  PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+		int surfi, 
+		const PointGeomInfo & gi1, 
+		const PointGeomInfo & gi2,
+		Point<3> & newp, PointGeomInfo & newgi) const
+  {
+    Point<3> hnewp;
+    hnewp = p1+secpoint*(p2-p1);
+
+    if (surfi > 0)
+      {
+      
+	double u = gi1.u+secpoint*(gi2.u-gi1.u);
+	double v = gi1.v+secpoint*(gi2.v-gi1.v);
+ 
+	if (!geometry.FastProject (surfi, hnewp, u, v))
+	  {
+	  //  cout << "Fast projection to surface fails! Using OCC projection" << endl;
+	    geometry.Project (surfi, hnewp);
+	  }
+
+	newgi.trignum = 1;
+        newgi.u = u;
+        newgi.v = v;
+      }
+  
+    newp = hnewp;
+  }
+
+
+  void OCCRefinementSurfaces :: 
+  PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+		int surfi1, int surfi2, 
+		const EdgePointGeomInfo & ap1, 
+		const EdgePointGeomInfo & ap2,
+		Point<3> & newp, EdgePointGeomInfo & newgi) const
+  {
+    double s0, s1;
+
+    Point<3> hnewp = p1+secpoint*(p2-p1);
+    gp_Pnt pnt(hnewp(0), hnewp(1), hnewp(2));
+    GeomAPI_ProjectPointOnCurve proj(pnt, BRep_Tool::Curve(TopoDS::Edge(geometry.emap(ap1.edgenr)), s0, s1));
+    pnt = proj.NearestPoint();
+    hnewp = Point<3> (pnt.X(), pnt.Y(), pnt.Z());
+    newp = hnewp;
+    newgi = ap1;
+  };
+
+
+  void OCCRefinementSurfaces :: ProjectToSurface (Point<3> & p, int surfi) const
+  {
+    if (surfi > 0)
+      geometry.Project (surfi, p);
+  };
+
+  void OCCRefinementSurfaces :: ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & gi) const
+  {
+    if (surfi > 0)
+      if (!geometry.FastProject (surfi, p, gi.u, gi.v))
+	{
+	  cout << "Fast projection to surface fails! Using OCC projection" << endl;
+	  geometry.Project (surfi, p);
+	}
+  };
+
+
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/occmeshsurf.hpp b/contrib/Netgen/libsrc/occ/occmeshsurf.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..198ade67cef5f4044e8d8f591d1100cffa10f6bc
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occmeshsurf.hpp
@@ -0,0 +1,203 @@
+#ifdef OCCGEOMETRY
+
+#ifndef FILE_OCCMESHSURF
+#define FILE_OCCMESHSURF
+
+#include "occgeom.hpp"
+
+#define PARAMETERSPACE -1
+#define PLANESPACE     1
+
+class OCCGeometry;
+
+class SingularMatrixException
+{};
+
+class UVBoundsException
+{};
+
+class OCCSurface
+{
+public:
+  TopoDS_Face topods_face;
+  Handle(Geom_Surface) occface;
+  TopAbs_Orientation orient;
+  int projecttype;
+
+protected:
+  Point<3> p1;
+  Point<3> p2;
+
+  /// in plane, directed p1->p2
+  Vec<3> ex;
+  /// in plane
+  Vec<3> ey;
+  /// outer normal direction
+  Vec<3> ez;
+
+  /// normal vector in p2
+  Vec<3> n2;
+
+  /// average normal vector
+  Vec<3> nmid;
+
+  // for transformation to parameter space
+  Point<2> psp1;
+  Point<2> psp2;
+  Vec<2> psex;
+  Vec<2> psey;
+  Mat<2,2> Amat, Amatinv;
+
+  // UV Bounds
+  double umin, umax, vmin, vmax;
+
+public:
+  OCCSurface (const TopoDS_Face & aface, int aprojecttype)
+  {
+    topods_face = aface;
+    occface = BRep_Tool::Surface(topods_face);
+    orient = topods_face.Orientation();
+    projecttype = aprojecttype;
+    ShapeAnalysis::GetFaceUVBounds (topods_face, umin, umax, vmin, vmax);
+    umin -= fabs(umax-umin)/100.0;
+    vmin -= fabs(vmax-vmin)/100.0;
+    umax += fabs(umax-umin)/100.0;
+    vmax += fabs(vmax-vmin)/100.0;
+    // projecttype = PLANESPACE;
+    /*
+    TopExp_Explorer exp1;
+    exp1.Init (topods_face, TopAbs_WIRE);
+    orient = TopAbs::Compose (orient, exp1.Current().Orientation());
+    */
+  };
+  
+  ~OCCSurface()
+  {};
+
+  void Project (Point<3> & p, PointGeomInfo & gi);
+
+  void GetNormalVector (const Point<3> & p,
+			const PointGeomInfo & geominfo,
+			Vec<3> & n) const;
+
+  /**
+    Defines tangential plane in ap1.
+    The local x-coordinate axis point to the direction of ap2 */
+  void DefineTangentialPlane (const Point<3> & ap1, 
+			      const PointGeomInfo & geominfo1,
+			      const Point<3> & ap2,
+			      const PointGeomInfo & geominfo2);
+
+
+  /// Transforms 3d point p3d to local coordinates pplane
+  void ToPlane (const Point<3> & p3d, const PointGeomInfo & geominfo,
+		Point<2> & pplane, double h, int & zone) const;
+  
+  /// Transforms point pplane in local coordinates to 3d point
+  void FromPlane (const Point<2> & pplane, 
+		  Point<3> & p3d,
+		  PointGeomInfo & gi,
+		  double h);
+};
+
+
+
+///
+class Meshing2OCCSurfaces : public Meshing2
+{
+  ///
+  OCCSurface surface;
+ 
+
+public:
+  ///
+  Meshing2OCCSurfaces (const TopoDS_Shape & asurf, const Box<3> & aboundingbox, int aprojecttype);
+
+  ///
+  int GetProjectionType ()
+  { return surface.projecttype; }
+
+protected:
+  ///
+  virtual void DefineTransformation (const Point3d & p1, const Point3d & p2,
+				     const PointGeomInfo * geominfo1,
+				     const PointGeomInfo * geominfo2);
+  ///
+  virtual void TransformToPlain (const Point3d & locpoint, 
+				 const MultiPointGeomInfo & geominfo,
+				 Point2d & plainpoint, 
+				 double h, int & zone);
+  ///
+
+  virtual int TransformFromPlain (Point2d & plainpoint,
+				  Point3d & locpoint,
+				  PointGeomInfo & gi,
+				  double h);
+  ///
+  virtual double CalcLocalH (const Point3d & p, double gh) const;
+  
+};
+
+
+
+///
+class MeshOptimize2dOCCSurfaces : public MeshOptimize2d
+  {
+  ///
+  const OCCGeometry & geometry;
+
+public:
+    ///
+    MeshOptimize2dOCCSurfaces (const OCCGeometry & ageometry); 
+   
+    ///
+    virtual void ProjectPoint (INDEX surfind, Point<3> & p) const;
+    ///
+    virtual void ProjectPoint2 (INDEX surfind, INDEX surfind2, Point<3> & p) const;
+    ///
+    virtual int ProjectPointGI (INDEX surfind, Point<3> & p, PointGeomInfo & gi) const;
+    ///
+    virtual void GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const;
+    ///
+    virtual void GetNormalVector(INDEX surfind, const Point<3> & p, PointGeomInfo & gi, Vec<3> & n) const;
+    
+    virtual int CalcPointGeomInfo(int surfind, PointGeomInfo& gi, const Point<3> & p3) const;
+};
+
+
+
+class OCCGeometry;
+
+
+class OCCRefinementSurfaces : public Refinement
+{
+  const OCCGeometry & geometry;
+
+public:
+  OCCRefinementSurfaces (const OCCGeometry & ageometry);
+  virtual ~OCCRefinementSurfaces ();
+  
+  virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+			     int surfi, 
+			     const PointGeomInfo & gi1, 
+			     const PointGeomInfo & gi2,
+			     Point<3> & newp, PointGeomInfo & newgi) const;
+
+  virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+			     int surfi1, int surfi2, 
+			     const EdgePointGeomInfo & ap1, 
+			     const EdgePointGeomInfo & ap2,
+			     Point<3> & newp, EdgePointGeomInfo & newgi) const;
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi) const;
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & gi) const;
+};
+
+
+
+#endif
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/occpkg.cpp b/contrib/Netgen/libsrc/occ/occpkg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aeb691e595126098be78cc1f541cba33392f1ce9
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occpkg.cpp
@@ -0,0 +1,1020 @@
+#ifdef OCCGEOMETRY
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <occgeom.hpp>
+
+
+#include <incvis.hpp>
+#include <visual.hpp>
+
+#include "../meshing/bcfunctions.hpp"
+
+#include "vsocc.hpp"
+
+
+extern "C" int Ng_occ_Init (Tcl_Interp * interp);
+
+
+
+namespace netgen
+{
+  extern NetgenGeometry * ng_geometry;
+  extern AutoPtr<Mesh> mesh;
+ 
+  char * err_needsoccgeometry = (char*) "This operation needs an OCC geometry";
+  extern char * err_needsmesh;
+  extern char * err_jobrunning;
+
+
+
+                          
+  class OCCGeometryRegister : public GeometryRegister
+  {
+  public:
+    virtual NetgenGeometry * Load (string filename) const;
+    virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const;
+
+    virtual void SetParameters (Tcl_Interp * interp) 
+    {
+      occparam.resthcloseedgefac =
+	atof (Tcl_GetVar (interp, "::stloptions.resthcloseedgefac", 0));
+      occparam.resthcloseedgeenable =
+	atoi (Tcl_GetVar (interp, "::stloptions.resthcloseedgeenable", 0));
+    }
+  };
+
+
+
+
+  int Ng_SetOCCVisParameters  (ClientData clientData,
+			       Tcl_Interp * interp,
+			       int argc, tcl_const char *argv[])
+  {
+#ifdef OCCGEOMETRY
+    int showvolume;
+    OCCGeometry * occgeometry = dynamic_cast<OCCGeometry*> (ng_geometry);
+
+    showvolume = atoi (Tcl_GetVar (interp, "::occoptions.showvolumenr", 0));
+
+    if (occgeometry)
+      if (showvolume != vispar.occshowvolumenr)
+	{
+	  if (showvolume < 0 || showvolume > occgeometry->NrSolids())
+	    {
+	      char buf[20];
+	      sprintf (buf, "%5i", vispar.occshowvolumenr);
+	      Tcl_SetVar (interp, "::occoptions.showvolumenr", buf, 0);
+	    }
+	  else
+	    {
+	      vispar.occshowvolumenr = showvolume;
+	      if (occgeometry)
+		occgeometry -> changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+	    }
+	}
+    
+    int temp;
+
+    temp = atoi (Tcl_GetVar (interp, "::occoptions.visproblemfaces", 0));
+
+    if ((bool) temp != vispar.occvisproblemfaces)
+      {
+	vispar.occvisproblemfaces = temp;
+	if (occgeometry)
+	  occgeometry -> changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+      }
+
+    vispar.occshowsurfaces = atoi (Tcl_GetVar (interp, "::occoptions.showsurfaces", 0));
+    vispar.occshowedges = atoi (Tcl_GetVar (interp, "::occoptions.showedges", 0));
+    vispar.occzoomtohighlightedentity = atoi (Tcl_GetVar (interp, "::occoptions.zoomtohighlightedentity", 0));
+    vispar.occdeflection = pow(10.0,-1-atof (Tcl_GetVar (interp, "::occoptions.deflection", 0)));
+
+#endif
+
+
+
+
+
+#ifdef ACIS
+    vispar.ACISshowfaces = atoi (Tcl_GetVar (interp, "::occoptions.showsurfaces", 0));
+    vispar.ACISshowedges = atoi (Tcl_GetVar (interp, "::occoptions.showedges", 0));
+    vispar.ACISshowsolidnr = atoi (Tcl_GetVar (interp, "::occoptions.showsolidnr", 0));
+    vispar.ACISshowsolidnr2 = atoi (Tcl_GetVar (interp, "::occoptions.showsolidnr2", 0));
+
+#endif
+
+
+
+    return TCL_OK;
+  }  
+
+
+
+
+  int Ng_GetOCCData (ClientData clientData,
+		     Tcl_Interp * interp,
+		     int argc, tcl_const char *argv[])
+  {
+#ifdef OCCGEOMETRY
+    OCCGeometry * occgeometry = dynamic_cast<OCCGeometry*> (ng_geometry);
+
+    static char buf[1000];
+    buf[0] = 0;
+    stringstream str;
+
+    if (argc >= 2)
+      {
+	if (strcmp (argv[1], "getentities") == 0)
+	  {
+	    if (occgeometry)
+	      {
+		occgeometry->GetTopologyTree(str);
+	      }
+	  }
+      }
+
+    Tcl_SetResult (interp, (char*)str.str().c_str(), TCL_VOLATILE);
+
+#endif
+    return TCL_OK;
+  }
+
+  
+
+  int Ng_OCCCommand (ClientData clientData,
+		     Tcl_Interp * interp,
+		     int argc, tcl_const char *argv[])
+  {
+#ifdef OCCGEOMETRY
+    OCCGeometry * occgeometry = dynamic_cast<OCCGeometry*> (ng_geometry);
+
+    stringstream str;
+    if (argc >= 2)
+      {
+	if (strcmp (argv[1], "isoccgeometryloaded") == 0)
+	  {
+	    if (occgeometry)
+	      str << "1 " << flush;
+	    else str << "0 " << flush;
+
+	    Tcl_SetResult (interp, (char*)str.str().c_str(), TCL_VOLATILE);
+	  }
+	if (occgeometry)
+	  {
+	    if (strcmp (argv[1], "buildvisualizationmesh") == 0)
+	      {
+		occgeometry->BuildVisualizationMesh(vispar.occdeflection);
+		occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+	      }
+	    if (strcmp (argv[1], "mesherror") == 0)
+	      {
+		if (occgeometry->ErrorInSurfaceMeshing())
+		  str << 1;
+		else
+		  str << 0;
+	      }
+	    if (strcmp (argv[1], "sewfaces") == 0)
+	      {
+		cout << "Before operation:" << endl;
+		occgeometry->PrintNrShapes();
+		occgeometry->SewFaces();
+		occgeometry->BuildFMap();
+		cout << endl << "After operation:" << endl;
+		occgeometry->PrintNrShapes();
+		occgeometry->BuildVisualizationMesh(vispar.occdeflection);
+		occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+	      }
+	    if (strcmp (argv[1], "makesolid") == 0)
+	      {
+		cout << "Before operation:" << endl;
+		occgeometry->PrintNrShapes();
+		occgeometry->MakeSolid();
+		occgeometry->BuildFMap();
+		cout << endl << "After operation:" << endl;
+		occgeometry->PrintNrShapes();
+		occgeometry->BuildVisualizationMesh(vispar.occdeflection);
+		occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+	      }
+	    if (strcmp (argv[1], "upgradetopology") == 0)
+	      {
+		cout << "Before operation:" << endl;
+		occgeometry->PrintNrShapes();
+		occgeometry->SewFaces();
+		occgeometry->MakeSolid();
+		occgeometry->BuildFMap();
+		cout << endl << "After operation:" << endl;
+		occgeometry->PrintNrShapes();
+		occgeometry->BuildVisualizationMesh(vispar.occdeflection);
+		occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+	      }
+	    if (strcmp (argv[1], "shapehealing") == 0)
+	      {
+		occgeometry->tolerance =
+		  atof (Tcl_GetVar (interp, "::occoptions.tolerance", 0));
+		occgeometry->fixsmalledges =
+		  atoi (Tcl_GetVar (interp, "::occoptions.fixsmalledges", 0));
+		occgeometry->fixspotstripfaces =
+		  atoi (Tcl_GetVar (interp, "::occoptions.fixspotstripfaces", 0));
+		occgeometry->sewfaces =
+		  atoi (Tcl_GetVar (interp, "::occoptions.sewfaces", 0));
+		occgeometry->makesolids =
+		  atoi (Tcl_GetVar (interp, "::occoptions.makesolids", 0));
+		occgeometry->splitpartitions =
+		  atoi (Tcl_GetVar (interp, "::occoptions.splitpartitions", 0));
+
+		//	      cout << "Before operation:" << endl;
+		//	      occgeometry->PrintNrShapes();
+		occgeometry->HealGeometry();
+		occgeometry->BuildFMap();
+		//	      cout << endl << "After operation:" << endl;
+		//	      occgeometry->PrintNrShapes();
+		occgeometry->BuildVisualizationMesh(vispar.occdeflection);
+		occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+	      }
+
+
+	    if (strcmp (argv[1], "highlightentity") == 0)
+	      {
+		if (strcmp (argv[2], "Face") == 0)
+		  {
+		    int nr = atoi (argv[3]);
+		    occgeometry->LowLightAll();
+
+		    occgeometry->fvispar[nr-1].Highlight();
+		    if (vispar.occzoomtohighlightedentity)
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONFULLCHANGE;
+		    else
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+		  }
+		if (strcmp (argv[2], "Shell") == 0)
+		  {
+		    int nr = atoi (argv[3]);
+		    occgeometry->LowLightAll();
+
+		    TopExp_Explorer exp;
+		    for (exp.Init (occgeometry->shmap(nr), TopAbs_FACE);
+			 exp.More(); exp.Next())
+		      {
+			int i = occgeometry->fmap.FindIndex (TopoDS::Face(exp.Current()));
+			occgeometry->fvispar[i-1].Highlight();
+		      }
+		    if (vispar.occzoomtohighlightedentity)
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONFULLCHANGE;
+		    else
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+		  }
+		if (strcmp (argv[2], "Solid") == 0)
+		  {
+		    int nr = atoi (argv[3]);
+		    occgeometry->LowLightAll();
+
+		    TopExp_Explorer exp;
+		    for (exp.Init (occgeometry->somap(nr), TopAbs_FACE);
+			 exp.More(); exp.Next())
+		      {
+			int i = occgeometry->fmap.FindIndex (TopoDS::Face(exp.Current()));
+			occgeometry->fvispar[i-1].Highlight();
+		      }
+		    if (vispar.occzoomtohighlightedentity)
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONFULLCHANGE;
+		    else
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+		  }
+		/*
+		  if (strcmp (argv[2], "CompSolid") == 0)
+		  {
+		  int nr = atoi (argv[3]);
+		  occgeometry->LowLightAll();
+
+		  TopExp_Explorer exp;
+		  for (exp.Init (occgeometry->cmap(nr), TopAbs_FACE);
+		  exp.More(); exp.Next())
+		  {
+		  int i = occgeometry->fmap.FindIndex (TopoDS::Face(exp.Current()));
+		  occgeometry->fvispar[i-1].Highlight();
+		  }
+		  occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+		  }
+		*/
+
+		if (strcmp (argv[2], "Edge") == 0)
+		  {
+		    int nr = atoi (argv[3]);
+		    occgeometry->LowLightAll();
+
+		    occgeometry->evispar[nr-1].Highlight();
+		    if (vispar.occzoomtohighlightedentity)
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONFULLCHANGE;
+		    else
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+		  }
+		if (strcmp (argv[2], "Wire") == 0)
+		  {
+		    int nr = atoi (argv[3]);
+		    occgeometry->LowLightAll();
+
+		    TopExp_Explorer exp;
+		    for (exp.Init (occgeometry->wmap(nr), TopAbs_EDGE);
+			 exp.More(); exp.Next())
+		      {
+			int i = occgeometry->emap.FindIndex (TopoDS::Edge(exp.Current()));
+			occgeometry->evispar[i-1].Highlight();
+		      }
+		    if (vispar.occzoomtohighlightedentity)
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONFULLCHANGE;
+		    else
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+		  }
+
+		if (strcmp (argv[2], "Vertex") == 0)
+		  {
+		    int nr = atoi (argv[3]);
+		    occgeometry->LowLightAll();
+
+		    occgeometry->vvispar[nr-1].Highlight();
+		    if (vispar.occzoomtohighlightedentity)
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONFULLCHANGE;
+		    else
+		      occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+		  }
+
+	      }
+
+
+
+	    if (strcmp (argv[1], "show") == 0)
+	      {
+		int nr = atoi (argv[3]);
+		occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+
+		if (strcmp (argv[2], "Face") == 0)
+		  {
+		    occgeometry->fvispar[nr-1].Show();
+		  }
+		if (strcmp (argv[2], "Shell") == 0)
+		  {
+		    TopExp_Explorer exp;
+		    for (exp.Init (occgeometry->shmap(nr), TopAbs_FACE);
+			 exp.More(); exp.Next())
+		      {
+			int i = occgeometry->fmap.FindIndex (TopoDS::Face(exp.Current()));
+			occgeometry->fvispar[i-1].Show();
+		      }
+		  }
+		if (strcmp (argv[2], "Solid") == 0)
+		  {
+		    TopExp_Explorer exp;
+		    for (exp.Init (occgeometry->somap(nr), TopAbs_FACE);
+			 exp.More(); exp.Next())
+		      {
+			int i = occgeometry->fmap.FindIndex (TopoDS::Face(exp.Current()));
+			occgeometry->fvispar[i-1].Show();
+		      }
+		  }
+		if (strcmp (argv[2], "Edge") == 0)
+		  {
+		    occgeometry->evispar[nr-1].Show();
+		  }
+		if (strcmp (argv[2], "Wire") == 0)
+		  {
+		    TopExp_Explorer exp;
+		    for (exp.Init (occgeometry->wmap(nr), TopAbs_EDGE);
+			 exp.More(); exp.Next())
+		      {
+			int i = occgeometry->emap.FindIndex (TopoDS::Edge(exp.Current()));
+			occgeometry->evispar[i-1].Show();
+		      }
+		  }
+	      }
+
+
+	    if (strcmp (argv[1], "hide") == 0)
+	      {
+		int nr = atoi (argv[3]);
+		occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+
+		if (strcmp (argv[2], "Face") == 0)
+		  {
+		    occgeometry->fvispar[nr-1].Hide();
+		  }
+		if (strcmp (argv[2], "Shell") == 0)
+		  {
+		    TopExp_Explorer exp;
+		    for (exp.Init (occgeometry->shmap(nr), TopAbs_FACE);
+			 exp.More(); exp.Next())
+		      {
+			int i = occgeometry->fmap.FindIndex (TopoDS::Face(exp.Current()));
+			occgeometry->fvispar[i-1].Hide();
+		      }
+		  }
+		if (strcmp (argv[2], "Solid") == 0)
+		  {
+		    TopExp_Explorer exp;
+		    for (exp.Init (occgeometry->somap(nr), TopAbs_FACE);
+			 exp.More(); exp.Next())
+		      {
+			int i = occgeometry->fmap.FindIndex (TopoDS::Face(exp.Current()));
+			occgeometry->fvispar[i-1].Hide();
+		      }
+		  }
+		if (strcmp (argv[2], "Edge") == 0)
+		  {
+		    occgeometry->evispar[nr-1].Hide();
+		  }
+		if (strcmp (argv[2], "Wire") == 0)
+		  {
+		    TopExp_Explorer exp;
+		    for (exp.Init (occgeometry->wmap(nr), TopAbs_EDGE);
+			 exp.More(); exp.Next())
+		      {
+			int i = occgeometry->emap.FindIndex (TopoDS::Edge(exp.Current()));
+			occgeometry->evispar[i-1].Hide();
+		      }
+		  }
+	      }
+
+
+
+	    if (strcmp (argv[1], "findsmallentities") == 0)
+	      {
+		stringstream str("");
+		occgeometry->CheckIrregularEntities(str);
+		Tcl_SetResult (interp, (char*)str.str().c_str(), TCL_VOLATILE);
+	      }
+	    if (strcmp (argv[1], "getunmeshedfaceinfo") == 0)
+	      {
+		occgeometry->GetUnmeshedFaceInfo(str);
+		Tcl_SetResult (interp, (char*)str.str().c_str(), TCL_VOLATILE);
+	      }
+	    if (strcmp (argv[1], "getnotdrawablefaces") == 0)
+	      {
+		occgeometry->GetNotDrawableFaces(str);
+		Tcl_SetResult (interp, (char*)str.str().c_str(), TCL_VOLATILE);
+	      }
+	    if (strcmp (argv[1], "redrawstatus") == 0)
+	      {
+		int i = atoi (argv[2]);
+		occgeometry->changed = i;
+	      }
+	    if (strcmp (argv[1], "swaporientation") == 0)
+	      {
+		IGESControl_Writer writer("millimeters", 1);
+		writer.AddShape (occgeometry->shape);
+		writer.Write ("1.igs");
+		/*
+		  int nr = atoi (argv[3]);
+
+		  //	      const_cast<TopoDS_Shape&> (occgeometry->fmap(nr)).Reverse();
+
+		  Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+		  rebuild->Apply(occgeometry->shape);
+
+		  TopoDS_Shape sh;
+
+		  //	      if (strcmp (argv[2], "CompSolid") == 0) sh = occgeometry->cmap(nr);
+		  if (strcmp (argv[2], "Solid") == 0) sh = occgeometry->somap(nr);
+		  if (strcmp (argv[2], "Shell") == 0) sh = occgeometry->shmap(nr);
+		  if (strcmp (argv[2], "Face") == 0) sh = occgeometry->fmap(nr);
+		  if (strcmp (argv[2], "Wire") == 0) sh = occgeometry->wmap(nr);
+		  if (strcmp (argv[2], "Edge") == 0) sh = occgeometry->emap(nr);
+
+		  rebuild->Replace(sh, sh.Reversed(), Standard_False);
+
+		  TopoDS_Shape newshape = rebuild->Apply(occgeometry->shape, TopAbs_SHELL, 1);
+		  occgeometry->shape = newshape;
+
+		  occgeometry->BuildFMap();
+		  occgeometry->BuildVisualizationMesh();
+		  occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+		*/
+	      }
+	    if (strcmp (argv[1], "marksingular") == 0)
+	      {
+		int nr = atoi (argv[3]);
+		cout << "marking " << argv[2] << " " << nr << endl;
+		char buf[2]; buf[0] = '0'; buf[1] = 0;
+		bool sing = false;
+		if (strcmp (argv[2], "Face") == 0)
+		  sing = occgeometry->fsingular[nr-1] = !occgeometry->fsingular[nr-1];
+		if (strcmp (argv[2], "Edge") == 0)
+		  sing = occgeometry->esingular[nr-1] = !occgeometry->esingular[nr-1];
+		if (strcmp (argv[2], "Vertex") == 0)
+		  sing = occgeometry->vsingular[nr-1] = !occgeometry->vsingular[nr-1];
+
+		if (sing) buf[0] = '1';
+
+                Tcl_SetVar (interp, "::ismarkedsingular", buf, 0);
+
+		stringstream str;
+		occgeometry->GetTopologyTree (str);
+
+		char* cstr = (char*)str.str().c_str();
+
+		(*testout) << cstr << endl;
+
+		char helpstr[1000];
+
+		while (strchr (cstr, '}'))
+		  {
+		    strncpy (helpstr, cstr+2, strlen(strchr(cstr+2, '}')));
+		    (*testout) << "***" << cstr << "***" << endl;
+		    cstr = strchr (cstr, '}');
+		  } 
+	      }
+	  }
+      }
+
+#endif
+    return TCL_OK;
+  }
+
+
+
+#ifdef OCCGEOMETRY
+  /*
+  void OCCConstructGeometry (OCCGeometry & geom);
+
+  int Ng_OCCConstruction (ClientData clientData,
+			  Tcl_Interp * interp,
+			  int argc, tcl_const char *argv[])
+  {
+    if (occgeometry)
+      OCCConstructGeometry (*occgeometry);
+    return TCL_OK;
+  }
+  */
+#endif
+
+
+
+
+  // Philippose - 30/01/2009
+  // TCL interface function for the Local Face Mesh size
+  // definition functionality
+  int Ng_SurfaceMeshSize (ClientData clientData,
+		                    Tcl_Interp * interp,
+		                    int argc, tcl_const char *argv[])
+  {
+#ifdef OCCGEOMETRY
+
+    static char buf[100];
+
+    if (argc < 2)
+    {
+	   Tcl_SetResult (interp, (char *)"Ng_SurfaceMeshSize needs arguments", TCL_STATIC);
+	   return TCL_ERROR;
+    }
+
+    OCCGeometry * occgeometry = dynamic_cast<OCCGeometry*> (ng_geometry);
+    if (!occgeometry)
+    {
+      Tcl_SetResult (interp, (char *)"Ng_SurfaceMeshSize currently supports only OCC (STEP/IGES) Files", TCL_STATIC);
+	   return TCL_ERROR;
+    }
+
+    // Update the face mesh sizes to reflect the global maximum mesh size
+    for(int i = 1; i <= occgeometry->NrFaces(); i++)
+    {
+           if(!occgeometry->GetFaceMaxhModified(i))
+           {
+              occgeometry->SetFaceMaxH(i, mparam.maxh);
+           }   
+    }
+
+    if (strcmp (argv[1], "setsurfms") == 0)
+    {
+	   int facenr = atoi (argv[2]);
+	   double surfms = atof (argv[3]);
+	   if (occgeometry && facenr >= 1 && facenr <= occgeometry->NrFaces())
+	     occgeometry->SetFaceMaxH(facenr, surfms);
+
+    }
+
+    if (strcmp (argv[1], "setall") == 0)
+    {
+	   double surfms = atof (argv[2]);
+	   if (occgeometry)
+	   {
+	     int nrFaces = occgeometry->NrFaces();
+	     for (int i = 1; i <= nrFaces; i++)
+	      occgeometry->SetFaceMaxH(i, surfms);
+	   }
+    }
+
+    if (strcmp (argv[1], "getsurfms") == 0)
+    {
+	   int facenr = atoi (argv[2]);
+	   if (occgeometry && facenr >= 1 && facenr <= occgeometry->NrFaces())
+	   {
+	     sprintf (buf, "%5.2f", occgeometry->GetFaceMaxH(facenr));
+	   }
+	   else
+	   {
+	     sprintf (buf, "%5.2f", mparam.maxh);
+	   }
+	   Tcl_SetResult (interp, buf, TCL_STATIC);
+    }
+
+    if (strcmp (argv[1], "getactive") == 0)
+    {
+	   sprintf (buf, "%d", occgeometry->SelectedFace());
+	   Tcl_SetResult (interp, buf, TCL_STATIC);
+    }
+
+    if (strcmp (argv[1], "setactive") == 0)
+    {
+	   int facenr = atoi (argv[2]);
+	   if (occgeometry && facenr >= 1 && facenr <= occgeometry->NrFaces())
+	   {
+	     occgeometry->SetSelectedFace (facenr);
+
+        occgeometry->LowLightAll();
+        occgeometry->fvispar[facenr-1].Highlight();
+        occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+	   }
+    }
+
+    if (strcmp (argv[1], "getnfd") == 0)
+    {
+	   if (occgeometry)
+	     sprintf (buf, "%d", occgeometry->NrFaces());
+	   else
+	     sprintf (buf, "0");
+	   Tcl_SetResult (interp, buf, TCL_STATIC);
+    }
+    return TCL_OK;
+#else // No OCCGEOMETRY 
+
+    Tcl_SetResult (interp, (char *)"Ng_SurfaceMeshSize currently supports only OCC (STEP/IGES) Files", TCL_STATIC);
+    return TCL_ERROR;
+    
+#endif // OCCGEOMETRY
+  }
+
+
+
+  // Philippose - 25/07/2010
+  // TCL interface function for extracting and eventually 
+  // setting or editing the current colours present in the mesh
+  int Ng_CurrentFaceColours (ClientData clientData,
+                             Tcl_Interp * interp,
+                             int argc, tcl_const char *argv[])
+  {
+     if(argc < 1)
+     {
+        Tcl_SetResult (interp, (char *)"Ng_GetCurrentFaceColours needs arguments", TCL_STATIC);
+        return TCL_ERROR;
+     }
+
+     if(!mesh.Ptr())
+     {
+        Tcl_SetResult (interp, (char *)"Ng_GetCurrentFaceColours: Valid netgen mesh required...please mesh the Geometry first", TCL_STATIC);
+	     return TCL_ERROR;
+     }
+
+     if(strcmp(argv[1], "getcolours") == 0)
+     {
+        stringstream outVar;
+        Array<Vec3d> face_colours;
+        GetFaceColours(*mesh, face_colours);
+
+        for(int i = 0; i < face_colours.Size();i++)
+        {
+           outVar << "{ " << face_colours[i].X(1)
+                  << " "  << face_colours[i].X(2)
+                  << " "  << face_colours[i].X(3)
+                  << " } ";
+        }
+
+        tcl_const char * valuevar = argv[2];
+        Tcl_SetVar  (interp, valuevar, (char*)outVar.str().c_str(), 0);
+     }
+
+     if(strcmp(argv[1], "showalso") == 0)
+     {
+        Array<Vec3d> face_colours;
+        GetFaceColours(*mesh,face_colours);
+
+        int colourind = atoi (argv[2]);
+
+        for(int i = 1; i <= mesh->GetNFD(); i++)
+        {
+           Array<SurfaceElementIndex> surfElems;
+           mesh->GetSurfaceElementsOfFace(i,surfElems);
+
+           if(ColourMatch(face_colours[colourind],mesh->GetFaceDescriptor(i).SurfColour()))
+           {
+              for(int j = 0; j < surfElems.Size(); j++)
+              {
+                 mesh->SurfaceElement(surfElems[j]).Visible(1);
+              }
+           }
+        }
+
+        mesh->SetNextTimeStamp();
+     }
+
+     if(strcmp(argv[1], "hidealso") == 0)
+     {
+        Array<Vec3d> face_colours;
+        GetFaceColours(*mesh,face_colours);
+
+        int colourind = atoi (argv[2]);
+
+        for(int i = 1; i <= mesh->GetNFD(); i++)
+        {
+           Array<SurfaceElementIndex> surfElems;
+           mesh->GetSurfaceElementsOfFace(i,surfElems);
+
+           if(ColourMatch(face_colours[colourind],mesh->GetFaceDescriptor(i).SurfColour()))
+           {
+              for(int j = 0; j < surfElems.Size(); j++)
+              {
+                 mesh->SurfaceElement(surfElems[j]).Visible(0);
+              }
+           }
+        }
+
+        mesh->SetNextTimeStamp();
+     }
+
+     if(strcmp(argv[1], "showonly") == 0)
+     {
+        Array<Vec3d> face_colours;
+        GetFaceColours(*mesh,face_colours);
+
+        int colourind = atoi (argv[2]);
+
+        for(int i = 1; i <= mesh->GetNFD(); i++)
+        {
+           Array<SurfaceElementIndex> surfElems;
+           mesh->GetSurfaceElementsOfFace(i,surfElems);
+
+           if(ColourMatch(face_colours[colourind],mesh->GetFaceDescriptor(i).SurfColour()))
+           {
+              for(int j = 0; j < surfElems.Size(); j++)
+              {
+                 mesh->SurfaceElement(surfElems[j]).Visible(1);
+              }
+           }
+           else
+           {
+              for(int j = 0; j < surfElems.Size(); j++)
+              {
+                 mesh->SurfaceElement(surfElems[j]).Visible(0);
+              }
+           }
+        }
+
+        mesh->SetNextTimeStamp();
+     }
+
+     if(strcmp(argv[1], "hideonly") == 0)
+     {
+        Array<Vec3d> face_colours;
+        GetFaceColours(*mesh,face_colours);
+
+        int colourind = atoi (argv[2]);
+
+        for(int i = 1; i <= mesh->GetNFD(); i++)
+        {
+           Array<SurfaceElementIndex> surfElems;
+           mesh->GetSurfaceElementsOfFace(i,surfElems);
+
+           if(ColourMatch(face_colours[colourind],mesh->GetFaceDescriptor(i).SurfColour()))
+           {
+              for(int j = 0; j < surfElems.Size(); j++)
+              {
+                 mesh->SurfaceElement(surfElems[j]).Visible(0);
+              }
+           }
+           else
+           {
+              for(int j = 0; j < surfElems.Size(); j++)
+              {
+                 mesh->SurfaceElement(surfElems[j]).Visible(1);
+              }
+           }
+        }
+
+        mesh->SetNextTimeStamp();
+     }
+
+     if(strcmp(argv[1], "showall") == 0)
+     {
+        for(int i = 1; i <= mesh->GetNSE(); i++)
+        {
+           mesh->SurfaceElement(i).Visible(1);
+        }
+
+        mesh->SetNextTimeStamp();
+     }
+
+     if(strcmp(argv[1], "hideall") == 0)
+     {
+        for(int i = 1; i <= mesh->GetNSE(); i++)
+        {
+           mesh->SurfaceElement(i).Visible(0);
+        }
+
+        mesh->SetNextTimeStamp();
+     }
+
+     return TCL_OK;
+  }
+
+
+
+
+  // Philippose - 10/03/2009
+  // TCL interface function for the Automatic Colour-based
+  // definition of boundary conditions for OCC Geometry
+  int Ng_AutoColourBcProps (ClientData clientData,
+		                      Tcl_Interp * interp,
+		                      int argc, tcl_const char *argv[])
+  {
+     if(argc < 1)
+     {
+        Tcl_SetResult (interp, (char *)"Ng_AutoColourBcProps needs arguments", TCL_STATIC);
+        return TCL_ERROR;
+     }
+
+     if(!mesh.Ptr())
+     {
+        Tcl_SetResult (interp, (char *)"Ng_AutoColourBcProps: Valid netgen mesh required...please mesh the Geometry first", TCL_STATIC);
+	     return TCL_ERROR;
+     }
+
+     if(strcmp(argv[1], "auto") == 0)
+     {
+        AutoColourBcProps(*mesh, 0);
+     }
+
+     if(strcmp(argv[1], "profile") == 0)
+     {
+        AutoColourBcProps(*mesh, argv[2]);
+     }
+
+     return TCL_OK;
+  }
+
+
+  int Ng_SetOCCParameters  (ClientData clientData,
+			    Tcl_Interp * interp,
+			    int argc, tcl_const char *argv[])
+  {
+    OCCGeometryRegister reg;
+    reg.SetParameters (interp);
+    /*
+    occparam.resthcloseedgefac =
+      atof (Tcl_GetVar (interp, "::stloptions.resthcloseedgefac", 0));
+
+    occparam.resthcloseedgeenable =
+      atoi (Tcl_GetVar (interp, "::stloptions.resthcloseedgeenable", 0));
+    */
+    return TCL_OK;
+  }
+
+
+
+
+  NetgenGeometry *  OCCGeometryRegister :: Load (string filename) const
+  {
+    const char * lgfilename = filename.c_str();
+
+
+    /*
+    if (strcmp (&cfilename[strlen(cfilename)-3], "geo") == 0)
+      {
+	PrintMessage (1, "Load OCCG geometry file ", cfilename);
+	
+	extern OCCGeometry * ParseOCCG (istream & istr);
+
+	ifstream infile(cfilename);
+
+	OCCGeometry * hgeom = ParseOCCG (infile);
+	if (!hgeom)
+	  throw NgException ("geo-file should start with 'algebraic3d'");
+
+	hgeom -> FindIdenticSurfaces(1e-8 * hgeom->MaxSize()); 
+	return hgeom;
+      }
+    */
+
+
+    if ((strcmp (&lgfilename[strlen(lgfilename)-4], "iges") == 0) ||
+	(strcmp (&lgfilename[strlen(lgfilename)-3], "igs") == 0) ||
+	(strcmp (&lgfilename[strlen(lgfilename)-3], "IGS") == 0) ||
+	(strcmp (&lgfilename[strlen(lgfilename)-4], "IGES") == 0))
+      {
+	PrintMessage (1, "Load IGES geometry file ", lgfilename);
+	OCCGeometry * occgeometry = LoadOCC_IGES (lgfilename);
+	return occgeometry;
+      }
+
+    else if ((strcmp (&lgfilename[strlen(lgfilename)-4], "step") == 0) ||
+		     (strcmp (&lgfilename[strlen(lgfilename)-3], "stp") == 0) ||
+		     (strcmp (&lgfilename[strlen(lgfilename)-3], "STP") == 0) ||
+		     (strcmp (&lgfilename[strlen(lgfilename)-4], "STEP") == 0))
+      {
+	PrintMessage (1, "Load STEP geometry file ", lgfilename);
+	OCCGeometry * occgeometry = LoadOCC_STEP (lgfilename);
+	return occgeometry;    
+      }
+    else if ((strcmp (&lgfilename[strlen(lgfilename)-4], "brep") == 0) ||
+	     (strcmp (&lgfilename[strlen(lgfilename)-4], "Brep") == 0) ||
+	     (strcmp (&lgfilename[strlen(lgfilename)-4], "BREP") == 0))
+      {
+	PrintMessage (1, "Load BREP geometry file ", lgfilename);
+	OCCGeometry * occgeometry = LoadOCC_BREP (lgfilename);
+	return occgeometry;
+      }
+    
+    return NULL;
+  }
+
+
+  static VisualSceneOCCGeometry vsoccgeom;
+
+  VisualScene * OCCGeometryRegister :: GetVisualScene (const NetgenGeometry * geom) const
+  {
+    OCCGeometry * geometry = dynamic_cast<OCCGeometry*> (ng_geometry);
+    if (geometry)
+      {
+	vsoccgeom.SetGeometry (geometry);
+	return &vsoccgeom;
+      }
+    return NULL;
+  }
+
+
+
+}
+
+
+
+using namespace netgen;
+
+int Ng_occ_Init (Tcl_Interp * interp)
+{
+  geometryregister.Append (new OCCGeometryRegister);
+
+
+    Tcl_CreateCommand (interp, "Ng_SetOCCVisParameters",
+		       Ng_SetOCCVisParameters,
+		       (ClientData)NULL,
+		       (Tcl_CmdDeleteProc*) NULL);
+
+    Tcl_CreateCommand (interp, "Ng_GetOCCData",
+		       Ng_GetOCCData,
+		       (ClientData)NULL,
+		       (Tcl_CmdDeleteProc*) NULL);
+
+    /*
+#ifdef OCCGEOMETRY
+    Tcl_CreateCommand (interp, "Ng_OCCConstruction",
+		       Ng_OCCConstruction,
+		       (ClientData)NULL,
+		       (Tcl_CmdDeleteProc*) NULL);
+#endif
+    */
+
+    Tcl_CreateCommand (interp, "Ng_OCCCommand",
+		       Ng_OCCCommand,
+		       (ClientData)NULL,
+		       (Tcl_CmdDeleteProc*) NULL);
+
+
+    Tcl_CreateCommand (interp, "Ng_SetOCCParameters", Ng_SetOCCParameters,
+		       (ClientData)NULL,
+		       (Tcl_CmdDeleteProc*) NULL);
+
+
+
+    // Philippose - 30/01/2009
+    // Register the TCL Interface Command for local face mesh size
+    // definition
+    Tcl_CreateCommand (interp, "Ng_SurfaceMeshSize", Ng_SurfaceMeshSize,
+		       (ClientData)NULL,
+		       (Tcl_CmdDeleteProc*) NULL);
+
+    Tcl_CreateCommand (interp, "Ng_AutoColourBcProps", Ng_AutoColourBcProps,
+		       (ClientData)NULL,
+		       (Tcl_CmdDeleteProc*) NULL);
+
+    // Philippose - 25/07/2010
+    // Register the TCL Interface Command for handling the face colours 
+    // present in the mesh
+    Tcl_CreateCommand(interp, "Ng_CurrentFaceColours", Ng_CurrentFaceColours,
+                      (ClientData)NULL,
+                      (Tcl_CmdDeleteProc*) NULL);
+
+
+  return TCL_OK;
+}
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/occ/utilities.h b/contrib/Netgen/libsrc/occ/utilities.h
new file mode 100644
index 0000000000000000000000000000000000000000..8cb9e3055e7c366933557ff66b95cc473996d8da
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/utilities.h
@@ -0,0 +1,112 @@
+//  SALOME Utils : general SALOME's definitions and tools
+//
+//  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+//  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
+// 
+//  This library is free software; you can redistribute it and/or 
+//  modify it under the terms of the GNU Lesser General Public 
+//  License as published by the Free Software Foundation; either 
+//  version 2.1 of the License. 
+// 
+//  This library 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 
+//  Lesser General Public License for more details. 
+// 
+//  You should have received a copy of the GNU Lesser General Public 
+//  License along with this library; if not, write to the Free Software 
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
+// 
+//  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
+//
+//
+//
+//  File   : utilities.h
+//  Author : Antoine YESSAYAN, Paul RASCLE, EDF
+//  Module : SALOME
+//  $Header: /cvs/netgen/netgen/libsrc/occ/utilities.h,v 1.3 2008/03/31 14:20:28 wabro Exp $
+
+/* ---  Definition macros file to print informations if _DEBUG_ is defined --- */
+
+#ifndef UTILITIES_H
+#define UTILITIES_H
+
+#include <string>
+#include <iostream>
+#include <cstdlib>
+// #include "SALOME_Log.hxx"
+
+/* ---  INFOS is always defined (without _DEBUG_): to be used for warnings, with release version --- */
+
+#define INFOS(msg)    {SLog->putMessage(*SLog<<__FILE__<<" ["<<__LINE__<<"] : "<<msg<<endl);}
+#define PYSCRIPT(msg) {SLog->putMessage(*SLog<<"---PYSCRIPT--- "<<msg<<endl);}
+
+/* --- To print date and time of compilation of current source --- */
+
+#if defined ( __GNUC__ )
+#define COMPILER		"g++" 
+#elif defined ( __sun )
+#define COMPILER		"CC" 
+#elif defined ( __KCC )
+#define COMPILER		"KCC" 
+#elif defined ( __PGI )
+#define COMPILER		"pgCC" 
+#elif defined ( __alpha )
+#define COMPILER		"cxx" 
+#else
+#define COMPILER		"undefined" 
+#endif
+
+#ifdef INFOS_COMPILATION
+#error INFOS_COMPILATION already defined
+#endif
+
+#define INFOS_COMPILATION { \
+			   SLog->putMessage(\
+					   *SLog<<__FILE__<<" ["<< __LINE__<<"] : "\
+					   << "COMPILED with " << COMPILER \
+					   << ", " << __DATE__ \
+					   << " at " << __TIME__ <<endl); }
+
+#ifdef _DEBUG_
+
+/* --- the following MACROS are useful at debug time --- */
+
+#define MYTRACE *SLog << "- Trace " << __FILE__ << " [" << __LINE__ << "] : " 
+
+#define MESSAGE(msg) {SLog->putMessage( MYTRACE <<msg<<endl<<ends); }
+#define SCRUTE(var)  {SLog->putMessage( MYTRACE << #var << "=" << var <<endl<<ends); }
+
+#define REPERE *SLog << "   --------------" << endl 
+#define BEGIN_OF(msg) {REPERE;MYTRACE<<"Begin of: "     <<msg<<endl;REPERE;} 
+#define END_OF(msg)   {REPERE;MYTRACE<<"Normal end of: "<<msg<<endl;REPERE;} 
+
+#define HERE {cout<<flush ;cerr<<"- Trace "<<__FILE__<<" ["<<__LINE__<<"] : "<<flush ;}
+
+#define INTERRUPTION(code) {HERE;cerr<<"INTERRUPTION return code= "<<code<< endl;std::exit(code);}
+
+#ifndef ASSERT
+#define ASSERT(condition) \
+        if (!(condition)){HERE;cerr<<"CONDITION "<<#condition<<" NOT VERIFIED"<<endl;INTERRUPTION(1);}
+#endif /* ASSERT */
+
+
+#else /* ifdef _DEBUG_*/
+
+#define HERE 
+#define SCRUTE(var) {}
+#define MESSAGE(msg) {}
+#define REPERE
+#define BEGIN_OF(msg) {}
+#define END_OF(msg) {}
+
+#define INTERRUPTION(code) {}
+
+#ifndef ASSERT
+#define ASSERT(condition) {}
+#endif /* ASSERT */
+
+
+#endif /* ifdef _DEBUG_*/
+
+#endif /* ifndef UTILITIES_H */
diff --git a/contrib/Netgen/libsrc/occ/vsocc.cpp b/contrib/Netgen/libsrc/occ/vsocc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..64a07dffc6481a42e422607072fcab38a914c9a3
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/vsocc.cpp
@@ -0,0 +1,764 @@
+#ifndef NOTCL
+
+#ifdef OCCGEOMETRY
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <meshing.hpp>
+
+#include <occgeom.hpp>
+
+#include "TopoDS_Shape.hxx"
+#include "TopoDS_Vertex.hxx"
+#include "TopExp_Explorer.hxx"
+#include "BRep_Tool.hxx"
+#include "TopoDS.hxx"
+#include "gp_Pnt.hxx"
+#include "Geom_Curve.hxx"
+#include "Poly_Triangulation.hxx"
+#include "Poly_Array1OfTriangle.hxx"
+#include "TColgp_Array1OfPnt2d.hxx"
+#include "Poly_Triangle.hxx"
+#include "Poly_Polygon3D.hxx"
+#include "Poly_PolygonOnTriangulation.hxx"
+
+#include <visual.hpp>
+
+#include "vsocc.hpp"
+
+namespace netgen
+{
+  // extern OCCGeometry * occgeometry;
+
+   /* *********************** Draw OCC Geometry **************** */
+
+   VisualSceneOCCGeometry :: VisualSceneOCCGeometry ()
+   : VisualScene()
+   {
+      trilists.SetSize(0);
+      linelists.SetSize(1);
+
+   }
+
+   VisualSceneOCCGeometry :: ~VisualSceneOCCGeometry ()
+   {
+      ;
+   }
+
+   void VisualSceneOCCGeometry :: DrawScene ()
+   {
+      if ( occgeometry->changed )
+      {
+         BuildScene();
+         occgeometry -> changed = 0;
+      }
+
+      glClearColor(backcolor, backcolor, backcolor, 1.0);
+      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+      SetLight();
+
+      glPushMatrix();
+      glMultMatrixf (transformationmat);
+
+      glShadeModel (GL_SMOOTH);
+      glDisable (GL_COLOR_MATERIAL);
+      glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+      glEnable (GL_BLEND);
+      glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      
+      //  glEnable (GL_LIGHTING);
+
+      double shine = vispar.shininess;
+      // double transp = vispar.transp;
+
+      glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine);
+      glLogicOp (GL_COPY);
+
+      glEnable (GL_NORMALIZE);
+
+      float mat_col[] = {  0.2f, 0.2f, 0.8f, 1.0f};
+      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+      glPolygonOffset (1, 1);
+      glEnable (GL_POLYGON_OFFSET_FILL);
+
+      // Philippose - 30/01/2009
+      // Added clipping planes to Geometry view
+      SetClippingPlane();
+
+      GLfloat matcoledge[] = {  0, 0, 1, 1};
+      GLfloat matcolhiedge[] = {  1, 0, 0, 1};
+
+      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcoledge);
+      glLineWidth (1.0f);
+
+      if (vispar.occshowedges) glCallList (linelists.Get(1));
+      if (vispar.occshowsurfaces) glCallList (trilists.Get(1));
+
+      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolhiedge);
+      glLineWidth (5.0f);
+
+      if (vispar.occshowedges) glCallList (linelists.Get(2));
+
+      for (int i = 1; i <= occgeometry->vmap.Extent(); i++)
+      if (occgeometry->vvispar[i-1].IsHighlighted())
+      {
+         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolhiedge);
+         glLineWidth (5.0f);
+
+         glBegin (GL_LINES);
+
+         gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(occgeometry->vmap(i)));
+         double d = rad/100;
+         glVertex3f (p.X()-d, p.Y(), p.Z());
+         glVertex3f (p.X()+d, p.Y(), p.Z());
+         glVertex3f (p.X(), p.Y()-d, p.Z());
+         glVertex3f (p.X(), p.Y()+d, p.Z());
+         glVertex3f (p.X(), p.Y(), p.Z()-d);
+         glVertex3f (p.X(), p.Y(), p.Z()+d);
+         glEnd();
+      }
+
+      glDisable (GL_POLYGON_OFFSET_FILL);
+
+      glPopMatrix();
+      //  DrawCoordinateCross ();
+      //  DrawNetgenLogo ();
+      glFinish();
+
+      glDisable (GL_POLYGON_OFFSET_FILL);
+   }
+
+   /*
+    void VisualSceneOCCGeometry :: BuildScene (int zoomall)
+    {
+    int i = 0, j, k;
+
+    TopExp_Explorer ex, ex_edge;
+
+    if (vispar.occvisproblemfaces || (occgeometry -> changed != 2))
+    {
+    Box<3> bb = occgeometry -> GetBoundingBox();
+
+    center = bb.Center();
+    rad = bb.Diam() / 2;
+
+
+
+    if (vispar.occvisproblemfaces)
+    {
+    for (i = 1; i <= occgeometry->fmap.Extent(); i++)
+    if (occgeometry->facemeshstatus[i-1] == -1)
+    {
+    GProp_GProps system;
+    BRepGProp::LinearProperties(occgeometry->fmap(i), system);
+    gp_Pnt pnt = system.CentreOfMass();
+    center = Point<3> (pnt.X(), pnt.Y(), pnt.Z());
+    cout << "Setting center to mid of face " << i << " = " << center << endl;
+    }
+    }
+
+
+    CalcTransformationMatrices();
+    }
+
+
+    for (i = 1; i <= linelists.Size(); i++)
+    glDeleteLists (linelists.Elem(i), 1);
+    linelists.SetSize(0);
+
+    linelists.Append (glGenLists (1));
+    glNewList (linelists.Last(), GL_COMPILE);
+
+    i = 0;
+    for (ex_edge.Init(occgeometry -> shape, TopAbs_EDGE);
+    ex_edge.More(); ex_edge.Next())
+    {
+    if (BRep_Tool::Degenerated(TopoDS::Edge(ex_edge.Current()))) continue;
+    i++;
+
+
+    TopoDS_Edge edge = TopoDS::Edge(ex_edge.Current());
+
+    Handle(Poly_PolygonOnTriangulation) aEdgePoly;
+    Handle(Poly_Triangulation) T;
+    TopLoc_Location aEdgeLoc;
+    BRep_Tool::PolygonOnTriangulation(edge, aEdgePoly, T, aEdgeLoc);
+
+    if(aEdgePoly.IsNull())
+    {
+    cout << "cannot visualize edge " << i << endl;
+    continue;
+    }
+
+    glBegin (GL_LINE_STRIP);
+
+    int nbnodes = aEdgePoly -> NbNodes();
+    for (j = 1; j <= nbnodes; j++)
+    {
+    gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc);
+    glVertex3f (p.X(), p.Y(), p.Z());
+    }
+
+    glEnd ();
+
+
+    }
+
+    glEndList ();
+
+    for (i = 1; i <= trilists.Size(); i++)
+    glDeleteLists (trilists.Elem(i), 1);
+    trilists.SetSize(0);
+
+
+    trilists.Append (glGenLists (1));
+    glNewList (trilists.Last(), GL_COMPILE);
+
+    i = 0;
+
+    TopExp_Explorer exp0, exp1, exp2, exp3;
+    int shapenr = 0;
+    for (exp0.Init(occgeometry -> shape, TopAbs_SOLID); exp0.More(); exp0.Next())
+    {
+    shapenr++;
+
+    if (vispar.occshowvolumenr != 0 &&
+    vispar.occshowvolumenr != shapenr) continue;
+
+    float mat_col[4];
+    mat_col[3] = 1;
+    switch (shapenr)
+    {
+    case 1:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.8;
+    break;
+    case 2:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.8;
+    break;
+    case 3:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.8;
+    break;
+    case 4:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.2;
+    break;
+    case 5:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.8;
+    break;
+    case 6:
+    mat_col[0] = 0.6;
+    mat_col[1] = 0.6;
+    mat_col[2] = 0.6;
+    break;
+    case 7:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.2;
+    break;
+    case 8:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.2;
+    break;
+    default:
+    //	  mat_col[0] = 1-(1.0/double(shapenr));
+    //	  mat_col[1] = 0.5;
+    mat_col[0] = 0.5+double((shapenr*shapenr*shapenr*shapenr) % 10)/20.0;
+    mat_col[1] = 0.5+double(int(shapenr*shapenr*shapenr*shapenr*sin(double(shapenr))) % 10)/20.0;
+    mat_col[2] = 0.5+double((shapenr*shapenr*shapenr) % 10)/20.0;
+    }
+
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+    for (exp1.Init(exp0.Current(), TopAbs_SHELL); exp1.More(); exp1.Next())
+    for (exp2.Init(exp1.Current().Composed(exp0.Current().Orientation()), TopAbs_FACE); exp2.More(); exp2.Next())
+    {
+    TopoDS_Face face = TopoDS::Face (exp2.Current().Composed(exp1.Current().Orientation()));
+
+    i = occgeometry->fmap.FindIndex(face);
+
+    TopLoc_Location loc;
+    Handle(Geom_Surface) surf = BRep_Tool::Surface (face);
+    BRepAdaptor_Surface sf(face, Standard_False);
+    BRepLProp_SLProps prop(sf, 1, 1e-5);
+    Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc);
+
+    if (triangulation.IsNull())
+    {
+    cout << "cannot visualize face " << i << endl;
+    continue;
+    }
+
+    if (vispar.occvisproblemfaces)
+    {
+    switch (occgeometry->facemeshstatus[i-1])
+    {
+    case 0:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.8;
+    break;
+    case 1:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.2;
+    break;
+    case -1:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.2;
+    break;
+    }
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+    }
+    glBegin (GL_TRIANGLES);
+
+    int ntriangles = triangulation -> NbTriangles();
+    for (j = 1; j <= ntriangles; j++)
+    {
+    Poly_Triangle triangle = (triangulation -> Triangles())(j);
+    for (k = 1; k <= 3; k++)
+    {
+    gp_Pnt2d uv = (triangulation -> UVNodes())(triangle(k));
+    gp_Pnt pnt;
+    gp_Vec du, dv;
+    prop.SetParameters (uv.X(), uv.Y());
+    surf->D0 (uv.X(), uv.Y(), pnt);
+    gp_Vec n;
+
+    if (prop.IsNormalDefined())
+    n = prop.Normal();
+    else
+    n = gp_Vec (0,0,0);
+
+    if (face.Orientation() == TopAbs_REVERSED) n *= -1;
+    glNormal3f (n.X(), n.Y(), n.Z());
+    glVertex3f (pnt.X(), pnt.Y(), pnt.Z());
+    }
+    }
+    glEnd ();
+
+    }
+    }
+
+
+    glEndList ();
+
+    }
+    */
+
+   void VisualSceneOCCGeometry :: BuildScene (int zoomall)
+   {
+     if (occgeometry -> changed == OCCGEOMETRYVISUALIZATIONFULLCHANGE)
+       {
+         occgeometry -> BuildVisualizationMesh (vispar.occdeflection);
+
+         center = occgeometry -> Center();
+         rad = occgeometry -> GetBoundingBox().Diam() / 2;
+
+         if (vispar.occzoomtohighlightedentity)
+         {
+            bool hilite = false;
+            bool hiliteonepoint = false;
+            Bnd_Box bb;
+
+            for (int i = 1; i <= occgeometry->fmap.Extent(); i++)
+            if (occgeometry->fvispar[i-1].IsHighlighted())
+            {
+               hilite = true;
+               BRepBndLib::Add (occgeometry->fmap(i), bb);
+            }
+
+            for (int i = 1; i <= occgeometry->emap.Extent(); i++)
+            if (occgeometry->evispar[i-1].IsHighlighted())
+            {
+               hilite = true;
+               BRepBndLib::Add (occgeometry->emap(i), bb);
+            }
+
+            for (int i = 1; i <= occgeometry->vmap.Extent(); i++)
+            if (occgeometry->vvispar[i-1].IsHighlighted())
+            {
+               hiliteonepoint = true;
+               BRepBndLib::Add (occgeometry->vmap(i), bb);
+            }
+
+            if (hilite || hiliteonepoint)
+            {
+               double x1,y1,z1,x2,y2,z2;
+               bb.Get (x1,y1,z1,x2,y2,z2);
+               Point<3> p1 = Point<3> (x1,y1,z1);
+               Point<3> p2 = Point<3> (x2,y2,z2);
+               Box<3> boundingbox(p1,p2);
+
+               center = boundingbox.Center();
+               if (hiliteonepoint)
+               rad = occgeometry -> GetBoundingBox().Diam() / 100;
+               else
+               rad = boundingbox.Diam() / 2;
+            }
+         }
+
+         CalcTransformationMatrices();
+      }
+
+      // Clear lists
+
+      for (int i = 1; i <= linelists.Size(); i++)
+      glDeleteLists (linelists.Elem(i), 1);
+      linelists.SetSize(0);
+
+      for (int i = 1; i <= trilists.Size(); i++)
+      glDeleteLists (trilists.Elem(i), 1);
+      trilists.SetSize(0);
+
+      // Total wireframe
+
+      linelists.Append (glGenLists (1));
+      glNewList (linelists.Last(), GL_COMPILE);
+
+      for (int i = 1; i <= occgeometry->emap.Extent(); i++)
+      {
+         TopoDS_Edge edge = TopoDS::Edge(occgeometry->emap(i));
+         if (BRep_Tool::Degenerated(edge)) continue;
+         if (occgeometry->evispar[i-1].IsHighlighted()) continue;
+
+         Handle(Poly_PolygonOnTriangulation) aEdgePoly;
+         Handle(Poly_Triangulation) T;
+         TopLoc_Location aEdgeLoc;
+         BRep_Tool::PolygonOnTriangulation(edge, aEdgePoly, T, aEdgeLoc);
+
+         if(aEdgePoly.IsNull())
+         {
+            (*testout) << "visualizing edge " << occgeometry->emap.FindIndex (edge)
+            << " without using the occ visualization triangulation" << endl;
+
+            double s0, s1;
+            Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1);
+
+            glBegin (GL_LINE_STRIP);
+            for (int i = 0; i<=50; i++)
+            {
+               gp_Pnt p = c->Value (s0 + i*(s1-s0)/50.0);
+               glVertex3f (p.X(),p.Y(),p.Z());
+            }
+            glEnd ();
+
+            continue;
+         }
+
+         int nbnodes = aEdgePoly -> NbNodes();
+         glBegin (GL_LINE_STRIP);
+         for (int j = 1; j <= nbnodes; j++)
+         {
+            gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc);
+            glVertex3f (p.X(), p.Y(), p.Z());
+         }
+         glEnd ();
+      }
+
+      glEndList ();
+
+      // Highlighted edge list
+
+      linelists.Append (glGenLists (1));
+      glNewList (linelists.Last(), GL_COMPILE);
+
+      for (int i = 1; i <= occgeometry->emap.Extent(); i++)
+      if (occgeometry->evispar[i-1].IsHighlighted())
+      {
+         TopoDS_Edge edge = TopoDS::Edge(occgeometry->emap(i));
+         if (BRep_Tool::Degenerated(edge)) continue;
+
+         Handle(Poly_PolygonOnTriangulation) aEdgePoly;
+         Handle(Poly_Triangulation) T;
+         TopLoc_Location aEdgeLoc;
+         BRep_Tool::PolygonOnTriangulation(edge, aEdgePoly, T, aEdgeLoc);
+
+         if(aEdgePoly.IsNull())
+         {
+            (*testout) << "visualizing edge " << occgeometry->emap.FindIndex (edge)
+            << " without using the occ visualization triangulation" << endl;
+
+            double s0, s1;
+            Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1);
+
+            glBegin (GL_LINE_STRIP);
+            for (int i = 0; i<=50; i++)
+            {
+               gp_Pnt p = c->Value (s0 + i*(s1-s0)/50.0);
+               glVertex3f (p.X(),p.Y(),p.Z());
+            }
+            glEnd ();
+
+            continue;
+         }
+
+         int nbnodes = aEdgePoly -> NbNodes();
+         glBegin (GL_LINE_STRIP);
+         for (int j = 1; j <= nbnodes; j++)
+         {
+            gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc);
+            glVertex3f (p.X(), p.Y(), p.Z());
+         }
+         glEnd ();
+      }
+
+      glEndList ();
+
+      // display faces
+
+      trilists.Append (glGenLists (1));
+      glNewList (trilists.Last(), GL_COMPILE);
+
+      for (int i = 1; i <= occgeometry->fmap.Extent(); i++)
+      {
+         if (!occgeometry->fvispar[i-1].IsVisible()) continue;
+
+         glLoadName (i);
+         float mat_col[4];
+         mat_col[3] = 1;
+
+         TopoDS_Face face = TopoDS::Face(occgeometry->fmap(i));
+
+         if (!occgeometry->fvispar[i-1].IsHighlighted())
+         {
+            // Philippose - 30/01/2009
+            // OpenCascade XDE Support
+            Quantity_Color face_colour;
+            // Philippose - 23/02/2009
+            // Check to see if colours have been extracted first!!
+            // Forum bug-fox (Jean-Yves - 23/02/2009)
+            if(!(occgeometry->face_colours.IsNull())
+               && (occgeometry->face_colours->GetColor(face,XCAFDoc_ColorSurf,face_colour)))
+            {
+               mat_col[0] = face_colour.Red();
+               mat_col[1] = face_colour.Green();
+               mat_col[2] = face_colour.Blue();
+            }
+            else
+            {
+               mat_col[0] = 0.0;
+               mat_col[1] = 1.0;
+               mat_col[2] = 0.0;
+            }
+         }
+         else
+         {
+            mat_col[0] = 0.8;
+            mat_col[1] = 0.2;
+            mat_col[2] = 0.2;
+         }
+
+         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+         TopLoc_Location loc;
+         Handle(Geom_Surface) surf = BRep_Tool::Surface (face);
+         BRepAdaptor_Surface sf(face, Standard_False);
+         BRepLProp_SLProps prop(sf, 1, 1e-5);
+         Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc);
+
+         if (triangulation.IsNull())
+         {
+            cout << "cannot visualize face " << i << endl;
+            occgeometry->fvispar[i-1].SetNotDrawable();
+            continue;
+         }
+
+         gp_Pnt2d uv;
+         gp_Pnt pnt;
+         gp_Vec n;
+
+         glBegin (GL_TRIANGLES);
+
+         int ntriangles = triangulation -> NbTriangles();
+         for (int j = 1; j <= ntriangles; j++)
+         {
+            Poly_Triangle triangle = (triangulation -> Triangles())(j);
+            gp_Pnt p[3];
+            for (int k = 1; k <= 3; k++)
+            p[k-1] = (triangulation -> Nodes())(triangle(k)).Transformed(loc);
+
+            for (int k = 1; k <= 3; k++)
+            {
+               uv = (triangulation -> UVNodes())(triangle(k));
+               prop.SetParameters (uv.X(), uv.Y());
+
+               //	      surf->D0 (uv.X(), uv.Y(), pnt);
+
+               if (prop.IsNormalDefined())
+               n = prop.Normal();
+               else
+               {
+                  (*testout) << "Visualization of face " << i
+                  << ": Normal vector not defined" << endl;
+                  //		  n = gp_Vec (0,0,0);
+                  gp_Vec a(p[0],p[1]);
+                  gp_Vec b(p[0],p[2]);
+                  n = b^a;
+               }
+
+               if (face.Orientation() == TopAbs_REVERSED) n *= -1;
+               glNormal3f (n.X(), n.Y(), n.Z());
+               glVertex3f (p[k-1].X(), p[k-1].Y(), p[k-1].Z());
+            }
+         }
+         glEnd ();
+
+      }
+      glEndList ();
+
+   }
+
+   void SelectFaceInOCCDialogTree (int facenr);
+
+   void VisualSceneOCCGeometry :: MouseDblClick (int px, int py)
+   {
+      int hits;
+
+      // select surface triangle by mouse click
+
+      GLuint selbuf[10000];
+      glSelectBuffer (10000, selbuf);
+
+      glRenderMode (GL_SELECT);
+
+      GLint viewport[4];
+      glGetIntegerv (GL_VIEWPORT, viewport);
+
+      glMatrixMode (GL_PROJECTION);
+      glPushMatrix();
+
+      GLdouble projmat[16];
+      glGetDoublev (GL_PROJECTION_MATRIX, projmat);
+
+      glLoadIdentity();
+      gluPickMatrix (px, viewport[3] - py, 1, 1, viewport);
+      glMultMatrixd (projmat);
+
+      glClearColor(backcolor, backcolor, backcolor, 1.0);
+      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+      glMatrixMode (GL_MODELVIEW);
+
+      glPushMatrix();
+      glMultMatrixf (transformationmat);
+
+      glInitNames();
+      glPushName (1);
+
+      glPolygonOffset (1, 1);
+      glEnable (GL_POLYGON_OFFSET_FILL);
+
+      glDisable(GL_CLIP_PLANE0);
+
+      // Philippose - 30/01/2009
+      // Enable clipping planes for Selection mode in OCC Geometry
+      if (vispar.clipenable)
+      {
+         Vec<3> n(clipplane[0], clipplane[1], clipplane[2]);
+         double len = Abs(n);
+         double mu = -clipplane[3] / (len*len);
+         Point<3> p (mu * n);
+         n /= len;
+         Vec<3> t1 = n.GetNormal ();
+         Vec<3> t2 = Cross (n, t1);
+
+         double xi1mid = (center - p) * t1;
+         double xi2mid = (center - p) * t2;
+
+         glLoadName (0);
+         glBegin (GL_QUADS);
+         glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid-rad) * t2);
+         glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid-rad) * t2);
+         glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid+rad) * t2);
+         glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid+rad) * t2);
+         glEnd ();
+      }
+
+      glCallList (trilists.Get(1));
+
+      glDisable (GL_POLYGON_OFFSET_FILL);
+
+      glPopName();
+
+      glMatrixMode (GL_PROJECTION);
+      glPopMatrix();
+
+      glMatrixMode (GL_MODELVIEW);
+      glPopMatrix();
+
+      glFlush();
+
+      hits = glRenderMode (GL_RENDER);
+
+      int minname = 0;
+      GLuint mindepth = 0;
+
+      // find clippingplane
+      GLuint clipdepth = 0; // GLuint(-1);
+
+      for (int i = 0; i < hits; i++)
+      {
+         int curname = selbuf[4*i+3];
+         if (!curname) clipdepth = selbuf[4*i+1];
+      }
+
+      for (int i = 0; i < hits; i++)
+      {
+         int curname = selbuf[4*i+3];
+         GLuint curdepth = selbuf[4*i+1];
+         if (curname && (curdepth> clipdepth) &&
+               (curdepth < mindepth || !minname))
+         {
+            mindepth = curdepth;
+            minname = curname;
+         }
+      }
+
+      occgeometry->LowLightAll();
+
+      if (minname)
+      {
+         occgeometry->fvispar[minname-1].Highlight();
+
+         if (vispar.occzoomtohighlightedentity)
+         occgeometry->changed = OCCGEOMETRYVISUALIZATIONFULLCHANGE;
+         else
+         occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+         cout << "Selected face: " << minname << endl;
+      }
+      else
+      {
+         occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+      }
+
+      glDisable(GL_CLIP_PLANE0);
+
+      SelectFaceInOCCDialogTree (minname);
+
+      // Philippose - 30/01/2009
+      // Set the currently selected face in the array
+      // for local face mesh size definition
+      occgeometry->SetSelectedFace(minname);
+
+      //  selecttimestamp = NextTimeStamp();
+   }
+
+}
+
+#endif
+
+#endif // NOTCL
diff --git a/contrib/Netgen/libsrc/occ/vsocc.hpp b/contrib/Netgen/libsrc/occ/vsocc.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3021fe7c70d945d58c2e9ee6278cadc2f7e74a8b
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/vsocc.hpp
@@ -0,0 +1,33 @@
+#ifndef FILE_VSOCC
+#define FILE_VSOCC
+
+/**************************************************************************/
+/* File:   vsocc.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   05. Jan. 2011                                                  */
+/**************************************************************************/
+
+namespace netgen
+{
+
+  class VisualSceneOCCGeometry : public VisualScene
+  {
+    Array<int> trilists;
+    Array<int> linelists;
+    int selsurf;
+    class OCCGeometry * occgeometry;
+  public:
+    VisualSceneOCCGeometry ();
+    virtual ~VisualSceneOCCGeometry ();
+    void SetGeometry (class OCCGeometry * ageom) { occgeometry = ageom; }
+
+    virtual void BuildScene (int zoomall = 0);
+    virtual void DrawScene ();
+    virtual void MouseDblClick (int px, int py);
+  };
+
+
+
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/stlgeom/Makefile.am b/contrib/Netgen/libsrc/stlgeom/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..c8ea07aaa549efe2826f93619723968c69c2431f
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/Makefile.am
@@ -0,0 +1,15 @@
+noinst_HEADERS = meshstlsurface.hpp stlgeom.hpp stlline.hpp \
+stltool.hpp stltopology.hpp vsstl.hpp
+
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc/include $(TCL_INCLUDES)
+METASOURCES = AUTO
+
+lib_LTLIBRARIES = libstl.la libstlvis.la
+
+libstl_la_SOURCES = meshstlsurface.cpp stlgeom.cpp stlgeomchart.cpp \
+	stlgeommesh.cpp stlline.cpp stltool.cpp stltopology.cpp
+
+
+libstlvis_la_SOURCES = stlpkg.cpp vsstl.cpp
+libstlvis_la_LIBADD = libstl.la $(top_builddir)/libsrc/linalg/libla.la 
+
diff --git a/contrib/Netgen/libsrc/stlgeom/meshstlsurface.cpp b/contrib/Netgen/libsrc/stlgeom/meshstlsurface.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fcbb1e021bbdbb71a4ad9182bff69c72da08e571
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/meshstlsurface.cpp
@@ -0,0 +1,1133 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+#include <meshing.hpp>
+
+
+#include "stlgeom.hpp"
+
+
+namespace netgen
+{
+
+static void STLFindEdges (STLGeometry & geom,
+			  class Mesh & mesh)
+{
+  int i, j;
+  double h;
+
+  h = mparam.maxh;
+
+  // mark edge points:
+  //int ngp = geom.GetNP();
+
+  geom.RestrictLocalH(mesh, h);
+  
+  PushStatusF("Mesh Lines");
+
+  Array<STLLine*> meshlines;
+  Array<Point3d> meshpoints;
+
+  PrintMessage(3,"Mesh Lines");
+
+  for (i = 1; i <= geom.GetNLines(); i++)
+    {
+      meshlines.Append(geom.GetLine(i)->Mesh(geom.GetPoints(), meshpoints, h, mesh)); 
+      SetThreadPercent(100.0 * (double)i/(double)geom.GetNLines());
+    }
+
+  geom.meshpoints.SetSize(0); //testing
+  geom.meshlines.SetSize(0);  //testing
+  int pim;
+  for (i = 1; i <= meshpoints.Size(); i++)
+    {
+      geom.meshpoints.Append(meshpoints.Get(i)); //testing
+
+      pim = mesh.AddPoint(meshpoints.Get(i));
+    }
+  //(++++++++++++++testing
+  for (i = 1; i <= geom.GetNLines(); i++)
+    {
+      geom.meshlines.Append(meshlines.Get(i));
+    }
+  //++++++++++++++testing)
+
+  PrintMessage(7,"feed with edges");
+
+  for (i = 1; i <= meshlines.Size(); i++)
+    {
+      STLLine* line = meshlines.Get(i);
+      (*testout) << "store line " << i << endl;
+      for (j = 1; j <= line->GetNS(); j++)
+	{
+	  int p1, p2;
+	  
+	  line->GetSeg(j, p1, p2);
+	  int trig1, trig2, trig1b, trig2b;
+
+	  if (p1 == p2) 
+	    cout << "Add Segment, p1 == p2 == " << p1 << endl;
+
+	  // Test auf geschlossener Rand mit 2 Segmenten 
+	      
+	  if ((j == 2) && (line->GetNS() == 2))
+	    {
+	      int oldp1, oldp2;
+	      line->GetSeg (1, oldp1, oldp2);
+	      if (oldp1 == p2 && oldp2 == p1)
+		{
+		  PrintMessage(7,"MESSAGE: don't use second segment");
+		  continue;
+		}
+	    }
+
+
+	  //mesh point number
+	  //p1 = geom2meshnum.Get(p1); // for unmeshed lines!!!
+	  //p2 = geom2meshnum.Get(p2); // for unmeshed lines!!!
+	  
+	  //left and right trigs
+	  trig1 = line->GetLeftTrig(j);
+	  trig2 = line->GetRightTrig(j);
+	  trig1b = line->GetLeftTrig(j+1);
+	  trig2b = line->GetRightTrig(j+1);
+	  
+	  (*testout) << "j = " << j << ", p1 = " << p1 << ", p2 = " << p2 << endl;
+	  (*testout) << "segm-trigs: "
+		   << "trig1 = " << trig1
+		   << ", trig1b = " << trig1b
+		   << ", trig2 = " << trig2
+		   << ", trig2b = " << trig2b << endl;
+
+	  if (trig1 <= 0 || trig2 <= 0 || trig1b <= 0 || trig2b <= 0)
+	    {
+	      cout << "negative trigs, "
+		   << ", trig1 = " << trig1
+		   << ", trig1b = " << trig1b
+		   << ", trig2 = " << trig2
+		   << ", trig2b = " << trig2b << endl;
+	    }
+	  /*
+	  (*testout) << "   trigs p1: " << trig1 << " - " << trig2 << endl;
+	  (*testout) << "   trigs p2: " << trig1b << " - " << trig2b << endl;
+	  (*testout) << "   charts p1: " << geom.GetChartNr(trig1) << " - " << geom.GetChartNr(trig2) << endl;
+	  (*testout) << "   charts p2: " << geom.GetChartNr(trig1b) << " - " << geom.GetChartNr(trig2b) << endl;
+	  */
+	  Point3d hp, hp2;
+	  Segment seg;
+	  seg[0] = p1;
+	  seg[1] = p2;
+	  seg.si = geom.GetTriangle(trig1).GetFaceNum();
+	  seg.edgenr = i;
+
+	  seg.epgeominfo[0].edgenr = i;
+	  seg.epgeominfo[0].dist = line->GetDist(j);
+	  seg.epgeominfo[1].edgenr = i;
+	  seg.epgeominfo[1].dist = line->GetDist(j+1);
+	  /*
+	  (*testout) << "seg = " 
+		     << "edgenr " << seg.epgeominfo[0].edgenr
+		     << " dist " << seg.epgeominfo[0].dist
+		     << " edgenr " << seg.epgeominfo[1].edgenr
+		     << " dist " << seg.epgeominfo[1].dist << endl;
+	  */
+	  
+	  seg.geominfo[0].trignum = trig1;
+	  seg.geominfo[1].trignum = trig1b;
+
+	  /*
+	  geom.SelectChartOfTriangle (trig1);
+	  hp = hp2 = mesh.Point (seg[0]);
+	  seg.geominfo[0].trignum = geom.Project (hp);
+
+	  (*testout) << "hp = " << hp2 << ", hp proj = " << hp << ", trignum = " << seg.geominfo[0].trignum << endl;
+	  if (Dist (hp, hp2) > 1e-5 || seg.geominfo[0].trignum == 0) 
+	    {
+	      (*testout) << "PROBLEM" << endl;
+	    }
+
+	  geom.SelectChartOfTriangle (trig1b);
+	  hp = hp2 = mesh.Point (seg[1]);
+	  seg.geominfo[1].trignum = geom.Project (hp);
+
+	  (*testout) << "hp = " << hp2 << ", hp proj = " << hp << ", trignum = " << seg.geominfo[1].trignum << endl;
+	  if (Dist (hp, hp2) > 1e-5 || seg.geominfo[1].trignum == 0) 
+	    {
+	      (*testout) << "PROBLEM" << endl;
+	    }
+	  */
+
+
+	  if (Dist (mesh.Point(seg[0]), mesh.Point(seg[1])) < 1e-10)
+	    {
+	      (*testout) << "ERROR: Line segment of length 0" << endl;
+	      (*testout) << "pi1, 2 = " << seg[0] << ", " << seg[1] << endl;
+	      (*testout) << "p1, 2 = " << mesh.Point(seg[0])
+			 << ", " << mesh.Point(seg[1]) << endl;
+	      throw NgException ("Line segment of length 0");
+	    }
+	  
+	  mesh.AddSegment (seg);
+
+
+	  Segment seg2;
+	  seg2[0] = p2;
+	  seg2[1] = p1;
+	  seg2.si = geom.GetTriangle(trig2).GetFaceNum();
+	  seg2.edgenr = i;
+
+	  seg2.epgeominfo[0].edgenr = i;
+	  seg2.epgeominfo[0].dist = line->GetDist(j+1);
+	  seg2.epgeominfo[1].edgenr = i;
+	  seg2.epgeominfo[1].dist = line->GetDist(j);
+	  /*
+	  (*testout) << "seg = " 
+		     << "edgenr " << seg2.epgeominfo[0].edgenr
+		     << " dist " << seg2.epgeominfo[0].dist
+		     << " edgenr " << seg2.epgeominfo[1].edgenr
+		     << " dist " << seg2.epgeominfo[1].dist << endl;
+	  */
+	  
+	  seg2.geominfo[0].trignum = trig2b;
+	  seg2.geominfo[1].trignum = trig2;
+	  
+	  /*
+	  geom.SelectChartOfTriangle (trig2);
+	  hp = hp2 = mesh.Point (seg[0]);
+	  seg2.geominfo[0].trignum = geom.Project (hp);
+
+	  (*testout) << "hp = " << hp2 << ", hp proj = " << hp << ", trignum = " << seg.geominfo[0].trignum << endl;
+	  if (Dist (hp, hp2) > 1e-5 || seg2.geominfo[0].trignum == 0) 
+	    {
+	      (*testout) << "Get GeomInfo PROBLEM" << endl;
+	    }
+
+
+	  geom.SelectChartOfTriangle (trig2b);
+	  hp = hp2 = mesh.Point (seg[1]);
+	  seg2.geominfo[1].trignum = geom.Project (hp);
+	  (*testout) << "hp = " << hp2 << ", hp proj = " << hp << ", trignum = " << seg.geominfo[1].trignum << endl;
+	  if (Dist (hp, hp2) > 1e-5 || seg2.geominfo[1].trignum == 0) 
+	    {
+	      (*testout) << "Get GeomInfo PROBLEM" << endl;
+	    }
+	  */	  
+
+	  mesh.AddSegment (seg2);
+
+
+	  /*
+	  // should be start triangle and end triangle
+	  int bothtrigs1[2] = { trig1, trig1 };
+	  meshing.AddBoundaryElement (p1, p2, sizeof (bothtrigs1), &bothtrigs1);
+	  
+	  int bothtrigs2[2] = { trig2, trig2 };
+	  meshing.AddBoundaryElement (p2, p1, sizeof (bothtrigs2), &bothtrigs2);
+	  */
+	}
+    }
+
+  PopStatus();
+}
+
+
+
+
+void STLSurfaceMeshing1 (STLGeometry & geom,
+			 class Mesh & mesh,
+			 int retrynr);
+
+int STLSurfaceMeshing (STLGeometry & geom,
+		       class Mesh & mesh)
+{
+  int i, j;
+  PrintFnStart("Do Surface Meshing");
+
+  geom.PrepareSurfaceMeshing();
+
+  if (mesh.GetNSeg() == 0)
+    STLFindEdges (geom, mesh);
+
+  int nopen;
+  int outercnt = 20;
+
+  //  mesh.Save ("mesh.edges");
+  
+  for (i = 1; i <= mesh.GetNSeg(); i++)
+    {
+      const Segment & seg = mesh.LineSegment (i);
+      if (seg.geominfo[0].trignum <= 0 || seg.geominfo[1].trignum <= 0)
+	{
+	  (*testout) << "Problem with segment " << i << ": " << seg << endl;
+	}
+    }
+
+
+  do
+    {
+      outercnt--;
+      if (outercnt <= 0)
+	  return MESHING3_OUTERSTEPSEXCEEDED;
+
+      if (multithread.terminate)
+	{
+	  return MESHING3_TERMINATE;
+	}
+
+      mesh.FindOpenSegments();
+      nopen = mesh.GetNOpenSegments();
+
+      if (nopen)
+	{
+	  int trialcnt = 0;
+	  while (nopen && trialcnt <= 5)
+	    {
+	      if (multithread.terminate)
+		{
+		  return MESHING3_TERMINATE;
+		}
+	      trialcnt++;
+	      STLSurfaceMeshing1 (geom, mesh, trialcnt);
+
+	      mesh.FindOpenSegments();
+	      nopen = mesh.GetNOpenSegments();
+
+	      if (nopen)
+		{
+		  geom.ClearMarkedSegs();
+		  for (i = 1; i <= nopen; i++)
+		    {
+		      const Segment & seg = mesh.GetOpenSegment (i);
+		      geom.AddMarkedSeg(mesh.Point(seg[0]),mesh.Point(seg[1]));
+		    }
+
+		  geom.InitMarkedTrigs();
+		  for (i = 1; i <= nopen; i++)
+		    {
+		      const Segment & seg = mesh.GetOpenSegment (i);
+		      geom.SetMarkedTrig(seg.geominfo[0].trignum,1);
+		      geom.SetMarkedTrig(seg.geominfo[1].trignum,1);
+		    }
+
+		  MeshOptimizeSTLSurface optmesh(geom);
+		  optmesh.SetFaceIndex (0);
+		  optmesh.SetImproveEdges (0);
+		  optmesh.SetMetricWeight (0);
+		  
+		  mesh.CalcSurfacesOfNode();
+		  optmesh.EdgeSwapping (mesh, 0);
+		  mesh.CalcSurfacesOfNode();
+		  optmesh.ImproveMesh (mesh, mparam);
+		}
+
+	      mesh.Compress();
+	      mesh.FindOpenSegments();
+	      nopen = mesh.GetNOpenSegments();
+
+	      if (trialcnt <= 5 && nopen)
+		{
+		  mesh.RemoveOneLayerSurfaceElements();
+
+		  if (trialcnt >= 4)
+		    {
+		      mesh.FindOpenSegments();
+		      mesh.RemoveOneLayerSurfaceElements();
+
+		      mesh.FindOpenSegments ();		  
+		      nopen = mesh.GetNOpenSegments();
+		    }
+		}
+	    }
+
+
+	  if (multithread.terminate)
+	    return MESHING3_TERMINATE;
+
+	  if (nopen)
+	    {
+	      
+	      PrintMessage(3,"Meshing failed, trying to refine");
+
+	      mesh.FindOpenSegments ();
+	      nopen = mesh.GetNOpenSegments();
+			  
+	      mesh.FindOpenSegments ();
+	      mesh.RemoveOneLayerSurfaceElements();
+	      mesh.FindOpenSegments ();
+	      mesh.RemoveOneLayerSurfaceElements();
+
+	      // Open edge-segments will be refined !
+	      INDEX_2_HASHTABLE<int> openseght (nopen+1);
+	      for (i = 1; i <= mesh.GetNOpenSegments(); i++)
+		{
+		  const Segment & seg = mesh.GetOpenSegment (i);
+		  INDEX_2 i2(seg[0], seg[1]);
+		  i2.Sort();
+		  openseght.Set (i2, 1);
+		}
+
+	      
+	      mesh.FindOpenSegments ();
+	      mesh.RemoveOneLayerSurfaceElements();
+	      mesh.FindOpenSegments ();
+	      mesh.RemoveOneLayerSurfaceElements();
+	      
+
+	      INDEX_2_HASHTABLE<int> newpht(100);
+
+	      int nsegold = mesh.GetNSeg();
+	      for (i = 1; i <= nsegold; i++)
+		{
+		  Segment seg = mesh.LineSegment(i);
+		  INDEX_2 i2(seg[0], seg[1]);
+		  i2.Sort();
+		  if (openseght.Used (i2))
+		    {
+		      // segment will be split
+		      PrintMessage(7,"Split segment ", int(seg[0]), "-", int(seg[1]));
+	      
+		      Segment nseg1, nseg2;
+		      EdgePointGeomInfo newgi;
+		      
+		      const EdgePointGeomInfo & gi1 = seg.epgeominfo[0];
+		      const EdgePointGeomInfo & gi2 = seg.epgeominfo[1];
+		      
+		      newgi.dist = 0.5 * (gi1.dist + gi2.dist);
+		      newgi.edgenr = gi1.edgenr;
+
+		      int hi;
+		      
+		      Point3d newp;
+		      int newpi;
+		      
+		      if (!newpht.Used (i2))
+			{
+			  newp = geom.GetLine (gi1.edgenr)->
+			    GetPointInDist (geom.GetPoints(), newgi.dist, hi);
+			  newpi = mesh.AddPoint (newp);
+			  newpht.Set (i2, newpi);
+			}
+		      else
+			{
+			  newpi = newpht.Get (i2);
+			  newp = mesh.Point (newpi);
+			}
+
+		      nseg1 = seg;
+		      nseg2 = seg;
+		      nseg1[1] = newpi;
+		      nseg1.epgeominfo[1] = newgi;
+		      
+		      nseg2[0] = newpi;
+		      nseg2.epgeominfo[0] = newgi;
+		      
+		      mesh.LineSegment(i) = nseg1;
+		      mesh.AddSegment (nseg2);
+		      
+		      mesh.RestrictLocalH (Center (mesh.Point(nseg1[0]),
+						   mesh.Point(nseg1[1])),
+					   Dist (mesh.Point(nseg1[0]),
+						 mesh.Point(nseg1[1])));
+		      mesh.RestrictLocalH (Center (mesh.Point(nseg2[0]),
+						   mesh.Point(nseg2[1])),
+					   Dist (mesh.Point(nseg2[0]),
+						 mesh.Point(nseg2[1])));
+		    }
+		}
+
+	    }
+
+	  nopen = -1;
+	}
+    
+      else
+
+	{
+	  PrintMessage(5,"mesh is closed, verifying ...");
+
+	  // no open elements, check wrong elemetns (intersecting..)
+
+
+
+	  PrintMessage(5,"check overlapping");
+	  // 	  mesh.FindOpenElements(); // would leed to locked points
+	  if(mesh.CheckOverlappingBoundary())
+	    {
+	      return MESHING3_BADSURFACEMESH;
+	    }
+
+
+	  geom.InitMarkedTrigs();
+
+	  for (i = 1; i <= mesh.GetNSE(); i++)
+	    if (mesh.SurfaceElement(i).BadElement())
+	      {
+		int trig = mesh.SurfaceElement(i).PNum(1);
+		geom.SetMarkedTrig(trig,1);
+		PrintMessage(7, "overlapping element, will be removed");
+	      }
+	  
+	  
+
+	  Array<Point3d> refpts;
+	  Array<double> refh;
+
+	  // was commented:
+
+	  for (i = 1; i <= mesh.GetNSE(); i++)
+	    if (mesh.SurfaceElement(i).BadElement())
+	      {
+		for (j = 1; j <= 3; j++)
+		  {
+		    refpts.Append (mesh.Point (mesh.SurfaceElement(i).PNum(j)));
+		    refh.Append (mesh.GetH (refpts.Last()) / 2);
+		  }
+		mesh.DeleteSurfaceElement(i);
+	      }
+	  	  
+	  // delete wrong oriented element
+	  for (i = 1; i <= mesh.GetNSE(); i++)
+	    {
+	      const Element2d & el = mesh.SurfaceElement(i);
+	      if (!el.PNum(1))
+		continue;
+
+	      Vec3d n = Cross (Vec3d (mesh.Point(el.PNum(1)), 
+				      mesh.Point(el.PNum(2))),
+			       Vec3d (mesh.Point(el.PNum(1)), 
+				      mesh.Point(el.PNum(3))));
+	      Vec3d ng = geom.GetTriangle(el.GeomInfoPi(1).trignum).Normal();
+	      if (n * ng < 0)
+		{
+		  refpts.Append (mesh.Point (mesh.SurfaceElement(i).PNum(1)));
+		  refh.Append (mesh.GetH (refpts.Last()) / 2);
+		  mesh.DeleteSurfaceElement(i);
+		}
+	    }
+	  // end comments
+
+	  for (i = 1; i <= refpts.Size(); i++)
+	    mesh.RestrictLocalH (refpts.Get(i), refh.Get(i));
+
+	  mesh.RemoveOneLayerSurfaceElements();
+
+	  mesh.Compress();
+	  
+	  mesh.FindOpenSegments ();
+	  nopen = mesh.GetNOpenSegments();
+
+	  /*
+	  if (!nopen)
+	    {
+	      // mesh is still ok
+
+	      void STLSurfaceOptimization (STLGeometry & geom,
+					   class Mesh & mesh,
+					   MeshingParameters & mparam)
+	      
+	    }
+	  */
+	}
+      
+    }
+  while (nopen);
+
+  mesh.Compress();
+  mesh.CalcSurfacesOfNode();
+
+  return MESHING3_OK;
+}
+
+
+
+
+
+
+void STLSurfaceMeshing1 (STLGeometry & geom,
+			 class Mesh & mesh,
+			 int retrynr)
+{
+  int i, j;
+  double h;
+  
+  
+  h = mparam.maxh;
+
+  mesh.FindOpenSegments();
+  
+  Array<int> spiralps(0);
+  spiralps.SetSize(0);
+  for (i = 1; i <= geom.GetNP(); i++)
+    {
+      if (geom.GetSpiralPoint(i)) {spiralps.Append(i);}
+    }
+  
+  PrintMessage(7,"NO spiralpoints = ", spiralps.Size());
+  //int spfound;
+  int sppointnum;
+  int spcnt = 0;
+
+  Array<int> meshsp(mesh.GetNP());
+  for (i = 1; i <= mesh.GetNP(); i++)
+    {
+      meshsp.Elem(i) = 0;
+      for (j = 1; j <= spiralps.Size(); j++)
+	if (Dist2(geom.GetPoint(spiralps.Get(j)), mesh.Point(i)) < 1e-20) 
+	  meshsp.Elem(i) = spiralps.Get(j);
+    }
+
+
+  Array<int> opensegsperface(mesh.GetNFD());
+  for (i = 1; i <= mesh.GetNFD(); i++)
+    opensegsperface.Elem(i) = 0;
+  for (i = 1; i <= mesh.GetNOpenSegments(); i++)
+    {
+      int si = mesh.GetOpenSegment (i).si;
+      if (si >= 1 && si <= mesh.GetNFD())
+	{
+	  opensegsperface.Elem(si)++;
+	}
+      else
+	{
+	  cerr << "illegal face index" << endl;
+	}
+    }
+
+
+  double starttime = GetTime ();
+
+  for (int fnr = 1; fnr <= mesh.GetNFD(); fnr++)
+    if (opensegsperface.Get(fnr))
+      {
+	if (multithread.terminate)
+	  return;
+	
+	PrintMessage(5,"Meshing surface ", fnr, "/", mesh.GetNFD());
+	MeshingSTLSurface meshing (geom, mparam);
+	
+	meshing.SetStartTime (starttime);
+	
+	for (i = 1; i <= mesh.GetNP(); i++)
+	  {
+	    /*
+	      spfound = 0;
+	      for (j = 1; j <= spiralps.Size(); j++)
+	      {
+	      if (Dist2(geom.GetPoint(spiralps.Get(j)),mesh.Point(i)) < 1e-20) 
+		{spfound =  1; sppointnum = spiralps.Get(j);}
+		}
+	    */
+	    sppointnum = 0;
+	    if (i <= meshsp.Size())
+	      sppointnum = meshsp.Get(i);
+	    
+	  //spfound = 0;
+	  if (sppointnum)
+	    {
+	      MultiPointGeomInfo mgi;
+  
+	      int ntrigs = geom.NOTrigsPerPoint(sppointnum);
+	      spcnt++;
+	      
+	      for (j = 0; j < ntrigs; j++)
+		{
+		  PointGeomInfo gi;
+		  gi.trignum = geom.TrigPerPoint(sppointnum, j+1);
+		  mgi.AddPointGeomInfo (gi);
+		}
+	      
+	      // Einfuegen von ConePoint: Point bekommt alle
+	      // Dreiecke (werden dann intern kopiert)
+	      // Ein Segment zum ConePoint muss vorhanden sein !!!
+	      
+	      meshing.AddPoint (mesh.Point(i), i, &mgi);
+	      
+	    }
+	  else
+	    {
+	      meshing.AddPoint (mesh.Point(i), i);
+	    }
+	}
+      
+      
+      for (i = 1; i <= mesh.GetNOpenSegments(); i++)
+	{
+	  const Segment & seg = mesh.GetOpenSegment (i);
+	  if (seg.si == fnr)
+	    meshing.AddBoundaryElement (seg[0], seg[1], seg.geominfo[0], seg.geominfo[1]);
+	}
+      
+      
+      PrintMessage(3,"start meshing, trialcnt = ", retrynr);
+
+      /*
+      (*testout) << "start meshing with h = " << h << endl;
+      */
+      meshing.GenerateMesh (mesh, mparam, h, fnr);  // face index
+
+      extern void Render();
+      Render();
+    }    
+      
+  
+  mesh.CalcSurfacesOfNode();
+}
+
+
+
+void STLSurfaceOptimization (STLGeometry & geom,
+			     class Mesh & mesh,
+			     MeshingParameters & meshparam)
+{
+  PrintFnStart("optimize STL Surface");
+
+
+  MeshOptimizeSTLSurface optmesh(geom);
+  //
+
+  int i;
+  /*
+  for (i = 1; i <= mparam.optsteps2d; i++)
+    {
+      EdgeSwapping (mesh, 1, 1);
+      CombineImprove (mesh, 1);
+      optmesh.ImproveMesh (mesh, 0, 10, 1, 1);
+    }
+  */
+
+  optmesh.SetFaceIndex (0);
+  optmesh.SetImproveEdges (0);
+  optmesh.SetMetricWeight (meshparam.elsizeweight);
+
+  PrintMessage(5,"optimize string = ", meshparam.optimize2d, " elsizew = ", meshparam.elsizeweight);
+
+  for (i = 1; i <= meshparam.optsteps2d; i++)
+    for (size_t j = 1; j <= strlen(meshparam.optimize2d); j++)
+      {
+	if (multithread.terminate)
+	  break;
+
+	//(*testout) << "optimize, before, step = " << meshparam.optimize2d[j-1] << mesh.Point (3679) << endl;
+
+	mesh.CalcSurfacesOfNode();
+	switch (meshparam.optimize2d[j-1])
+	  {
+	  case 's': 
+	    {
+	      optmesh.EdgeSwapping (mesh, 0);
+	      break;
+	    }
+	  case 'S': 
+	    {
+	      optmesh.EdgeSwapping (mesh, 1);
+	      break;
+	    }
+	  case 'm': 
+	    {
+	      optmesh.ImproveMesh(mesh, mparam);
+	      break;
+	    }
+	  case 'c': 
+	    {
+	      optmesh.CombineImprove (mesh);
+	      break;
+	    }
+	  }
+	//(*testout) << "optimize, after, step = " << meshparam.optimize2d[j-1] << mesh.Point (3679) << endl;
+      }
+
+  geom.surfaceoptimized = 1;
+
+  mesh.Compress();
+  mesh.CalcSurfacesOfNode();
+
+
+}
+
+
+
+MeshingSTLSurface :: MeshingSTLSurface (STLGeometry & ageom,
+					const MeshingParameters & mp)
+  : Meshing2(mp, ageom.GetBoundingBox()), geom(ageom)
+{
+  ;
+}
+
+void MeshingSTLSurface :: DefineTransformation (const Point3d & p1, const Point3d & p2,
+						const PointGeomInfo * geominfo,
+						const PointGeomInfo * geominfo2)
+{
+  transformationtrig = geominfo[0].trignum;
+  
+  geom.DefineTangentialPlane(p1, p2, transformationtrig);
+}
+
+void MeshingSTLSurface :: TransformToPlain (const Point3d & locpoint, const MultiPointGeomInfo & gi,
+					    Point2d & plainpoint, double h, int & zone)
+{
+  int trigs[10000];
+  int i;
+
+  if (gi.GetNPGI() >= 9999) 
+    {
+      PrintError("In Transform to plane: increase size of trigs!!!");
+    }
+
+  for (i = 1; i <= gi.GetNPGI(); i++)
+    trigs[i-1] = gi.GetPGI(i).trignum;
+  trigs[gi.GetNPGI()] = 0;
+
+  //  int trig = gi.trignum;
+  //   (*testout) << "locpoint = " << locpoint;
+
+  Point<2> hp2d;
+  geom.ToPlane (locpoint, trigs, hp2d, h, zone, 1);
+  plainpoint = hp2d;
+
+  //  geom.ToPlane (locpoint, NULL, plainpoint, h, zone, 1);
+  /*
+  (*testout) << " plainpoint = " << plainpoint
+	     << " h = " << h 
+	     << endl;
+  */
+}
+
+/*
+int MeshingSTLSurface :: ComputeLineGeoInfo (const Point3d & p1, const Point3d & p2,
+					      int & geoinfosize, void *& geoinfo)
+{
+  static int geomtrig[2] = { 0, 0 };
+
+  Point3d hp;
+  hp = p1;
+  geomtrig[0] = geom.Project (hp);
+
+  hp = p2;
+  geomtrig[1] = geom.Project (hp);
+  
+  geoinfosize = sizeof (geomtrig);
+  geoinfo = &geomtrig;
+
+  if (geomtrig[0] == 0)
+    {
+      return 1;
+    }
+  return 0;
+}
+*/
+
+
+int MeshingSTLSurface :: ComputePointGeomInfo (const Point3d & p, PointGeomInfo & gi)
+{
+  // compute triangle of point,
+  // if non-unique: 0
+
+  Point<3> hp = p;
+  gi.trignum = geom.Project (hp);
+
+  if (!gi.trignum)
+    {
+      return 1;
+    }
+
+  return 0;
+}
+
+
+int MeshingSTLSurface :: 
+ChooseChartPointGeomInfo (const MultiPointGeomInfo & mpgi, 
+			  PointGeomInfo & pgi)
+{
+  int i;
+
+  for (i = 1; i <= mpgi.GetNPGI(); i++)
+    if (geom.TrigIsInOC (mpgi.GetPGI(i).trignum, geom.meshchart))
+      {
+	pgi = mpgi.GetPGI(i);
+	return 0;
+      }
+  /*
+  for (i = 0; i < mpgi.cnt; i++)
+    {
+      //      (*testout) << "d" << endl;
+      if (geom.TrigIsInOC (mpgi.mgi[i].trignum, geom.meshchart))
+	{
+	  pgi = mpgi.mgi[i];
+	  return 0;
+	}
+    }
+  */
+  PrintMessage(7,"INFORM: no gi on chart");
+  pgi.trignum = 1;
+  return 1;
+}
+
+
+
+int MeshingSTLSurface :: 
+IsLineVertexOnChart (const Point3d & p1, const Point3d & p2,
+		     int endpoint, const PointGeomInfo & gi)
+{
+  Vec3d baselinenormal = geom.meshtrignv;
+
+  int lineendtrig = gi.trignum;
+
+  
+  return geom.TrigIsInOC (lineendtrig, geom.meshchart);
+
+  //  Vec3d linenormal = geom.GetTriangleNormal (lineendtrig);
+  //  return ( (baselinenormal * linenormal) > cos (30 * (M_PI/180)) );
+}
+
+void MeshingSTLSurface :: 
+GetChartBoundary (Array<Point2d > & points, 
+		  Array<Point3d > & points3d,
+		  Array<INDEX_2> & lines, double h) const
+{
+  points.SetSize (0);
+  points3d.SetSize (0);
+  lines.SetSize (0);
+  geom.GetMeshChartBoundary (points, points3d, lines, h);
+}
+
+
+
+
+int MeshingSTLSurface :: TransformFromPlain (Point2d & plainpoint,
+					     Point3d & locpoint, 
+					     PointGeomInfo & gi, 
+					     double h)
+{
+  //return 0, wenn alles OK
+  Point<3> hp3d;
+  int res = geom.FromPlane (plainpoint, hp3d, h);
+  locpoint = hp3d;
+  ComputePointGeomInfo (locpoint, gi);
+  return res;
+}
+
+
+int MeshingSTLSurface :: 
+BelongsToActiveChart (const Point3d & p, 
+		      const PointGeomInfo & gi)
+{
+  return (geom.TrigIsInOC(gi.trignum, geom.meshchart) != 0);
+}
+
+
+
+double MeshingSTLSurface :: CalcLocalH (const Point3d & p, double gh) const
+{
+  return gh;
+}
+
+double MeshingSTLSurface :: Area () const
+{
+  return geom.Area();
+}
+
+
+
+
+
+
+MeshOptimizeSTLSurface :: MeshOptimizeSTLSurface (STLGeometry & ageom)
+  : MeshOptimize2d(), geom(ageom)
+{
+  ;
+}
+
+
+void MeshOptimizeSTLSurface :: SelectSurfaceOfPoint (const Point<3> & p,
+						     const PointGeomInfo & gi)
+{
+  //  (*testout) << "sel char: " << gi.trignum << endl;
+  
+  geom.SelectChartOfTriangle (gi.trignum);
+  //  geom.SelectChartOfPoint (p);
+}
+
+
+void MeshOptimizeSTLSurface :: ProjectPoint (INDEX surfind, Point<3> & p) const
+{
+  if (!geom.Project (p))
+    {
+      PrintMessage(7,"project failed");
+      
+      if (!geom.ProjectOnWholeSurface(p)) 
+	{
+	  PrintMessage(7, "project on whole surface failed");
+	}
+    }
+
+  //  geometry.GetSurface(surfind)->Project (p);
+}
+
+void MeshOptimizeSTLSurface :: ProjectPoint2 (INDEX surfind, INDEX surfind2, Point<3> & p) const
+{
+  /*
+  ProjectToEdge ( geometry.GetSurface(surfind), 
+		  geometry.GetSurface(surfind2), p);
+  */
+}
+
+int  MeshOptimizeSTLSurface :: CalcPointGeomInfo(PointGeomInfo& gi, const Point<3> & p3) const
+{
+  Point<3> hp = p3;
+  gi.trignum = geom.Project (hp);
+
+  if (gi.trignum)
+    {
+      return 1;
+    }
+
+  return 0;
+  
+}
+
+void MeshOptimizeSTLSurface :: GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const
+{
+  n = geom.GetChartNormalVector();
+  
+  /*
+  geometry.GetSurface(surfind)->CalcGradient (p, n);
+  n /= n.Length();
+  if (geometry.GetSurface(surfind)->Inverse())
+    n *= -1;
+  */
+}
+  
+
+
+
+
+
+
+
+
+
+RefinementSTLGeometry :: RefinementSTLGeometry (const STLGeometry & ageom)
+  : Refinement(), geom(ageom)
+{
+  ;
+}
+
+RefinementSTLGeometry :: ~RefinementSTLGeometry ()
+{
+  ;
+}
+  
+void RefinementSTLGeometry :: 
+PointBetween  (const Point<3> & p1, const Point<3> & p2, double secpoint,
+	       int surfi, 
+	       const PointGeomInfo & gi1, 
+	       const PointGeomInfo & gi2,
+	       Point<3> & newp, PointGeomInfo & newgi) const
+{
+  newp = p1+secpoint*(p2-p1);
+
+  /*
+  (*testout) << "surf-between: p1 = " << p1 << ", p2 = " << p2
+	     << ", gi = " << gi1 << " - " << gi2 << endl;
+  */
+
+  if (gi1.trignum > 0)
+    {
+      //      ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum);
+
+      Point<3> np1 = newp;
+      Point<3> np2 = newp;
+      ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum);
+      int tn1 = geom.Project (np1);
+
+      ((STLGeometry&)geom).SelectChartOfTriangle (gi2.trignum);
+      int tn2 = geom.Project (np2);
+
+      newgi.trignum = tn1; //urspruengliche version
+      newp = np1;          //urspruengliche version
+
+      if (!newgi.trignum) 
+	{ newgi.trignum = tn2; newp = np2; }
+      if (!newgi.trignum) newgi.trignum = gi1.trignum;
+
+      /*    
+      if (tn1 != 0 && tn2 != 0 && ((STLGeometry&)geom).GetAngle(tn1,tn2) < M_PI*0.05)	{
+	  newgi.trignum = tn1;
+	  newp = np1;
+	}
+      else
+	{
+	  newp = ((STLGeometry&)geom).PointBetween(p1, gi1.trignum, p2, gi2.trignum);
+	  tn1 = ((STLGeometry&)geom).Project(newp);
+	  newgi.trignum = tn1;
+
+	  if (!tn1) 
+	    {
+	      newp = Center (p1, p2);
+	      newgi.trignum = 0;
+	      
+	    }
+	}
+      */
+    }
+  else
+    {
+      //      (*testout) << "WARNING: PointBetween got geominfo = 0" << endl;
+      newp =  p1+secpoint*(p2-p1);
+      newgi.trignum = 0;
+    }
+     
+  //  (*testout) << "newp = " << newp << ", ngi = " << newgi << endl;
+}
+
+void RefinementSTLGeometry ::
+PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+	      int surfi1, int surfi2, 
+	      const EdgePointGeomInfo & gi1, 
+	      const EdgePointGeomInfo & gi2,
+	      Point<3> & newp, EdgePointGeomInfo & newgi) const
+{
+  /*
+  (*testout) << "edge-between: p1 = " << p1 << ", p2 = " << p2
+	     << ", gi1,2 = " << gi1 << ", " << gi2 << endl;
+  */
+  /*
+  newp = Center (p1, p2);
+  ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum);
+  newgi.trignum = geom.Project (newp);
+  */
+  int hi;
+  newgi.dist = (1.0-secpoint) * gi1.dist + secpoint*gi2.dist;
+  newgi.edgenr = gi1.edgenr;
+
+  /*
+  (*testout) << "p1 = " << p1 << ", p2 = " << p2 << endl;
+  (*testout) << "refedge: " << gi1.edgenr
+	     << " d1 = " << gi1.dist << ", d2 = " << gi2.dist << endl;
+  */
+  newp = geom.GetLine (gi1.edgenr)->GetPointInDist (geom.GetPoints(), newgi.dist, hi);
+
+  //  (*testout) << "newp = " << newp << endl;
+}
+
+
+void RefinementSTLGeometry :: ProjectToSurface (Point<3> & p, int surfi) const
+{
+  cout << "RefinementSTLGeometry :: ProjectToSurface not implemented!" << endl;
+};
+
+
+void RefinementSTLGeometry :: ProjectToSurface (Point<3> & p, int surfi,
+						PointGeomInfo & gi) const
+{
+  ((STLGeometry&)geom).SelectChartOfTriangle (gi.trignum);
+  gi.trignum = geom.Project (p);
+  //  if (!gi.trignum) 
+  //    cout << "projectSTL failed" << endl;
+};
+
+ 
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/meshstlsurface.hpp b/contrib/Netgen/libsrc/stlgeom/meshstlsurface.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e678439b4eb4ee2bc5b5e2340dc861c4fe2c63a
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/meshstlsurface.hpp
@@ -0,0 +1,121 @@
+#ifndef FILE_MESHSTLSURF
+#define FILE_MESHSTLSURF
+
+/* *************************************************************************/
+/* File:   meshstlsurf.hpp                                                 */
+/* Author: Johannes Gerstmayr, Joachim Schoeberl                           */
+/* Date:   01. Aug. 99                                                     */
+/* *************************************************************************/
+
+/*
+
+The interface between mesh generation and stl geometry
+
+*/
+
+
+/// 
+class MeshingSTLSurface : public Meshing2
+{
+  ///
+  STLGeometry & geom;
+  ///
+  int transformationtrig;
+public:
+  ///
+  MeshingSTLSurface (STLGeometry & ageom, const MeshingParameters & mp);
+
+protected:
+  ///
+  virtual void DefineTransformation (const Point3d & p1, const Point3d & p2,
+				     const PointGeomInfo * geominfo1,
+				     const PointGeomInfo * geominfo2);
+  ///
+  virtual void TransformToPlain (const Point3d & locpoint, const MultiPointGeomInfo & geominfo,
+      Point2d & plainpoint, double h, int & zone);
+  ///
+  virtual int TransformFromPlain (Point2d & plainpoint,
+				  Point3d & locpoint, 
+				  PointGeomInfo & gi,
+				  double h);
+  ///
+  virtual int BelongsToActiveChart (const Point3d & p, 
+				    const PointGeomInfo & gi);
+
+  ///
+  virtual int ComputePointGeomInfo (const Point3d & p, PointGeomInfo & gi);
+  ///
+  virtual int ChooseChartPointGeomInfo (const MultiPointGeomInfo & mpgi, 
+					 PointGeomInfo & pgi);
+
+  ///
+  virtual int IsLineVertexOnChart (const Point3d & p1, const Point3d & p2,
+				   int endpoint, const PointGeomInfo & gi);
+
+  virtual void GetChartBoundary (Array<Point2d > & points, 
+				 Array<Point3d > & poitns3d,
+				 Array<INDEX_2> & lines, double h) const;
+
+  ///
+  virtual double CalcLocalH (const Point3d & p, double gh) const;
+
+  ///
+  virtual double Area () const;
+};
+
+
+
+///
+class MeshOptimizeSTLSurface : public MeshOptimize2d
+  {
+  ///
+    STLGeometry & geom;
+
+public:
+    ///
+    MeshOptimizeSTLSurface (STLGeometry & ageom); 
+   
+    ///
+    virtual void SelectSurfaceOfPoint (const Point<3> & p,
+				       const PointGeomInfo & gi);
+    ///
+    virtual void ProjectPoint (INDEX surfind, Point<3> & p) const;
+    ///
+    virtual void ProjectPoint2 (INDEX surfind, INDEX surfind2, Point<3> & p) const;
+    ///
+    virtual int CalcPointGeomInfo(PointGeomInfo& gi, const Point<3> & p3) const;
+    ///
+    virtual void GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const;
+};
+
+
+
+
+class RefinementSTLGeometry : public Refinement
+{
+  const STLGeometry & geom;
+
+public:
+  RefinementSTLGeometry (const STLGeometry & ageom);
+  virtual ~RefinementSTLGeometry ();
+  
+  virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+			     int surfi, 
+			     const PointGeomInfo & gi1, 
+			     const PointGeomInfo & gi2,
+			     Point<3> & newp, PointGeomInfo & newgi) const;
+
+  virtual void PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint,
+			     int surfi1, int surfi2, 
+			     const EdgePointGeomInfo & ap1, 
+			     const EdgePointGeomInfo & ap2,
+			     Point<3> & newp, EdgePointGeomInfo & newgi) const;
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi) const;
+  virtual void ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & gi) const;
+};
+
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/stlgeom/stlgeom.cpp b/contrib/Netgen/libsrc/stlgeom/stlgeom.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b0cfe87aa53fc5fc5e1c0935a24efb4f5c35a19e
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlgeom.cpp
@@ -0,0 +1,3506 @@
+#include <meshing.hpp>
+
+#include "stlgeom.hpp"
+
+namespace netgen
+{
+
+//globalen searchtree fuer gesamte geometry aktivieren
+int geomsearchtreeon = 0;
+
+int usechartnormal = 1;  
+
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+void STLMeshing (STLGeometry & geom,
+		 Mesh & mesh)
+{
+  geom.Clear();
+  geom.BuildEdges();
+  geom.MakeAtlas(mesh);
+  geom.CalcFaceNums();
+  geom.AddFaceEdges();
+  geom.LinkEdges();
+
+  mesh.ClearFaceDescriptors();
+  for (int i = 1; i <= geom.GetNOFaces(); i++)
+    mesh.AddFaceDescriptor (FaceDescriptor (i, 1, 0, 0));
+}
+
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//+++++++++++++++++++   STL GEOMETRY   ++++++++++++++++++++++++++++
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+  STLGeometry :: STLGeometry()
+  /*
+  : edges(), edgesperpoint(),
+    normals(),  externaledges(),
+    atlas(), chartmark(), 
+    lines(), outerchartspertrig(), vicinity(), markedtrigs(), markedsegs(),
+    lineendpoints(), spiralpoints(), selectedmultiedge()
+  */
+{
+	edgedata = new STLEdgeDataList(*this);
+  externaledges.SetSize(0);
+  Clear();
+  meshchart = 0; // initialize all ?? JS
+
+  if (geomsearchtreeon)
+    searchtree = new Box3dTree (GetBoundingBox().PMin() - Vec3d(1,1,1),
+				GetBoundingBox().PMax() + Vec3d(1,1,1));
+  else
+    searchtree = NULL;
+
+  status = STL_GOOD;
+  statustext = "Good Geometry";
+  smoothedges = NULL;
+}
+
+STLGeometry :: ~STLGeometry()
+{
+  delete edgedata;
+}
+
+void STLGeometry :: Save (string filename) const
+{
+  const char  * cfilename = filename.c_str();
+  if (strlen(cfilename) < 4) 
+    throw NgException ("illegal filename");
+
+  if (strlen(cfilename) > 3 &&
+      strcmp (&cfilename[strlen(cfilename)-3], "stl") == 0)
+    {
+      STLTopology::Save (cfilename);
+    }
+  else if (strlen(cfilename) > 4 &&
+	   strcmp (&cfilename[strlen(cfilename)-4], "stlb") == 0)
+    {
+      SaveBinary (cfilename,"Binary STL Geometry");
+      
+    }
+  else if (strlen(cfilename) > 4 &&
+	   strcmp (&cfilename[strlen(cfilename)-4], "stle") == 0)
+    {
+      SaveSTLE (cfilename);
+    }
+}
+
+
+
+int STLGeometry :: GenerateMesh (Mesh*& mesh, MeshingParameters & mparam,
+				 int perfstepsstart, int perfstepsend)
+{
+  return STLMeshingDummy (this, mesh, mparam, perfstepsstart, perfstepsend);
+}
+
+
+const Refinement & STLGeometry :: GetRefinement () const
+{
+  return RefinementSTLGeometry (*this);
+}
+
+
+
+void STLGeometry :: STLInfo(double* data)
+{
+  data[0] = GetNT();
+
+  Box<3> b = GetBoundingBox();
+  data[1] = b.PMin()(0);
+  data[2] = b.PMax()(0);
+  data[3] = b.PMin()(1);
+  data[4] = b.PMax()(1);
+  data[5] = b.PMin()(2);
+  data[6] = b.PMax()(2);
+
+  int i;
+ 
+  int cons = 1;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      if (NONeighbourTrigs(i) != 3) {cons = 0;}
+    }
+  data[7] = cons;
+}
+
+void STLGeometry :: MarkNonSmoothNormals()
+{
+
+  PrintFnStart("Mark Non-Smooth Normals");
+
+  int i,j;
+
+  markedtrigs.SetSize(GetNT());
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      SetMarkedTrig(i, 0);
+    }
+
+  double dirtyangle = stlparam.yangle/180.*M_PI;
+
+  int cnt = 0;
+  int lp1,lp2;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      for (j = 1; j <= NONeighbourTrigs(i); j++)
+	{
+	  if (GetAngle(i, NeighbourTrig(i,j)) > dirtyangle)
+	    {
+	      GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)), lp1, lp2);
+	      if (!IsEdge(lp1,lp2))
+		{
+		  if (!IsMarkedTrig(i)) {SetMarkedTrig(i,1); cnt++;}
+		}
+	    }
+	}
+    }
+
+  PrintMessage(5,"marked ",cnt," non-smooth trig-normals");
+
+}
+
+void STLGeometry :: SmoothNormals()
+{
+  multithread.terminate = 0;
+
+  //  UseExternalEdges();
+
+  BuildEdges();
+
+
+  DenseMatrix m(3), hm(3);
+  Vector rhs(3), sol(3), hv(3), hv2(3);
+
+  Vec<3> ri;
+
+  double wnb = stldoctor.smoothnormalsweight;   // neigbour normal weight
+  double wgeom = 1-wnb;   // geometry normal weight
+
+
+  // minimize 
+  //  wgeom sum_T  \sum ri  \| ri^T (n - n_geom) \|^2  
+  //  + wnb sum_SE  \| ri x (n - n_nb) \|^2
+  
+  int i, j, k, l;
+  int nt = GetNT();
+  
+  PushStatusF("Smooth Normals");
+    
+  //int testmode;
+
+  for (i = 1; i <= nt; i++)
+    {
+
+      SetThreadPercent( 100.0 * (double)i / (double)nt);
+
+      const STLTriangle & trig = GetTriangle (i);
+      
+      m = 0;
+      rhs = 0;
+
+      // normal of geometry:
+      Vec<3> ngeom = trig.GeomNormal(points);
+      ngeom.Normalize();
+
+      for (j = 1; j <= 3; j++)
+	{ 
+	  int pi1 = trig.PNumMod (j);
+	  int pi2 = trig.PNumMod (j+1);
+
+	  // edge vector
+	  ri = GetPoint (pi2) - GetPoint (pi1);
+	  
+	  for (k = 0; k < 3; k++)
+	    for (l = 0; l < 3; l++)
+	      hm.Elem(k+1, l+1) = wgeom * ri(k) * ri(l);
+	  
+	  
+	  for (k = 0; k < 3; k++)
+	    hv(k) = ngeom(k);
+	  
+	  hm.Mult (hv, hv2);
+	  /*
+	  if (testmode)
+	    (*testout) << "add vec " << hv2 << endl 
+		       << " add m " << hm << endl;
+	  */
+	  rhs.Add (1, hv2);
+	  m += hm;
+
+
+	  int nbt = 0;
+	  int fp1,fp2;
+	  for (k = 1; k <= NONeighbourTrigs(i); k++)
+	    {
+	      trig.GetNeighbourPoints(GetTriangle(NeighbourTrig(i, k)),fp1,fp2);
+	      if (fp1 == pi1 && fp2 == pi2)
+		{
+		  nbt = NeighbourTrig(i, k);
+		}
+	    }
+
+	  if (!nbt)
+	    {
+	      cerr << "ERROR: stlgeom::Smoothnormals, nbt = 0" << endl;
+	    }
+
+	  // smoothed normal
+	  Vec<3> nnb = GetTriangle(nbt).Normal();   // neighbour normal
+	  nnb.Normalize();
+
+	  if (!IsEdge(pi1,pi2)) 
+	    {
+	      double lr2 = ri * ri;
+	      for (k = 0; k < 3; k++)
+		{
+		  for (l = 0; l < k; l++)
+		    {
+		      hm.Elem(k+1, l+1) = -wnb * ri(k) * ri(l);
+		      hm.Elem(l+1, k+1) = -wnb * ri(k) * ri(l);
+		    }
+		  
+		  hm.Elem(k+1, k+1) = wnb * (lr2 - ri(k) * ri(k));
+		}
+	      
+	      for (k = 0; k < 3; k++)
+		hv(k) = nnb(k);
+	      
+	      hm.Mult (hv, hv2);
+	      /*
+	      if (testmode)
+		(*testout) << "add nb vec " << hv2 << endl 
+			   << " add nb m " << hm << endl;
+	      */
+
+	      rhs.Add (1, hv2);
+	      m += hm;
+	    }
+	}
+
+      m.Solve (rhs, sol);
+      Vec3d newn(sol(0), sol(1), sol(2));
+      newn /= (newn.Length() + 1e-24);      
+
+      GetTriangle(i).SetNormal(newn);
+      // setnormal (sol);
+    }
+
+  /*
+  for (i = 1; i <= nt; i++)
+    SetMarkedTrig(i, 0);      		
+
+
+
+  int crloop;
+  for (crloop = 1; crloop <= 3; crloop++)
+    {
+
+  // find critical:
+
+  Array<INDEX_2> critpairs;
+  for (i = 1; i <= nt; i++)
+    {
+      const STLTriangle & trig = GetTriangle (i);
+      
+      Vec3d ngeom = GetTriangleNormal (i); // trig.Normal(points);
+      ngeom /= (ngeom.Length() + 1e-24);
+
+      for (j = 1; j <= 3; j++)
+	{ 
+	  int pi1 = trig.PNumMod (j);
+	  int pi2 = trig.PNumMod (j+1);
+
+	  int nbt = 0;
+	  int fp1,fp2;
+	  for (k = 1; k <= NONeighbourTrigs(i); k++)
+	    {
+	      trig.GetNeighbourPoints(GetTriangle(NeighbourTrig(i, k)),fp1,fp2);
+	      if (fp1 == pi1 && fp2 == pi2)
+		{
+		  nbt = NeighbourTrig(i, k);
+		}
+	    }
+	  
+	  if (!nbt)
+	    {
+	      cerr << "ERROR: stlgeom::Smoothnormals, nbt = 0" << endl;
+	    }
+
+	  Vec3d nnb = GetTriangleNormal(nbt);   // neighbour normal
+	  nnb /= (nnb.Length() + 1e-24);
+
+	  if (!IsEdge(pi1,pi2)) 
+	    {
+	      if (Angle (nnb, ngeom) > 150 * M_PI/180)
+		{
+		  SetMarkedTrig(i, 1);      		
+		  SetMarkedTrig(nbt, 1);      		
+		  critpairs.Append (INDEX_2 (i, nbt));
+		}
+	    }
+
+	}
+    }
+
+  if (!critpairs.Size())
+    {
+      break;
+    }
+
+  if (critpairs.Size())
+    {
+
+      Array<int> friends;
+      double area1 = 0, area2 = 0;
+
+      for (i = 1; i <= critpairs.Size(); i++)
+	{
+	  int tnr1 = critpairs.Get(i).I1();
+	  int tnr2 = critpairs.Get(i).I2();
+	  (*testout) << "t1 = " << tnr1 << ", t2 = " << tnr2
+		     << " angle = " << Angle (GetTriangleNormal (tnr1),
+					      GetTriangleNormal (tnr2))
+		     << endl;
+
+	  // who has more friends ?
+	  int side;
+	  area1 = 0;
+	  area2 = 0;
+	  for (side = 1; side <= 2; side++)
+	    {
+	      friends.SetSize (0);
+	      friends.Append ( (side == 1) ? tnr1 : tnr2);
+
+	      for (j = 1; j <= 3; j++)
+		{
+		  int fsize = friends.Size();
+		  for (k = 1; k <= fsize; k++)
+		    {
+		      int testtnr = friends.Get(k);
+		      Vec3d ntt = GetTriangleNormal(testtnr);
+		      ntt /= (ntt.Length() + 1e-24);
+		      
+		      for (l = 1; l <= NONeighbourTrigs(testtnr); l++)
+			{
+			  int testnbnr = NeighbourTrig(testtnr, l);
+			  Vec3d nbt = GetTriangleNormal(testnbnr);
+			  nbt /= (nbt.Length() + 1e-24);
+
+			  if (Angle (nbt, ntt) < 15 * M_PI/180)
+			    {
+			      int ii;
+			      int found = 0;
+			      for (ii = 1; ii <= friends.Size(); ii++)
+				{
+				  if (friends.Get(ii) == testnbnr)
+				    {
+				      found = 1;
+				      break;
+				    }
+				}
+			      if (!found)
+				friends.Append (testnbnr);
+			    }
+			}
+		    }
+		}
+
+	      // compute area:
+	      for (k = 1; k <= friends.Size(); k++)
+		{
+		  double area = 
+		    GetTriangle (friends.Get(k)).Area(points);
+
+		  if (side == 1)
+		    area1 += area;
+		  else
+		    area2 += area;
+		}
+	      
+	    }
+
+	  (*testout) << "area1 = " << area1 << " area2 = " << area2 << endl;
+	  if (area1 < 0.1 * area2)
+	    {
+	      Vec3d n = GetTriangleNormal (tnr1);
+	      n *= -1;
+	      SetTriangleNormal(tnr1, n);
+	    }
+	  if (area2 < 0.1 * area1)
+	    {
+	      Vec3d n = GetTriangleNormal (tnr2);
+	      n *= -1;
+	      SetTriangleNormal(tnr2, n);
+	    }
+	}
+    }
+    }
+  */
+
+  calcedgedataanglesnew = 1;
+  PopStatus();
+}
+
+
+int STLGeometry :: AddEdge(int ap1, int ap2)
+{
+  STLEdge e(ap1,ap2);
+  e.SetLeftTrig(GetLeftTrig(ap1,ap2));
+  e.SetRightTrig(GetRightTrig(ap1,ap2));
+  return edges.Append(e);
+}
+
+void STLGeometry :: STLDoctorConfirmEdge()
+{
+  StoreEdgeData();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
+    {
+      if (stldoctor.selectmode == 1)
+	{
+	  int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+	  int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+	  edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus (ED_CONFIRMED);
+	}
+      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+	{
+	  int i;
+	  for (i = 1; i <= selectedmultiedge.Size(); i++)
+	    {
+	      int ap1 = selectedmultiedge.Get(i).i1;
+	      int ap2 = selectedmultiedge.Get(i).i2;
+	      edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus (ED_CONFIRMED);
+	    }
+	}
+    }
+}
+
+void STLGeometry :: STLDoctorCandidateEdge()
+{
+  StoreEdgeData();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
+    {
+      if (stldoctor.selectmode == 1)
+	{
+	  int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+	  int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+	  edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus (ED_CANDIDATE);
+	}
+      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+	{
+	  int i;
+	  for (i = 1; i <= selectedmultiedge.Size(); i++)
+	    {
+	      int ap1 = selectedmultiedge.Get(i).i1;
+	      int ap2 = selectedmultiedge.Get(i).i2;
+	      edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus (ED_CANDIDATE);
+	    }
+	}
+    }
+}
+
+void STLGeometry :: STLDoctorExcludeEdge()
+{
+  StoreEdgeData();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
+    {
+      if (stldoctor.selectmode == 1)
+	{
+	  int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+	  int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+	  edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus(ED_EXCLUDED);
+	}
+      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+	{
+	  int i;
+	  for (i = 1; i <= selectedmultiedge.Size(); i++)
+	    {
+	      int ap1 = selectedmultiedge.Get(i).i1;
+	      int ap2 = selectedmultiedge.Get(i).i2;
+	      edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus(ED_EXCLUDED);
+	    }
+	}
+    }
+}
+
+void STLGeometry :: STLDoctorUndefinedEdge()
+{
+  StoreEdgeData();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
+    {
+      if (stldoctor.selectmode == 1)
+	{
+	  int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+	  int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+	  edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus(ED_UNDEFINED);
+	}
+      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+	{
+	  int i;
+	  for (i = 1; i <= selectedmultiedge.Size(); i++)
+	    {
+	      int ap1 = selectedmultiedge.Get(i).i1;
+	      int ap2 = selectedmultiedge.Get(i).i2;
+	      edgedata->Elem(edgedata->GetEdgeNum(ap1,ap2)).SetStatus(ED_UNDEFINED);
+	    }
+	}
+    }
+}
+
+void STLGeometry :: STLDoctorSetAllUndefinedEdges()
+{
+  edgedata->ResetAll();
+}
+
+void STLGeometry :: STLDoctorEraseCandidateEdges()
+{
+  StoreEdgeData();
+  edgedata->ChangeStatus(ED_CANDIDATE, ED_UNDEFINED);
+}
+
+void STLGeometry :: STLDoctorConfirmCandidateEdges()
+{
+  StoreEdgeData();
+  edgedata->ChangeStatus(ED_CANDIDATE, ED_CONFIRMED);
+}
+
+void STLGeometry :: STLDoctorConfirmedToCandidateEdges()
+{
+  StoreEdgeData();
+  edgedata->ChangeStatus(ED_CONFIRMED, ED_CANDIDATE);
+}
+
+void STLGeometry :: STLDoctorDirtyEdgesToCandidates()
+{
+  StoreEdgeData();
+}
+
+void STLGeometry :: STLDoctorLongLinesToCandidates()
+{
+  StoreEdgeData();
+}
+
+twoint STLGeometry :: GetNearestSelectedDefinedEdge()
+{
+  Point<3> pestimate = Center(GetTriangle(GetSelectTrig()).center,
+  			     GetPoint(GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig())));
+    //Point3d pestimate = GetTriangle(GetSelectTrig()).center;
+
+  int i, j, en;
+  Array<int> vic;
+  GetVicinity(GetSelectTrig(),4,vic);
+  
+
+  twoint fedg;
+  fedg.i1 = 0;
+  fedg.i2 = 0;
+  double mindist = 1E50;
+  double dist;
+  Point<3> p;
+
+  for (i = 1; i <= vic.Size(); i++)
+  {
+    const STLTriangle& t = GetTriangle(vic.Get(i));
+    for (j = 1; j <= 3; j++)
+      {
+	en = edgedata->GetEdgeNum(t.PNum(j),t.PNumMod(j+1));
+	if (edgedata->Get(en).GetStatus() != ED_UNDEFINED)
+	  {
+	    p = pestimate;
+	    dist = GetDistFromLine(GetPoint(t.PNum(j)),GetPoint(t.PNumMod(j+1)),p);
+	    if (dist < mindist)
+	      {
+		mindist = dist;
+		fedg.i1 = t.PNum(j);
+		fedg.i2 = t.PNumMod(j+1);
+	      }
+	  }
+      }
+  }
+  return fedg;
+}
+ 
+void STLGeometry :: BuildSelectedMultiEdge(twoint ep)
+{
+  if (edgedata->Size() == 0 || 
+      !GetEPPSize()) 
+    {
+      return; 
+    }
+
+  selectedmultiedge.SetSize(0);
+  int tenum = GetTopEdgeNum (ep.i1, ep.i2);
+
+  if (edgedata->Get(tenum).GetStatus() == ED_UNDEFINED)
+    {
+      twoint epnew = GetNearestSelectedDefinedEdge();
+      if (epnew.i1) 
+	{
+	  ep = epnew;
+	  tenum = GetTopEdgeNum (ep.i1, ep.i2);
+	}
+    }
+
+  selectedmultiedge.Append(twoint(ep));
+
+  if (edgedata->Get(tenum).GetStatus() == ED_UNDEFINED)
+    {
+      return;
+    }
+
+  edgedata->BuildLineWithEdge(ep.i1,ep.i2,selectedmultiedge);
+}
+
+void STLGeometry :: BuildSelectedEdge(twoint ep)
+{
+  if (edgedata->Size() == 0 || 
+      !GetEPPSize()) 
+    {
+      return; 
+    }
+
+  selectedmultiedge.SetSize(0);
+
+  selectedmultiedge.Append(twoint(ep));
+}
+
+void STLGeometry :: BuildSelectedCluster(twoint ep)
+{
+  if (edgedata->Size() == 0 || 
+      !GetEPPSize()) 
+    {
+      return; 
+    }
+
+  selectedmultiedge.SetSize(0);
+
+  int tenum = GetTopEdgeNum (ep.i1, ep.i2);
+
+  if (edgedata->Get(tenum).GetStatus() == ED_UNDEFINED)
+    {
+      twoint epnew = GetNearestSelectedDefinedEdge();
+      if (epnew.i1) 
+	{
+	  ep = epnew;
+	  tenum = GetTopEdgeNum (ep.i1, ep.i2);
+	}
+    }
+
+  selectedmultiedge.Append(twoint(ep));
+
+  if (edgedata->Get(tenum).GetStatus() == ED_UNDEFINED)
+    {
+      return;
+    }
+
+  edgedata->BuildClusterWithEdge(ep.i1,ep.i2,selectedmultiedge);
+}
+
+void STLGeometry :: ImportEdges()
+{
+  StoreEdgeData();
+
+  PrintMessage(5, "import edges from file 'edges.ng'");
+  ifstream fin("edges.ng");
+
+  int ne;
+  fin >> ne;
+
+  Array<Point<3> > eps;
+
+  int i;
+  Point<3> p;
+  for (i = 1; i <= 2*ne; i++)
+    {
+      fin >> p(0); 
+      fin >> p(1); 
+      fin >> p(2);
+      eps.Append(p);
+    }
+  AddEdges(eps);
+}
+
+void STLGeometry :: AddEdges(const Array<Point<3> >& eps)
+{
+  int i;
+  int ne = eps.Size()/2;
+  
+  Array<int> epsi;
+  Box<3> bb = GetBoundingBox();
+  bb.Increase(1);
+
+  Point3dTree ptree (bb.PMin(), 
+			 bb.PMax());
+  Array<int> pintersect;
+
+  double gtol = GetBoundingBox().Diam()/1.E10;
+  Point<3> p;
+
+  for (i = 1; i <= GetNP(); i++)
+    {
+      p = GetPoint(i);
+      ptree.Insert (p, i);
+    }
+  
+  int error = 0;
+  for (i = 1; i <= 2*ne; i++)
+    {
+      p = eps.Get(i);
+      Point3d pmin = p - Vec3d (gtol, gtol, gtol);
+      Point3d pmax = p + Vec3d (gtol, gtol, gtol);
+	  
+      ptree.GetIntersecting (pmin, pmax, pintersect);
+      if (pintersect.Size() > 1)
+	{
+	  PrintError("Found too much points in epsilon-dist");
+	  error = 1;
+	}
+      else if (pintersect.Size() == 0)
+	{
+	  error = 1;
+	  PrintError("edgepoint does not exist!");
+	  PrintMessage(5,"p=",Point3d(eps.Get(i)));
+	}
+      else
+	{
+	  epsi.Append(pintersect.Get(1));
+	}
+    }
+
+  if (error) return;
+
+  int en;
+  for (i = 1; i <= ne; i++)
+    {
+      if (epsi.Get(2*i-1) == epsi.Get(2*i)) {PrintError("Edge with zero length!");}
+      else 
+	{
+	  en = edgedata->GetEdgeNum(epsi.Get(2*i-1),epsi.Get(2*i));
+	  edgedata->Elem(en).SetStatus (ED_CONFIRMED);
+	}
+    }
+
+}
+
+
+
+void STLGeometry :: ImportExternalEdges(const char * filename)
+{
+  //AVL edges!!!!!!
+
+  ifstream inf (filename);
+  char ch;
+  //int cnt = 0;
+  int records, units, i, j;
+  PrintFnStart("Import edges from ",filename);
+  
+  const int flen=30;
+  char filter[flen+1];
+  filter[flen] = 0;
+  char buf[20];
+
+  Array<Point3d> importpoints;
+  Array<int> importlines;
+  Array<int> importpnums;
+
+  while (inf.good())
+    {
+      inf.get(ch);
+      //      (*testout) << cnt << ": " << ch << endl;
+      
+      for (i = 0; i < flen; i++)
+	filter[i] = filter[i+1];
+      filter[flen-1] = ch;
+      //      (*testout) << filter << endl;
+
+      if (strcmp (filter+flen-7, "RECORDS") == 0)
+	{
+	  inf.get(ch);  // '='
+	  inf >> records;
+	}
+      if (strcmp (filter+flen-5, "UNITS") == 0)
+	{
+	  inf.get(ch);  // '='
+	  inf >> units;
+	}
+
+      if (strcmp (filter+flen-17, "EDGE NODE NUMBERS") == 0)
+	{
+	  int nodenr;
+	  importlines.SetSize (units);
+	  for (i = 1; i <= units; i++)
+	    {
+	      inf >> nodenr;
+	      importlines.Elem(i) = nodenr;
+	      //	      (*testout) << nodenr << endl;
+	    }
+	}
+
+      if (strcmp (filter+flen-23, "EDGE POINT COORD IN DIR") == 0)
+	{
+	  int coord;
+
+	  inf >> coord;
+	  
+	  importpoints.SetSize (units);
+
+	  inf >> ch;
+	  inf.putback (ch);
+
+	  for (i = 1; i <= units; i++)
+	    {
+	      for (j = 0; j < 12; j++)
+		inf.get (buf[j]);
+	      buf[12] = 0;
+
+	      importpoints.Elem(i).X(coord) = 1000 * atof (buf);
+	    }
+	}
+    }
+
+  /*
+  (*testout) << "lines: " << endl;
+  for (i = 1; i <= importlines.Size(); i++)
+    (*testout) << importlines.Get(i) << endl;
+  (*testout) << "points: " << endl;
+  for (i = 1; i <= importpoints.Size(); i++)
+    (*testout) << importpoints.Get(i) << endl;
+  */
+
+
+
+  importpnums.SetSize (importpoints.Size());
+  
+
+  Box3d bb (GetBoundingBox().PMin() + Vec3d (-1,-1,-1),
+	    GetBoundingBox().PMax() + Vec3d (1, 1, 1));
+
+  Point3dTree ptree (bb.PMin(), 
+			 bb.PMax());
+
+
+  PrintMessage(7,"stl - bb: ",bb.PMin(), " - ", bb.PMax());
+  
+  Box3d ebb;
+  ebb.SetPoint (importpoints.Get(1));
+  for (i = 1; i <= importpoints.Size(); i++)
+    ebb.AddPoint (importpoints.Get(i));
+  PrintMessage(7,"edgep - bb: ", ebb.PMin(), " - ", ebb.PMax());
+
+  Array<int> pintersect;
+
+  double gtol = GetBoundingBox().Diam()/1.E6;
+
+  for (i = 1; i <= GetNP(); i++)
+    {
+      Point3d p = GetPoint(i);
+      //      (*testout) << "stlpt: " << p << endl;
+      ptree.Insert (p, i);
+    }
+  
+
+  for (i = 1; i <= importpoints.Size(); i++)
+    {
+      Point3d p = importpoints.Get(i);
+      Point3d pmin = p - Vec3d (gtol, gtol, gtol);
+      Point3d pmax = p + Vec3d (gtol, gtol, gtol);
+	  
+      ptree.GetIntersecting (pmin, pmax, pintersect);
+      if (pintersect.Size() > 1)
+	{
+	  importpnums.Elem(i) = 0;
+	  PrintError("Found too many points in epsilon-dist");
+	}
+      else if (pintersect.Size() == 0)
+	{
+	  importpnums.Elem(i) = 0;
+	  PrintError("Edgepoint does not exist!");
+	}
+      else
+	{
+	  importpnums.Elem(i) = pintersect.Get(1);
+	}
+    }
+
+  //  if (!error) 
+    {
+      PrintMessage(7,"found all edge points in stl file");
+
+
+      StoreEdgeData();
+
+      int oldp = 0;
+
+      for (i = 1; i <= importlines.Size(); i++)
+	{
+	  int newp = importlines.Get(i);
+	  if (!importpnums.Get(abs(newp)))
+	    newp = 0;
+
+	  if (oldp && newp)
+	    {
+	      int en = edgedata->GetEdgeNum(importpnums.Get(oldp), 
+					   importpnums.Get(abs(newp)));
+	      edgedata->Elem(en).SetStatus (ED_CONFIRMED);
+	    }
+	  
+	  if (newp < 0)
+	    oldp = 0;
+	  else
+	    oldp = newp;
+	}
+    }
+
+
+}
+
+
+
+void STLGeometry :: ExportEdges()
+{
+  PrintFnStart("Save edges to file 'edges.ng'");
+
+  ofstream fout("edges.ng");
+  fout.precision(16);
+
+  int n = edgedata->GetNConfEdges();
+  
+  fout << n << endl;
+
+  int i;
+  for (i = 1; i <= edgedata->Size(); i++)
+    {
+      if (edgedata->Get(i).GetStatus() == ED_CONFIRMED)
+	{
+	  const STLTopEdge & e = edgedata->Get(i);
+	  fout << GetPoint(e.PNum(1))(0) << " " << GetPoint(e.PNum(1))(1) << " " << GetPoint(e.PNum(1))(2) << endl;
+	  fout << GetPoint(e.PNum(2))(0) << " " << GetPoint(e.PNum(2))(1) << " " << GetPoint(e.PNum(2))(2) << endl;
+	}
+    }
+
+}
+
+void STLGeometry :: LoadEdgeData(const char* file)
+{
+  StoreEdgeData();
+
+  PrintFnStart("Load edges from file '", file, "'");
+  ifstream fin(file);
+
+  edgedata->Read(fin);
+
+  //  calcedgedataanglesnew = 1;
+}
+
+void STLGeometry :: SaveEdgeData(const char* file)
+{
+  PrintFnStart("save edges to file '", file, "'");
+  ofstream fout(file);
+
+  edgedata->Write(fout);
+}
+
+
+
+
+
+
+
+/*
+void STLGeometry :: SaveExternalEdges()
+{
+  ofstream fout("externaledgesp3.ng");
+  fout.precision(16);
+
+  int n = NOExternalEdges();
+  fout << n << endl;
+
+  int i;
+  for (i = 1; i <= n; i++)
+    {
+      twoint e = GetExternalEdge(i);
+      fout << GetPoint(e.i1)(0) << " " << GetPoint(e.i1)(1) << " " << GetPoint(e.i1)(2) << endl;
+      fout << GetPoint(e.i2)(0) << " " << GetPoint(e.i2)(1) << " " << GetPoint(e.i2)(2) << endl;
+    }
+
+}
+*/
+void STLGeometry :: StoreExternalEdges()
+{
+  storedexternaledges.SetSize(0);
+  undoexternaledges = 1;
+  int i;
+  for (i = 1; i <= externaledges.Size(); i++)
+    {
+      storedexternaledges.Append(externaledges.Get(i));      
+    }
+
+}
+
+void STLGeometry :: UndoExternalEdges()
+{
+  if (!undoexternaledges) 
+    {
+      PrintMessage(1, "undo not further possible!");
+      return;
+    }
+  RestoreExternalEdges();
+  undoexternaledges = 0;
+}
+
+void STLGeometry :: RestoreExternalEdges()
+{
+  externaledges.SetSize(0);
+  int i;
+  for (i = 1; i <= storedexternaledges.Size(); i++)
+    {
+      externaledges.Append(storedexternaledges.Get(i));      
+    }
+
+}
+
+
+void STLGeometry :: AddExternalEdgeAtSelected()
+{
+  StoreExternalEdges();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
+    {
+      int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+      int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+      if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}
+    }
+}
+
+void STLGeometry :: AddClosedLinesToExternalEdges()
+{
+  StoreExternalEdges();
+
+  int i, j;
+  for (i = 1; i <= GetNLines(); i++)
+    {
+      STLLine* l = GetLine(i);
+      if (l->StartP() == l->EndP()) 
+	{
+	  for (j = 1; j < l->NP(); j++)
+	    {
+	      int ap1 = l->PNum(j);
+	      int ap2 = l->PNum(j+1);
+
+	      if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}	      
+	    }
+	}
+    }
+}
+
+void STLGeometry :: AddLongLinesToExternalEdges()
+{
+  StoreExternalEdges();
+
+  double diamfact = stldoctor.dirtytrigfact;
+  double diam = GetBoundingBox().Diam();
+
+  int i, j;
+  for (i = 1; i <= GetNLines(); i++)
+    {
+      STLLine* l = GetLine(i);
+      if (l->GetLength(points) >= diamfact*diam) 
+	{
+	  for (j = 1; j < l->NP(); j++)
+	    {
+	      int ap1 = l->PNum(j);
+	      int ap2 = l->PNum(j+1);
+
+	      if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}	      
+	    }
+	}
+    }
+}
+
+void STLGeometry :: AddAllNotSingleLinesToExternalEdges()
+{
+  StoreExternalEdges();
+
+  int i, j;
+  for (i = 1; i <= GetNLines(); i++)
+    {
+      STLLine* l = GetLine(i);
+      if (GetNEPP(l->StartP()) > 1 || GetNEPP(l->EndP()) > 1) 
+	{
+	  for (j = 1; j < l->NP(); j++)
+	    {
+	      int ap1 = l->PNum(j);
+	      int ap2 = l->PNum(j+1);
+
+	      if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}	      
+	    }
+	}
+    }
+}
+
+void STLGeometry :: DeleteDirtyExternalEdges()
+{
+  //delete single triangle edges and single edge-lines in clusters"
+  StoreExternalEdges();
+
+  int i, j;
+  for (i = 1; i <= GetNLines(); i++)
+    {
+      STLLine* l = GetLine(i);
+      if (l->NP() <= 3 || (l->StartP() == l->EndP() && l->NP() == 4))
+	{
+	  for (j = 1; j < l->NP(); j++)
+	    {
+	      int ap1 = l->PNum(j);
+	      int ap2 = l->PNum(j+1);
+
+	      if (IsExternalEdge(ap1,ap2)) {DeleteExternalEdge(ap1,ap2);}	      
+	    }
+	}
+    }
+}
+
+void STLGeometry :: AddExternalEdgesFromGeomLine()
+{
+  StoreExternalEdges();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
+    {
+      int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+      int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+
+      if (IsEdge(ap1,ap2))
+	{
+	  int edgenum = IsEdgeNum(ap1,ap2);
+	  if (!IsExternalEdge(ap1,ap2)) {AddExternalEdge(ap1,ap2);}
+	  
+	  int noend = 1;
+	  int startp = ap1;
+	  int laste = edgenum;
+	  int np1, np2;
+	  while (noend)
+	    {
+	      if (GetNEPP(startp) == 2)
+		{
+		  if (GetEdgePP(startp,1) != laste) {laste = GetEdgePP(startp,1);}
+		  else {laste = GetEdgePP(startp,2);}
+		  np1 = GetEdge(laste).PNum(1);
+		  np2 = GetEdge(laste).PNum(2);
+		  
+		  if (!IsExternalEdge(np1, np2)) {AddExternalEdge(np1, np2);}
+		  else {noend = 0;}
+		  if (np1 != startp) {startp = np1;}
+		  else {startp = np2;}
+		}
+	      else {noend = 0;}
+	    }
+
+	  startp = ap2;
+	  laste = edgenum;
+	  noend = 1;
+	  while (noend)
+	    {
+	      if (GetNEPP(startp) == 2)
+		{
+		  if (GetEdgePP(startp,1) != laste) {laste = GetEdgePP(startp,1);}
+		  else {laste = GetEdgePP(startp,2);}
+		  np1 = GetEdge(laste).PNum(1);
+		  np2 = GetEdge(laste).PNum(2);
+		  
+		  if (!IsExternalEdge(np1, np2)) {AddExternalEdge(np1, np2);}
+		  else {noend = 0;}
+		  if (np1 != startp) {startp = np1;}
+		  else {startp = np2;}
+		}
+	      else {noend = 0;}
+	    }
+	  
+	}
+
+    }
+  
+}
+
+void STLGeometry :: ClearEdges()
+{
+  edgesfound = 0;
+  edges.SetSize(0);
+  //edgedata->SetSize(0);
+  // externaledges.SetSize(0);
+  edgesperpoint.SetSize(0);
+  undoexternaledges = 0;
+
+}
+
+void STLGeometry :: STLDoctorBuildEdges()
+{
+  //  if (!trigsconverted) {return;}
+  ClearEdges();
+
+  meshlines.SetSize(0);
+  FindEdgesFromAngles();
+}
+
+void STLGeometry :: DeleteExternalEdgeAtSelected()
+{
+  StoreExternalEdges();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
+    {
+      int ap1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+      int ap2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+      if (IsExternalEdge(ap1,ap2)) {DeleteExternalEdge(ap1,ap2);}
+    }
+}
+
+void STLGeometry :: DeleteExternalEdgeInVicinity()
+{
+  StoreExternalEdges();
+  if (!stldoctor.showvicinity || vicinity.Size() != GetNT()) {return;}
+
+  int i, j, ap1, ap2;
+  
+  for (i = 1; i <= GetNT(); i++)
+    {
+      if (vicinity.Elem(i))
+	{
+	  for (j = 1; j <= 3; j++)
+	    {
+	      ap1 = GetTriangle(i).PNum(j);
+	      ap2 = GetTriangle(i).PNumMod(j+1);
+
+	      if (IsExternalEdge(ap1,ap2))
+		{
+		  DeleteExternalEdge(ap1,ap2);
+		}
+	    }
+	}
+    }
+}
+
+void STLGeometry :: BuildExternalEdgesFromEdges()
+{
+  StoreExternalEdges();
+
+  if (GetNE() == 0) {PrintWarning("Edges possibly not generated!");}
+
+  int i;
+  externaledges.SetSize(0);
+
+  for (i = 1; i <= GetNE(); i++)
+    {
+      STLEdge e = GetEdge(i);
+      AddExternalEdge(e.PNum(1), e.PNum(2));
+    }
+
+}
+
+
+void STLGeometry :: AddExternalEdge(int ap1, int ap2)
+{
+  externaledges.Append(twoint(ap1,ap2));
+}
+
+void STLGeometry :: DeleteExternalEdge(int ap1, int ap2)
+{
+
+  int i;
+  int found = 0;
+  for (i = 1; i <= NOExternalEdges(); i++)
+    {
+      if ((GetExternalEdge(i).i1 == ap1 && GetExternalEdge(i).i2 == ap2) ||
+	  (GetExternalEdge(i).i1 == ap2 && GetExternalEdge(i).i2 == ap1)) {found = 1;};
+      if (found && i < NOExternalEdges())
+	{
+	  externaledges.Elem(i) = externaledges.Get(i+1);
+	}
+    }
+  if (!found) {PrintWarning("edge not found");}
+  else
+    {
+      externaledges.SetSize(externaledges.Size()-1);
+    }
+
+}
+
+int STLGeometry :: IsExternalEdge(int ap1, int ap2)
+{
+  int i;
+  for (i = 1; i <= NOExternalEdges(); i++)
+    {
+      if ((GetExternalEdge(i).i1 == ap1 && GetExternalEdge(i).i2 == ap2) ||
+	  (GetExternalEdge(i).i1 == ap2 && GetExternalEdge(i).i2 == ap1)) {return 1;};
+    }
+  return 0;
+}
+
+void STLGeometry :: DestroyDirtyTrigs()
+{
+
+  PrintFnStart("Destroy dirty triangles");
+  PrintMessage(5,"original number of triangles=", GetNT());
+
+  //destroy every triangle with other than 3 neighbours;
+  int changed = 1;
+  int i, j, k;
+  while (changed)
+    {
+      changed = 0;
+      Clear();
+
+      for (i = 1; i <= GetNT(); i++)
+	{
+	  int dirty = NONeighbourTrigs(i) < 3;
+
+	  for (j = 1; j <= 3; j++)
+	    {
+	      int pnum = GetTriangle(i).PNum(j);
+	      /*
+	      if (pnum == 1546)
+		{
+		// for (k = 1; k <=  NOTrigsPerPoint(pnum); k++)
+		}
+	      */
+	      if (NOTrigsPerPoint(pnum) <= 2) 
+		dirty = 1;
+	    }
+	  
+	  int pi1 = GetTriangle(i).PNum(1);
+	  int pi2 = GetTriangle(i).PNum(2);
+	  int pi3 = GetTriangle(i).PNum(3);
+	  if (pi1 == pi2 || pi1 == pi3 || pi2 == pi3)
+	    {
+	      PrintMessage(5,"triangle with Volume 0: ", i, "  nodes: ", pi1, ", ", pi2, ", ", pi3);
+	      dirty = 1;
+	    }
+
+	  if (dirty)
+	    {
+	      for (k = i+1; k <= GetNT(); k++)
+		{
+		  trias.Elem(k-1) = trias.Get(k);
+		  // readtrias: not longer permanent, JS
+		  //		  readtrias.Elem(k-1) = readtrias.Get(k); 
+		}
+	      int size = GetNT();
+	      trias.SetSize(size-1);
+	      //	      readtrias.SetSize(size-1);
+	      changed = 1;
+	      break;
+	    }
+	}
+    }  
+
+  FindNeighbourTrigs();
+  PrintMessage(5,"final number of triangles=", GetNT());
+}
+
+void STLGeometry :: CalcNormalsFromGeometry()
+{
+  int i;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      const STLTriangle & tr = GetTriangle(i);
+      const Point3d& ap1 = GetPoint(tr.PNum(1));
+      const Point3d& ap2 = GetPoint(tr.PNum(2));
+      const Point3d& ap3 = GetPoint(tr.PNum(3));
+
+      Vec3d normal = Cross (ap2-ap1, ap3-ap1);
+      
+      if (normal.Length() != 0)
+	{
+	  normal /= (normal.Length());		  
+	}
+      GetTriangle(i).SetNormal(normal);
+    }
+  PrintMessage(5,"Normals calculated from geometry!!!");
+
+  calcedgedataanglesnew = 1;
+}
+
+void STLGeometry :: SetSelectTrig(int trig)
+{
+  stldoctor.selecttrig = trig;
+}
+
+int STLGeometry :: GetSelectTrig() const
+{
+  return stldoctor.selecttrig;
+}
+
+void STLGeometry :: SetNodeOfSelTrig(int n)
+{
+  stldoctor.nodeofseltrig = n;
+}
+
+int STLGeometry :: GetNodeOfSelTrig() const
+{
+  return stldoctor.nodeofseltrig;
+}
+
+void STLGeometry :: MoveSelectedPointToMiddle()
+{
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
+    {
+      int p = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+      Point<3> pm(0.,0.,0.); //Middlevector;
+      Point<3> p0(0.,0.,0.);
+      PrintMessage(5,"original point=", Point3d(GetPoint(p)));
+
+      int i;
+      int cnt = 0;
+      for (i = 1; i <= trigsperpoint.EntrySize(p); i++)
+	{
+	  const STLTriangle& tr = GetTriangle(trigsperpoint.Get(p,i));
+	  int j;
+	  for (j = 1; j <= 3; j++)
+	    {
+	      if (tr.PNum(j) != p)
+		{
+		  cnt++;
+		  pm(0) += GetPoint(tr.PNum(j))(0);
+		  pm(1) += GetPoint(tr.PNum(j))(1);
+		  pm(2) += GetPoint(tr.PNum(j))(2);
+		}
+	    }
+	}
+
+      Point<3> origp = GetPoint(p);
+      double fact = 0.2;
+
+      SetPoint(p, p0 + fact*(1./(double)cnt)*(pm-p0)+(1.-fact)*(origp-p0));
+
+      PrintMessage(5,"middle point=", Point3d (GetPoint(p)));
+      
+      PrintMessage(5,"moved point ", Point3d (p));
+
+    }
+}
+
+void STLGeometry :: PrintSelectInfo()
+{
+
+  //int trig = GetSelectTrig();
+  //int p = GetTriangle(trig).PNum(GetNodeOfSelTrig());
+  
+  PrintMessage(1,"touch triangle ", GetSelectTrig()
+       , ", local node ", GetNodeOfSelTrig()
+       , " (=", GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig()), ")");
+  if (AtlasMade() && GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
+    {
+      PrintMessage(1,"           chartnum=",GetChartNr(GetSelectTrig()));
+      /*      
+      PointBetween(Center(Center(GetPoint(GetTriangle(270).PNum(1)),
+				 GetPoint(GetTriangle(270).PNum(2))),
+			  GetPoint(GetTriangle(270).PNum(3))),270,
+		   Center(Center(GetPoint(GetTriangle(trig).PNum(1)),
+				 GetPoint(GetTriangle(trig).PNum(2))),
+			  GetPoint(GetTriangle(trig).PNum(3))),trig);
+      */
+      //PointBetween(Point3d(5.7818, 7.52768, 4.14879),260,Point3d(6.80292, 6.55392, 4.70184),233);
+    }
+}
+
+void STLGeometry :: ShowSelectedTrigChartnum()
+{
+  int st = GetSelectTrig();
+
+  if (st >= 1 && st <= GetNT() && AtlasMade())
+    PrintMessage(1,"selected trig ", st, " has chartnumber ", GetChartNr(st));
+}
+
+void STLGeometry :: ShowSelectedTrigCoords()
+{
+  int st = GetSelectTrig();
+
+  /*
+  //testing!!!!
+  Array<int> trigs;
+  GetSortedTrianglesAroundPoint(GetTriangle(st).PNum(GetNodeOfSelTrig()),st,trigs);
+  */
+
+  if (st >= 1 && st <= GetNT())
+    {
+      PrintMessage(1, "coordinates of selected trig ", st, ":");
+      PrintMessage(1, "   p1 = ", GetTriangle(st).PNum(1), " = ", 
+		   Point3d (GetPoint(GetTriangle(st).PNum(1))));
+      PrintMessage(1, "   p2 = ", GetTriangle(st).PNum(2), " = ", 
+		   Point3d (GetPoint(GetTriangle(st).PNum(2))));
+      PrintMessage(1, "   p3 = ", GetTriangle(st).PNum(3), " = ", 
+		   Point3d (GetPoint(GetTriangle(st).PNum(3))));
+    }
+}
+
+void STLGeometry :: LoadMarkedTrigs()
+{
+  PrintFnStart("load marked trigs from file 'markedtrigs.ng'");
+  ifstream fin("markedtrigs.ng");
+
+  int n;
+  fin >> n;
+  if (n != GetNT() || n == 0) {PrintError("Not a suitable marked-trig-file!"); return;}
+
+  int i, m;
+  for (i = 1; i <= n; i++)
+    {
+      fin >> m;
+      SetMarkedTrig(i, m);      
+    }
+
+  fin >> n;
+  if (n != 0) 
+    {
+ 
+      Point<3> ap1, ap2;
+      for (i = 1; i <= n; i++)
+	{
+	  fin >> ap1(0); fin >> ap1(1); fin >> ap1(2);
+	  fin >> ap2(0); fin >> ap2(1); fin >> ap2(2);
+	  AddMarkedSeg(ap1,ap2);      
+	}
+    }
+}
+
+void STLGeometry :: SaveMarkedTrigs()
+{
+  PrintFnStart("save marked trigs to file 'markedtrigs.ng'");
+  ofstream fout("markedtrigs.ng");
+
+  int n = GetNT();
+  fout << n << endl;
+
+  int i;
+  for (i = 1; i <= n; i++)
+    {
+      fout << IsMarkedTrig(i) << "\n";
+    }
+
+  n = GetNMarkedSegs();
+  fout << n << endl;
+
+  Point<3> ap1,ap2;
+  for (i = 1; i <= n; i++)
+    {
+      GetMarkedSeg(i,ap1,ap2);
+      fout << ap1(0) << " " << ap1(1) << " " << ap1(2) << "  ";
+      fout << ap2(0) << " " << ap2(1) << " " << ap2(2) << " " << "\n";
+    }
+
+}
+
+void STLGeometry :: NeighbourAnglesOfSelectedTrig()
+{
+  int st = GetSelectTrig();
+
+  if (st >= 1 && st <= GetNT())
+    {
+      int i;
+      PrintMessage(1,"Angle to triangle ", st, ":");
+      for (i = 1; i <= NONeighbourTrigs(st); i++)
+	{
+	  PrintMessage(1,"   triangle ", NeighbourTrig(st,i), ": angle = ", 
+		       180./M_PI*GetAngle(st, NeighbourTrig(st,i)), "°",
+		       ", calculated = ", 180./M_PI*Angle(GetTriangle(st).GeomNormal(points), 
+							  GetTriangle(NeighbourTrig(st,i)).GeomNormal(points)), "°");
+	}
+    }
+}
+
+void STLGeometry :: GetVicinity(int starttrig, int size, Array<int>& vic)
+{
+  if (starttrig == 0 || starttrig > GetNT()) {return;} 
+
+  Array<int> vicarray;
+  vicarray.SetSize(GetNT());
+
+  int i;
+  for (i = 1; i <= vicarray.Size(); i++)
+    {
+      vicarray.Elem(i) = 0;
+    }
+ 
+  vicarray.Elem(starttrig) = 1;
+  
+  int j = 0,k;
+
+  Array <int> list1;
+  list1.SetSize(0);
+  Array <int> list2;
+  list2.SetSize(0);
+  list1.Append(starttrig);
+
+  while (j < size)
+    {
+      j++;
+      for (i = 1; i <= list1.Size(); i++)
+	{
+	  for (k = 1; k <= NONeighbourTrigs(i); k++)
+	    {
+	      int nbtrig = NeighbourTrig(list1.Get(i),k);
+	      if (nbtrig && vicarray.Get(nbtrig) == 0)
+		{
+		  list2.Append(nbtrig);
+		  vicarray.Elem(nbtrig) = 1;
+		}
+	    }
+	}
+      list1.SetSize(0);
+      for (i = 1; i <= list2.Size(); i++)
+	{
+	  list1.Append(list2.Get(i));
+	}
+      list2.SetSize(0);
+    }
+
+  vic.SetSize(0);
+  for (i = 1; i <= vicarray.Size(); i++)
+    {
+      if (vicarray.Get(i)) {vic.Append(i);}
+    }
+}
+
+void STLGeometry :: CalcVicinity(int starttrig)
+{
+  if (starttrig == 0 || starttrig > GetNT()) {return;} 
+
+  vicinity.SetSize(GetNT());
+
+  if (!stldoctor.showvicinity) {return;}
+
+  int i;
+  for (i = 1; i <= vicinity.Size(); i++)
+    {
+      vicinity.Elem(i) = 0;
+    }
+ 
+  vicinity.Elem(starttrig) = 1;
+  
+  int j = 0,k;
+
+  Array <int> list1;
+  list1.SetSize(0);
+  Array <int> list2;
+  list2.SetSize(0);
+  list1.Append(starttrig);
+
+  //  int cnt = 1;
+  while (j < stldoctor.vicinity)
+    {
+      j++;
+      for (i = 1; i <= list1.Size(); i++)
+	{
+	  for (k = 1; k <= NONeighbourTrigs(i); k++)
+	    {
+	      int nbtrig = NeighbourTrig(list1.Get(i),k);
+	      if (nbtrig && vicinity.Get(nbtrig) == 0)
+		{
+		  list2.Append(nbtrig);
+		  vicinity.Elem(nbtrig) = 1;
+		  //cnt++;
+		}
+	    }
+	}
+      list1.SetSize(0);
+      for (i = 1; i <= list2.Size(); i++)
+	{
+	  list1.Append(list2.Get(i));
+	}
+      list2.SetSize(0);
+    }
+
+}
+
+int STLGeometry :: Vicinity(int trig) const 
+{
+  if (trig <= vicinity.Size() && trig >=1)
+    {
+      return vicinity.Get(trig);
+    }
+  else {PrintSysError("In STLGeometry::Vicinity");}
+  return 0;
+}
+
+void STLGeometry :: InitMarkedTrigs()
+{
+  markedtrigs.SetSize(GetNT());
+  int i;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      SetMarkedTrig(i, 0);
+    }
+}
+
+void STLGeometry :: MarkDirtyTrigs()
+{
+  PrintFnStart("mark dirty trigs");
+  int i,j;
+
+  markedtrigs.SetSize(GetNT());
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      SetMarkedTrig(i, 0);
+    }
+
+  int found;
+  double dirtyangle = stlparam.yangle/2./180.*M_PI;
+  int cnt = 0;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      found = 0;
+      for (j = 1; j <= NONeighbourTrigs(i); j++)
+	{
+	  if (GetAngle(i, NeighbourTrig(i,j)) > dirtyangle)
+	    {
+	      found++;
+	    }
+	}
+      if (found && GetTriangle(i).MinHeight(points) < 
+	  stldoctor.dirtytrigfact*GetTriangle(i).MaxLength(points))
+	{
+	  SetMarkedTrig(i, 1); cnt++;
+	}
+      /*
+      else if (found == 3)
+	{
+	  SetMarkedTrig(i, 1); cnt++;	  
+	}
+      */
+    }
+
+  PrintMessage(1, "marked ", cnt, " dirty trigs");
+}
+
+
+void STLGeometry :: MarkTopErrorTrigs()
+{
+  int cnt = 0;
+  markedtrigs.SetSize(GetNT());
+  for (int i = 1; i <= GetNT(); i++)
+    {
+      const STLTriangle & trig = GetTriangle(i);
+
+      SetMarkedTrig(i, trig.flags.toperror);
+      if (trig.flags.toperror) cnt++;
+    }
+  PrintMessage(1,"marked ", cnt, " inconsistent triangles");
+}
+
+
+
+double STLGeometry :: CalcTrigBadness(int i)
+{
+  int j;
+  double maxbadness = 0;
+  int ap1, ap2;
+  for (j = 1; j <= NONeighbourTrigs(i); j++)
+    {
+      GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)), ap1, ap2);
+      
+      if (!IsEdge(ap1,ap2) && GetGeomAngle(i, NeighbourTrig(i,j)) > maxbadness)
+	{
+	  maxbadness = GetGeomAngle(i, NeighbourTrig(i,j));
+	}
+    }
+  return maxbadness;
+
+}
+
+void STLGeometry :: GeomSmoothRevertedTrigs()
+{
+  //double revertedangle = stldoctor.smoothangle/180.*M_PI;
+  double fact = stldoctor.dirtytrigfact;
+
+  MarkRevertedTrigs();
+
+  int i, j, k, l, p;
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      if (IsMarkedTrig(i)) 
+	{
+	  for (j = 1; j <= 3; j++)
+	    {
+	      double origbadness = CalcTrigBadness(i);
+
+	      p = GetTriangle(i).PNum(j);
+	      Point<3> pm(0.,0.,0.); //Middlevector;
+	      Point<3> p0(0.,0.,0.);
+
+	      int cnt = 0;
+
+	      for (k = 1; k <= trigsperpoint.EntrySize(p); k++)
+		{
+		  const STLTriangle& tr = GetTriangle(trigsperpoint.Get(p,k));
+		  for (l = 1; l <= 3; l++)
+		    {
+		      if (tr.PNum(l) != p)
+			{
+			  cnt++;
+			  pm(0) += GetPoint(tr.PNum(l))(0);
+			  pm(1) += GetPoint(tr.PNum(l))(1);
+			  pm(2) += GetPoint(tr.PNum(l))(2);
+			}
+		    }
+		}
+	      Point3d origp = GetPoint(p);
+	      Point3d newp = p0 + fact*(1./(double)cnt)*(pm-p0)+(1.-fact)*(origp-p0);
+
+	      SetPoint(p, newp);
+
+	      if (CalcTrigBadness(i) > 0.9*origbadness) {SetPoint(p,origp); PrintDot('f');}
+	      else {PrintDot('s');}
+	    }
+	}
+    }
+  MarkRevertedTrigs();
+}
+
+void STLGeometry :: MarkRevertedTrigs()
+{
+  int i,j;
+  if (edgesperpoint.Size() != GetNP()) {BuildEdges();}
+
+  PrintFnStart("mark reverted trigs");
+
+  InitMarkedTrigs();
+
+  int found;
+  double revertedangle = stldoctor.smoothangle/180.*M_PI;
+
+  int cnt = 0;
+  int ap1, ap2;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      found = 0;
+      for (j = 1; j <= NONeighbourTrigs(i); j++)
+	{
+	  GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)), ap1, ap2);
+
+	  if (!IsEdge(ap1,ap2))
+	    {
+              if (GetGeomAngle(i, NeighbourTrig(i,j)) > revertedangle)
+		{
+		  found = 1;
+		  break;
+		}
+	    }
+	}
+      
+      if (found)
+	{
+	  SetMarkedTrig(i, 1); cnt++;
+	}
+      
+    }
+
+  PrintMessage(5, "found ", cnt, " reverted trigs");
+
+
+}
+
+void STLGeometry :: SmoothDirtyTrigs()
+{
+  PrintFnStart("smooth dirty trigs");
+
+  MarkDirtyTrigs();
+
+  int i,j;
+  int changed = 1;
+  int ap1, ap2;
+  
+  while (changed)
+    {
+      changed = 0;
+      for (i = 1; i <= GetNT(); i++)
+	{
+	  if (IsMarkedTrig(i))
+	    {
+	      int foundtrig = 0;
+	      double maxlen = 0;
+	      // JS: darf normalvector nicht ueber kurze Seite erben
+	      maxlen = GetTriangle(i).MaxLength(GetPoints()) / 2.1; //JG: bei flachem dreieck auch kurze Seite
+
+	      for (j = 1; j <= NONeighbourTrigs(i); j++)
+		{
+		  if (!IsMarkedTrig(NeighbourTrig(i,j)))
+		    {
+		      GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)),ap1,ap2);
+		      if (Dist(GetPoint(ap1),GetPoint(ap2)) >= maxlen)
+			{
+			  foundtrig = NeighbourTrig(i,j);
+			  maxlen = Dist(GetPoint(ap1),GetPoint(ap2));
+			}
+		    }
+		}
+	      if (foundtrig)
+		{
+		  GetTriangle(i).SetNormal(GetTriangle(foundtrig).Normal());
+		  changed = 1;
+		  SetMarkedTrig(i,0);
+		}
+	    }
+	}
+    }
+
+  calcedgedataanglesnew = 1;
+
+
+  MarkDirtyTrigs();
+
+  int cnt = 0;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      if (IsMarkedTrig(i)) {cnt++;}
+    }
+
+  PrintMessage(5,"NO marked dirty trigs=", cnt);
+
+}
+
+int STLGeometry :: IsMarkedTrig(int trig) const 
+{
+  if (trig <= markedtrigs.Size() && trig >=1)
+    {
+      return markedtrigs.Get(trig);
+    }
+  else {PrintSysError("In STLGeometry::IsMarkedTrig");}
+
+  return 0;  
+}
+
+void STLGeometry :: SetMarkedTrig(int trig, int num)
+{
+  if (trig <= markedtrigs.Size() && trig >=1)
+    {
+      markedtrigs.Elem(trig) = num;
+    }
+  else {PrintSysError("In STLGeometry::SetMarkedTrig");}
+}
+
+void STLGeometry :: Clear()
+{
+  PrintFnStart("Clear");
+
+  surfacemeshed = 0;
+  surfaceoptimized = 0;
+  volumemeshed = 0;
+
+  selectedmultiedge.SetSize(0);
+  meshlines.SetSize(0);
+  // neighbourtrigs.SetSize(0);
+  outerchartspertrig.SetSize(0);
+  atlas.SetSize(0);
+  ClearMarkedSegs();
+  ClearSpiralPoints();
+  ClearLineEndPoints();
+
+  SetSelectTrig(0);
+  SetNodeOfSelTrig(1);
+  facecnt = 0;
+
+  SetThreadPercent(100.);
+
+  ClearEdges();
+}
+
+double STLGeometry :: Area()
+{
+  double ar = 0;
+  int i;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      ar += GetTriangle(i).Area(points);
+    }
+  return ar;
+}
+
+double STLGeometry :: GetAngle(int t1, int t2)
+{
+  return Angle(GetTriangle(t1).Normal(),GetTriangle(t2).Normal());
+}
+
+double STLGeometry :: GetGeomAngle(int t1, int t2)
+{
+  Vec3d n1 = GetTriangle(t1).GeomNormal(points);
+  Vec3d n2 = GetTriangle(t2).GeomNormal(points);
+  return Angle(n1,n2);
+}
+
+
+void STLGeometry :: InitSTLGeometry(const Array<STLReadTriangle> & readtrias)
+{
+  PrintFnStart("Init STL Geometry");
+  STLTopology::InitSTLGeometry(readtrias);
+
+  int i, k;
+
+  //const double geometry_tol_fact = 1E8; //distances lower than max_box_size/tol are ignored
+
+  int np = GetNP();
+  PrintMessage(5,"NO points= ", GetNP());
+  normals.SetSize(GetNP());
+  Array<int> normal_cnt(GetNP()); // counts number of added normals in a point
+
+  for (i = 1; i <= np; i++)
+    {
+      normal_cnt.Elem(i) = 0;
+      normals.Elem(i) = Vec3d (0,0,0);
+    }
+
+  for(i = 1; i <= GetNT(); i++)
+    {
+      //      STLReadTriangle t = GetReadTriangle(i);
+      //      STLTriangle st;
+
+      Vec<3> n = GetTriangle(i).Normal ();
+
+      for (k = 1; k <= 3; k++)
+	{
+	  int pi = GetTriangle(i).PNum(k);
+	  
+	  normal_cnt.Elem(pi)++;
+	  SetNormal(pi, GetNormal(pi) + n);
+	}
+    } 
+
+  //normalize the normals
+  for (i = 1; i <= GetNP(); i++)
+    {
+      SetNormal(i,1./(double)normal_cnt.Get(i)*GetNormal(i));
+    }
+
+  trigsconverted = 1;
+
+  vicinity.SetSize(GetNT());
+  markedtrigs.SetSize(GetNT());
+  for (i = 1; i <= GetNT(); i++)
+    {
+      markedtrigs.Elem(i) = 0;
+      vicinity.Elem(i) = 1;
+    }
+
+  ha_points.SetSize(GetNP());
+  for (i = 1; i <= GetNP(); i++)
+    ha_points.Elem(i) = 0;
+
+  calcedgedataanglesnew = 0;
+  edgedatastored = 0;
+  edgedata->Clear();
+
+
+  if (GetStatus() == STL_ERROR) return;
+
+  CalcEdgeData();
+  CalcEdgeDataAngles();
+
+  ClearLineEndPoints();
+
+  CheckGeometryOverlapping();
+}
+
+void STLGeometry :: TopologyChanged()
+{
+  calcedgedataanglesnew = 1;
+}
+
+int STLGeometry :: CheckGeometryOverlapping()
+{
+  int i, j, k;
+
+  Box<3> geombox = GetBoundingBox();
+  Point<3> pmin = geombox.PMin();
+  Point<3> pmax = geombox.PMax();
+
+  Box3dTree setree(pmin, pmax);
+  Array<int> inters;
+
+  int oltrigs = 0;
+  markedtrigs.SetSize(GetNT());
+
+  for (i = 1; i <= GetNT(); i++)
+    SetMarkedTrig(i, 0);
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      const STLTriangle & tri = GetTriangle(i);
+      
+      Point<3> tpmin = tri.box.PMin();
+      Point<3> tpmax = tri.box.PMax();
+      Vec<3> diag = tpmax - tpmin;
+
+      tpmax = tpmax + 0.001 * diag;
+      tpmin = tpmin - 0.001 * diag;
+
+      setree.Insert (tpmin, tpmax, i);
+    }
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      const STLTriangle & tri = GetTriangle(i);
+      
+      Point<3> tpmin = tri.box.PMin();
+      Point<3> tpmax = tri.box.PMax();
+
+      setree.GetIntersecting (tpmin, tpmax, inters);
+
+      for (j = 1; j <= inters.Size(); j++)
+	{
+	  const STLTriangle & tri2 = GetTriangle(inters.Get(j));
+
+	  const Point<3> *trip1[3], *trip2[3];	
+	  Point<3> hptri1[3], hptri2[3];
+	  /*
+	  for (k = 1; k <= 3; k++)
+	    {
+	      trip1[k-1] = &GetPoint (tri.PNum(k));
+	      trip2[k-1] = &GetPoint (tri2.PNum(k));
+	    }
+	  */
+
+	  for (k = 0; k < 3; k++)
+	    {
+	      hptri1[k] = GetPoint (tri[k]);
+	      hptri2[k] = GetPoint (tri2[k]);
+	      trip1[k] = &hptri1[k];
+	      trip2[k] = &hptri2[k];
+	    }
+
+	  if (IntersectTriangleTriangle (&trip1[0], &trip2[0]))
+	    {
+	      oltrigs++;
+	      PrintMessage(5,"Intersecting Triangles: trig ",i," with ",inters.Get(j),"!");
+	      SetMarkedTrig(i, 1);
+	      SetMarkedTrig(inters.Get(j), 1);
+	    }
+	}
+    }
+
+  PrintMessage(3,"Check Geometry Overlapping: overlapping triangles = ",oltrigs);
+  return oltrigs;
+}
+
+/*
+void STLGeometry :: InitSTLGeometry()
+{
+  STLTopology::InitSTLGeometry();
+
+  int i, j, k;
+
+  const double geometry_tol_fact = 1E8; //distances lower than max_box_size/tol are ignored
+
+
+  trias.SetSize(0);
+  points.SetSize(0);
+  normals.SetSize(0);
+
+  Array<int> normal_cnt; // counts number of added normals in a point
+
+  Box3d bb (GetBoundingBox().PMin() + Vec3d (-1,-1,-1),
+	    GetBoundingBox().PMax() + Vec3d (1, 1, 1));
+
+  Point3dTree pointtree (bb.PMin(), 
+			 bb.PMax());
+  Array<int> pintersect;
+
+  double gtol = GetBoundingBox().CalcDiam()/geometry_tol_fact;
+
+  for(i = 1; i <= GetReadNT(); i++)
+    {
+      //if (i%500==499) {(*mycout) << (double)i/(double)GetReadNT()*100. << "%" << endl;}
+
+      STLReadTriangle t = GetReadTriangle(i);
+      STLTriangle st;
+      Vec3d n = t.normal;
+
+      for (k = 0; k < 3; k++)
+	{
+	  Point3d p = t.pts[k];
+
+	  Point3d pmin = p - Vec3d (gtol, gtol, gtol);
+	  Point3d pmax = p + Vec3d (gtol, gtol, gtol);
+	  
+	  pointtree.GetIntersecting (pmin, pmax, pintersect);
+	  
+	  if (pintersect.Size() > 1)
+	    (*mycout) << "found too much  " << char(7) << endl;
+	  int foundpos = 0;
+	  if (pintersect.Size())
+	    foundpos = pintersect.Get(1);
+
+	  if (foundpos) 
+	    {
+	      normal_cnt[foundpos]++;
+	      SetNormal(foundpos,GetNormal(foundpos)+n);
+	      //	      (*testout) << "found p " << p << endl;
+	    }
+	  else
+	    {
+	      foundpos = AddPoint(p);
+	      AddNormal(n);
+	      normal_cnt.Append(1);
+
+	      pointtree.Insert (p, foundpos);
+	    }
+	  //(*mycout) << "foundpos=" << foundpos << endl;
+	  st.pts[k] = foundpos;
+	}
+
+      if ( (st.pts[0] == st.pts[1]) || 
+	   (st.pts[0] == st.pts[2]) || 
+	   (st.pts[1] == st.pts[2]) )
+	{
+	  (*mycout) << "ERROR: STL Triangle degenerated" << endl;
+	}
+      else
+	{
+	  // do not add ? js
+	  AddTriangle(st);
+	}
+      //(*mycout) << "TRIG" << i << " = " << st << endl;
+      
+    } 
+  //normal the normals
+  for (i = 1; i <= GetNP(); i++)
+    {
+      SetNormal(i,1./(double)normal_cnt[i]*GetNormal(i));
+    }
+
+  trigsconverted = 1;
+
+  vicinity.SetSize(GetNT());
+  markedtrigs.SetSize(GetNT());
+  for (i = 1; i <= GetNT(); i++)
+    {
+      markedtrigs.Elem(i) = 0;
+      vicinity.Elem(i) = 1;
+    }
+
+  ha_points.SetSize(GetNP());
+  for (i = 1; i <= GetNP(); i++)
+    ha_points.Elem(i) = 0;
+
+  calcedgedataanglesnew = 0;
+  edgedatastored = 0;
+  edgedata->Clear();
+
+  CalcEdgeData();
+  CalcEdgeDataAngles();
+
+  ClearLineEndPoints();
+
+  (*mycout) << "done" << endl;
+}
+*/
+
+
+
+void STLGeometry :: SetLineEndPoint(int pn) 
+{
+  if (pn <1 || pn > lineendpoints.Size()) {PrintSysError("Illegal pnum in SetLineEndPoint!!!"); return; }
+  lineendpoints.Elem(pn) = 1;
+}
+
+int STLGeometry :: IsLineEndPoint(int pn) 
+{
+  //  return 0;
+  if (pn <1 || pn > lineendpoints.Size()) 
+    {PrintSysError("Illegal pnum in IsLineEndPoint!!!"); return 0;}
+  return lineendpoints.Get(pn);
+}
+
+void STLGeometry :: ClearLineEndPoints()
+{
+  lineendpoints.SetSize(GetNP());
+  int i;
+  for (i = 1; i <= GetNP(); i++)
+    {
+      lineendpoints.Elem(i) = 0;
+    }
+}
+
+int STLGeometry :: IsEdge(int ap1, int ap2)
+{
+  int i,j;
+  for (i = 1; i <= GetNEPP(ap1); i++)
+    {
+      for (j = 1; j <= GetNEPP(ap2); j++)
+	{
+	  if (GetEdgePP(ap1,i) == GetEdgePP(ap2,j)) {return 1;}
+	}
+    }
+  return 0;
+}
+
+int STLGeometry :: IsEdgeNum(int ap1, int ap2)
+{
+  int i,j;
+  for (i = 1; i <= GetNEPP(ap1); i++)
+    {
+      for (j = 1; j <= GetNEPP(ap2); j++)
+	{
+	  if (GetEdgePP(ap1,i) == GetEdgePP(ap2,j)) {return GetEdgePP(ap1,i);}
+	}
+    }
+  return 0;
+}
+
+
+void STLGeometry :: BuildEdges()
+{
+  //PrintFnStart("build edges");
+  edges.SetSize(0);
+  meshlines.SetSize(0);
+  FindEdgesFromAngles();
+}
+
+void STLGeometry :: UseExternalEdges()
+{
+  int i;
+  for (i = 1; i <= NOExternalEdges(); i++)
+    {
+      AddEdge(GetExternalEdge(i).i1,GetExternalEdge(i).i2);
+    }
+  //BuildEdgesPerPointy();
+}
+
+void STLGeometry :: UndoEdgeChange()
+{
+  if (edgedatastored) 
+    {
+      RestoreEdgeData();
+    }
+  else
+    {
+      PrintWarning("no edge undo possible");
+    }
+}
+
+
+void STLGeometry :: StoreEdgeData()
+{
+  //  edgedata_store = *edgedata;
+  
+  edgedata->Store();
+  edgedatastored = 1;
+
+  // put stlgeom-edgedata to stltopology edgedata 
+  /*
+  int i;
+  for (i = 1; i <= GetNTE(); i++)
+    {
+      const STLTopEdge & topedge = GetTopEdge (i);
+      int ednum = edgedata->GetEdgeNum (topedge.PNum(1),
+				       topedge.PNum(2));
+      topedges.Elem(i).SetStatus (edgedata->Get (ednum).status);
+    }
+  */
+}
+
+void STLGeometry :: RestoreEdgeData()
+{
+  //  *edgedata = edgedata_store;
+  edgedata->Restore();
+  edgedatastored=0;
+}
+
+
+void STLGeometry :: CalcEdgeData()
+{
+  PushStatus("Calc Edge Data");
+
+  int np1, np2;
+  int i;
+  
+  int ecnt = 0;
+  edgedata->SetSize(GetNT()/2*3);
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      SetThreadPercent((double)i/(double)GetNT()*100.);
+      
+      const STLTriangle & t1 = GetTriangle(i);
+
+      for (int j = 1; j <= NONeighbourTrigs(i); j++)
+	{
+	  int nbti = NeighbourTrig(i,j);
+	  if (nbti > i)
+	    {
+	      const STLTriangle & t2 = GetTriangle(nbti);
+
+	      if (t1.IsNeighbourFrom(t2))
+		{
+		  ecnt++; if (ecnt > edgedata->Size()) {PrintError("In Calc edge data, illegal geometry");}
+
+		  t1.GetNeighbourPoints(t2,np1,np2);
+
+		  /* ang = GetAngle(i,nbti);
+		     if (ang < -M_PI) {ang += 2*M_PI;}*/
+
+
+		  // edgedata->Add(STLEdgeData(0, np1, np2, i, nbti),ecnt);
+		  edgedata->Elem(ecnt).SetStatus(ED_UNDEFINED);
+
+		  // edgedata->Elem(ecnt).top = this;
+		  // edgedata->Elem(ecnt).topedgenr = GetTopEdgeNum (np1, np2);
+		}
+	    }
+	}      
+    }
+  
+  //BuildEdgesPerPoint();
+  PopStatus();  
+}
+
+void STLGeometry :: CalcEdgeDataAngles()
+{
+  PrintMessage(5,"calc edge data angles");
+
+  int i;
+
+  for (i = 1; i <= GetNTE(); i++)
+    {
+      STLTopEdge & edge = GetTopEdge (i);
+      double cosang = 
+	GetTriangle(edge.TrigNum(1)).Normal() *
+	GetTriangle(edge.TrigNum(2)).Normal();
+      edge.SetCosAngle (cosang);
+    }
+
+  for (i = 1; i <= edgedata->Size(); i++)
+    {
+      /*
+      const STLEdgeData& e = edgedata->Get(i);
+      ang = GetAngle(e.lt,e.rt);
+      if (ang < -M_PI) {ang += 2*M_PI;}
+      edgedata->Elem(i).angle = fabs(ang);
+      */
+    }
+  
+}
+
+void STLGeometry :: FindEdgesFromAngles()
+{
+  //  PrintFnStart("find edges from angles");
+
+  double min_edge_angle = stlparam.yangle/180.*M_PI;
+  double cont_min_edge_angle = stlparam.contyangle/180.*M_PI;
+
+  double cos_min_edge_angle = cos (min_edge_angle);
+  double cos_cont_min_edge_angle = cos (cont_min_edge_angle);
+
+  if (calcedgedataanglesnew) {CalcEdgeDataAngles(); calcedgedataanglesnew = 0;}
+
+  int i;
+  for (i = 1; i <= edgedata->Size(); i++)
+    {
+      STLTopEdge & sed = edgedata->Elem(i);
+      if (sed.GetStatus() == ED_CANDIDATE || 
+	  sed.GetStatus() == ED_UNDEFINED)
+	{
+	  if (sed.CosAngle() <= cos_min_edge_angle)
+	    {
+	      sed.SetStatus (ED_CANDIDATE);
+	    }
+	  else
+	    {
+	      sed.SetStatus(ED_UNDEFINED);
+	    }
+	} 
+    }
+
+  if (stlparam.contyangle < stlparam.yangle)
+    {
+      int changed = 1;
+      int its = 0;
+      while (changed && stlparam.contyangle < stlparam.yangle)
+	{
+	  its++;
+	  //(*mycout) << "." << flush;
+	  changed = 0;
+	  for (i = 1; i <= edgedata->Size(); i++)
+	    {
+	      STLTopEdge & sed = edgedata->Elem(i);
+	      if (sed.CosAngle() <= cos_cont_min_edge_angle 
+		  && sed.GetStatus() == ED_UNDEFINED && 
+		  (edgedata->GetNConfCandEPP(sed.PNum(1)) == 1 || 
+		   edgedata->GetNConfCandEPP(sed.PNum(2)) == 1))
+		{
+		  changed = 1;
+		  sed.SetStatus (ED_CANDIDATE);
+		}
+	    }
+	}
+    }
+  
+  int confcand = 0;
+  if (edgedata->GetNConfEdges() == 0) 
+    {
+      confcand = 1;
+    }
+  
+  for (i = 1; i <= edgedata->Size(); i++)
+    {
+      STLTopEdge & sed = edgedata->Elem(i);
+      if (sed.GetStatus() == ED_CONFIRMED || 
+	  (sed.GetStatus() == ED_CANDIDATE && confcand))
+	{
+	  STLEdge se(sed.PNum(1),sed.PNum(2));
+	  se.SetLeftTrig(sed.TrigNum(1));
+	  se.SetRightTrig(sed.TrigNum(2));
+	  AddEdge(se);
+	}
+    }
+  BuildEdgesPerPoint();
+
+  
+
+  //(*mycout) << "its for continued angle = " << its << endl;
+  PrintMessage(5,"built ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree");
+  
+}
+
+/*
+void STLGeometry :: FindEdgesFromAngles()
+{
+  double yangle = stlparam.yangle;
+  char * savetask = multithread.task;
+  multithread.task = "find edges";
+
+  const double min_edge_angle = yangle/180.*M_PI;
+
+  int np1, np2;
+  double ang;
+  int i;
+
+  //(*mycout) << "area=" << Area() << endl;
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      multithread.percent = (double)i/(double)GetReadNT()*100.;
+      
+      const STLTriangle & t1 = GetTriangle(i);
+      //NeighbourTrigs(nt,i);
+
+      for (int j = 1; j <= NONeighbourTrigs(i); j++)
+	{
+	  int nbti = NeighbourTrig(i,j);
+	  if (nbti > i)
+	    {
+	      const STLTriangle & t2 = GetTriangle(nbti);
+
+	      if (t1.IsNeighbourFrom(t2))
+		{
+		  ang = GetAngle(i,nbti);
+		  if (ang < -M_PI*0.5) {ang += 2*M_PI;}
+
+		  t1.GetNeighbourPoints(t2,np1,np2);
+		  
+		  if (fabs(ang) >= min_edge_angle)
+		    {
+		      STLEdge se(np1,np2);
+		      se.SetLeftTrig(i);
+		      se.SetRightTrig(nbti);
+		      AddEdge(se);
+		    }
+		}
+	    }
+	}      
+    }
+  
+  (*mycout) << "added " << GetNE() << " edges" << endl;
+
+  //BuildEdgesPerPoint();
+
+  multithread.percent = 100.;
+  multithread.task = savetask;
+  
+}
+*/
+void STLGeometry :: BuildEdgesPerPoint()
+{
+  //cout << "*** build edges per point" << endl;
+  edgesperpoint.SetSize(GetNP());
+
+  //add edges to points
+  int i;
+  for (i = 1; i <= GetNE(); i++)
+    {
+      //(*mycout) << "EDGE " << GetEdge(i).PNum(1) << " - " << GetEdge(i).PNum(2) << endl;
+      for (int j = 1; j <= 2; j++)
+	{
+	  AddEdgePP(GetEdge(i).PNum(j),i);
+	}
+    }
+}
+
+void STLGeometry :: AddFaceEdges()
+{
+  PrintFnStart("Add starting edges for faces");
+
+  //für Kugel eine STLLine hinzufügen (Vorteil: verfeinerbar, unabhängig von Auflösung der Geometrie!!!):
+  //Grenze von 1. gefundener chart
+
+  Array<int> edgecnt;
+  Array<int> chartindex;
+  edgecnt.SetSize(GetNOFaces());
+  chartindex.SetSize(GetNOFaces());
+
+  int i,j;
+  for (i = 1; i <= GetNOFaces(); i++)
+    {
+      edgecnt.Elem(i) = 0;
+      chartindex.Elem(i) = 0;
+    }
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      int fn = GetTriangle(i).GetFaceNum();
+      if (!chartindex.Get(fn)) {chartindex.Elem(fn) = GetChartNr(i);}
+      for (j = 1; j <= 3; j++)
+	{
+	  edgecnt.Elem(fn) += GetNEPP(GetTriangle(i).PNum(j));
+	}
+    }
+
+  for (i = 1; i <= GetNOFaces(); i++)
+    {
+      if (!edgecnt.Get(i)) {PrintMessage(5,"Face", i, " has no edge!");}
+    }
+  
+  int changed = 0;
+  int k, ap1, ap2;
+  for (i = 1; i <= GetNOFaces(); i++)
+    {
+      if (!edgecnt.Get(i))
+      {
+	const STLChart& c = GetChart(chartindex.Get(i));
+	for (j = 1; j <= c.GetNChartT(); j++)
+	  {
+	    const STLTriangle& t1 = GetTriangle(c.GetChartTrig(j));
+	    for (k = 1; k <= 3; k++)
+	      {
+		int nt = NeighbourTrig(c.GetChartTrig(j),k);
+		if (GetChartNr(nt) != chartindex.Get(i))
+		  {
+		    t1.GetNeighbourPoints(GetTriangle(nt),ap1,ap2);
+		    AddEdge(ap1,ap2);
+		    changed = 1;
+		  }
+	      }
+	  }
+      }
+      
+    }
+  
+  if (changed) BuildEdgesPerPoint();
+  
+}
+
+void STLGeometry :: LinkEdges()
+{
+  PushStatusF("Link Edges");
+  PrintMessage(5,"have now ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree");
+
+  int i;
+
+  lines.SetSize(0);
+  int starte(0);
+  int edgecnt = 0;
+  int found;
+  int rev(0); //indicates, that edge is inserted reverse
+
+  //worked edges
+  Array<int> we(GetNE());
+
+  //setlineendpoints; wenn 180°, dann keine endpunkte
+  //nur punkte mit 2 edges kommen in frage, da bei mehr oder weniger punkten ohnehin ein meshpoint hinkommt
+
+  Vec3d v1,v2;
+  double cos_eca = cos(stlparam.edgecornerangle/180.*M_PI);
+  int ecnt = 0;
+  int lp1, lp2;
+  if (stlparam.edgecornerangle < 180)
+    {
+      for (i = 1; i <= GetNP(); i++)
+	{
+	  if (GetNEPP(i) == 2)
+	    {
+	      if (GetEdge(GetEdgePP(i,1)).PNum(2) == GetEdge(GetEdgePP(i,2)).PNum(1) ||
+		  GetEdge(GetEdgePP(i,1)).PNum(1) == GetEdge(GetEdgePP(i,2)).PNum(2))
+		{
+		  lp1 = 1; lp2 = 2;
+		}
+	      else
+		{
+		  lp1 = 2; lp2 = 1;
+		}
+
+	      v1 = Vec3d(GetPoint(GetEdge(GetEdgePP(i,1)).PNum(1)),
+			 GetPoint(GetEdge(GetEdgePP(i,1)).PNum(2)));
+	      v2 = Vec3d(GetPoint(GetEdge(GetEdgePP(i,2)).PNum(lp1)),
+			 GetPoint(GetEdge(GetEdgePP(i,2)).PNum(lp2)));
+	      if ((v1*v2)/sqrt(v1.Length2()*v2.Length2()) < cos_eca) 
+		{
+		  //(*testout) << "add edgepoint " << i << endl;
+		  SetLineEndPoint(i);
+		  ecnt++;
+		}
+	    }	  
+	}
+    }
+  PrintMessage(5, "added ", ecnt, " mesh_points due to edge corner angle (", 
+	       stlparam.edgecornerangle, " degree)");
+
+  for (i = 1; i <= GetNE(); i++) {we.Elem(i) = 0;}
+
+  while(edgecnt < GetNE())
+    {
+      SetThreadPercent((double)edgecnt/(double)GetNE()*100.);
+
+      STLLine* line = new STLLine(this);
+
+      //find start edge
+      int j = 1;
+      found = 0;
+      //try second time, if only rings are left!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+      int second = 0;
+
+      //find a starting edge at point with 1 or more than 2 edges or at lineendpoint
+      while (!found && j<=GetNE())
+	{
+	  if (!we.Get(j))
+	    {
+	      if (GetNEPP(GetEdge(j).PNum(1)) != 2 || IsLineEndPoint(GetEdge(j).PNum(1)))
+		{
+		  starte = j;
+		  found = 1;
+		  rev = 0;
+		}
+	      else 
+	      if (GetNEPP(GetEdge(j).PNum(2)) != 2 || IsLineEndPoint(GetEdge(j).PNum(2)))
+		{
+		  starte = j;
+		  found = 1;
+		  rev = 1;
+		}
+	      else if (second)
+		{
+		  starte = j;
+		  found = 1;
+		  rev = 0; //0 or 1 are possible
+		}
+	    }
+	  j++;
+	  if (!second && j == GetNE()) {second = 1; j = 1;}
+	}
+
+      if (!found) {PrintSysError("No starting edge found, edgecnt=", edgecnt, ", GETNE=", GetNE());}
+
+      line->AddPoint(GetEdge(starte).PNum(1+rev));
+      line->AddPoint(GetEdge(starte).PNum(2-rev));
+      if (!rev)
+	{
+	  line->AddLeftTrig(GetEdge(starte).LeftTrig());
+	  line->AddRightTrig(GetEdge(starte).RightTrig());
+	}
+      else
+	{
+	  line->AddLeftTrig(GetEdge(starte).RightTrig());
+	  line->AddRightTrig(GetEdge(starte).LeftTrig());
+	}
+      edgecnt++; we.Elem(starte) = 1;
+
+      //add segments to line as long as segments other than starting edge are found or lineendpoint is reached 
+      found = 1;
+      int other;
+      while(found)
+	{
+	  found = 0;
+	  int fp = GetEdge(starte).PNum(2-rev);
+	  if (GetNEPP(fp) == 2 && !IsLineEndPoint(fp))
+	    {
+	      //find the "other" edge of point fp
+	      other = 0;
+	      if (GetEdgePP(fp,1) == starte) {other = 1;}
+
+	      starte = GetEdgePP(fp,1+other);
+
+	      //falls ring -> aufhoeren !!!!!!!!!!!
+	      if (!we.Elem(starte))
+		{
+		  found = 1;
+		  rev = 0;
+		  if (GetEdge(starte).PNum(2) == fp) {rev = 1;}
+		  else if (GetEdge(starte).PNum(1) != fp) {PrintSysError("In Link Edges!");}
+
+		  line->AddPoint(GetEdge(starte).PNum(2-rev));
+		  if (!rev) 
+		    {
+		      line->AddLeftTrig(GetEdge(starte).LeftTrig());
+		      line->AddRightTrig(GetEdge(starte).RightTrig());
+		    }
+		  else
+		    {
+		      line->AddLeftTrig(GetEdge(starte).RightTrig());
+		      line->AddRightTrig(GetEdge(starte).LeftTrig());
+		    }
+		  edgecnt++; we.Elem(starte) = 1;
+		}
+	    }     
+	}
+      AddLine(line);      
+    }
+  PrintMessage(5,"number of lines generated = ", GetNLines());
+
+  //check, which lines must have at least one midpoint
+  INDEX_2_HASHTABLE<int> lineht(GetNLines()+1);
+
+  for (i = 1; i <= GetNLines(); i++)
+    {
+      if (GetLine(i)->StartP() == GetLine(i)->EndP())
+	{
+	  GetLine(i)->DoSplit();	  
+	}
+    }
+
+  for (i = 1; i <= GetNLines(); i++)
+    {
+      INDEX_2 lineep (GetLine(i)->StartP(),GetLine(i)->EndP());
+      lineep.Sort();
+
+      if (lineht.Used (lineep))
+	{
+	  GetLine(i)->DoSplit();
+	  int other = lineht.Get(lineep);
+	  GetLine(other)->DoSplit();
+	}
+      else
+	{
+	  lineht.Set (lineep, i);
+	}
+    }
+
+  for (i = 1; i <= GetNLines(); i++)
+    {
+      STLLine* line = GetLine(i);
+      for (int ii = 1; ii <= line->GetNS(); ii++)
+	{
+	  int ap1, ap2;
+	  line->GetSeg(ii,ap1,ap2);
+	  //	  (*mycout) << "SEG " << p1 << " - " << p2 << endl;
+	}
+    }
+
+  PopStatus();
+}
+
+int STLGeometry :: GetNOBodys()
+{
+  int markedtrigs1 = 0;
+  int starttrig = 1;
+  int i, k, nnt;
+  int bodycnt = 0;
+
+  Array<int> bodynum(GetNT());
+
+  for (i = 1; i <= GetNT(); i++)
+    bodynum.Elem(i)=0;
+
+
+  while (markedtrigs1 < GetNT())
+    {
+      for (i = starttrig; i <= GetNT(); i++)
+	{
+	  if (!bodynum.Get(i))
+	    {
+	      starttrig = i;
+	      break;
+	    }
+	} 
+      //add all triangles around starttriangle, which is reachable without going over an edge
+      Array<int> todolist;
+      Array<int> nextlist;
+      bodycnt++;
+      markedtrigs1++;
+      bodynum.Elem(starttrig) = bodycnt;
+      todolist.Append(starttrig);
+
+      while(todolist.Size())
+	{
+	  for (i = 1; i <= todolist.Size(); i++)
+	    {
+	      //const STLTriangle& tt = GetTriangle(todolist.Get(i));
+	      for (k = 1; k <= NONeighbourTrigs(todolist.Get(i)); k++)
+		{
+		  nnt = NeighbourTrig(todolist.Get(i),k);
+		  if (!bodynum.Get(nnt))
+		    {
+		      nextlist.Append(nnt);
+		      bodynum.Elem(nnt) = bodycnt;
+		      markedtrigs1++;
+		    }
+		}
+	    }
+	  
+	  todolist.SetSize(0);
+	  for (i = 1; i <= nextlist.Size(); i++)
+	    {
+	      todolist.Append(nextlist.Get(i));
+	    }
+	  nextlist.SetSize(0);	  
+	}
+    }
+  PrintMessage(3, "Geometry has ", bodycnt, " separated bodys");
+
+  return bodycnt;
+}
+
+void STLGeometry :: CalcFaceNums()
+{
+  int markedtrigs1 = 0;
+  int starttrig(0);
+  int laststarttrig = 1;
+  int i, k, nnt;
+  facecnt = 0;
+
+
+  for (i = 1; i <= GetNT(); i++)
+    GetTriangle(i).SetFaceNum(0);
+
+
+  while (markedtrigs1 < GetNT())
+    {
+      for (i = laststarttrig; i <= GetNT(); i++)
+	{
+	  if (!GetTriangle(i).GetFaceNum()) 
+	    {
+	      starttrig = i;
+	      laststarttrig = i;
+	      break;
+	    }
+	} 
+      //add all triangles around starttriangle, which is reachable without going over an edge
+      Array<int> todolist;
+      Array<int> nextlist;
+      facecnt++;
+      markedtrigs1++;
+      GetTriangle(starttrig).SetFaceNum(facecnt);
+      todolist.Append(starttrig);
+      int ap1, ap2;
+
+      while(todolist.Size())
+	{
+	  for (i = 1; i <= todolist.Size(); i++)
+	    {
+	      const STLTriangle& tt = GetTriangle(todolist.Get(i));
+	      for (k = 1; k <= NONeighbourTrigs(todolist.Get(i)); k++)
+		{
+		  nnt = NeighbourTrig(todolist.Get(i),k);
+		  STLTriangle& nt = GetTriangle(nnt);
+		  if (!nt.GetFaceNum())
+		    {
+		      tt.GetNeighbourPoints(nt,ap1,ap2);
+		      if (!IsEdge(ap1,ap2))
+			{
+			  nextlist.Append(nnt);
+			  nt.SetFaceNum(facecnt);
+			  markedtrigs1++;
+			}
+		    }
+		}
+	    }
+	  
+	  todolist.SetSize(0);
+	  for (i = 1; i <= nextlist.Size(); i++)
+	    {
+	      todolist.Append(nextlist.Get(i));
+	    }
+	  nextlist.SetSize(0);	  
+	}
+    }
+  GetNOBodys();
+  PrintMessage(3,"generated ", facecnt, " faces");
+}
+ 
+void STLGeometry :: ClearSpiralPoints()
+{
+  spiralpoints.SetSize(GetNP());
+  int i;
+  for (i = 1; i <= spiralpoints.Size(); i++)
+    {
+      spiralpoints.Elem(i) = 0;
+    }
+}
+
+
+void STLGeometry :: BuildSmoothEdges ()
+{
+  if (smoothedges) delete smoothedges;
+
+  smoothedges = new INDEX_2_HASHTABLE<int> (GetNE()/10 + 1);
+
+
+  // Jack: Ok ?
+  //  UseExternalEdges();
+
+  PushStatusF("Build Smooth Edges");
+
+  int i, j;//, k, l;
+  int nt = GetNT();
+  Vec3d ng1, ng2;
+
+  for (i = 1; i <= nt; i++)
+    {
+      if (multithread.terminate)
+	{PopStatus();return;}
+
+      SetThreadPercent(100.0 * (double)i / (double)nt);
+
+      const STLTriangle & trig = GetTriangle (i);
+      
+      ng1 = trig.GeomNormal(points);
+      ng1 /= (ng1.Length() + 1e-24);
+
+      for (j = 1; j <= 3; j++)
+	{ 
+	  int nbt = NeighbourTrig (i, j);
+	  
+	  ng2 = GetTriangle(nbt).GeomNormal(points);
+	  ng2 /= (ng2.Length() + 1e-24);
+	  
+	  
+	  int pi1, pi2;
+
+	  trig.GetNeighbourPoints(GetTriangle(nbt), pi1, pi2);
+
+	  if (!IsEdge(pi1,pi2)) 
+	    {
+	      if (ng1 * ng2 < 0)
+		{
+		  PrintMessage(7,"smoothedge found");
+		  INDEX_2 i2(pi1, pi2);
+		  i2.Sort();
+		  smoothedges->Set (i2, 1);
+		}
+	    }
+	}
+    }
+
+  PopStatus();
+}
+
+
+
+
+
+int STLGeometry :: IsSmoothEdge (int pi1, int pi2) const
+{
+  if (!smoothedges)
+    return 0;
+  INDEX_2 i2(pi1, pi2);
+  i2.Sort();
+  return smoothedges->Used (i2);
+}
+
+
+
+
+//function is not used now
+int IsInArray(int n, const Array<int>& ia)
+{
+  int i;
+  for (i = 1; i <= ia.Size(); i++)
+    {
+      if (ia.Get(i) == n) {return 1;}
+    }
+  return 0;
+}
+
+void STLGeometry :: AddConeAndSpiralEdges()
+{
+  PrintMessage(5,"have now ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree");
+
+  PrintFnStart("AddConeAndSpiralEdges");
+
+  int i,j,k,n;
+  //  int changed = 0;
+
+  //check edges, where inner chart and no outer chart come together without an edge
+  int np1, np2, nt;
+  int cnt = 0;
+
+  for (i = 1; i <= GetNOCharts(); i++)
+    {
+      STLChart& chart = GetChart(i);
+      for (j = 1; j <= chart.GetNChartT(); j++)
+	{
+	  int t = chart.GetChartTrig(j); 
+	  const STLTriangle& tt = GetTriangle(t);
+
+	  for (k = 1; k <= 3; k++)
+	    {
+	      nt = NeighbourTrig(t,k); 
+	      if (GetChartNr(nt) != i && !TrigIsInOC(nt,i))
+		{	      
+		  tt.GetNeighbourPoints(GetTriangle(nt),np1,np2);
+		  if (!IsEdge(np1,np2))
+		    {
+		      STLEdge se(np1,np2);
+		      se.SetLeftTrig(t);
+		      se.SetRightTrig(nt);
+		      int edgenum = AddEdge(se);
+		      AddEdgePP(np1,edgenum);
+		      AddEdgePP(np2,edgenum);
+		      //changed = 1;
+		      PrintWarning("Found a spiral like structure: chart=", i,
+				   ", trig=", t, ", p1=", np1, ", p2=", np2);
+		      cnt++;
+		    }
+		}
+	    }
+	}
+	  
+    }
+
+  PrintMessage(5, "found ", cnt, " spiral like structures");
+  PrintMessage(5, "added ", cnt, " edges due to spiral like structures");
+  
+  cnt = 0;
+  int edgecnt = 0;
+
+  Array<int> trigsaroundp;
+  Array<int> chartpointchecked; //gets number of chart, if in this chart already checked
+  chartpointchecked.SetSize(GetNP());
+
+  for (i = 1; i <= GetNP(); i++)
+    {
+      chartpointchecked.Elem(i) = 0;
+    }
+
+  int onoc, notonoc, tpp, pn;
+  int ap1, ap2, tn1, tn2, l, problem;
+
+  if (!stldoctor.conecheck) {PrintWarning("++++++++++++ \ncone checking deactivated by user!!!!!\n+++++++++++++++"); return ;}
+
+  PushStatus("Find Critical Points");
+
+  int addedges = 0;
+
+  for (i = 1; i <= GetNOCharts(); i++)
+    {
+      SetThreadPercent((double)i/(double)GetNOCharts()*100.);
+      if (multithread.terminate)
+	{PopStatus();return;}
+
+      STLChart& chart = GetChart(i);
+      for (j = 1; j <= chart.GetNChartT(); j++)
+	{
+	  int t = chart.GetChartTrig(j); 
+	  const STLTriangle& tt = GetTriangle(t);
+
+	  for (k = 1; k <= 3; k++)
+	    {
+	      pn = tt.PNum(k);
+	      if (chartpointchecked.Get(pn) == i)
+		{continue;}
+	      
+	      int checkpoint = 0;
+	      for (n = 1; n <= trigsperpoint.EntrySize(pn); n++)
+		{
+		  if (trigsperpoint.Get(pn,n) != t && 
+		      GetChartNr(trigsperpoint.Get(pn,n)) != i &&
+		      !TrigIsInOC(trigsperpoint.Get(pn,n),i)) {checkpoint = 1;};
+		}
+	      if (checkpoint)
+		{
+		  chartpointchecked.Elem(pn) = i;
+
+		  int worked = 0;
+		  int spworked = 0;
+		  GetSortedTrianglesAroundPoint(pn,t,trigsaroundp);
+		  trigsaroundp.Append(t);
+		      
+		  problem = 0;
+		  for (l = 2; l <= trigsaroundp.Size()-1; l++)
+		    {
+		      tn1 = trigsaroundp.Get(l-1);
+		      tn2 = trigsaroundp.Get(l);
+		      const STLTriangle& t1 = GetTriangle(tn1);
+		      const STLTriangle& t2 = GetTriangle(tn2);
+		      t1.GetNeighbourPoints(t2, ap1, ap2);
+		      if (IsEdge(ap1,ap2)) break;
+		      
+		      if (GetChartNr(tn2) != i && !TrigIsInOC(tn2,i)) {problem = 1;}
+		    }
+
+		  if (problem)
+		    {
+		      for (l = 2; l <= trigsaroundp.Size()-1; l++)
+			{
+			  tn1 = trigsaroundp.Get(l-1);
+			  tn2 = trigsaroundp.Get(l);
+			  const STLTriangle& t1 = GetTriangle(tn1);
+			  const STLTriangle& t2 = GetTriangle(tn2);
+			  t1.GetNeighbourPoints(t2, ap1, ap2);
+			  if (IsEdge(ap1,ap2)) break;
+			  
+			  if ((GetChartNr(tn1) == i && GetChartNr(tn2) != i && TrigIsInOC(tn2,i)) ||
+			      (GetChartNr(tn2) == i && GetChartNr(tn1) != i && TrigIsInOC(tn1,i))) 				 
+			    {
+			      if (addedges || !GetNEPP(pn))
+				{
+				  STLEdge se(ap1,ap2);
+				  se.SetLeftTrig(tn1);
+				  se.SetRightTrig(tn2);
+				  int edgenum = AddEdge(se);
+				  AddEdgePP(ap1,edgenum);
+				  AddEdgePP(ap2,edgenum);
+				  edgecnt++;
+				}
+			      if (!addedges && !GetSpiralPoint(pn))
+				{
+				  SetSpiralPoint(pn);
+				  spworked = 1;
+				}
+			      worked = 1;
+			    }
+			}
+		    }
+		  //backwards:
+		  problem = 0;
+		  for (l = trigsaroundp.Size()-1; l >= 2; l--)
+		    {
+		      tn1 = trigsaroundp.Get(l+1);
+		      tn2 = trigsaroundp.Get(l);
+		      const STLTriangle& t1 = GetTriangle(tn1);
+		      const STLTriangle& t2 = GetTriangle(tn2);
+		      t1.GetNeighbourPoints(t2, ap1, ap2);
+		      if (IsEdge(ap1,ap2)) break;
+		      
+		      if (GetChartNr(tn2) != i && !TrigIsInOC(tn2,i)) {problem = 1;}
+		    }
+		  if (problem)
+		    for (l = trigsaroundp.Size()-1; l >= 2; l--)
+		      {
+			tn1 = trigsaroundp.Get(l+1);
+			tn2 = trigsaroundp.Get(l);
+			const STLTriangle& t1 = GetTriangle(tn1);
+			const STLTriangle& t2 = GetTriangle(tn2);
+			t1.GetNeighbourPoints(t2, ap1, ap2);
+			if (IsEdge(ap1,ap2)) break;
+			
+			if ((GetChartNr(tn1) == i && GetChartNr(tn2) != i && TrigIsInOC(tn2,i)) ||
+			    (GetChartNr(tn2) == i && GetChartNr(tn1) != i && TrigIsInOC(tn1,i))) 				 
+			  {
+			    if (addedges || !GetNEPP(pn))
+			      {
+				STLEdge se(ap1,ap2);
+				se.SetLeftTrig(tn1);
+				se.SetRightTrig(tn2);
+				int edgenum = AddEdge(se);
+				AddEdgePP(ap1,edgenum);
+				AddEdgePP(ap2,edgenum);
+				edgecnt++;
+			      }
+			    if (!addedges && !GetSpiralPoint(pn))
+			      {
+				SetSpiralPoint(pn);
+				spworked = 1;
+				//if (GetNEPP(pn) == 0) {(*mycout) << "ERROR: spiralpoint with no edge found!" << endl;}
+			      }
+			    worked = 1;
+			  }
+		      }
+
+		  if (worked)
+		    {		      
+		      //(*testout) << "set edgepoint due to spirals: pn=" << i << endl;
+		      SetLineEndPoint(pn);
+		    }
+		  if (spworked)
+		    {		
+		      /*      
+		      (*mycout) << "Warning: Critical Point " << tt.PNum(k) 
+			   << "( chart " << i << ", trig " << t
+			   << ") has been neutralized!!!" << endl;
+		      */
+		      cnt++;
+		    }
+		  //		  markedpoints.Elem(tt.PNum(k)) = 1;
+		}
+	    }
+	}
+    }
+  PrintMessage(5, "found ", cnt, " critical points!");
+  PrintMessage(5, "added ", edgecnt, " edges due to critical points!");
+
+  PopStatus();
+
+  //search points where inner chart and outer chart and "no chart" trig come together at edge-point
+
+  PrintMessage(7,"search for special chart points");
+  for (i = 1; i <= GetNOCharts(); i++)
+    {
+      STLChart& chart = GetChart(i);
+      for (j = 1; j <= chart.GetNChartT(); j++)
+	{
+	  int t = chart.GetChartTrig(j); 
+	  const STLTriangle& tt = GetTriangle(t);
+
+	  for (k = 1; k <= 3; k++)
+	    {
+	      pn = tt.PNum(k);
+	      if (GetNEPP(pn) == 2)
+		{
+		  onoc = 0;
+		  notonoc = 0;
+		  for (n = 1; n <= trigsperpoint.EntrySize(pn); n++)
+		    {
+		      tpp = trigsperpoint.Get(pn,n);
+		      if (tpp != t && GetChartNr(tpp) != i)
+			{
+			  if (TrigIsInOC(tpp,i)) {onoc = 1;}
+			  if (!TrigIsInOC(tpp,i)) {notonoc = 1;}
+			}
+		    }
+		  if (onoc && notonoc && !IsLineEndPoint(pn)) 
+		    {
+		      GetSortedTrianglesAroundPoint(pn,t,trigsaroundp);
+		      int here = 1; //we start on this side of edge, !here = there
+		      int thereOC = 0;
+		      int thereNotOC = 0;
+		      for (l = 2; l <= trigsaroundp.Size(); l++)
+			{
+			  GetTriangle(trigsaroundp.Get(l-1)).
+			    GetNeighbourPoints(GetTriangle(trigsaroundp.Get(l)), ap1, ap2);
+			  if (IsEdge(ap1,ap2)) {here = (here+1)%2;}
+			  if (!here && TrigIsInOC(trigsaroundp.Get(l),i)) {thereOC = 1;}
+			  if (!here && !TrigIsInOC(trigsaroundp.Get(l),i)) {thereNotOC = 1;}
+			}
+		      if (thereOC && thereNotOC)
+			{
+			  //(*mycout) << "Special OCICnotC - point " << pn << " found!" << endl;
+			  //(*testout) << "set edgepoint due to spirals: pn=" << i << endl;
+			  SetLineEndPoint(pn);
+			}
+		    }
+		}
+	    }
+	}
+    }
+  PrintMessage(5,"have now ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree");
+}
+
+//get trigs at a point, started with starttrig, then every left
+void STLGeometry :: GetSortedTrianglesAroundPoint(int p, int starttrig, Array<int>& trigs)
+{
+  int acttrig = starttrig;
+  trigs.SetAllocSize(trigsperpoint.EntrySize(p));
+  trigs.SetSize(0);
+  trigs.Append(acttrig);
+  int i, j, t, ap1, ap2, locindex1(0), locindex2(0);
+
+  //(*mycout) << "trigs around point " << p << endl;
+
+  int end = 0;
+  while (!end)
+    {
+      const STLTriangle& at = GetTriangle(acttrig);
+      for (i = 1; i <= trigsperpoint.EntrySize(p); i++)
+	{
+	  t = trigsperpoint.Get(p,i);
+	  const STLTriangle& nt = GetTriangle(t);
+	  if (at.IsNeighbourFrom(nt))
+	    {
+	      at.GetNeighbourPoints(nt, ap1, ap2);
+	      if (ap2 == p) {Swap(ap1,ap2);}
+	      if (ap1 != p) {PrintSysError("In GetSortedTrianglesAroundPoint!!!");}
+	      
+	      for (j = 1; j <= 3; j++) 
+		{
+		  if (at.PNum(j) == ap1) {locindex1 = j;};
+		  if (at.PNum(j) == ap2) {locindex2 = j;};
+		}
+	      if ((locindex2+1)%3+1 == locindex1) 
+		{
+		  if (t != starttrig)
+		    {
+		      trigs.Append(t);
+		      //		      (*mycout) << "trig " << t << endl;
+		      acttrig = t;
+		    }
+		  else
+		    {
+		      end = 1;
+		    }
+		  break;
+		}
+	    }
+	}
+    }
+  
+}
+
+/*
+int STLGeometry :: NeighbourTrig(int trig, int nr) const
+{
+  return neighbourtrigs.Get(trig,nr);
+}
+*/
+
+
+
+void STLGeometry :: SmoothGeometry ()
+{
+  int i, j, k;
+  
+  double maxerr0, maxerr;
+
+  for (i = 1; i <= GetNP(); i++)
+    {
+      if (GetNEPP(i)) continue;
+      
+      maxerr0 = 0;
+      for (j = 1; j <= NOTrigsPerPoint(i); j++)
+	{
+	  int tnum = TrigPerPoint(i, j);
+	  double err = Angle (GetTriangle(tnum).Normal (), 
+			      GetTriangle(tnum).GeomNormal(GetPoints()));
+	  if (err > maxerr0)
+	    maxerr0 = err;
+	}
+
+      Point3d pi = GetPoint (i);
+      if (maxerr0 < 1.1) continue;    // about 60 degree
+
+      maxerr0 /= 2;  // should be at least halfen
+      
+      for (k = 1; k <= NOTrigsPerPoint(i); k++)
+	{
+	  const STLTriangle & trig = GetTriangle (TrigPerPoint (i, k));
+	  Point3d c = Center(GetPoint (trig.PNum(1)),
+			     GetPoint (trig.PNum(2)),
+			     GetPoint (trig.PNum(3)));
+
+	  Point3d np = pi + 0.1 * (c - pi);
+	  SetPoint (i, np);
+	  
+	  maxerr = 0;
+	  for (j = 1; j <= NOTrigsPerPoint(i); j++)
+	    {
+	      int tnum = TrigPerPoint(i, j);
+	      double err = Angle (GetTriangle(tnum).Normal (), 
+				  GetTriangle(tnum).GeomNormal(GetPoints()));
+	      if (err > maxerr)
+		maxerr = err;
+	    }
+	  
+	  if (maxerr < maxerr0)
+	    {
+	      pi = np;
+	    }
+	}
+
+      SetPoint (i, pi);
+    }
+}
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/stlgeom.hpp b/contrib/Netgen/libsrc/stlgeom/stlgeom.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..05c7b014fb1e9687c5c24adb7773e52c52ef9a42
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlgeom.hpp
@@ -0,0 +1,459 @@
+#ifndef FILE_STLGEOM
+#define FILE_STLGEOM
+
+/**************************************************************************/
+/* File:   stlgeom.hpp                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Author2: Johannes Gerstmayr                                            */
+/* Date:   26. Jul. 99                                                    */
+/**************************************************************************/
+
+/**
+   STL Geometry
+
+
+   Terminology:
+   
+   Point ... coordinates of STL triangles
+   Triangle  (short Trig)  STL triangle
+   TopEdge .... edge in topology, boundary of STL triangles (many)
+   Edge .... Edges which will occur in the mesh (confirmed edges, less)
+*/
+
+
+#include <meshing.hpp>
+
+
+namespace netgen
+{
+  extern int IsInArray(int n, const Array<int>& ia);
+  extern int AddIfNotExists(Array<int>& list, int x);
+  
+  extern DLL_HEADER MeshingParameters mparam;
+  
+
+
+#include "stltopology.hpp"
+#include "stltool.hpp"
+#include "stlline.hpp"
+ 
+
+
+
+
+
+
+  class STLEdgeDataList
+  {
+    Array<int> storedstatus;
+    STLTopology & geom;
+  public:
+  
+    STLEdgeDataList(STLTopology & ageom);
+    ~STLEdgeDataList();
+
+    void Store ();
+    void Restore ();
+
+    void SetSize(int /* size */) { };
+    void Clear() { };
+    int Size() const { return geom.GetNTE(); }
+    const STLTopEdge & Get(int i) const { return geom.GetTopEdge(i); }
+    STLTopEdge & Elem(int i) { return geom.GetTopEdge(i); }
+
+    int GetNEPP(int pn) const {return geom.NTopEdgesPerPoint(pn); }
+    int GetEdgePP(int pn, int vi) const {return geom.TopEdgePerPoint(pn, vi);};
+
+    //void AddEdgePP(int pn, int vn) { } ;
+
+    void ResetAll();
+    void ChangeStatus(int status1, int status2);
+
+    int GetEdgeNum(int np1, int np2) const
+    { return geom.GetTopEdgeNum (np1, np2); }
+
+    int GetNConfEdges() const;
+
+    void Write(ofstream& of) const;
+    void Read(ifstream& ifs);
+
+    void BuildLineWithEdge(int ep1, int ep2, Array<twoint>& line);
+    void BuildClusterWithEdge(int ep1, int ep2, Array<twoint>& line);
+
+    int GetNEPPStat(int p, int status) const;
+    int GetNConfCandEPP(int p) const;
+  };
+
+
+
+
+
+
+  class STLGeometry : public STLTopology, public NetgenGeometry
+  {
+    // edges to be meshed:
+    Array<STLEdge> edges;
+    //edges per point
+    TABLE<int> edgesperpoint;
+
+    // line: a connection of edges
+    Array<STLLine*> lines;
+    Array<int> lineendpoints; //per geometrypoint, 1 = is endpoint; 0 = no endpoint,
+
+    Array<Vec3d> normals; //normals belong to points!
+
+    Array<twoint> externaledges;
+
+    int undoexternaledges;
+    Array<twoint> storedexternaledges;
+
+    STLEdgeDataList * edgedata;
+    //  STLEdgeDataList edgedata_store;
+    int calcedgedataanglesnew;
+
+    int edgedatastored;
+
+
+
+    int facecnt; 
+    //meshpoint is only set, if an edge is at this point!!!
+
+    Array<int> vicinity; //is one, if a triangle belongs to vicinity (eg. of selecttrig)
+    Array<int> markedtrigs; //is one, if a triangle belongs to marked triangles (calcdirtystrigs)
+    Array<Point3d> markedsegs; //every pointpair is a segment!!!  
+    Array<twoint> selectedmultiedge;
+
+
+    //spiralpoints:
+    Array<int> spiralpoints;
+    //
+    Array<STLChart*> atlas;
+    //marks all already charted trigs with chartnumber
+    Array<int> chartmark; 
+    //outerchartspertrig, ascending sorted
+    TABLE<int> outerchartspertrig;
+
+
+    //for meshing and project:
+    Array<int> meshcharttrigs; //per trig: 1=belong to chart, 0 not
+    int meshchart;
+
+    Array<int> ha_points;  // help array, np long, filled with 0 
+
+
+    // sharp geometric edges not declared as edges
+    // (not considered for spiral check)
+    INDEX_2_HASHTABLE<int> * smoothedges;
+
+
+    //transformation:
+    Vec<3> meshtrignv;
+    Vec<3> ex, ey, ez;
+    Point<3> p1;
+
+  public:
+    int edgesfound;
+    int surfacemeshed;
+    int surfaceoptimized;
+    int volumemeshed;
+
+    int trigsconverted; //when STLTriangles exist -> 1
+
+    //for selecting nodes
+    //int selecttrig, nodeofseltrig;
+
+    //only for testing;
+    Array<STLLine*> meshlines;
+    Array<Point3d> meshpoints;
+
+  public:
+    STLGeometry();
+    virtual ~STLGeometry();
+
+
+    void Clear();
+
+    virtual void Save (string filename) const;
+
+
+    void STLInfo(double* data);
+    //stldoctor:
+    void SmoothNormals();
+    void MarkNonSmoothNormals();
+
+    void CalcEdgeData();
+    void CalcEdgeDataAngles();
+
+    const STLEdgeDataList& EdgeDataList() const {return *edgedata;}
+
+    void UndoEdgeChange();
+    void StoreEdgeData();
+    void RestoreEdgeData();
+
+    //void ClearSelectedMultiEdge() {selectedmultiedge.SetSize(0);}
+    //void AddSelectedMultiEdge(twoint ep) {selectedmultiedge.Append(ep);}
+    //int SelectedMultiEdgeSize() {return selectedmultiedge.Size();}
+    const Array<twoint>& SelectedMultiEdge() {return selectedmultiedge;}
+    twoint GetNearestSelectedDefinedEdge();
+    void BuildSelectedMultiEdge(twoint ep);
+    void BuildSelectedEdge(twoint ep);
+    void BuildSelectedCluster(twoint ep);
+
+    void ImportEdges();
+    void AddEdges(const Array<Point<3> >& eps);
+    void ExportEdges();
+    void LoadEdgeData(const char* file);
+    void SaveEdgeData(const char* file);
+    //  void SetEdgeAtSelected(int mode);
+  
+
+    void STLDoctorConfirmEdge();
+    void STLDoctorCandidateEdge();
+    void STLDoctorExcludeEdge();
+    void STLDoctorUndefinedEdge();
+
+    void STLDoctorSetAllUndefinedEdges();
+    void STLDoctorEraseCandidateEdges();
+    void STLDoctorConfirmCandidateEdges();
+    void STLDoctorConfirmedToCandidateEdges();
+
+    void STLDoctorDirtyEdgesToCandidates();
+    void STLDoctorLongLinesToCandidates();
+
+    void UndoExternalEdges();
+    void StoreExternalEdges();
+    void RestoreExternalEdges();
+
+    void ImportExternalEdges(const char * filename);  // Flame edges, JS
+    //  void LoadExternalEdges();
+
+    void BuildExternalEdgesFromEdges();
+    void SaveExternalEdges();
+    void AddExternalEdgeAtSelected();
+    void AddClosedLinesToExternalEdges();
+    void AddLongLinesToExternalEdges();
+    void AddAllNotSingleLinesToExternalEdges();
+    void STLDoctorBuildEdges();
+    void AddExternalEdgesFromGeomLine();
+    void DeleteDirtyExternalEdges();
+    void DeleteExternalEdgeAtSelected();
+    void DeleteExternalEdgeInVicinity();
+    void AddExternalEdge(int p1, int p2);
+    void DeleteExternalEdge(int p1, int p2);
+    int IsExternalEdge(int p1, int p2);
+    int NOExternalEdges() const {return externaledges.Size();}
+    twoint GetExternalEdge(int i) const {return externaledges.Get(i);}
+
+    void DestroyDirtyTrigs();
+    void CalcNormalsFromGeometry();
+    void MoveSelectedPointToMiddle();
+    void NeighbourAnglesOfSelectedTrig();
+    void PrintSelectInfo();
+    void ShowSelectedTrigChartnum();
+    void ShowSelectedTrigCoords();
+    void SmoothGeometry ();
+
+
+    void LoadMarkedTrigs();
+    void SaveMarkedTrigs();
+    void ClearMarkedSegs() {markedsegs.SetSize(0);}
+    void AddMarkedSeg(const Point<3> & ap1, const Point<3> & ap2) 
+    {
+      markedsegs.Append(ap1);markedsegs.Append(ap2);
+    }
+
+    void GetMarkedSeg(int i, Point<3> & ap1, Point<3> & ap2) 
+    {
+      ap1=markedsegs.Get(i*2-1); 
+      ap2=markedsegs.Get(i*2);
+    }
+    int GetNMarkedSegs() {return markedsegs.Size()/2;}
+    void CalcVicinity(int starttrig);
+    void GetVicinity(int starttrig, int size, Array<int>& vic);
+
+    int Vicinity(int trig) const;
+
+    void InitMarkedTrigs();
+    void MarkDirtyTrigs();
+    void SmoothDirtyTrigs();
+    void GeomSmoothRevertedTrigs();
+    void MarkRevertedTrigs();
+    double CalcTrigBadness(int i);
+    int IsMarkedTrig(int trig) const;
+    void SetMarkedTrig(int trig, int num);
+    void MarkTopErrorTrigs ();
+
+    //Selected triangle
+    void SetSelectTrig(int trig);
+    int GetSelectTrig() const;
+    void SetNodeOfSelTrig(int n);
+    int GetNodeOfSelTrig() const;
+
+
+    int AddNormal(const Vec3d& n) {return normals.Append(n);}
+    const Vec3d & GetNormal(int nr) const {return normals.Get(nr);}
+    void SetNormal(int nr, const Vec3d& n) {normals.Elem(nr) = n;}
+
+    int AddEdge(const STLEdge& v) {return edges.Append(v);}
+    int AddEdge(int p1, int p2);
+
+    STLEdge GetEdge(int nr) {return edges.Get(nr);}
+    int GetNE() {return edges.Size();}
+
+    double Area();
+
+    double GetAngle(int t1, int t2);
+    double GetGeomAngle(int t1, int t2);
+    //if triangles t1 and t2 touch, return 1 and in p1, p2 the touching points
+    //int TrigsTouch(int t1, int t2, int& p1, int& p2);
+
+
+  
+    ///
+
+    ///ReadTriangle->STLTriangle, initialise some important variables, always after load!!!
+    virtual void InitSTLGeometry (const Array<STLReadTriangle> & readtrigs);
+    virtual void TopologyChanged(); //do some things, if topology changed!
+    int CheckGeometryOverlapping();
+
+    //get NO edges per point
+    int GetEPPSize() const {return edgesperpoint.Size();};
+    int GetNEPP(int pn) 
+    {
+      if (edgesperpoint.Size() == 0) {BuildEdgesPerPoint();}
+      return edgesperpoint.EntrySize(pn);
+    };
+    int GetEdgePP(int pn, int vi)
+    {
+      if (edgesperpoint.Size() == 0) {BuildEdgesPerPoint();}
+      return edgesperpoint.Get(pn,vi);
+    };
+    void AddEdgePP(int pn, int vn) {edgesperpoint.Add1(pn,vn);};
+    //von 2 punkten ermitteln, ob sie eine Kante sind
+    int IsEdge(int p1, int p2);
+    int IsEdgeNum(int p1, int p2);
+
+    ///Build EdgeSegments
+    void ClearEdges();
+    void BuildEdges();
+    void BuildEdgesPerPoint();
+    void UseExternalEdges();
+
+
+    void FindEdgesFromAngles();
+    void CalcFaceNums();
+    int GetNOBodys();
+    int GetNOFaces() {return facecnt;}
+    void LinkEdges();
+
+    void AddConeAndSpiralEdges();
+    void AddFaceEdges(); //each face should have at least one starting edge (outherwise it won't be meshed)
+
+    void GetDirtyChartTrigs(int chartnum, STLChart& chart, const Array<int>& outercharttrigs, 
+			    Array<int>& chartpointchecked, Array<int>& dirtytrigs);
+
+    void ClearSpiralPoints();
+    void SetSpiralPoint(int pn) {spiralpoints.Elem(pn) = 1;};
+    int GetSpiralPoint(int pn) const {return spiralpoints.Get(pn);};
+
+    void GetSortedTrianglesAroundPoint(int p, int starttrig, Array<int>& trigs);
+
+    // smooth edges: sharp geometric edges not declared as edges
+    void BuildSmoothEdges ();
+    int IsSmoothEdge (int pi1, int pi2) const;
+
+
+    //make charts with regions of a max. angle
+    void MakeAtlas(class Mesh & mesh);
+
+    //outerchartspertrig, sorted!
+    int GetOCPTSize() const {return outerchartspertrig.Size();};
+    int GetNOCPT(int tn) const {return outerchartspertrig.EntrySize(tn);};
+    int GetOCPT(int tn, int vi) const {return outerchartspertrig.Get(tn,vi);};
+    void SetOCPT(int tn, int vi, int ocn) {outerchartspertrig.Set(tn,vi,ocn);};
+    void AddOCPT(int tn, int ocn) {outerchartspertrig.Add1(tn, ocn);};
+    int TrigIsInOC(int tn, int ocn) const;
+ 
+    //get chart number of a trig or 0 if unmarked
+    int GetChartNr(int i) const;
+    int GetMarker(int i) const 
+    { return chartmark.Get(i); }
+    void SetMarker(int nr, int m);
+    int GetNOCharts() const;
+    //get a chart from atlas
+    const STLChart& GetChart(int nr) const;
+    STLChart& GetChart(int nr) {return *(atlas.Get(nr));};
+    int AtlasMade() const;
+  
+    void GetInnerChartLimes(Array<twoint>& limes, int chartnum);
+
+    //FOR MESHING
+    int GetMeshChartNr () { return meshchart; }
+    void GetMeshChartBoundary (Array<Point2d > & points,
+			       Array<Point3d > & points3d,
+			       Array<INDEX_2> & lines, double h);
+
+
+    Point<3> PointBetween(const Point<3> & p1, int t1, const Point<3> & p2, int t2);
+
+    //select triangles in meshcharttrigs of actual (defined by trig) whole chart
+    void PrepareSurfaceMeshing();
+    //
+    void DefineTangentialPlane(const Point<3> & ap1, const Point<3> & ap2, int trig);
+    //
+    void SelectChartOfTriangle (int trignum);
+    //
+    void SelectChartOfPoint (const Point<3> & p);
+    //
+    const Vec<3> & GetChartNormalVector () const { return meshtrignv; }
+
+    // list of trigs
+    void ToPlane (const Point<3> & locpoint, int * trigs, Point<2> & plainpoint, 
+		  double h, int& zone, int checkchart);
+    //return 0, wenn alles OK, 1 sonst
+    int FromPlane (const Point<2> & plainpoint, Point<3> & locpoint, double h);
+  
+    //get nearest point in actual chart and return any triangle where it lies on
+    int ProjectNearest(Point<3> & p3d) const;
+    //project point with normal nv from last define tangential plane
+
+    int LastTrig() const;
+    int Project(Point<3> & p3d) const;
+    int ProjectOnWholeSurface (Point<3> & p3d) const;
+
+    int GetNLines() const {return lines.Size();}
+    int AddLine(STLLine* line) {return lines.Append(line);}
+    STLLine* GetLine(int nr) const {return lines.Get(nr);}
+    int GetLineP(int lnr, int pnr) const {return lines.Get(lnr)->PNum(pnr);}
+    int GetLineNP(int nr) const {return lines.Get(nr)->NP();}
+
+    void SetLineEndPoint(int pn);
+    int IsLineEndPoint(int pn);
+    int LineEndPointsSet() const {return lineendpoints.Size() == GetNP();}
+    void ClearLineEndPoints();
+
+    void RestrictLocalH(class Mesh & mesh, double gh);
+    void RestrictLocalHCurv(class Mesh & mesh, double gh);
+    void RestrictHChartDistOneChart(int chartnum, Array<int>& acttrigs, class Mesh & mesh, 
+				    double gh, double fact, double minh);
+
+    friend class MeshingSTLSurface;
+
+
+    virtual int GenerateMesh (Mesh*& mesh, MeshingParameters & mparam,
+			      int perfstepsstart, int perfstepsend);
+    
+    virtual const Refinement & GetRefinement () const;
+  };
+ 
+
+#include "meshstlsurface.hpp"
+
+
+
+  extern int STLMeshingDummy (STLGeometry* stlgeometry, Mesh*& mesh, MeshingParameters & mparam,
+			      int perfstepsstart, int perfstepsend);
+
+
+}
+#endif
diff --git a/contrib/Netgen/libsrc/stlgeom/stlgeomchart.cpp b/contrib/Netgen/libsrc/stlgeom/stlgeomchart.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..105ba8c6413a74c742c29182574d5b96f887105e
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlgeomchart.cpp
@@ -0,0 +1,798 @@
+//20.11.1999 third part of stlgeom.cc, functions with chart and atlas
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+#include <meshing.hpp>
+
+#include "stlgeom.hpp"
+
+namespace netgen
+{
+
+int chartdebug = 0;
+
+
+
+void STLGeometry :: MakeAtlas(Mesh & mesh)
+{
+
+  double h, h2;
+
+  h = mparam.maxh;
+   
+
+  PushStatusF("Make Atlas");
+
+  int i,j,k,l;
+
+  double atlasminh = 5e-3 * Dist (boundingbox.PMin(), boundingbox.PMax());
+  PrintMessage(5, "atlasminh = ", atlasminh);
+
+  //speedup for make atlas
+  if (GetNT() > 50000)
+    {
+      mesh.SetGlobalH(0.05*Dist (boundingbox.PMin(), boundingbox.PMax()));
+    }
+
+
+  atlas.SetSize(0);
+  ClearSpiralPoints();
+  BuildSmoothEdges();
+  
+
+  double chartangle = stlparam.chartangle;
+  double outerchartangle = stlparam.outerchartangle;
+
+  chartangle = chartangle/180.*M_PI;
+  outerchartangle = outerchartangle/180.*M_PI;
+
+  double coschartangle = cos(chartangle);
+  double cosouterchartangle = cos(outerchartangle);
+  double cosouterchartanglehalf = cos(0.5*outerchartangle);
+  double sinchartangle = sin(chartangle);
+  double sinouterchartangle = sin(outerchartangle);
+
+  Array<int> outermark(GetNT()); //marks all trigs form actual outer region
+  Array<int> outertested(GetNT()); //marks tested trigs for outer region
+  Array<int> pointstochart(GetNP()); //point in chart becomes chartnum
+  Array<int> innerpointstochart(GetNP()); //point in chart becomes chartnum
+  Array<int> chartpoints; //point in chart becomes chartnum
+  Array<int> innerchartpoints;
+  Array<int> dirtycharttrigs;
+  Array<int> chartpointchecked;
+
+  Array<int> chartdistacttrigs; //outercharttrigs
+  chartdistacttrigs.SetSize(GetNT());
+  for (i = 1; i <= GetNT(); i++)
+    {
+      chartdistacttrigs.Elem(i) = 0;
+    }
+  
+  STLBoundary chartbound(this); //knows the actual chart boundary
+  //int chartboundarydivisions = 10;
+  markedsegs.SetSize(0); //for testing!!!
+
+  chartpointchecked.SetSize(GetNP()); //for dirty-chart-trigs
+
+  outermark.SetSize(GetNT());
+  outertested.SetSize(GetNT());
+  pointstochart.SetSize(GetNP());
+  innerpointstochart.SetSize(GetNP());
+  chartmark.SetSize(GetNT());
+
+  for (i = 1; i <= GetNP(); i++)
+    {
+      innerpointstochart.Elem(i) = 0;
+      pointstochart.Elem(i) = 0;
+      chartpointchecked.Elem(i) = 0;
+    }
+
+  double eps = 1e-12 * Dist (boundingbox.PMin(), boundingbox.PMax());
+
+  int spiralcheckon = stldoctor.spiralcheck;
+  if (!spiralcheckon) {PrintWarning("++++++++++++\nspiral deactivated by user!!!!\n+++++++++++++++"); }
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      chartmark.Elem(i) = 0;
+    }
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      outermark.Elem(i) = 0;
+      outertested.Elem(i) = 0;
+    }
+
+  int markedtrigcnt = 0;
+  int found = 1;
+  double atlasarea = Area();
+  double workedarea = 0;
+  double showinc = 100.*5000./(double)GetNT();
+  double nextshow = 0;
+  Point<3> startp;
+  int lastunmarked = 1;
+  int prelastunmarked;
+
+  PrintMessage(5,"one dot per 5000 triangles: ");
+
+  while(markedtrigcnt < GetNT() && found)
+    {      
+      if (multithread.terminate)
+	{PopStatus();return;}
+
+      if (workedarea / atlasarea*100. >= nextshow) 
+      	{PrintDot(); nextshow+=showinc;}
+
+      SetThreadPercent(100.0 * workedarea / atlasarea);
+
+      /*
+      for (j = 1; j <= GetNT(); j++)
+	{
+	  outermark.Elem(j) = 0;
+	}
+      */
+      STLChart * chart = new STLChart(this);
+      atlas.Append(chart);
+
+      //find unmarked trig
+      prelastunmarked = lastunmarked;
+      j = lastunmarked;
+      found = 0;
+      while (!found && j <= GetNT())
+	{
+	  if (!GetMarker(j)) {found = 1; lastunmarked = j;}
+	  else {j++;}
+	}
+
+      chartpoints.SetSize(0);  
+      innerchartpoints.SetSize(0);
+      chartbound.Clear();
+      chartbound.SetChart(chart);
+
+      if (!found) {PrintSysError("Make Atlas, no starttrig found"); return;}
+
+      //find surrounding trigs
+      int starttrig = j;
+
+      double tdist;
+      startp = GetPoint(GetTriangle(starttrig).PNum(1));
+
+      int accepted;
+      int chartnum = GetNOCharts();
+	  
+      Vec<3> sn = GetTriangle(starttrig).Normal();
+      chart->SetNormal (startp, sn);
+
+
+      SetMarker(starttrig, chartnum);
+      markedtrigcnt++;
+      chart->AddChartTrig(starttrig);
+      chartbound.AddTriangle(GetTriangle(starttrig));
+
+      workedarea += GetTriangle(starttrig).Area(points);
+
+      for (i = 1; i <= 3; i++)
+	{	      
+	  innerpointstochart.Elem(GetTriangle(starttrig).PNum(i)) = chartnum;
+	  pointstochart.Elem(GetTriangle(starttrig).PNum(i)) = chartnum;
+	  chartpoints.Append(GetTriangle(starttrig).PNum(i));
+	  innerchartpoints.Append(GetTriangle(starttrig).PNum(i));
+	}
+
+      Vec<3> n2, n3;
+      int changed = 1;
+      int nt;
+      int ic;
+      int oldstartic = 1;
+      int oldstartic2;
+      int np1, np2;
+
+      while (changed)
+	{   
+	  changed = 0;
+	  oldstartic2 = oldstartic;
+	  oldstartic = chart->GetNT();
+	  //	      for (ic = oldstartic2; ic <= chart->GetNT(); ic++)
+	  for (ic = oldstartic2; ic <= oldstartic; ic++)
+	    {
+	      i = chart->GetTrig(ic);
+	      if (GetMarker(i) == chartnum)
+		{
+		  for (j = 1; j <= NONeighbourTrigs(i); j++)
+		    {
+		      nt = NeighbourTrig(i,j);
+		      GetTriangle(i).GetNeighbourPoints(GetTriangle(nt),np1,np2);
+		      if (GetMarker(nt) == 0 && !IsEdge(np1,np2))
+			{
+			  n2 = GetTriangle(nt).Normal();
+			  if ( (n2 * sn) >= coschartangle )
+			    {
+			      
+			      accepted = 1;
+			      /*
+				//alter spiralentest, schnell, aber ungenau
+			      for (k = 1; k <= 3; k++)
+				{
+				  //find overlapping charts:
+				  Point3d pt = GetPoint(GetTriangle(nt).PNum(k));
+				  if (innerpointstochart.Get(GetTriangle(nt).PNum(k)) != chartnum)
+				    {
+				      for (l = 1; l <= chartpoints.Size(); l++)
+					{
+					  Vec3d vptpl(GetPoint(chartpoints.Get(l)), pt);
+					  double vlen = vptpl.Length();
+					  if (vlen > 0)
+					    {
+					      vptpl /= vlen;
+					      if ( fabs( vptpl * sn) > sinchartangle )
+						{
+						  accepted = 0;
+						  break;
+						}
+					    } 
+					}
+
+				    }
+				}
+			      */
+			      
+			      int nnp1, nnp2; 
+			      int nnt; 
+			      //find overlapping charts exacter: 
+			      for (k = 1; k <= 3; k++) 
+				{ 
+				  nnt = NeighbourTrig(nt,k);
+				  if (GetMarker(nnt) != chartnum)
+				    {
+				      GetTriangle(nt).GetNeighbourPoints(GetTriangle(nnt),nnp1,nnp2);
+
+				      accepted = chartbound.TestSeg(GetPoint(nnp1),
+								    GetPoint(nnp2),
+								    sn,sinchartangle,1 /*chartboundarydivisions*/ ,points, eps);
+
+
+				      n3 = GetTriangle(nnt).Normal();
+				      if ( (n3 * sn) >= coschartangle  &&
+					   IsSmoothEdge (nnp1, nnp2) )
+					accepted = 1;
+				    }
+				  if (!accepted) {break;}
+				}
+			      
+			      /*
+				mindist = 1E50;
+				for (int ii = 1; ii <= 3; ii++)
+				{
+				tdist = Dist(GetPoint(GetTriangle(nt).PNum(ii)),startp);
+				if (tdist < mindist) {mindist = tdist;}
+				}
+				if (mindist > maxdist1) {accepted = 0;}
+			      */
+
+			      if (accepted)
+				{
+				  SetMarker(nt, chartnum); 
+				  changed = 1;
+				  markedtrigcnt++;
+				  workedarea += GetTriangle(nt).Area(points);
+				  chart->AddChartTrig(nt);
+
+				  chartbound.AddTriangle(GetTriangle(nt));
+
+				  for (k = 1; k <= 3; k++)
+				    {
+				      if (innerpointstochart.Get(GetTriangle(nt).PNum(k))
+					  != chartnum) 
+					{
+					  innerpointstochart.Elem(GetTriangle(nt).PNum(k)) = chartnum;
+					  pointstochart.Elem(GetTriangle(nt).PNum(k)) = chartnum;
+					  chartpoints.Append(GetTriangle(nt).PNum(k));
+					  innerchartpoints.Append(GetTriangle(nt).PNum(k));
+					}
+				    }
+				}
+			    }	       
+			}
+		    }
+		}
+	    }
+	}
+
+
+      //find outertrigs
+
+      //      chartbound.Clear(); 
+      // warum, ic-bound auf edge macht Probleme js ???
+
+
+      outermark.Elem(starttrig) = chartnum;
+      //chart->AddOuterTrig(starttrig);
+      changed = 1;
+      oldstartic = 1;
+      while (changed)
+	{   
+	  changed = 0;
+	  oldstartic2 = oldstartic;
+	  oldstartic = chart->GetNT();
+	  //for (ic = oldstartic2; ic <= chart->GetNT(); ic++)
+	  for (ic = oldstartic2; ic <= oldstartic; ic++)
+	    {
+	      i = chart->GetTrig(ic);
+
+	      if (outermark.Get(i) == chartnum)
+		{
+		  for (j = 1; j <= NONeighbourTrigs(i); j++)
+		    {
+		      nt = NeighbourTrig(i,j);
+		      if (outermark.Get(nt) == chartnum)
+			continue;
+
+		      const STLTriangle & ntrig = GetTriangle(nt);
+		      GetTriangle(i).GetNeighbourPoints(GetTriangle(nt),np1,np2);
+
+		      if (IsEdge (np1, np2))
+			continue;
+
+
+		      /*
+		      if (outertested.Get(nt) == chartnum)
+			continue;
+		      */
+		      outertested.Elem(nt) = chartnum;
+			  
+
+		      n2 = GetTriangle(nt).Normal();
+		      /*
+			double ang;
+			ang = Angle(n2,sn);
+			if (ang < -M_PI*0.5) {ang += 2*M_PI;}
+			    
+			(*testout) << "ang < ocharang = " << (fabs(ang) <= outerchartangle);
+			(*testout) << " = " << ( (n2 * sn) >= cosouterchartangle) << endl;
+			    
+			//			      if (fabs(ang) <= outerchartangle) 
+		      */
+		      //abfragen, ob noch im tolerierten Winkel
+		      if ( (n2 * sn) >= cosouterchartangle )
+			{
+			  accepted = 1;
+
+			  int isdirtytrig = 0;
+			  Vec<3> gn = GetTriangle(nt).GeomNormal(points);
+			  double gnlen = gn.Length();
+			  
+			  if (n2 * gn <= cosouterchartanglehalf * gnlen)
+			    {isdirtytrig = 1;}
+			  
+			  //zurueckweisen, falls eine Spiralartige outerchart entsteht
+			  int nnp1, nnp2; 
+			  int nnt; 
+			  //find overlapping charts exacter: 
+			  //do not check dirty trigs!
+			  
+
+			  if (spiralcheckon && !isdirtytrig)
+			    for (k = 1; k <= 3; k++) 
+			      { 
+				nnt = NeighbourTrig(nt,k);
+				
+				if (outermark.Elem(nnt) != chartnum)
+				  {
+				    GetTriangle(nt).GetNeighbourPoints(GetTriangle(nnt),nnp1,nnp2);
+
+				    accepted = 
+				      chartbound.TestSeg(GetPoint(nnp1),GetPoint(nnp2),
+							 sn,sinouterchartangle, 0 /*chartboundarydivisions*/ ,points, eps);
+				    
+
+				    n3 = GetTriangle(nnt).Normal();
+				    if ( (n3 * sn) >= cosouterchartangle  &&
+					 IsSmoothEdge (nnp1, nnp2) )
+				      accepted = 1;
+				  }
+				if (!accepted) {break;}
+			      }
+			  
+			  //}
+		      
+		      
+			  // outer chart is only small environment of
+			  //    inner chart:
+			  if (accepted)
+			    {
+			      accepted = 0;
+
+			      for (k = 1; k <= 3; k++)
+				{
+				  if (innerpointstochart.Get(ntrig.PNum(k)) == chartnum)
+				    {
+				      accepted = 1; 
+				      break;
+				    }
+				}
+
+			      if (!accepted)
+				for (k = 1; k <= 3; k++)
+				  {
+				    Point<3> pt = GetPoint(ntrig.PNum(k));					  
+				    h2 = sqr(mesh.GetH(pt));
+				      
+				    for (l = 1; l <= innerchartpoints.Size(); l++)
+				      {
+					tdist = Dist2(pt, GetPoint (innerchartpoints.Get(l)));
+					if (tdist < 4 * h2)
+					  {
+					    accepted = 1; 
+					    break;
+					  }
+				      }
+				    if (accepted) {break;}
+				  }
+			    }
+
+			      
+			  if (accepted)
+			    {
+			      changed = 1;
+			      outermark.Elem(nt) = chartnum;
+
+			      if (GetMarker(nt) != chartnum)
+				{
+				  chartbound.AddTriangle(GetTriangle(nt));
+				  chart->AddOuterTrig(nt);
+				  for (k = 1; k <= 3; k++)
+				    {
+				      if (pointstochart.Get(GetTriangle(nt).PNum(k))
+					  != chartnum) 
+					{
+					  pointstochart.Elem(GetTriangle(nt).PNum(k)) = chartnum;
+					  chartpoints.Append(GetTriangle(nt).PNum(k));
+					}
+				    }
+				}
+			    }
+			}	       
+		    }
+		}
+	    }            
+	}
+      //end of while loop for outer chart
+      GetDirtyChartTrigs(chartnum, *chart, outermark, chartpointchecked, dirtycharttrigs);
+      //dirtycharttrigs are local (chart) point numbers!!!!!!!!!!!!!!!!
+
+      if (dirtycharttrigs.Size() != 0 && 
+	  (dirtycharttrigs.Size() != chart->GetNChartT() || dirtycharttrigs.Size() != 1))
+	{
+	  if (dirtycharttrigs.Size() == chart->GetNChartT() && dirtycharttrigs.Size() != 1)
+	    {
+	      //if all trigs would be eliminated -> leave 1 trig!
+	      dirtycharttrigs.SetSize(dirtycharttrigs.Size() - 1);
+	    }
+	  for (k = 1; k <= dirtycharttrigs.Size(); k++)
+	    {
+	      int tn = chart->GetChartTrig(dirtycharttrigs.Get(k));
+	      outermark.Elem(tn) = 0; //not necessary, for later use
+	      SetMarker(tn, 0); 
+	      markedtrigcnt--;
+	      workedarea -= GetTriangle(tn).Area(points);
+	    }
+	  chart->MoveToOuterChart(dirtycharttrigs);
+	  lastunmarked = 1;
+	  lastunmarked = prelastunmarked;
+	}
+
+      //calculate an estimate meshsize, not to produce to large outercharts, with factor 2 larger!
+      RestrictHChartDistOneChart(chartnum, chartdistacttrigs, mesh, h, 0.5, atlasminh);
+    }
+  
+  PrintMessage(5,"");
+  PrintMessage(5,"NO charts=", atlas.Size());
+
+  int cnttrias = 0;
+  //int found2;
+  outerchartspertrig.SetSize(GetNT());
+
+  for (i = 1; i <= atlas.Size(); i++)
+    {
+      //found2 = 1;
+      for (j = 1; j <= GetChart(i).GetNT(); j++)
+	{
+	  int tn = GetChart(i).GetTrig(j);
+	  AddOCPT(tn,i);
+
+	}
+      
+      cnttrias += GetChart(i).GetNT();
+    }
+  PrintMessage(5, "NO outer chart trias=", cnttrias);
+
+  //sort outerchartspertrig
+  for (i = 1; i <= GetNT(); i++)
+    {
+      int swap;
+      for (k = 1; k < GetNOCPT(i); k++)
+	{
+
+	  for (j = 1; j < GetNOCPT(i); j++)
+	    {
+	      swap = GetOCPT(i,j);
+	      if (GetOCPT(i,j+1) < swap)
+		{
+		  SetOCPT(i,j,GetOCPT(i,j+1));
+		  SetOCPT(i,j+1,swap);
+		}
+	    }
+	}
+      
+      // check make atlas
+      if (GetChartNr(i) <= 0 || GetChartNr(i) > GetNOCharts()) 
+	{
+	  PrintSysError("Make Atlas: chartnr(", i, ")=0!!");
+	};
+    }
+
+  mesh.SetGlobalH(mparam.maxh);
+  mesh.SetMinimalH(mparam.minh);
+  
+  
+  AddConeAndSpiralEdges();
+  
+  PrintMessage(5,"Make Atlas finished");
+
+  PopStatus();
+}
+
+
+int STLGeometry::TrigIsInOC(int tn, int ocn) const
+{
+  if (tn < 1 || tn > GetNT())
+    {
+      // assert (1);
+      abort ();
+      PrintSysError("STLGeometry::TrigIsInOC illegal tn: ", tn);
+      
+      return 0;
+    }
+
+  /*
+  int firstval = 0;
+  int i;
+  for (i = 1; i <= GetNOCPT(tn); i++)
+    {
+      if (GetOCPT(tn, i) == ocn) {firstval = 1;}
+    }
+  */
+
+  int found = 0;
+
+  int inc = 1;
+  while (inc <= GetNOCPT(tn)) {inc *= 2;}
+  inc /= 2;
+
+  int start = inc;
+
+  while (!found && inc > 0)
+    {
+      if (GetOCPT(tn,start) > ocn) {inc = inc/2; start -= inc;}
+      else if (GetOCPT(tn,start) < ocn) {inc = inc/2; if (start+inc <= GetNOCPT(tn)) {start += inc;}}
+      else {found = 1;}
+    }
+
+  return GetOCPT(tn, start) == ocn;
+}
+
+int STLGeometry :: GetChartNr(int i) const
+{
+  if (i > chartmark.Size()) 
+    {
+      PrintSysError("GetChartNr(", i, ") not possible!!!");
+      i = 1;
+    }
+  return chartmark.Get(i);
+}
+/*
+int STLGeometry :: GetMarker(int i) const
+{
+  return chartmark.Get(i);
+}
+*/
+void STLGeometry :: SetMarker(int nr, int m) 
+{
+  chartmark.Elem(nr) = m;
+}
+int STLGeometry :: GetNOCharts() const
+{
+  return atlas.Size();
+}
+const STLChart& STLGeometry :: GetChart(int nr) const 
+{
+  if (nr > atlas.Size()) 
+    {
+      PrintSysError("GetChart(", nr, ") not possible!!!");
+      nr = 1;
+    }
+  return *(atlas.Get(nr));
+}
+
+int STLGeometry :: AtlasMade() const
+{
+  return chartmark.Size() != 0;
+}
+
+
+//return 1 if not exists
+int AddIfNotExists(Array<int>& list, int x)
+{
+  int i;
+  for (i = 1; i <= list.Size(); i++)
+    {
+      if (list.Get(i) == x) {return 0;} 
+    }
+  list.Append(x);
+  return 1;
+}
+
+void STLGeometry :: GetInnerChartLimes(Array<twoint>& limes, int chartnum)
+{
+  int j, k;
+  
+  int t, nt, np1, np2;
+  
+  limes.SetSize(0);
+
+  STLChart& chart = GetChart(chartnum);
+
+  for (j = 1; j <= chart.GetNChartT(); j++)
+    {
+      t = chart.GetChartTrig(j); 
+      const STLTriangle& tt = GetTriangle(t);
+      for (k = 1; k <= 3; k++)
+	{
+	  nt = NeighbourTrig(t,k); 
+	  if (GetChartNr(nt) != chartnum)
+	    {	      
+	      tt.GetNeighbourPoints(GetTriangle(nt),np1,np2);
+	      if (!IsEdge(np1,np2))
+		{
+		  limes.Append(twoint(np1,np2));
+		  /*
+		  p3p1 = GetPoint(np1);
+		  p3p2 = GetPoint(np2);
+		  if (AddIfNotExists(limes,np1)) 
+		    {
+		      plimes1.Append(p3p1); 
+		      //plimes1trigs.Append(t);
+		      //plimes1origin.Append(np1);
+		    }
+		  if (AddIfNotExists(limes1,np2)) 
+		    {
+		      plimes1.Append(p3p2); 
+		      //plimes1trigs.Append(t);
+		      //plimes1origin.Append(np2); 			      
+		    }
+		  //chart.AddILimit(twoint(np1,np2));
+		  
+		  for (int di = 1; di <= divisions; di++)
+		    {
+		      double f1 = (double)di/(double)(divisions+1.);
+		      double f2 = (divisions+1.-(double)di)/(double)(divisions+1.);
+		      
+		      plimes1.Append(Point3d(p3p1.X()*f1+p3p2.X()*f2,
+					     p3p1.Y()*f1+p3p2.Y()*f2,
+					     p3p1.Z()*f1+p3p2.Z()*f2));
+		      //plimes1trigs.Append(t);
+		      //plimes1origin.Append(0); 			      
+		    }
+		  */
+		}
+	    }
+	}
+    }
+}
+	 
+
+
+void STLGeometry :: GetDirtyChartTrigs(int chartnum, STLChart& chart,
+				       const Array<int>& outercharttrigs,
+				       Array<int>& chartpointchecked,
+				       Array<int>& dirtytrigs)
+{
+  dirtytrigs.SetSize(0);
+  int j,k,n;
+
+  int np1, np2, nt;
+  int cnt = 0;
+
+  for (j = 1; j <= chart.GetNChartT(); j++)
+    {
+      int t = chart.GetChartTrig(j); 
+      const STLTriangle& tt = GetTriangle(t);
+      
+      for (k = 1; k <= 3; k++)
+	{
+	  nt = NeighbourTrig(t,k); 
+	  if (GetChartNr(nt) != chartnum && outercharttrigs.Get(nt) != chartnum)
+	    {	      
+	      tt.GetNeighbourPoints(GetTriangle(nt),np1,np2);
+	      if (!IsEdge(np1,np2))
+		{
+		  dirtytrigs.Append(j); //local numbers!!!
+		  cnt++;
+		  break; //only once per trig!!!
+		}
+	    }
+	}
+    }
+  cnt = 0;
+
+  int ap1, ap2, tn1, tn2, l, problem, pn;
+  Array<int> trigsaroundp;
+
+  for (j = chart.GetNChartT(); j >= 1; j--)
+    {
+      int t = chart.GetChartTrig(j); 
+      const STLTriangle& tt = GetTriangle(t);
+      
+      for (k = 1; k <= 3; k++)
+	{
+	  pn = tt.PNum(k);
+	  //if (chartpointchecked.Get(pn) == chartnum)
+	  //{continue;}
+	  
+	  int checkpoint = 0;
+	  for (n = 1; n <= trigsperpoint.EntrySize(pn); n++)
+	    {
+	      if (trigsperpoint.Get(pn,n) != t && //ueberfluessig???
+		  GetChartNr(trigsperpoint.Get(pn,n)) != chartnum &&
+		  outercharttrigs.Get(trigsperpoint.Get(pn,n)) != chartnum) {checkpoint = 1;};
+	    }
+	  if (checkpoint)
+	    {
+	      chartpointchecked.Elem(pn) = chartnum;
+
+	      GetSortedTrianglesAroundPoint(pn,t,trigsaroundp);
+	      trigsaroundp.Append(t); //ring
+	      
+	      problem = 0;
+	      //forward:
+	      for (l = 2; l <= trigsaroundp.Size()-1; l++)
+		{
+		  tn1 = trigsaroundp.Get(l-1);
+		  tn2 = trigsaroundp.Get(l);
+		  const STLTriangle& t1 = GetTriangle(tn1);
+		  const STLTriangle& t2 = GetTriangle(tn2);
+		  t1.GetNeighbourPoints(t2, ap1, ap2);
+		  if (IsEdge(ap1,ap2)) break;
+		  
+		  if (GetChartNr(tn2) != chartnum && outercharttrigs.Get(tn2) != chartnum) {problem = 1;}
+		}
+
+	      //backwards:
+	      for (l = trigsaroundp.Size()-1; l >= 2; l--)
+		{
+		  tn1 = trigsaroundp.Get(l+1);
+		  tn2 = trigsaroundp.Get(l);
+		  const STLTriangle& t1 = GetTriangle(tn1);
+		  const STLTriangle& t2 = GetTriangle(tn2);
+		  t1.GetNeighbourPoints(t2, ap1, ap2);
+		  if (IsEdge(ap1,ap2)) break;
+		  
+		  if (GetChartNr(tn2) != chartnum && outercharttrigs.Get(tn2) != chartnum) {problem = 1;}
+		}
+	      if (problem && !IsInArray(j,dirtytrigs))
+		{
+		  dirtytrigs.Append(j);
+		  cnt++;
+		  break; //only once per triangle
+		}
+	    }
+	}
+    }
+  
+}
+
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/stlgeommesh.cpp b/contrib/Netgen/libsrc/stlgeom/stlgeommesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c20c6acfc35c1abc6ae8e496df10d04e131b901d
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlgeommesh.cpp
@@ -0,0 +1,1590 @@
+//20.11.1999 second part of stlgeom.cc, mainly mesh functions
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+#include <meshing.hpp>
+
+#include "stlgeom.hpp"
+
+namespace netgen
+{
+int EdgeUsed(int p1, int p2, Array<INDEX_2>& edges, INDEX_2_HASHTABLE<int>& hashtab)
+{
+  if (p1 > p2) {swap (p1,p2);}
+
+  if (hashtab.Used(INDEX_2(p1,p2))) 
+    {return hashtab.Get(INDEX_2(p1,p2));}
+
+  return 0;
+}
+
+Point<3> STLGeometry :: PointBetween(const Point<3> & ap1, int t1, 
+				     const Point<3> & ap2, int t2)
+{
+  //funktioniert nicht in allen Fällen!
+
+  PrintWarning("Point between");
+
+
+  ClearMarkedSegs();
+
+  InitMarkedTrigs();
+  SetMarkedTrig(t1,1);
+  SetMarkedTrig(t2,1);
+
+  TABLE<Point3d> edgepoints;
+  TABLE<double> edgepointdists;
+  TABLE<int> edgepointorigines;
+  TABLE<int> edgepointoriginps;
+
+  Array<int> edgetrigs;
+  Array<INDEX_2> edgepointnums;
+  Array<int> edgetriglocinds;
+
+  int size = 3*GetNT();
+  INDEX_2_HASHTABLE<int> hashtab(size);
+
+  int divisions = 10;
+
+  edgepoints.SetSize(size);
+  edgepointdists.SetSize(size);
+  edgepointorigines.SetSize(size);
+  edgepointoriginps.SetSize(size);
+
+  edgetrigs.SetSize(size);
+  edgepointnums.SetSize(size);
+  edgetriglocinds.SetSize(size);
+
+  Array<int> edgelist1;
+  Array<int> edgelist2;
+
+  edgelist1.SetSize(0);
+  edgelist2.SetSize(0);
+
+
+  int i, j, k, l, m;
+  int edgecnt = 0;
+
+  //first triangle:
+  for (i = 1; i <= 3; i++)
+    {
+      int ptn1 = GetTriangle(t1).PNum(i);
+      int ptn2 = GetTriangle(t1).PNumMod(i+1);
+
+      if (ptn1 > ptn2) {swap(ptn1,ptn2);}
+
+      Point3d pt1 = GetPoint(ptn1);
+      Point3d pt2 = GetPoint(ptn2);
+
+      edgecnt++;
+      edgetrigs.Elem(edgecnt) = t1;
+      edgepointnums.Elem(edgecnt) = INDEX_2(ptn1,ptn2);
+      hashtab.Set(edgepointnums.Get(edgecnt),edgecnt);
+
+      edgetriglocinds.Elem(edgecnt) = i;
+      edgelist1.Append(edgecnt);
+
+      for (j = 1; j <= divisions; j++)
+	{
+	  double lfact = (double)j/(double)divisions;
+	  Point3d pbtw(lfact*pt1.X()+(1.-lfact)*pt2.X(),
+		       lfact*pt1.Y()+(1.-lfact)*pt2.Y(),
+		       lfact*pt1.Z()+(1.-lfact)*pt2.Z());
+
+	  //AddMarkedSeg(ap1,pbtw);
+	
+	  edgepoints.Add1(edgecnt,pbtw);
+	  edgepointdists.Add1(edgecnt,Dist(pbtw,ap1));
+	  edgepointorigines.Add1(edgecnt,0);
+	  edgepointoriginps.Add1(edgecnt,0);
+	}
+    }
+
+  int finished = 0;
+  int endpointorigine = 0;
+  int endpointoriginp = 0;
+  double endpointmindist = 1E50;
+
+  int maxsize = 0;
+  while (!finished)
+    {
+      finished = 1;
+      
+      if (edgelist1.Size() > maxsize) {maxsize = edgelist1.Size();}
+
+      for (i = 1; i <= edgelist1.Size(); i++)
+	{
+	  int en = edgelist1.Get(i);
+	  int trig = edgetrigs.Get(en);
+	  int edgenum = edgetriglocinds.Get(en);
+	  int tn = NeighbourTrigSorted(trig,edgenum);
+
+	  if (tn != t2)
+	    {
+	      for (k = 1; k <= 3; k++)
+		{
+		  int pnt1 = GetTriangle(tn).PNum(k);
+		  int pnt2 = GetTriangle(tn).PNumMod(k+1);
+		      
+		  if (pnt1 > pnt2) {swap(pnt1,pnt2);}
+
+		  Point3d pt1 = GetPoint(pnt1);
+		  Point3d pt2 = GetPoint(pnt2);
+		      
+		  //AddMarkedSeg(pt1,pt2);
+		  
+		  //if (!(pnt1 == ep1 && pnt2 == ep2))
+		  //  {
+		  int edgeused = 0;
+		  edgenum = EdgeUsed(pnt1, pnt2, edgepointnums, hashtab);
+		  if (edgenum != en)
+		    {
+		      if (edgenum != 0) 
+			{edgeused = 1;}
+		      else 
+			{
+			  edgecnt++; 
+			  edgenum = edgecnt;
+			  
+			  edgetrigs.Elem(edgenum) = tn;
+			  edgepointnums.Elem(edgenum) = INDEX_2(pnt1,pnt2);
+			  hashtab.Set(edgepointnums.Get(edgenum),edgenum);
+			  edgetriglocinds.Elem(edgenum) = k;
+			}
+		      
+		      if (edgenum > size || edgenum == 0) {PrintSysError("edgenum = ", edgenum);}
+			  
+		      double minofmindist = 1E50;
+		      int changed = 0;
+		      
+		      for (l = 1; l <= divisions; l++)
+			{
+			  double lfact = (double)l/(double)divisions;
+			  Point3d pbtw(lfact*pt1.X()+(1.-lfact)*pt2.X(),
+				       lfact*pt1.Y()+(1.-lfact)*pt2.Y(),
+				       lfact*pt1.Z()+(1.-lfact)*pt2.Z());
+			  
+			  double mindist = 1E50;
+			  int index=0;
+			  
+			  for (m = 1; m <= divisions; m++)
+			    {
+			      const Point3d& p = edgepoints.Get(en,m);
+			      if (Dist(pbtw,p) + edgepointdists.Get(en,m) < mindist)
+				{mindist = Dist(pbtw,p) + edgepointdists.Get(en,m); index = m;}
+			    }
+			  
+			  //if (mindist < endpointmindist) {finished = 0;}
+			  if (mindist < minofmindist) {minofmindist = mindist;}
+			  
+			  
+			  if (!edgeused)
+			    {
+			      //AddMarkedSeg(pbtw,edgepoints.Get(en,index));
+
+			      edgepoints.Add1(edgenum,pbtw);
+			      edgepointdists.Add1(edgenum,mindist);
+			      edgepointorigines.Add1(edgenum,en);
+			      edgepointoriginps.Add1(edgenum,index);
+			      changed = 1;
+			    }
+			  else
+			    {
+			      if (mindist < edgepointdists.Get(edgenum,l))
+				{
+				  edgepointdists.Set(edgenum,l,mindist);
+				  edgepointorigines.Set(edgenum,l,en);
+				  edgepointoriginps.Set(edgenum,l,index);
+				  changed = 1;
+				}			      
+			    }
+			}
+		      if (minofmindist < endpointmindist-1E-10 && changed)
+			{
+			  finished = 0;
+			  edgelist2.Append(edgenum);
+			}
+		    }
+		}
+	    }
+	  else
+	    {
+	      double mindist = 1E50;
+	      int index(0);
+	      for (m = 1; m <= divisions; m++)
+		{
+		  const Point3d& p = edgepoints.Get(en,m);
+		  if (Dist(ap2,p) + edgepointdists.Get(en,m) < mindist)
+		    {mindist = Dist(ap2,p) + edgepointdists.Get(en,m); index = m;}
+		}
+	      if (mindist < endpointmindist)
+		{
+		  endpointorigine = en;
+		  endpointoriginp = index;
+		  endpointmindist = mindist;
+		}
+	    }
+	}
+      edgelist1.SetSize(0);
+      for (i = 1; i <= edgelist2.Size(); i++)
+	{
+	  edgelist1.Append(edgelist2.Get(i));
+	}
+    }
+
+  if (!endpointorigine) {PrintSysError("No connection found!");}
+
+  Array<Point3d> plist;
+
+  plist.Append(ap2);
+  int laste = endpointorigine;
+  int lastp = endpointoriginp;
+  int lle, llp;
+
+
+  while (laste)
+    {
+      plist.Append(edgepoints.Get(laste,lastp));
+
+      lle = laste;
+      llp = lastp; 
+      laste = edgepointorigines.Get(lle,llp);
+      lastp = edgepointoriginps.Get(lle,llp);
+    }
+
+  plist.Append(ap1);
+
+  for (i = 1; i <= plist.Size()-1; i++)
+    {
+      AddMarkedSeg(plist.Get(i),plist.Get(i+1));
+    }
+
+  PrintMessage(5,"PointBetween: complexity=", maxsize);
+
+
+  Point3d pm;
+  double dist = 0;
+  int found = 0;
+  
+  for (i = 1; i <= plist.Size()-1; i++)
+    {
+      dist += Dist(plist.Get(i),plist.Get(i+1));
+      if (dist > endpointmindist*0.5) 
+	{
+	  double segl = Dist(plist.Get(i), plist.Get(i+1));
+	  double d = dist - endpointmindist * 0.5;
+	  pm = Point3d(d/segl*plist.Get(i).X() + (1.-d/segl)*plist.Get(i+1).X(),
+		       d/segl*plist.Get(i).Y() + (1.-d/segl)*plist.Get(i+1).Y(),
+		       d/segl*plist.Get(i).Z() + (1.-d/segl)*plist.Get(i+1).Z());
+	  found = 1;
+	  break;
+	}
+    }
+  if (!found) {PrintWarning("Problem in PointBetween"); pm = Center(ap1,ap2);}
+
+  AddMarkedSeg(pm, Point3d(0.,0.,0.));
+  
+  return pm;
+  
+}
+
+
+void STLGeometry :: PrepareSurfaceMeshing()
+{
+  meshchart = -1; //clear no old chart
+  meshcharttrigs.SetSize(GetNT());
+  int i;
+  for (i = 1; i <= GetNT(); i++) 
+    {meshcharttrigs.Elem(i) = 0;}
+}
+
+void STLGeometry::GetMeshChartBoundary (Array<Point2d > & apoints,
+					Array<Point3d > & points3d,
+					Array<INDEX_2> & alines, double h)
+{
+  int i, j;
+  twoint seg, newseg;
+  int zone;
+  Point<2> p2;
+
+  const STLChart& chart = GetChart(meshchart);
+
+
+  for (i = 1; i <= chart.GetNOLimit(); i++)
+    {
+      seg = chart.GetOLimit(i);
+      INDEX_2 i2;
+      for (j = 1; j <= 2; j++)
+	{
+	  int pi = (j == 1) ? seg.i1 : seg.i2;
+	  int lpi;
+	  if (ha_points.Get(pi) == 0)
+	    {
+	      const Point<3> & p3d = GetPoint (pi);
+	      Point<2> p2d;
+
+	      points3d.Append (p3d);
+	      ToPlane(p3d, 0, p2d, h, zone, 0);
+	      apoints.Append (p2d);
+	      
+	      lpi = apoints.Size();
+	      ha_points.Elem(pi) = lpi;
+	    }
+	  else
+	    lpi = ha_points.Get(pi);
+
+	  i2.I(j) = lpi;
+	}
+      alines.Append (i2);
+
+      /*
+      seg = chart.GetOLimit(i);
+      psize = points.Size();
+
+      newseg.i1 = psize+1;
+      newseg.i2 = psize+2;
+
+      ToPlane(GetPoint(seg.i1), 0, p2, h, zone, 0);
+      points.Append(p2);
+      points3d.Append (GetPoint(seg.i1));
+      ToPlane(GetPoint(seg.i2), 0, p2, h, zone, 0);
+      points.Append(p2);
+      points3d.Append (GetPoint(seg.i2));
+      lines.Append (INDEX_2 (points.Size()-1, points.Size()));
+      */
+    }
+
+  for (i = 1; i <= chart.GetNOLimit(); i++)
+    {
+      seg = chart.GetOLimit(i);
+      ha_points.Elem(seg.i1) = 0;
+      ha_points.Elem(seg.i2) = 0;
+    }
+}
+
+void STLGeometry :: DefineTangentialPlane (const Point<3> & ap1, const Point<3> & ap2, int trig)
+{
+  p1 = ap1; //save for ToPlane, in data of STLGeometry class
+  Point<3> p2 = ap2; //only locally used
+
+  meshchart = GetChartNr(trig);
+
+  if (usechartnormal)
+    meshtrignv = GetChart(meshchart).GetNormal();
+  else
+    meshtrignv = GetTriangle(trig).Normal();
+
+  //meshtrignv = GetTriangle(trig).Normal(points);
+
+  meshtrignv /= meshtrignv.Length();
+
+  GetTriangle(trig).ProjectInPlain(points, meshtrignv, p2);
+
+
+  ez = meshtrignv;
+  ez /= ez.Length();
+  ex = p2 - p1;
+  ex -= (ex * ez) * ez;
+  ex /= ex.Length();
+  ey = Cross (ez, ex);
+
+}
+
+
+void STLGeometry :: SelectChartOfTriangle (int trignum)
+{
+  meshchart = GetChartNr(trignum);
+  meshtrignv = GetTriangle(trignum).Normal();	
+}
+
+
+void STLGeometry :: SelectChartOfPoint (const Point<3> & p)
+{
+  int i, ii;
+
+  Array<int> trigsinbox;
+  
+  Box<3> box(p,p);
+  box.Increase (1e-6);
+  GetTrianglesInBox (box, trigsinbox);
+  
+
+  //  for (i = 1; i <= GetNT(); i++)
+  for (ii = 1; ii <= trigsinbox.Size(); ii++)
+    {
+      i = trigsinbox.Get(ii);
+      Point<3> hp = p;
+      if (GetTriangle(i).GetNearestPoint(points, hp) <= 1E-8)
+	{
+	  SelectChartOfTriangle (i);
+	  break;
+      }
+    }
+  return;
+}
+
+
+
+void STLGeometry :: ToPlane (const Point<3> & locpoint, int * trigs,
+			     Point<2> & plainpoint, double h, int& zone,
+			     int checkchart)
+{
+  if (checkchart)
+    {
+
+      //check if locpoint lies on actual chart:
+      zone = 0;
+      
+      
+      //  Point3d p;
+      int i = 1;
+      const STLChart& chart = GetChart(meshchart);
+      int foundinchart = 0;
+      const double range = 1e-6; //1e-4 old
+      
+      
+      
+      
+      if (trigs)
+	{
+	  int * htrigs = trigs;
+	  while (*htrigs)
+	    {
+	      if (TrigIsInOC (*htrigs, meshchart))
+		{
+		  foundinchart = 1;
+		  break;
+		}
+	      htrigs++;
+	    }
+	}
+      
+      else
+	{
+	  Array<int> trigsinbox;
+
+	  if (!geomsearchtreeon)
+	    {
+	      //alter chart-tree
+	      Box<3> box(locpoint, locpoint);
+	      box.Increase (range);
+	      chart.GetTrianglesInBox (box.PMin(), box.PMax(), trigsinbox);
+	    }
+	  else
+	    {
+	      Array<int> trigsinbox2;
+	      Box<3> box(locpoint, locpoint);
+	      box.Increase (range);
+	      GetTrianglesInBox (box, trigsinbox2);
+	      for (i = 1; i <= trigsinbox2.Size(); i++)
+		{
+		  if (TrigIsInOC(trigsinbox2.Get(i),meshchart)) {trigsinbox.Append(trigsinbox2.Get(i));}
+		}
+	      
+	    }
+	  
+	  
+	  for (i = 1; i <= trigsinbox.Size(); i++)
+	    {
+	      Point<3> p = locpoint;
+	      if (GetTriangle(trigsinbox.Get(i)).GetNearestPoint(points, p) 
+		  <= 1E-8)
+		{
+		  foundinchart = 1;
+		  break;
+		}
+	      
+	    }
+	}
+      
+  //do not use this point (but do correct projection (joachim)
+      if (!foundinchart) 
+	{
+	  zone = -1; // plainpoint.X() = 11111; plainpoint.Y() = 11111; return; 
+	}
+    }
+  
+  else
+    {
+      zone = 0;
+    }
+  
+  //transform in plane
+  Vec<3> p1p = locpoint - p1;
+  plainpoint(0) = (p1p * ex) / h;
+  plainpoint(1) = (p1p * ey) / h;
+
+}
+
+int STLGeometry :: FromPlane (const Point<2> & plainpoint, 
+			      Point<3> & locpoint, double h)
+{
+  Point2d plainpoint2 (plainpoint);
+
+  plainpoint2.X() *= h;
+  plainpoint2.Y() *= h;
+  Vec3d p1p = plainpoint2.X() * ex + plainpoint2.Y() * ey;
+  locpoint = p1 + p1p;
+
+
+  int rv = Project(locpoint);
+  if (!rv) {return 1;} //project nicht gegangen
+  return 0;
+}
+
+int lasttrig;
+int STLGeometry :: LastTrig() const {return lasttrig;};
+
+//project normal to tangential plane
+int STLGeometry :: Project(Point<3> & p3d) const
+{
+  Point<3> p, pf;
+
+  int i, j;
+  int fi = 0;
+  int cnt = 0;
+  int different = 0;
+  const double lamtol = 1e-6;
+
+  const STLChart& chart = GetChart(meshchart);
+
+  int nt = chart.GetNT();
+
+   QuadraticFunction3d quadfun(p3d, meshtrignv);
+ 
+   /*
+     Vec3d hv = meshtrignv;
+     hv /= hv.Length();
+     Vec3d t1, t2;
+     hv.GetNormal (t1);
+     Cross (hv, t1, t2);
+   */
+  
+  for (j = 1; j <= nt; j++)
+    {
+      i = chart.GetTrig(j);
+
+      const Point<3> & c = GetTriangle(i).center;
+      /*
+      double d1 = t1 * (c-p3d);
+      double d2 = t2 * (c-p3d);
+      */
+      /*
+      if (d1 * d1 + d2 * d2 > sqr (GetTriangle(i).rad))
+	continue;
+      */
+      if (quadfun.Eval(c) > sqr (GetTriangle(i).rad))
+	continue;
+
+      p = p3d;
+      Vec<3> lam;
+      int err = GetTriangle(i).ProjectInPlain(points, meshtrignv, p, lam);      
+      int inside = (err == 0 && lam(0) > -lamtol && 
+		    lam(1) > -lamtol && (1-lam(0)-lam(1)) > -lamtol);
+
+
+      /*
+      p = p3d;
+      GetTriangle(i).ProjectInPlain(points, meshtrignv, p);
+      if (GetTriangle(i).PointInside(points, p)) 
+      */
+      if (inside)
+	{
+	  if (cnt != 0) 
+	    {
+	      if (Dist2(p,pf)>=1E-16) 
+		{
+		  //		  (*testout) << "ERROR: found two points to project which are different" << endl;
+		  //(*testout) << "p=" << p << ", pf=" << pf << endl;
+		  different = 1;
+		}
+	    }
+	  pf = p; fi = i; cnt++;
+	}
+
+      if (inside)
+	break;
+
+    }
+
+  //  if (cnt == 2) {(*testout) << "WARNING: found 2 triangles to project" << endl;}
+  //if (cnt == 3) {(*testout) << "WARNING: found 3 triangles to project" << endl;}
+  //if (cnt > 3) {(*testout) << "WARNING: found more than 3 triangles to project" << endl;}
+
+  if (fi != 0) {lasttrig = fi;}
+  if (fi != 0 && !different) {p3d = pf; return fi;}
+
+  //  (*testout) << "WARNING: Project failed" << endl;
+  return 0;
+  
+}
+
+//project normal to tangential plane
+int STLGeometry :: ProjectOnWholeSurface(Point<3> & p3d) const
+{
+  Point<3> p, pf;
+
+  int i;
+  int fi = 0;
+  int cnt = 0;
+  int different = 0;
+  const double lamtol = 1e-6;
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      p = p3d;
+      Vec<3> lam;
+      int err =
+	GetTriangle(i).ProjectInPlain(points, meshtrignv, p, lam);      
+      int inside = (err == 0 && lam(0) > -lamtol && 
+		    lam(1) > -lamtol && (1-lam(0)-lam(1)) > -lamtol);
+
+      /*
+      p = p3d;
+      GetTriangle(i).ProjectInPlain(points, meshtrignv, p);
+      if (GetTriangle(i).PointInside(points, p)) 
+      */
+      if (inside)
+	{
+	  if (cnt != 0) 
+	    {
+	      if (Dist2(p,pf)>=1E-16) 
+		{
+		  //		  (*testout) << "ERROR: found two points to project which are different" << endl;
+		  //		  (*testout) << "p=" << p << ", pf=" << pf << endl;
+		  different = 1;
+		}
+	    }
+	  pf = p; fi = i; cnt++;
+	}
+    }
+  /*
+  if (cnt == 2) {(*testout) << "WARNING: found 2 triangles to project" << endl;}
+  if (cnt == 3) {(*testout) << "WARNING: found 3 triangles to project" << endl;}
+  if (cnt > 3) {(*testout) << "WARNING: found more than 3 triangles to project" << endl;}
+  */
+  if (fi != 0) {lasttrig = fi;}
+  if (fi != 0 && !different) {p3d = pf; return fi;}
+
+  //  (*testout) << "WARNING: Project failed" << endl;
+  return 0;
+  
+}
+
+
+int STLGeometry :: ProjectNearest(Point<3> & p3d) const
+{
+  Point<3> p, pf = 0.0;
+
+  //set new chart
+  const STLChart& chart = GetChart(meshchart);
+  int i;
+  double nearest = 1E50;
+  double dist;
+  int ft = 0;
+
+  for (i = 1; i <= chart.GetNT(); i++)
+    {
+      p = p3d;
+      dist  = GetTriangle(chart.GetTrig(i)).GetNearestPoint(points, p);
+      if (dist < nearest)
+	{
+	  pf = p;
+	  nearest = dist;
+	  ft = chart.GetTrig(i);
+	}      
+    }
+  p3d = pf;
+  //if (!ft) {(*testout) << "ERROR: ProjectNearest failed" << endl;}
+  
+  return ft;
+}
+
+
+
+	
+//Restrict local h due to curvature for make atlas
+void STLGeometry :: RestrictLocalHCurv(class Mesh & mesh, double gh)
+{
+  PushStatusF("Restrict H due to surface curvature");
+
+  //bei jedem Dreieck alle Nachbardreiecke vergleichen, und, fallskein Kante dazwischen,
+  //die Meshsize auf ein bestimmtes Mass limitieren
+  int i,j;
+
+  int ap1,ap2,p3,p4;
+  Point<3> p1p, p2p, p3p, p4p;
+  Vec<3> n, ntn;
+  double rzyl, localh;
+
+  //  double localhfact = 0.5;
+  // double geometryignorelength = 1E-4;
+  double minlocalh = stlparam.atlasminh;
+
+  Box<3> bb = GetBoundingBox();
+  //  mesh.SetLocalH(bb.PMin() - Vec3d(10, 10, 10),bb.PMax() + Vec3d(10, 10, 10),
+  //		 mparam.grading);
+
+  //  mesh.SetGlobalH(gh);
+
+  double mincalch = 1E10;
+  double maxcalch = -1E10
+;
+  double objectsize = bb.Diam();
+  double geometryignoreedgelength = objectsize * 1e-5;
+
+
+  if (stlparam.resthatlasenable)
+    {
+      Array<double> minh; //minimales h pro punkt
+      minh.SetSize(GetNP());
+      for (i = 1; i <= GetNP(); i++)
+	{
+	  minh.Elem(i) = gh;
+	}
+      
+      for (i = 1; i <= GetNT(); i++)
+	{
+	  SetThreadPercent((double)i/(double)GetNT()*100.);
+
+	  if (multithread.terminate)
+	    {PopStatus(); return;}
+
+	  const STLTriangle& trig = GetTriangle(i);
+	  n = GetTriangle(i).Normal();
+	  for (j = 1; j <= 3; j++)
+	    {
+	      const STLTriangle& nt = GetTriangle(NeighbourTrig(i,j));
+	      
+	      trig.GetNeighbourPointsAndOpposite(nt,ap1,ap2,p3);	    	    
+	      
+	      //checken, ob ap1-ap2 eine Kante sind
+	      if (IsEdge(ap1,ap2)) continue;
+	      
+	      p4 = trig.PNum(1) + trig.PNum(2) + trig.PNum(3) - ap1 - ap2;
+	      
+	      p1p = GetPoint(ap1); p2p = GetPoint(ap2); 
+	      p3p = GetPoint(p3); p4p = GetPoint(p4);
+	      
+	      double h1 = GetDistFromInfiniteLine(p1p,p2p, p4p);
+	      double h2 = GetDistFromInfiniteLine(p1p,p2p, p3p);
+	      double diaglen = Dist (p1p, p2p);
+	      
+	      if (diaglen < geometryignoreedgelength)
+		continue;
+	      rzyl = ComputeCylinderRadius 
+		(n, GetTriangle(NeighbourTrig(i,j)).Normal(), 
+		 h1, h2);
+	      
+	      
+	      if (h1 < 1e-3 * diaglen && h2 < 1e-3 * diaglen)
+		continue;
+	      if (h1 < 1e-5 * objectsize && h2 < 1e-5 * objectsize)
+		continue;
+	      
+	      
+	      //	      rzyl = mindist/(2*sinang);
+	      localh = 10.*rzyl / stlparam.resthatlasfac;
+	      if (localh < mincalch) {mincalch = localh;}
+	      if (localh > maxcalch) {maxcalch = localh;}
+
+	      if (localh < minlocalh) {localh = minlocalh;}
+	      if (localh < gh)
+		{
+		  minh.Elem(ap1) = min2(minh.Elem(ap1),localh);
+		  minh.Elem(ap2) = min2(minh.Elem(ap2),localh);
+		}
+	      
+	      mesh.RestrictLocalHLine(p1p, p2p, localh);
+	    }
+	  
+	}
+    }
+  PrintMessage(5, "done\nATLAS H: nmin local h=", mincalch);
+  PrintMessage(5, "ATLAS H: max local h=", maxcalch);
+  PrintMessage(5, "Local h tree has ", mesh.LocalHFunction().GetNBoxes(), " boxes of size ",
+	       (int)sizeof(GradingBox));
+
+  PopStatus();
+
+}
+  //restrict local h due to near edges and due to outer chart distance
+void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh)
+{
+  
+  //bei jedem Dreieck alle Nachbardreiecke vergleichen, und, fallskein Kante dazwischen,
+  //die Meshsize auf ein bestimmtes Mass limitieren
+  int i,j;
+
+  int ap1,ap2,p3,p4;
+  Point3d p1p, p2p, p3p, p4p;
+  Vec3d n, ntn;
+  double rzyl, localh;
+
+  //  double localhfact = 0.5;
+  // double geometryignorelength = 1E-4;
+
+  Box<3> bb = GetBoundingBox();
+  //mesh.SetLocalH(bb.PMin() - Vec3d(10, 10, 10),bb.PMax() + Vec3d(10, 10, 10),
+  //		 mparam.grading);
+
+  //mesh.SetGlobalH(gh);
+
+  double mincalch = 1E10;
+  double maxcalch = -1E10;
+
+  double objectsize = bb.Diam();
+  double geometryignoreedgelength = objectsize * 1e-5;
+
+  if (stlparam.resthsurfcurvenable)
+    {
+      PushStatusF("Restrict H due to surface curvature");
+
+      Array<double> minh; //minimales h pro punkt
+      minh.SetSize(GetNP());
+      for (i = 1; i <= GetNP(); i++)
+	{
+	  minh.Elem(i) = gh;
+	}
+
+      for (i = 1; i <= GetNT(); i++)
+	{
+	  SetThreadPercent((double)i/(double)GetNT()*100.);
+	  if (i%20000==19999) {PrintMessage(7, (double)i/(double)GetNT()*100. , "%");}
+
+	  if (multithread.terminate)
+	    {PopStatus(); return;}
+	  
+	  const STLTriangle& trig = GetTriangle(i);
+	  n = GetTriangle(i).Normal();
+	  for (j = 1; j <= 3; j++)
+	    {
+	      const STLTriangle& nt = GetTriangle(NeighbourTrig(i,j));
+	      
+	      trig.GetNeighbourPointsAndOpposite(nt,ap1,ap2,p3);	    	    
+	      
+	      //checken, ob ap1-ap2 eine Kante sind
+	      if (IsEdge(ap1,ap2)) continue;
+	      
+	      p4 = trig.PNum(1) + trig.PNum(2) + trig.PNum(3) - ap1 - ap2;
+	      
+	      p1p = GetPoint(ap1); p2p = GetPoint(ap2); 
+	      p3p = GetPoint(p3); p4p = GetPoint(p4);
+	      
+	      double h1 = GetDistFromInfiniteLine(p1p,p2p, p4p);
+	      double h2 = GetDistFromInfiniteLine(p1p,p2p, p3p);
+	      double diaglen = Dist (p1p, p2p);
+	      
+	      if (diaglen < geometryignoreedgelength)
+		continue;
+	      rzyl = ComputeCylinderRadius 
+		(n, GetTriangle (NeighbourTrig(i,j)).Normal(), 
+		 h1, h2);
+	      
+	      
+	      if (h1 < 1e-3 * diaglen && h2 < 1e-3 * diaglen)
+		continue;
+	      
+	      if (h1 < 1e-5 * objectsize && h2 < 1e-5 * objectsize)
+		continue;
+	      
+	      
+	      //	      rzyl = mindist/(2*sinang);
+	      localh = rzyl / stlparam.resthsurfcurvfac;
+	      if (localh < mincalch) {mincalch = localh;}
+	      if (localh > maxcalch) {maxcalch = localh;}
+	      if (localh < gh) 
+		{
+		  minh.Elem(ap1) = min2(minh.Elem(ap1),localh);
+		  minh.Elem(ap2) = min2(minh.Elem(ap2),localh);
+		}
+	      
+	      //if (localh < 0.2) {localh = 0.2;}
+
+	      if(localh < objectsize)
+		mesh.RestrictLocalHLine(p1p, p2p, localh);
+	      (*testout) << "restrict h along " << p1p << " - " << p2p << " to " << localh << endl;
+	      
+	      if (localh < 0.1)
+		{
+		  localh = 0.1;
+		}
+	      
+	    }
+	}
+      PrintMessage(7, "done\nmin local h=", mincalch, "\nmax local h=", maxcalch);
+      PopStatus();
+    }
+
+  if (stlparam.resthcloseedgeenable)
+    {
+      PushStatusF("Restrict H due to close edges");
+      //geht nicht für spiralen!!!!!!!!!!!!!!!!!!
+      
+      double disttohfact = sqr(10.0 / stlparam.resthcloseedgefac);
+      int k,l;
+      double h1, h2, dist;
+      int rc = 0;
+      Point3d p3p1;
+      double mindist = 1E50;
+      
+      PrintMessage(7,"build search tree...");
+      Box3dTree* lsearchtree = new Box3dTree (GetBoundingBox().PMin() - Vec3d(1,1,1),
+					     GetBoundingBox().PMax() + Vec3d(1,1,1));
+      
+      Array<Point3d> pmins(GetNLines());
+      Array<Point3d> pmaxs(GetNLines());
+
+      double maxhline;
+      for (i = 1; i <= GetNLines(); i++)
+	{
+	  maxhline = 0;
+	  STLLine* l1 = GetLine(i);
+	  Point3d pmin(GetPoint(l1->StartP())), pmax(GetPoint(l1->StartP())), px;
+
+	  for (j = 2; j <= l1->NP(); j++)
+	    {
+	      px = GetPoint(l1->PNum(j));
+	      maxhline = max2(maxhline,mesh.GetH(px));
+	      pmin.SetToMin (px);
+	      pmax.SetToMax (px);
+	    }
+	  Box3d box(pmin,pmax);
+	  box.Increase(maxhline);
+
+	  lsearchtree->Insert (box.PMin(), box.PMax(), i);
+	  pmins.Elem(i) = box.PMin();
+	  pmaxs.Elem(i) = box.PMax();
+	}
+
+      Array<int> linenums;
+      int k2;
+
+      for (i = 1; i <= GetNLines(); i++)
+	{
+	  SetThreadPercent((double)i/(double)GetNLines()*100.);
+	  if (multithread.terminate)
+	    {PopStatus(); return;}
+
+	  linenums.SetSize(0);
+	  lsearchtree->GetIntersecting(pmins.Get(i),pmaxs.Get(i),linenums);
+	      
+	  STLLine* l1 = GetLine(i);
+	  for (j = 1; j <= l1->NP(); j++)
+	    {
+	      p3p1 = GetPoint(l1->PNum(j));
+	      h1 = sqr(mesh.GetH(p3p1));
+	      
+	      for (k2 = 1; k2 <= linenums.Size(); k2++)
+		{
+		  k = linenums.Get(k2);
+		  if (k <= i) {continue;} 
+		  /*  
+		   //old, without searchtrees
+		     for (k = i+1; k <= GetNLines(); k++)
+		     {
+		  */
+		  STLLine* l2 = GetLine(k);
+		  for (l = 1; l <= l2->NP(); l++)
+		    {
+		      const Point3d& p3p2 = GetPoint(l2->PNum(l));
+		      h2 = sqr(mesh.GetH(p3p2));
+		      dist = Dist2(p3p1,p3p2)*disttohfact;		  
+		      if (dist > 1E-12)
+			{
+			  if (dist < h1) 
+			    {
+			      mesh.RestrictLocalH(p3p1,sqrt(dist)); 
+			      rc++;
+			      mindist = min2(mindist,sqrt(dist));
+			    }
+			  if (dist < h2) 
+			    {
+			      mesh.RestrictLocalH(p3p2,sqrt(dist)); 
+			      rc++;
+			      mindist = min2(mindist,sqrt(dist));
+			    }
+			}
+		    }
+		}	  
+	    }
+	}
+      PrintMessage(5, "done\n Restricted h in ", rc, " points due to near edges!");
+      PopStatus(); 
+    }
+
+  if (stlparam.resthedgeangleenable)
+    {
+      PushStatusF("Restrict h due to close edges");
+
+      int lp1, lp2;
+      Vec3d v1,v2;
+      mincalch = 1E50;
+      maxcalch = -1E50;
+
+      for (i = 1; i <= GetNP(); i++)
+	{
+	  SetThreadPercent((double)i/(double)GetNP()*100.);
+	  if (multithread.terminate)
+	    {PopStatus(); return;}
+
+	  if (GetNEPP(i) == 2 && !IsLineEndPoint(i))
+	    {
+	      if (GetEdge(GetEdgePP(i,1)).PNum(2) == GetEdge(GetEdgePP(i,2)).PNum(1) ||
+		  GetEdge(GetEdgePP(i,1)).PNum(1) == GetEdge(GetEdgePP(i,2)).PNum(2))
+		{
+		  lp1 = 1; lp2 = 2;
+		}
+	      else
+		{
+		  lp1 = 2; lp2 = 1;
+		}
+
+	      v1 = Vec3d(GetPoint(GetEdge(GetEdgePP(i,1)).PNum(1)),
+			 GetPoint(GetEdge(GetEdgePP(i,1)).PNum(2)));
+	      v2 = Vec3d(GetPoint(GetEdge(GetEdgePP(i,2)).PNum(lp1)),
+			 GetPoint(GetEdge(GetEdgePP(i,2)).PNum(lp2)));
+
+	      rzyl = ComputeCylinderRadius(v1, v2, v1.Length(), v2.Length());
+	      	      
+	      localh = rzyl / stlparam.resthedgeanglefac;
+	      if (localh < mincalch) {mincalch = localh;}
+	      if (localh > maxcalch) {maxcalch = localh;}
+	      
+	      if (localh != 0)
+		mesh.RestrictLocalH(GetPoint(i), localh);
+	    }	  
+	}
+      PrintMessage(7,"edge-angle min local h=", mincalch, "\nedge-angle max local h=", maxcalch);
+      PopStatus();
+    }
+
+  if (stlparam.resthchartdistenable)
+    {
+      PushStatusF("Restrict H due to outer chart distance");
+      
+      // mesh.LocalHFunction().Delete();
+
+      //berechne minimale distanz von chart zu einem nicht-outerchart-punkt in jedem randpunkt einer chart
+      
+      Array<int> acttrigs; //outercharttrigs
+      acttrigs.SetSize(GetNT());
+      for (i = 1; i <= GetNT(); i++)
+	{
+	  acttrigs.Elem(i) = 0;
+	}
+      for (i = 1; i <= GetNOCharts(); i++)
+	{
+	  SetThreadPercent((double)i/(double)GetNOCharts()*100.);
+	  if (multithread.terminate)
+	    {PopStatus(); return;}
+
+	  RestrictHChartDistOneChart(i, acttrigs, mesh, gh, 1., 0.);
+	}
+      
+      PopStatus();
+    }
+
+  if (stlparam.resthlinelengthenable)
+    {
+      //restrict h due to short lines
+      PushStatusF("Restrict H due to line-length");
+      
+      double minhl = 1E50;
+      double linefact = 1./stlparam.resthlinelengthfac;
+      double l;
+      for (i = 1; i <= GetNLines(); i++)
+	{
+	  SetThreadPercent((double)i/(double)GetNLines()*100.);
+	  if (multithread.terminate)
+	    {PopStatus(); return;}
+	  
+	  l = GetLine(i)->GetLength(points);
+	  
+	  const Point3d& pp1 = GetPoint(GetLine(i)->StartP());
+	  const Point3d& pp2 = GetPoint(GetLine(i)->EndP());
+	  
+	  if (l != 0)
+	    {
+	      minhl = min2(minhl,l*linefact);
+	      
+	      mesh.RestrictLocalH(pp1, l*linefact);
+	      mesh.RestrictLocalH(pp2, l*linefact);      
+	    }
+	}
+      PopStatus();
+      PrintMessage(5, "minh due to line length=", minhl);
+  }
+}
+
+void STLGeometry :: RestrictHChartDistOneChart(int chartnum, Array<int>& acttrigs, 
+					       class Mesh & mesh, double gh, double fact, double minh)
+{
+  int i = chartnum;
+  int j;
+
+  double limessafety = stlparam.resthchartdistfac*fact;  // original: 2
+  double localh;
+
+  double f1,f2;
+  //  mincalch = 1E10;
+  //maxcalch = -1E10;  
+  Array<int> limes1;
+  Array<int> limes2;
+	  
+  Array<Point3d> plimes1;
+  Array<Point3d> plimes2;
+	  
+  Array<int> plimes1trigs; //check from wich trig the points come
+  Array<int> plimes2trigs;
+	  
+  Array<int> plimes1origin; //either the original pointnumber or zero, if new point
+
+  int divisions = 10;
+	  
+  int k, t, nt, np1, np2;
+  Point3d p3p1, p3p2;
+  STLTriangle tt;
+      
+  limes1.SetSize(0);
+  limes2.SetSize(0);
+  plimes1.SetSize(0);
+  plimes2.SetSize(0);
+  plimes1trigs.SetSize(0);
+  plimes2trigs.SetSize(0);
+  plimes1origin.SetSize(0);
+
+  STLChart& chart = GetChart(i);
+  chart.ClearOLimit();
+  chart.ClearILimit();
+
+  for (j = 1; j <= chart.GetNChartT(); j++)
+    {
+      t = chart.GetChartTrig(j); 
+      tt = GetTriangle(t);
+      for (k = 1; k <= 3; k++)
+	{
+	  nt = NeighbourTrig(t,k); 
+	  if (GetChartNr(nt) != i)
+	    {	      
+	      tt.GetNeighbourPoints(GetTriangle(nt),np1,np2);
+	      if (!IsEdge(np1,np2) && !GetSpiralPoint(np1) && !GetSpiralPoint(np2))
+		{
+		  p3p1 = GetPoint(np1);
+		  p3p2 = GetPoint(np2);
+		  if (AddIfNotExists(limes1,np1)) 
+		    {
+		      plimes1.Append(p3p1); 
+		      plimes1trigs.Append(t);
+		      plimes1origin.Append(np1); 			      
+		    }
+		  if (AddIfNotExists(limes1,np2)) 
+		    {
+		      plimes1.Append(p3p2); 
+		      plimes1trigs.Append(t);
+		      plimes1origin.Append(np2); 			      
+		    }
+		  chart.AddILimit(twoint(np1,np2));
+
+		  for (int di = 1; di <= divisions; di++)
+		    {
+		      f1 = (double)di/(double)(divisions+1.);
+		      f2 = (divisions+1.-(double)di)/(double)(divisions+1.);
+			      
+		      plimes1.Append(Point3d(p3p1.X()*f1+p3p2.X()*f2,
+					     p3p1.Y()*f1+p3p2.Y()*f2,
+					     p3p1.Z()*f1+p3p2.Z()*f2));
+		      plimes1trigs.Append(t);
+		      plimes1origin.Append(0); 			      
+		    }
+		}
+	    }
+	}
+    }
+	  
+	 
+  for (j = 1; j <= chart.GetNT(); j++)
+    {
+      acttrigs.Elem(chart.GetTrig(j)) = i;
+    }
+	  
+  for (j = 1; j <= chart.GetNOuterT(); j++)
+    {
+      t = chart.GetOuterTrig(j); 
+      tt = GetTriangle(t);
+      for (k = 1; k <= 3; k++)
+	{
+	  nt = NeighbourTrig(t,k);
+
+	  if (acttrigs.Get(nt) != i)
+	    {
+	      tt.GetNeighbourPoints(GetTriangle(nt),np1,np2);
+		      
+	      if (!IsEdge(np1,np2))
+		{
+		  p3p1 = GetPoint(np1);
+		  p3p2 = GetPoint(np2);
+			  
+		  if (AddIfNotExists(limes2,np1)) {plimes2.Append(p3p1); plimes2trigs.Append(t);}
+		  if (AddIfNotExists(limes2,np2)) {plimes2.Append(p3p2); plimes2trigs.Append(t);}
+		  chart.AddOLimit(twoint(np1,np2));
+
+		  for (int di = 1; di <= divisions; di++)
+		    {
+		      f1 = (double)di/(double)(divisions+1.);
+		      f2 = (divisions+1.-(double)di)/(double)(divisions+1.);
+			      
+		      plimes2.Append(Point3d(p3p1.X()*f1+p3p2.X()*f2,
+					     p3p1.Y()*f1+p3p2.Y()*f2,
+					     p3p1.Z()*f1+p3p2.Z()*f2));
+		      plimes2trigs.Append(t);
+		    }
+		}
+	    }
+	}
+    }
+	  
+	  
+  double chartmindist = 1E50;
+
+  if (plimes2.Size())
+    {
+      Box3d bbox;
+      bbox.SetPoint (plimes2.Get(1));
+      for (j = 2; j <= plimes2.Size(); j++)
+	bbox.AddPoint (plimes2.Get(j));
+      Point3dTree stree(bbox.PMin(), bbox.PMax());
+      for (j = 1; j <= plimes2.Size(); j++)
+	stree.Insert (plimes2.Get(j), j);
+      Array<int> foundpts;
+	  
+      for (j = 1; j <= plimes1.Size(); j++)
+	{
+	  double mindist = 1E50;
+	  double dist;
+
+	  const Point3d & ap1 = plimes1.Get(j);
+	  double boxs = mesh.GetH (plimes1.Get(j)) * limessafety;
+
+	  Point3d pmin = ap1 - Vec3d (boxs, boxs, boxs);
+	  Point3d pmax = ap1 + Vec3d (boxs, boxs, boxs);
+
+	  stree.GetIntersecting (pmin, pmax, foundpts);
+
+
+	  for (int kk = 1; kk <= foundpts.Size(); kk++)
+	    {
+	      k = foundpts.Get(kk);
+	      dist = Dist2(plimes1.Get(j),plimes2.Get(k));
+	      if (dist < mindist) 
+		{
+		  mindist = dist;
+		}
+	    }
+
+	  /*
+	    const Point3d & ap1 = plimes1.Get(j);
+	    double his = mesh.GetH (plimes1.Get(j));
+
+	    double xmin = ap1.X() - his * limessafety;
+	    double xmax = ap1.X() + his * limessafety;	      
+	    double ymin = ap1.Y() - his * limessafety;
+	    double ymax = ap1.Y() + his * limessafety;	      
+	    double zmin = ap1.Z() - his * limessafety;
+	    double zmax = ap1.Z() + his * limessafety;	      
+
+	    for (k = 1; k <= plimes2.Size(); k++)
+	    {
+	    const Point3d & ap2 = plimes2.Get(k);
+	    if (ap2.X() >= xmin && ap2.X() <= xmax &&
+	    ap2.Y() >= ymin && ap2.Y() <= ymax &&
+	    ap2.Z() >= zmin && ap2.Z() <= zmax)
+	    {
+	    dist = Dist2(plimes1.Get(j),plimes2.Get(k));
+	    if (dist < mindist) 
+	    {
+	    mindist = dist;
+	    }
+	    }
+	    }
+	  */
+	  mindist = sqrt(mindist);
+	  localh = mindist/limessafety;
+
+	  if (localh < minh && localh != 0) {localh = minh;} //minh is generally 0! (except make atlas)
+	  if (localh < gh && localh > 0)
+	    {
+	      mesh.RestrictLocalH(plimes1.Get(j), localh);
+	      //	      if (mindist < mincalch) {mincalch = mindist;}
+	      //	      if (mindist > maxcalch) {maxcalch = mindist;}
+	      if (mindist < chartmindist) {chartmindist = mindist;}
+	    }
+	}
+    }
+
+}
+
+
+int STLMeshingDummy (STLGeometry* stlgeometry, Mesh*& mesh, MeshingParameters & mparam,
+			    int perfstepsstart, int perfstepsend)
+{
+  if (perfstepsstart > perfstepsend) return 0;
+
+  multithread.terminate = 0;
+  int success = 1;
+  //int trialcntouter = 0;
+
+  if (perfstepsstart <= MESHCONST_MESHEDGES)
+    {
+
+      mesh = new Mesh();
+      mesh->geomtype = Mesh::GEOM_STL;
+
+      mesh -> SetGlobalH (mparam.maxh);
+      mesh -> SetLocalH (stlgeometry->GetBoundingBox().PMin() - Vec3d(10, 10, 10),
+			 stlgeometry->GetBoundingBox().PMax() + Vec3d(10, 10, 10),
+			 mparam.grading);
+      mesh -> LoadLocalMeshSize (mparam.meshsizefilename);
+      
+      success = 0;
+  
+      //mesh->DeleteMesh();
+ 
+      STLMeshing (*stlgeometry, *mesh);
+
+      stlgeometry->edgesfound = 1;
+      stlgeometry->surfacemeshed = 0;
+      stlgeometry->surfaceoptimized = 0;
+      stlgeometry->volumemeshed = 0;
+    }
+
+  if (multithread.terminate)
+    return 0;
+
+  if (perfstepsstart <= MESHCONST_MESHSURFACE && 
+      perfstepsend >= MESHCONST_MESHSURFACE)
+    {
+
+      if (!stlgeometry->edgesfound) 
+	{
+	  PrintUserError("You have to do 'analyse geometry' first!!!");
+	  return 0; 
+	}
+      if (stlgeometry->surfacemeshed || stlgeometry->surfacemeshed) 
+	{
+	  PrintUserError("Already meshed. Please start again with 'Analyse Geometry'!!!"); 
+	  return 0; 
+	}
+
+      success = 0;
+      int retval = STLSurfaceMeshing (*stlgeometry, *mesh);
+      if (retval == MESHING3_OK)
+	{
+	  PrintMessage(3,"Success !!!!");
+	  stlgeometry->surfacemeshed = 1;
+	  stlgeometry->surfaceoptimized = 0;
+	  stlgeometry->volumemeshed = 0;
+	  success = 1;
+	} 
+      else if (retval == MESHING3_OUTERSTEPSEXCEEDED)
+	{
+	  PrintError("Give up because of too many trials. Meshing aborted!");
+	}
+      else if (retval == MESHING3_TERMINATE)
+	{
+	  PrintWarning("Meshing Stopped by user!");
+	}
+      else
+	{
+	  PrintError("Surface meshing not successful. Meshing aborted!");
+	}
+      
+#ifdef STAT_STREAM
+      (*statout) << mesh->GetNSeg() << " & " << endl
+		 << mesh->GetNSE() << " & " << endl
+		 << GetTime() << " & ";
+#endif
+    }
+  if (multithread.terminate)
+    return 0;
+
+  if (success)
+    {
+      if (perfstepsstart <= MESHCONST_OPTSURFACE && 
+	  perfstepsend >= MESHCONST_OPTSURFACE)
+	{
+	  if (!stlgeometry->edgesfound) 
+	    {
+	      PrintUserError("You have to do 'meshing->analyse geometry' first!!!"); 
+	      return 0; 
+	    }
+	  if (!stlgeometry->surfacemeshed) 
+	    {
+	      PrintUserError("You have to do 'meshing->mesh surface' first!!!"); 
+	      return 0; 
+	    }
+	  if (stlgeometry->volumemeshed) 
+	    {
+	      PrintWarning("Surface optimization with meshed volume is dangerous!!!"); 
+	    }
+
+	  /*
+	  if (!optstring || strlen(optstring) == 0)
+	    {
+	      mparam.optimize2d = "smcm";
+	    }
+	  else
+	    {
+	      mparam.optimize2d = optstring;
+	    }
+	  */
+
+	  STLSurfaceOptimization (*stlgeometry, *mesh, mparam);
+	  
+	  if (stlparam.recalc_h_opt)
+	    {
+	      mesh -> SetLocalH (stlgeometry->GetBoundingBox().PMin() - Vec3d(10, 10, 10),
+				 stlgeometry->GetBoundingBox().PMax() + Vec3d(10, 10, 10),
+				 mparam.grading);
+	      mesh -> LoadLocalMeshSize (mparam.meshsizefilename);	      
+	      mesh -> CalcLocalHFromSurfaceCurvature (mparam.grading, 
+						      stlparam.resthsurfmeshcurvfac);
+	      mparam.optimize2d = "cmsmSm";
+	      STLSurfaceOptimization (*stlgeometry, *mesh, mparam);
+#ifdef STAT_STREAM
+	      (*statout) << GetTime() << " & ";
+#endif
+
+	      extern void Render();
+	      Render();
+	    }
+	  stlgeometry->surfaceoptimized = 1;
+	}
+      if (multithread.terminate)
+	return 0;
+
+      if (perfstepsstart <= MESHCONST_MESHVOLUME && 
+	  perfstepsend >= MESHCONST_MESHVOLUME)
+	{
+	  if (stlgeometry->volumemeshed) 
+	    {
+	      PrintUserError("Volume already meshed!"); return 0;
+	    }
+
+	  if (!stlgeometry->edgesfound) 
+	    {
+	      PrintUserError("You have to do 'meshing->analyse geometry' first!!!"); 
+	      return 0; 
+	    }
+	  if (!stlgeometry->surfacemeshed) 
+	    {
+	      PrintUserError("You have to do 'meshing->mesh surface' first!!!"); 
+	      return 0; 
+	    }
+	  if (!stlgeometry->surfaceoptimized) 
+	    {
+	      PrintWarning("You should do 'meshing->optimize surface' first!!!"); 
+	    }
+
+
+	  PrintMessage(5,"Check Overlapping boundary: ");
+	  mesh->FindOpenElements();
+	  mesh->CheckOverlappingBoundary();
+	  PrintMessage(5,"");
+
+
+	  if (stlparam.recalc_h_opt)
+	    {
+	      mesh -> SetLocalH (stlgeometry->GetBoundingBox().PMin() - Vec3d(10, 10, 10),
+				 stlgeometry->GetBoundingBox().PMax() + Vec3d(10, 10, 10),
+				 mparam.grading);	  
+	      mesh -> LoadLocalMeshSize (mparam.meshsizefilename);
+	      mesh -> CalcLocalH (mparam.grading);
+	    }
+	  
+	  
+	  PrintMessage(5,"Volume meshing");
+	  int retval = MeshVolume (mparam, *mesh);
+	  if (retval == MESHING3_OK)
+	    {
+	      RemoveIllegalElements(*mesh);
+	      stlgeometry->volumemeshed = 1;
+	    } 
+	  else if (retval == MESHING3_OUTERSTEPSEXCEEDED)
+	    {
+	      PrintError("Give up because of too many trials. Meshing aborted!");
+	      return 0;
+	    }
+	  else if (retval == MESHING3_TERMINATE)
+	    {
+	      PrintWarning("Meshing Stopped by user!");
+	    }
+	  else
+	    {
+	      PrintError("Volume meshing not successful. Meshing aborted!");
+	      return 0;
+	    }
+
+#ifdef STAT_STREAM
+	  (*statout) << GetTime() << " & " << endl;
+#endif
+	  MeshQuality3d (*mesh);
+	}
+
+      if (multithread.terminate)
+	return 0;
+
+      if (perfstepsstart <= MESHCONST_OPTVOLUME && 
+	  perfstepsend >= MESHCONST_OPTVOLUME)
+	{
+	  if (!stlgeometry->edgesfound) 
+	    {
+	      PrintUserError("You have to do 'meshing->analyse geometry' first!!!"); 
+	      return 0; 
+	    }
+	  if (!stlgeometry->surfacemeshed) 
+	    {
+	      PrintUserError("You have to do 'meshing->mesh surface' first!!!"); 
+	      return 0; 
+	    }
+	  if (!stlgeometry->volumemeshed) 
+	    {
+	      PrintUserError("You have to do 'meshing->mesh volume' first!!!"); 
+	      return 0; 
+	    }
+
+	  /*
+	  if (!optstring || strlen(optstring) == 0)
+	    {
+	      mparam.optimize3d = "cmdmstm";
+	    }
+	  else
+	    {
+	      mparam.optimize3d = optstring;
+	    }
+	  */
+
+	  OptimizeVolume (mparam, *mesh);
+	  
+#ifdef STAT_STREAM
+	  (*statout) << GetTime() << " & " << endl;
+	  (*statout) << mesh->GetNE() << " & " << endl
+		     << mesh->GetNP() << " " << '\\' << '\\' << " \\" << "hline" << endl;
+#endif
+
+	  extern void Render();
+	  Render();
+	}
+    }
+  
+
+  return 0;
+}
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/stlline.cpp b/contrib/Netgen/libsrc/stlgeom/stlline.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..15226edaf8679818d6373df0825509fccdf69d6c
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlline.cpp
@@ -0,0 +1,780 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+#include <meshing.hpp>
+
+#include "stlgeom.hpp"
+
+namespace netgen
+{
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//++++++++++++++  EDGE DATA     ++++++++++++++++++++++++++++++++++++++++++
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+/*
+void STLEdgeData :: Write(ofstream& of) const
+{
+  of // << angle << " "
+     << p1 << " "
+     << p2 << " "
+     << lt << " "
+     << rt << " "
+    //     << status
+     << endl;
+}
+
+void STLEdgeData :: Read(ifstream& ifs)
+{
+  // ifs >> angle;
+  ifs >> p1;
+  ifs >> p2;
+  ifs >> lt;
+  ifs >> rt;
+  //  ifs >> status;
+}
+
+
+int STLEdgeData :: GetStatus () const
+{
+  if (topedgenr <= 0 || topedgenr > top->GetNTE()) return 0;
+  return top->GetTopEdge (topedgenr).GetStatus(); 
+}
+
+void STLEdgeData ::SetStatus (int stat)
+{
+  if (topedgenr >= 1 && topedgenr <= top->GetNTE())
+    top->GetTopEdge (topedgenr).SetStatus(stat); 
+}
+
+
+float STLEdgeData :: CosAngle() const
+{
+  return top->GetTopEdge (topedgenr).CosAngle(); 
+}
+
+
+
+void STLEdgeDataList :: ResetAll()
+{
+  int i;
+  for (i = 1; i <= edgedata.Size(); i++)
+    {
+      edgedata.Elem(i).SetUndefined();
+    }
+}
+
+void STLEdgeDataList :: ResetCandidates()
+{
+  int i;
+  for (i = 1; i <= edgedata.Size(); i++)
+    {
+      if (edgedata.Get(i).Candidate())
+	{edgedata.Elem(i).SetUndefined();}
+    }
+}
+
+int STLEdgeDataList :: GetNConfEdges() const
+{
+  int i;
+  int cnt = 0;
+  for (i = 1; i <= edgedata.Size(); i++)
+    {
+      if (edgedata.Get(i).Confirmed()) {cnt++;}
+    }
+  return cnt;
+}
+
+void STLEdgeDataList :: ConfirmCandidates()
+{
+  int i;
+  for (i = 1; i <= edgedata.Size(); i++)
+    {
+      if (edgedata.Get(i).Candidate())
+	{edgedata.Elem(i).SetConfirmed();}
+    }
+}
+
+int STLEdgeDataList :: GetEdgeNum(int np1, int np2) const
+{
+  INDEX_2 ed(np1,np2);
+  ed.Sort();
+  if (hashtab.Used(ed))
+    {
+      return hashtab.Get(ed);
+    }
+
+//   int i;
+//   for (i = 1; i <= Size(); i++)
+//     {
+//       if ((Get(i).p1 == np1 && Get(i).p2 == np2) ||
+// 	  (Get(i).p2 == np1 && Get(i).p1 == np2))
+// 	{
+// 	  return i;
+// 	}
+//     }
+
+  return 0;
+}
+
+const STLEdgeDataList& STLEdgeDataList :: operator=(const STLEdgeDataList& edl)
+{
+  int i;
+  SetSize(edl.Size());
+  for (i = 1; i <= Size(); i++)
+    {
+      Add(edl.Get(i), i);
+    }
+  return *this;
+} 
+
+void STLEdgeDataList :: Add(const STLEdgeData& ed, int i)
+{
+  INDEX_2 edge(ed.p1,ed.p2);
+  edge.Sort();
+  hashtab.Set(edge, i);
+  Elem(i) = ed;
+  AddEdgePP(ed.p1,i);
+  AddEdgePP(ed.p2,i);
+}
+
+void STLEdgeDataList :: Write(ofstream& of) const
+{
+  of.precision(16);
+  int i;
+  of << Size() << endl;
+  
+  for (i = 1; i <= Size(); i++)
+    {
+      Get(i).Write(of);
+    }
+}
+
+void STLEdgeDataList :: Read(ifstream& ifs)
+{
+  int i,n;
+  ifs >> n;
+
+  SetSize(n);
+  STLEdgeData ed;
+  for (i = 1; i <= n; i++)
+    {
+      ed.Read(ifs);
+      Add(ed,i);
+    }
+}
+
+int STLEdgeDataList :: GetNEPPStat(int p, int status) const
+{
+  int i;
+  int cnt = 0;
+  for (i = 1; i <= GetNEPP(p); i++)
+    {
+      if (Get(GetEdgePP(p,i)).GetStatus() == status)
+	{
+	  cnt++;
+	}
+    }
+  return cnt;
+}
+
+int STLEdgeDataList :: GetNConfCandEPP(int p) const
+{
+  int i;
+  int cnt = 0;
+  for (i = 1; i <= GetNEPP(p); i++)
+    {
+      if (Get(GetEdgePP(p,i)).ConfCand())
+	{
+	  cnt++;
+	}
+    }
+  return cnt;
+}
+
+
+void STLEdgeDataList :: BuildLineWithEdge(int ep1, int ep2, Array<twoint>& line)
+{
+  int status = Get(GetEdgeNum(ep1,ep2)).GetStatus();
+
+  int found, pstart, p, en, pnew, ennew;
+  int closed = 0;
+  int j, i;
+  for (j = 1; j <= 2; j++)
+    {
+      if (j == 1) {p = ep1;}
+      if (j == 2) {p = ep2;}
+
+      pstart = p;
+      en = GetEdgeNum(ep1,ep2);
+
+      found = 1;
+      while (found && !closed)
+	{
+	  found = 0;
+	  
+	  if (GetNEPPStat(p,status) == 2)
+	    {
+	      for (i = 1; i <= GetNEPP(p); i++)
+		{		
+		  const STLEdgeData& e = Get(GetEdgePP(p,i));
+		  if (GetEdgePP(p,i) != en && e.GetStatus() == status) 
+		    {
+		      if (e.p1 == p) 
+			{pnew = e.p2;}
+		      else 
+			{pnew = e.p1;}
+
+		      ennew = GetEdgePP(p,i);
+		    }
+		}
+	      if (pnew == pstart) {closed = 1;}
+	      else
+		{
+		  line.Append(twoint(p,pnew));
+		  p = pnew;
+		  en = ennew;
+		  found = 1;
+		}
+	    }
+	}
+    }
+  
+}
+*/
+
+
+
+
+STLEdgeDataList :: STLEdgeDataList (STLTopology & ageom)
+  : geom(ageom)
+{
+  ;
+}
+
+STLEdgeDataList :: ~STLEdgeDataList()
+{
+  ;
+}
+
+
+void STLEdgeDataList :: Store ()
+{
+  int i, ne = geom.GetNTE();
+  storedstatus.SetSize(ne);
+  for (i = 1; i <= ne; i++)
+    {
+      storedstatus.Elem(i) = Get(i).GetStatus();
+    }
+}
+
+void STLEdgeDataList :: Restore ()
+{
+  int i, ne = geom.GetNTE();
+  if (storedstatus.Size() == ne)
+    for (i = 1; i <= ne; i++)
+      geom.GetTopEdge(i).SetStatus (storedstatus.Elem(i));
+}
+
+
+void STLEdgeDataList :: ResetAll()
+{
+  int i, ne = geom.GetNTE();
+  for (i = 1; i <= ne; i++)
+    geom.GetTopEdge (i).SetStatus (ED_UNDEFINED);
+}
+
+int STLEdgeDataList :: GetNConfEdges() const
+{
+  int i, ne = geom.GetNTE();
+  int cnt = 0;
+  for (i = 1; i <= ne; i++)
+    if (geom.GetTopEdge (i).GetStatus() == ED_CONFIRMED)
+      cnt++;
+  return cnt; 
+}
+
+void STLEdgeDataList :: ChangeStatus(int status1, int status2)
+{
+  int i, ne = geom.GetNTE();
+  for (i = 1; i <= ne; i++)
+    if (geom.GetTopEdge (i).GetStatus() == status1)
+      geom.GetTopEdge (i).SetStatus (status2);
+}
+
+/*
+void STLEdgeDataList :: Add(const STLEdgeData& ed, int i)
+{
+  INDEX_2 edge(ed.p1,ed.p2);
+  edge.Sort();
+  hashtab.Set(edge, i);
+  Elem(i) = ed;
+  AddEdgePP(ed.p1,i);
+  AddEdgePP(ed.p2,i);
+}
+*/
+
+void STLEdgeDataList :: Write(ofstream& of) const
+{
+  
+  /*
+  of.precision(16);
+  int i;
+  of << Size() << endl;
+  
+  for (i = 1; i <= Size(); i++)
+    {
+      Get(i).Write(of);
+    }
+
+  */
+  of.precision(16);
+  int i, ne = geom.GetNTE();
+  //of << GetNConfEdges() << endl;
+  of << geom.GetNTE() << endl;
+
+  for (i = 1; i <= ne; i++)
+    {
+      const STLTopEdge & edge = geom.GetTopEdge(i);
+      //if (edge.GetStatus() == ED_CONFIRMED)
+      of << edge.GetStatus() << " ";
+
+      const Point3d & p1 = geom.GetPoint (edge.PNum(1));
+      const Point3d & p2 = geom.GetPoint (edge.PNum(2));
+      of << p1.X() << " "
+	 << p1.Y() << " "
+	 << p1.Z() << " "
+	 << p2.X() << " "
+	 << p2.Y() << " "
+	 << p2.Z() << endl;
+    }
+  
+}
+
+void STLEdgeDataList :: Read(ifstream& ifs)
+{
+  int i, nce;
+  Point3d p1, p2;
+  int pi1, pi2;
+  int status, ednum;
+
+  ifs >> nce;
+  for (i = 1; i <= nce; i++)
+    {
+      ifs >> status;
+      ifs >> p1.X() >> p1.Y() >> p1.Z();
+      ifs >> p2.X() >> p2.Y() >> p2.Z();
+
+      pi1 = geom.GetPointNum (p1);
+      pi2 = geom.GetPointNum (p2);
+      ednum = geom.GetTopEdgeNum (pi1, pi2);
+
+
+      if (ednum)
+	{ 
+	  geom.GetTopEdge(ednum).SetStatus (status);
+	//	geom.GetTopEdge (ednum).SetStatus (ED_CONFIRMED);
+	}
+    }
+    /*
+  int i,n;
+  ifs >> n;
+
+  SetSize(n);
+  STLEdgeData ed;
+  for (i = 1; i <= n; i++)
+    {
+      ed.Read(ifs);
+      Add(ed,i);
+    }
+  */
+}
+
+int STLEdgeDataList :: GetNEPPStat(int p, int status) const
+{
+  int i;
+  int cnt = 0;
+  for (i = 1; i <= GetNEPP(p); i++)
+    {
+      if (Get(GetEdgePP(p,i)).GetStatus() == status)
+	{
+	  cnt++;
+	}
+    }
+  return cnt;
+}
+
+int STLEdgeDataList :: GetNConfCandEPP(int p) const
+{
+  int i;
+  int cnt = 0;
+  for (i = 1; i <= GetNEPP(p); i++)
+    {
+      if (Get(GetEdgePP(p,i)).GetStatus() == ED_CANDIDATE || 
+	  Get(GetEdgePP(p,i)).GetStatus() == ED_CONFIRMED)
+	{
+	  cnt++;
+	}
+    }
+  return cnt;
+}
+
+
+void STLEdgeDataList :: BuildLineWithEdge(int ep1, int ep2, Array<twoint>& line)
+{
+  int status = Get(GetEdgeNum(ep1,ep2)).GetStatus();
+
+  int found, pstart, p(0), en, pnew(0), ennew(0);
+  int closed = 0;
+  int j, i;
+  for (j = 1; j <= 2; j++)
+    {
+      if (j == 1) {p = ep1;}
+      if (j == 2) {p = ep2;}
+
+      pstart = p;
+      en = GetEdgeNum(ep1,ep2);
+
+      found = 1;
+      while (found && !closed)
+	{
+	  found = 0;
+	  
+	  if (GetNEPPStat(p,status) == 2)
+	    {
+	      for (i = 1; i <= GetNEPP(p); i++)
+		{		
+		  const STLTopEdge & e = Get(GetEdgePP(p,i));
+		  if (GetEdgePP(p,i) != en && e.GetStatus() == status) 
+		    {
+		      if (e.PNum(1) == p) 
+			{pnew = e.PNum(2);}
+		      else 
+			{pnew = e.PNum(1);}
+
+		      ennew = GetEdgePP(p,i);
+		    }
+		}
+	      if (pnew == pstart) {closed = 1;}
+	      else
+		{
+		  line.Append(twoint(p,pnew));
+		  p = pnew;
+		  en = ennew;
+		  found = 1;
+		}
+	    }
+	}
+    }
+  
+}
+
+int Exists(int p1, int p2, const Array<twoint>& line)
+{
+  int i;
+  for (i = 1; i <= line.Size(); i++)
+    {
+      if (line.Get(i).i1 == p1 && line.Get(i).i2 == p2 ||
+	  line.Get(i).i1 == p2 && line.Get(i).i2 == p1) {return 1;}
+    }
+  return 0;
+}
+
+void STLEdgeDataList :: BuildClusterWithEdge(int ep1, int ep2, Array<twoint>& line)
+{
+  int status = Get(GetEdgeNum(ep1,ep2)).GetStatus();
+
+  int p(0), en;
+  int j, i, k;
+  int oldend;
+  int newend = 1;
+  int pnew, ennew(0);
+
+  int changed = 1;
+  while (changed)
+    {
+      changed = 0;
+      for (j = 1; j <= 2; j++)
+	{
+	  oldend = newend;
+	  newend = line.Size();
+	  for (k = oldend; k <= line.Size(); k++)
+	    {
+	      if (j == 1) p = line.Get(k).i1;
+	      if (j == 2) p = line.Get(k).i2;
+	      en = GetEdgeNum(line.Get(k).i1, line.Get(k).i2);
+
+	      for (i = 1; i <= GetNEPP(p); i++)
+		{		
+		  pnew = 0;
+		  const STLTopEdge & e = Get(GetEdgePP(p,i));
+		  if (GetEdgePP(p,i) != en && e.GetStatus() == status) 
+		    {
+		      if (e.PNum(1) == p) 
+			{pnew = e.PNum(2);}
+		      else 
+			{pnew = e.PNum(1);}
+
+		      ennew = GetEdgePP(p,i);
+		    }
+		  if (pnew && !Exists(p,pnew,line))
+		    {
+		      changed = 1;
+		      line.Append(twoint(p,pnew));
+		      p = pnew;
+		      en = ennew;
+		    }
+		}
+	      
+	    }
+	}
+
+    }
+
+}
+
+
+
+
+
+
+
+
+
+
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//+++++++++++++++++++   STL LINE    +++++++++++++++++++++++++++++++
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+STLLine :: STLLine(const STLGeometry * ageometry)
+  : pts(), lefttrigs(), righttrigs()
+{
+  geometry = ageometry;
+  split = 0;
+};
+
+int STLLine :: GetNS() const
+{
+  if (pts.Size() <= 1) {return 0;}
+  return pts.Size()-1;
+}
+void STLLine :: GetSeg(int nr, int& p1, int& p2) const
+{
+  p1 = pts.Get(nr);
+  p2 = pts.Get(nr+1);
+}
+
+int STLLine :: GetLeftTrig(int nr) const 
+{
+  if (nr > lefttrigs.Size()) {PrintSysError("In STLLine::GetLeftTrig!!!"); return 0;}
+  return lefttrigs.Get(nr);
+};
+
+int STLLine :: GetRightTrig(int nr) const 
+{
+  if (nr > righttrigs.Size()) {PrintSysError("In STLLine::GetRightTrig!!!"); return 0;}
+  return righttrigs.Get(nr);
+};
+
+double STLLine :: GetSegLen(const Array<Point<3> >& ap, int nr) const
+{
+  return Dist(ap.Get(PNum(nr)),ap.Get(PNum(nr+1)));
+}
+
+double STLLine :: GetLength(const Array<Point<3> >& ap) const
+{
+  double len = 0;
+  for (int i = 2; i <= pts.Size(); i++)
+    {
+      len += (ap.Get(pts.Get(i)) - ap.Get(pts.Get(i-1))).Length();
+    }
+  return len;
+}
+
+void STLLine :: GetBoundingBox (const Array<Point<3> > & ap, Box<3> & box) const
+{
+  box.Set (ap.Get (pts[0]));
+  for (int i = 1; i < pts.Size(); i++)
+    box.Add (ap.Get(pts[i]));
+}
+
+
+
+Point<3> STLLine :: 
+GetPointInDist(const Array<Point<3> >& ap, double dist, int& index) const
+{
+  if (dist <= 0)
+    {
+      index = 1;
+      return ap.Get(StartP());
+    }
+  
+  double len = 0;
+  int i;
+  for (i = 1; i < pts.Size(); i++)
+    {
+      double seglen = Dist (ap.Get(pts.Get(i)),
+			    ap.Get(pts.Get(i+1)));
+
+      if (len + seglen > dist)
+	{
+	  index = i;
+	  double relval = (dist - len) / (seglen + 1e-16);
+	  Vec3d v (ap.Get(pts.Get(i)), ap.Get(pts.Get(i+1)));
+	  return ap.Get(pts.Get(i)) + relval * v;
+	}
+
+      len += seglen;
+    }
+
+  index = pts.Size() - 1;
+  return ap.Get(EndP());
+}
+
+
+/*
+double stlgh;
+double GetH(const Point3d& p, double x) 
+{
+  return stlgh;//+0.5)*(x+0.5);
+}
+*/
+STLLine* STLLine :: Mesh(const Array<Point<3> >& ap, 
+			 Array<Point3d>& mp, double ghi,
+			 class Mesh& mesh) const
+{
+  STLLine* line = new STLLine(geometry);
+
+  //stlgh = ghi; //uebergangsloesung!!!!
+  
+  double len = GetLength(ap);
+  double inthl = 0; //integral of 1/h
+  double dist = 0;
+  double h;
+  int ind;
+  Point3d p;
+
+  int i, j;
+
+  Box<3> bbox;
+  GetBoundingBox (ap, bbox);
+  double diam = bbox.Diam();
+
+  double minh = mesh.LocalHFunction().GetMinH (bbox.PMin(), bbox.PMax());
+
+  double maxseglen = 0;
+  for (i = 1; i <= GetNS(); i++)
+    maxseglen = max2 (maxseglen, GetSegLen (ap, i));
+  
+  int nph = 10+int(maxseglen / minh); //anzahl der integralauswertungen pro segment
+
+  Array<double> inthi(GetNS()*nph);
+  Array<double> curvelen(GetNS()*nph);
+
+
+  for (i = 1; i <= GetNS(); i++)
+    {
+      //double seglen = GetSegLen(ap,i);
+      for (j = 1; j <= nph; j++)
+	{
+	  p = GetPointInDist(ap,dist,ind);
+	  //h = GetH(p,dist/len);
+	  h = mesh.GetH(p);
+
+	  
+	  dist += GetSegLen(ap,i)/(double)nph;
+	  
+	  inthl += GetSegLen(ap,i)/nph/(h);
+	  inthi.Elem((i-1)*nph+j) = GetSegLen(ap,i)/nph/h;
+	  curvelen.Elem((i-1)*nph+j) = GetSegLen(ap,i)/nph;
+	}
+    }
+
+
+  int inthlint = int(inthl+1);
+
+  if ( (inthlint < 3) && (StartP() == EndP()))
+    {
+      inthlint = 3;
+    }
+  if ( (inthlint == 1) && ShouldSplit())
+    {
+      inthlint = 2; 
+    }
+     
+  double fact = inthl/(double)inthlint;
+  dist = 0;
+  j = 1;
+
+
+  p = ap.Get(StartP());
+  int pn = AddPointIfNotExists(mp, p, 1e-10*diam);
+
+  int segn = 1;
+  line->AddPoint(pn);
+  line->AddLeftTrig(GetLeftTrig(segn));
+  line->AddRightTrig(GetRightTrig(segn));
+  line->AddDist(dist);
+
+  inthl = 0; //restart each meshseg
+  for (i = 1; i <= inthlint; i++)
+    {
+      while (inthl < 1.000000001 && j <= inthi.Size())
+      //      while (inthl-1. < 1e-9) && j <= inthi.Size())
+	{
+	  inthl += inthi.Get(j)/fact;
+	  dist += curvelen.Get(j);
+	  j++;
+	}
+
+      //went to far:
+      j--;
+      double tofar = (inthl - 1)/inthi.Get(j);
+      inthl -= tofar*inthi.Get(j);
+      dist -= tofar*curvelen.Get(j)*fact;
+
+      if (i == inthlint && fabs(dist - len) >= 1E-8) 
+	{
+	  PrintSysError("meshline failed!!!"); 
+	}
+
+      if (i != inthlint) 
+	{
+	  p = GetPointInDist(ap,dist,ind);
+	  pn = AddPointIfNotExists(mp, p, 1e-10*diam);
+	  segn = ind;
+	  line->AddPoint(pn);
+	  line->AddLeftTrig(GetLeftTrig(segn));
+	  line->AddRightTrig(GetRightTrig(segn));
+	  line->AddDist(dist);
+	}
+
+      inthl = tofar*inthi.Get(j);
+      dist += tofar*curvelen.Get(j)*fact;
+      j++;
+    }
+
+  p = ap.Get(EndP());
+  pn = AddPointIfNotExists(mp, p, 1e-10*diam);
+  segn = GetNS();
+  line->AddPoint(pn);
+  line->AddLeftTrig(GetLeftTrig(segn));
+  line->AddRightTrig(GetRightTrig(segn));
+  line->AddDist(dist);
+  
+  for (int ii = 1; ii <= line->GetNS(); ii++)
+    {
+      int p1, p2;
+      line->GetSeg(ii,p1,p2);
+    }
+  /*  
+  (*testout) << "line, " << ap.Get(StartP()) << "-" << ap.Get(EndP())
+	     << " len = " << Dist (ap.Get(StartP()), ap.Get(EndP())) << endl;
+  */
+  return line;
+}
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/stlline.hpp b/contrib/Netgen/libsrc/stlgeom/stlline.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..06ce5857097516420a1495f51083d802bf7c706e
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlline.hpp
@@ -0,0 +1,188 @@
+#ifndef FILE_STLLINE
+#define FILE_STLLINE
+
+
+/**************************************************************************/
+/* File:   stlline.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Author2: Johannes Gerstmayr                                            */
+/* Date:   20. Nov. 99                                                    */
+/**************************************************************************/
+
+class STLGeometry;
+class STLTopology;
+
+class STLEdge
+{
+public:
+  int pts[2];
+  int trigs[2]; //left and right trig
+
+  STLEdge (const int * apts) {pts[0] = apts[0]; pts[1] = apts[1];}
+  STLEdge (int v1, int v2) {pts[0] = v1; pts[1] = v2;}
+  STLEdge () {pts[0]=0;pts[1]=0;}
+  int PNum(int i) const {return pts[(i-1)];}
+
+  int LeftTrig() const {return trigs[0];}
+  int RightTrig() const {return trigs[1];}
+  void SetLeftTrig(int i) {trigs[0] = i;}
+  void SetRightTrig(int i) {trigs[1] = i;}
+};
+
+enum STL_ED_STATUS { ED_EXCLUDED, ED_CONFIRMED, ED_CANDIDATE, ED_UNDEFINED };
+                       
+
+/*
+
+class STLEdgeData
+{
+public:
+  //  float angle;
+  int p1;
+  int p2;
+  int lt; //left trig
+  int rt; //right trig
+  //  int status;
+
+  STLTopology * top;  // pointer to stl topology
+  int topedgenr;  // number of corresponding topology edge
+
+  STLEdgeData() {}; 
+  STLEdgeData(float anglei, int p1i, int p2i, int lti, int rti) 
+{
+//     angle = anglei; 
+p1 = p1i; p2 = p2i;
+      lt = lti; rt = rti;
+    }
+
+  int GetStatus () const;
+  void SetStatus (int stat);
+
+  void SetExcluded() { SetStatus (ED_EXCLUDED); }
+  void SetConfirmed() { SetStatus (ED_CONFIRMED); }
+  void SetCandidate() { SetStatus (ED_CANDIDATE); }
+  void SetUndefined() { SetStatus (ED_UNDEFINED); }
+
+  int Excluded() const {return GetStatus() == ED_EXCLUDED;}
+  int Confirmed() const {return GetStatus() == ED_CONFIRMED;}
+  int Candidate() const {return GetStatus() == ED_CANDIDATE;}
+  int Undefined() const {return GetStatus() == ED_UNDEFINED;}
+  int ConfCand() const {return GetStatus() == ED_CONFIRMED || GetStatus() == ED_CANDIDATE;}
+
+  float CosAngle() const; 
+
+  void Write(ofstream& of) const;
+  void Read(ifstream& ifs);
+};
+
+class STLEdgeDataList
+{
+private:
+  INDEX_2_HASHTABLE<int> hashtab;
+  Array<STLEdgeData> edgedata;
+  TABLE<int> edgesperpoint;
+  
+public:
+
+  STLEdgeDataList():edgedata(),hashtab(1),edgesperpoint() {};
+  const STLEdgeDataList& operator=(const STLEdgeDataList& edl); 
+  void SetSize(int size) 
+    {
+      edgedata.SetSize(size);
+      hashtab.SetSize(size);
+      edgesperpoint.SetSize(size);
+    }
+  void Clear() {SetSize(0);}
+  int Size() const {return edgedata.Size();}
+  const STLEdgeData& Get(int i) const {return edgedata.Get(i);}
+  STLEdgeData& Elem(int i) {return edgedata.Elem(i);}
+  void Add(const STLEdgeData& ed, int i);
+
+  int GetNEPP(int pn) const 
+    {
+      return edgesperpoint.EntrySize(pn);
+    };
+  int GetEdgePP(int pn, int vi) const
+    {
+      return edgesperpoint.Get(pn,vi);
+    };
+  void AddEdgePP(int pn, int vn) {edgesperpoint.Add(pn,vn);};
+
+  void ResetAll();
+  void ResetCandidates();
+  void ConfirmCandidates();
+  int GetEdgeNum(int np1, int np2) const;
+
+  int GetNConfEdges() const;
+
+  void Write(ofstream& of) const;
+  void Read(ifstream& ifs);
+
+  void BuildLineWithEdge(int ep1, int ep2, Array<twoint>& line);
+
+  int GetNEPPStat(int p, int status) const;
+  int GetNConfCandEPP(int p) const;
+};
+*/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//a line defined by several points (polyline)
+class STLLine
+{
+private:
+  const STLGeometry * geometry;
+  Array<int> pts;
+  Array<int> lefttrigs;
+  Array<int> righttrigs;
+  Array<double> dists;
+  int split;
+
+public:
+  STLLine(const STLGeometry * ageometry);
+  void AddPoint(int i) {pts.Append(i);}
+  int PNum(int i) const {return pts.Get(i);}
+  int NP() const {return pts.Size();}
+  int GetNS() const;
+  void GetSeg(int nr, int& p1, int& p2) const;
+  double GetSegLen(const Array<Point<3> >& ap, int nr) const;
+  int GetLeftTrig(int nr) const;
+  int GetRightTrig(int nr) const;
+  double GetDist(int nr) const { return dists.Get(nr);};
+  void GetBoundingBox (const Array<Point<3> > & ap, Box<3> & box) const;
+
+  void AddLeftTrig(int nr) {lefttrigs.Append(nr);}
+  void AddRightTrig(int nr) {righttrigs.Append(nr);}
+  void AddDist (double dist) {dists.Append(dist); }
+  int StartP() const {return pts.Get(1);}
+  int EndP() const {return pts.Get(pts.Size());}
+    
+  double GetLength(const Array<Point<3> >& ap) const;
+
+  //suche punkt in entfernung (in linienkoordinaten) dist
+  //in index ist letzter punkt VOR dist (d.h. max pts.Size()-1)
+  Point<3> GetPointInDist(const Array<Point<3> >& ap, double dist, int& index) const;
+
+  //return a meshed polyline
+  STLLine* Mesh(const Array<Point<3> >& ap, 
+		Array<Point3d>& mp, double ghi,
+		class Mesh& mesh) const;
+
+  void DoSplit() {split = 1;}
+  int ShouldSplit() const {return split;}
+};
+
+#endif
diff --git a/contrib/Netgen/libsrc/stlgeom/stlpkg.cpp b/contrib/Netgen/libsrc/stlgeom/stlpkg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4282633a4c410ede08f6fd82292cc55880003b2f
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlpkg.cpp
@@ -0,0 +1,622 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+
+#include <meshing.hpp>
+
+
+#include <incvis.hpp>
+#include <visual.hpp>
+
+#include <stlgeom.hpp>
+
+#include "vsstl.hpp"
+
+extern "C" int Ng_STL_Init (Tcl_Interp * interp);
+
+
+
+namespace netgen
+{
+  extern NetgenGeometry * ng_geometry;
+  extern AutoPtr<Mesh> mesh;
+
+  static VisualSceneSTLGeometry vsstlgeom;
+  static VisualSceneSTLMeshing vsstlmeshing;
+
+  char * err_needsstlgeometry = (char*) "This operation needs an STL geometry";
+
+
+
+
+
+  class STLGeometryRegister : public GeometryRegister
+  {
+  public:
+    virtual NetgenGeometry * Load (string filename) const;
+    virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const;
+    virtual void SetParameters (Tcl_Interp * interp) 
+    {
+      stlparam.yangle =
+	atof (Tcl_GetVar (interp, "::stloptions.yangle", 0));
+      stlparam.contyangle =
+	atof (Tcl_GetVar (interp, "::stloptions.contyangle", 0));
+      stlparam.edgecornerangle =
+	atof (Tcl_GetVar (interp, "::stloptions.edgecornerangle", 0));
+      stlparam.chartangle =
+	atof (Tcl_GetVar (interp, "::stloptions.chartangle", 0));
+      stlparam.outerchartangle =
+	atof (Tcl_GetVar (interp, "::stloptions.outerchartangle", 0));
+
+      stlparam.usesearchtree =
+	atoi (Tcl_GetVar (interp, "::stloptions.usesearchtree", 0));
+
+
+      stlparam.atlasminh =
+	atof (Tcl_GetVar (interp, "::stloptions.atlasminh", 0));
+
+      stlparam.resthsurfcurvfac =
+	atof (Tcl_GetVar (interp, "::stloptions.resthsurfcurvfac", 0));
+      stlparam.resthsurfcurvenable =
+	atoi (Tcl_GetVar (interp, "::stloptions.resthsurfcurvenable", 0));
+
+      stlparam.resthatlasfac =
+	atof (Tcl_GetVar (interp, "::stloptions.resthatlasfac", 0));
+      stlparam.resthatlasenable =
+	atoi (Tcl_GetVar (interp, "::stloptions.resthatlasenable", 0));
+
+      stlparam.resthchartdistfac =
+	atof (Tcl_GetVar (interp, "::stloptions.resthchartdistfac", 0));
+      stlparam.resthchartdistenable =
+	atoi (Tcl_GetVar (interp, "::stloptions.resthchartdistenable", 0));
+
+      stlparam.resthlinelengthfac =
+	atof (Tcl_GetVar (interp, "::stloptions.resthlinelengthfac", 0));
+      stlparam.resthlinelengthenable =
+	atoi (Tcl_GetVar (interp, "::stloptions.resthlinelengthenable", 0));
+
+      stlparam.resthcloseedgefac =
+	atof (Tcl_GetVar (interp, "::stloptions.resthcloseedgefac", 0));
+      stlparam.resthcloseedgeenable =
+	atoi (Tcl_GetVar (interp, "::stloptions.resthcloseedgeenable", 0));
+
+      stlparam.resthedgeanglefac =
+	atof (Tcl_GetVar (interp, "::stloptions.resthedgeanglefac", 0));
+      stlparam.resthedgeangleenable =
+	atoi (Tcl_GetVar (interp, "::stloptions.resthedgeangleenable", 0));
+
+      stlparam.resthsurfmeshcurvfac =
+	atof (Tcl_GetVar (interp, "::stloptions.resthsurfmeshcurvfac", 0));
+      stlparam.resthsurfmeshcurvenable =
+	atoi (Tcl_GetVar (interp, "::stloptions.resthsurfmeshcurvenable", 0));
+
+      stlparam.recalc_h_opt =
+	atoi (Tcl_GetVar (interp, "::stloptions.recalchopt", 0));
+      //  stlparam.Print (cout);      
+    }
+  };
+
+
+
+  int Ng_SetSTLParameters  (ClientData clientData,
+			    Tcl_Interp * interp,
+			    int argc, tcl_const char *argv[])
+  {
+    STLGeometryRegister reg;
+    reg.SetParameters (interp);
+
+    return TCL_OK;
+  }
+
+  
+
+
+
+
+
+
+  int Ng_STLDoctor (ClientData clientData,
+		    Tcl_Interp * interp,
+		    int argc, tcl_const char *argv[])
+  {
+    //cout << "STL doctor" << endl;
+    STLGeometry * stlgeometry = 
+      dynamic_cast<STLGeometry*> (ng_geometry);
+      
+
+    stldoctor.drawmeshededges =
+      atoi (Tcl_GetVar (interp, "::stldoctor.drawmeshededges", 0));
+
+    stldoctor.geom_tol_fact =
+      atof (Tcl_GetVar (interp, "::stldoctor.geom_tol_fact", 0));
+
+
+    stldoctor.useexternaledges =
+      atoi (Tcl_GetVar (interp, "::stldoctor.useexternaledges", 0));
+
+    stldoctor.showfaces =
+      atoi (Tcl_GetVar (interp, "::stldoctor.showfaces", 0));
+
+    stldoctor.conecheck =
+      atoi (Tcl_GetVar (interp, "::stldoctor.conecheck", 0));
+
+    stldoctor.spiralcheck =
+      atoi (Tcl_GetVar (interp, "::stldoctor.spiralcheck", 0));
+
+    stldoctor.selectwithmouse =
+      atoi (Tcl_GetVar (interp, "::stldoctor.selectwithmouse", 0));
+
+    stldoctor.showedgecornerpoints =
+      atoi (Tcl_GetVar (interp, "::stldoctor.showedgecornerpoints", 0));
+
+    stldoctor.showmarkedtrigs =
+      atoi (Tcl_GetVar (interp, "::stldoctor.showmarkedtrigs", 0));
+
+    stldoctor.showtouchedtrigchart =
+      atoi (Tcl_GetVar (interp, "::stldoctor.showtouchedtrigchart", 0));
+
+    //cout << "smt=" << stldoctor.showmarkedtrigs << endl;
+
+    stldoctor.dirtytrigfact =
+      atof (Tcl_GetVar (interp, "::stldoctor.dirtytrigfact", 0));
+
+    stldoctor.smoothnormalsweight =
+      atof (Tcl_GetVar (interp, "::stldoctor.smoothnormalsweight", 0));
+
+    stldoctor.smoothangle =
+      atof (Tcl_GetVar (interp, "::stldoctor.smoothangle", 0));
+
+    stldoctor.selectmode =
+      atoi (Tcl_GetVar (interp, "::stldoctor.selectmode", 0));
+
+    stldoctor.edgeselectmode =
+      atoi (Tcl_GetVar (interp, "::stldoctor.edgeselectmode", 0));
+
+    stldoctor.longlinefact =
+      atoi (Tcl_GetVar (interp, "::stldoctor.longlinefact", 0));
+
+    stldoctor.showexcluded =
+      atoi (Tcl_GetVar (interp, "::stldoctor.showexcluded", 0));
+
+
+
+    if (!stldoctor.selectwithmouse)
+      {
+	stldoctor.selecttrig =
+	  atoi (Tcl_GetVar (interp, "::stldoctor.selecttrig", 0));
+
+	stldoctor.nodeofseltrig =
+	  atoi (Tcl_GetVar (interp, "::stldoctor.nodeofseltrig", 0));
+      }
+
+    stldoctor.showvicinity =
+      atoi (Tcl_GetVar (interp, "::stldoctor.showvicinity", 0));
+
+    stldoctor.vicinity =
+      atoi (Tcl_GetVar (interp, "::stldoctor.vicinity", 0));
+
+
+    if (argc >= 2)
+      {
+	if (!stlgeometry)
+	  {
+	    Tcl_SetResult (interp, err_needsstlgeometry, TCL_STATIC);
+	    return TCL_ERROR;
+	  }
+
+	if (strcmp (argv[1], "destroy0trigs") == 0)
+	  {
+	    stlgeometry->DestroyDirtyTrigs();
+	  }
+	else if (strcmp (argv[1], "movepointtomiddle") == 0)
+	  {
+	    stlgeometry->MoveSelectedPointToMiddle();
+	  }
+	else if (strcmp (argv[1], "calcnormals") == 0)
+	  {
+	    stlgeometry->CalcNormalsFromGeometry();
+	  }
+	else if (strcmp (argv[1], "showchartnum") == 0)
+	  {
+	    stlgeometry->ShowSelectedTrigChartnum();
+	  }
+	else if (strcmp (argv[1], "showcoords") == 0)
+	  {
+	    stlgeometry->ShowSelectedTrigCoords();
+	  }
+	else if (strcmp (argv[1], "loadmarkedtrigs") == 0)
+	  {
+	    stlgeometry->LoadMarkedTrigs();
+	  }
+	else if (strcmp (argv[1], "savemarkedtrigs") == 0)
+	  {
+	    stlgeometry->SaveMarkedTrigs();
+	  }
+	else if (strcmp (argv[1], "neighbourangles") == 0)
+	  {
+	    stlgeometry->NeighbourAnglesOfSelectedTrig();
+	  }
+	else if (strcmp (argv[1], "vicinity") == 0)
+	  {
+	    stlgeometry->CalcVicinity(stldoctor.selecttrig);
+	  }
+	else if (strcmp (argv[1], "markdirtytrigs") == 0)
+	  {
+	    stlgeometry->MarkDirtyTrigs();
+	  }
+	else if (strcmp (argv[1], "smoothdirtytrigs") == 0)
+	  {
+	    stlgeometry->SmoothDirtyTrigs();
+	  }
+	else if (strcmp (argv[1], "smoothrevertedtrigs") == 0)
+	  {
+	    stlgeometry->GeomSmoothRevertedTrigs();
+	  }
+	else if (strcmp (argv[1], "invertselectedtrig") == 0)
+	  {
+	    stlgeometry->InvertTrig(stlgeometry->GetSelectTrig());
+	  }
+	else if (strcmp (argv[1], "deleteselectedtrig") == 0)
+	  {
+	    stlgeometry->DeleteTrig(stlgeometry->GetSelectTrig());
+	  }
+	else if (strcmp (argv[1], "smoothgeometry") == 0)
+	  {
+	    stlgeometry->SmoothGeometry();
+	  }
+	else if (strcmp (argv[1], "orientafterselectedtrig") == 0)
+	  {
+	    stlgeometry->OrientAfterTrig(stlgeometry->GetSelectTrig());
+	  }
+	else if (strcmp (argv[1], "marktoperrortrigs") == 0)
+	  {
+	    stlgeometry->MarkTopErrorTrigs();
+	  }
+	else if (strcmp (argv[1], "exportedges") == 0)
+	  {
+	    stlgeometry->ExportEdges();
+	  }
+	else if (strcmp (argv[1], "importedges") == 0)
+	  {
+	    stlgeometry->ImportEdges();
+	  }
+	else if (strcmp (argv[1], "importexternaledges") == 0)
+	  {
+	    stlgeometry->ImportExternalEdges(argv[2]);
+	  }
+	else if (strcmp (argv[1], "loadedgedata") == 0)
+	  {
+	    if (argc >= 3)
+	      {
+		stlgeometry->LoadEdgeData(argv[2]);
+	      }
+	  }
+	else if (strcmp (argv[1], "saveedgedata") == 0)
+	  {
+	    if (argc >= 3)
+	      {
+		stlgeometry->SaveEdgeData(argv[2]);
+	      }
+	  }
+
+	else if (strcmp (argv[1], "buildexternaledges") == 0)
+	  {
+	    stlgeometry->BuildExternalEdgesFromEdges();
+	  }
+	else if (strcmp (argv[1], "smoothnormals") == 0)
+	  {
+	    stlgeometry->SmoothNormals();
+	  }
+	else if (strcmp (argv[1], "marknonsmoothnormals") == 0)
+	  {
+	    stlgeometry->MarkNonSmoothNormals();
+	  }
+	else if (strcmp (argv[1], "addexternaledge") == 0)
+	  {
+	    stlgeometry->AddExternalEdgeAtSelected();
+	  }
+	else if (strcmp (argv[1], "addgeomline") == 0)
+	  {
+	    stlgeometry->AddExternalEdgesFromGeomLine();
+	  }
+	else if (strcmp (argv[1], "addlonglines") == 0)
+	  {
+	    stlgeometry->AddLongLinesToExternalEdges();
+	  }
+	else if (strcmp (argv[1], "addclosedlines") == 0)
+	  {
+	    stlgeometry->AddClosedLinesToExternalEdges();
+	  }
+	else if (strcmp (argv[1], "addnotsinglelines") == 0)
+	  {
+	    stlgeometry->AddAllNotSingleLinesToExternalEdges();
+	  }
+	else if (strcmp (argv[1], "deletedirtyexternaledges") == 0)
+	  {
+	    stlgeometry->DeleteDirtyExternalEdges();
+	  }
+	else if (strcmp (argv[1], "deleteexternaledge") == 0)
+	  {
+	    stlgeometry->DeleteExternalEdgeAtSelected();
+	  }
+	else if (strcmp (argv[1], "deletevicexternaledge") == 0)
+	  {
+	    stlgeometry->DeleteExternalEdgeInVicinity();
+	  }
+
+	else if (strcmp (argv[1], "addlonglines") == 0)
+	  {
+	    stlgeometry->STLDoctorLongLinesToCandidates();
+	  }
+	else if (strcmp (argv[1], "deletedirtyedges") == 0)
+	  {
+	    stlgeometry->STLDoctorDirtyEdgesToCandidates();
+	  }
+	else if (strcmp (argv[1], "undoedgechange") == 0)
+	  {
+	    stlgeometry->UndoEdgeChange();
+	  }
+	else if (strcmp (argv[1], "buildedges") == 0)
+	  {
+	    stlgeometry->STLDoctorBuildEdges();
+	  }
+	else if (strcmp (argv[1], "confirmedge") == 0)
+	  {
+	    stlgeometry->STLDoctorConfirmEdge();
+	  }
+	else if (strcmp (argv[1], "candidateedge") == 0)
+	  {
+	    stlgeometry->STLDoctorCandidateEdge();
+	  }
+	else if (strcmp (argv[1], "excludeedge") == 0)
+	  {
+	    stlgeometry->STLDoctorExcludeEdge();
+	  }
+	else if (strcmp (argv[1], "undefinededge") == 0)
+	  {
+	    stlgeometry->STLDoctorUndefinedEdge();
+	  }
+	else if (strcmp (argv[1], "setallundefinededges") == 0)
+	  {
+	    stlgeometry->STLDoctorSetAllUndefinedEdges();
+	  }
+	else if (strcmp (argv[1], "erasecandidateedges") == 0)
+	  {
+	    stlgeometry->STLDoctorEraseCandidateEdges();
+	  }
+	else if (strcmp (argv[1], "confirmcandidateedges") == 0)
+	  {
+	    stlgeometry->STLDoctorConfirmCandidateEdges();
+	  }
+	else if (strcmp (argv[1], "confirmedtocandidateedges") == 0)
+	  {
+	    stlgeometry->STLDoctorConfirmedToCandidateEdges();
+	  }
+      }
+
+    return TCL_OK;
+  }
+
+
+
+
+
+
+
+
+
+  NetgenGeometry *  STLGeometryRegister :: Load (string filename) const
+  {
+    const char * cfilename = filename.c_str();
+
+    if (strcmp (&cfilename[strlen(cfilename)-3], "stl") == 0)
+      {
+	PrintMessage (1, "Load STL geometry file ", cfilename);
+
+	ifstream infile(cfilename);
+
+	STLGeometry * hgeom = STLGeometry :: Load (infile);
+	hgeom -> edgesfound = 0;
+	return hgeom;
+      }
+    else if (strcmp (&cfilename[strlen(cfilename)-4], "stlb") == 0)
+      {
+	PrintMessage (1, "Load STL binary geometry file ", cfilename);
+
+	ifstream infile(cfilename);
+
+	STLGeometry * hgeom = STLGeometry :: LoadBinary (infile);
+	hgeom -> edgesfound = 0;
+	return hgeom;
+      }
+    else if (strcmp (&cfilename[strlen(cfilename)-3], "nao") == 0)
+      {
+	PrintMessage (1, "Load naomi (F. Kickinger) geometry file ", cfilename);
+
+	ifstream infile(cfilename);
+
+	STLGeometry * hgeom = STLGeometry :: LoadNaomi (infile);
+	hgeom -> edgesfound = 0;
+	return hgeom;
+      }
+
+    
+    return NULL;
+  }
+
+
+
+
+
+
+
+
+
+  int Ng_STLInfo  (ClientData clientData,
+		   Tcl_Interp * interp,
+		   int argc, tcl_const char *argv[])
+  {
+    double data[10];
+    static char buf[20];
+
+    STLGeometry * stlgeometry = dynamic_cast<STLGeometry*> (ng_geometry);
+
+    if (!stlgeometry)
+      {
+	Tcl_SetResult (interp, err_needsstlgeometry, TCL_STATIC);
+	return TCL_ERROR;
+      }
+
+
+
+    if (stlgeometry)
+      {
+	stlgeometry->STLInfo(data);
+	//      cout << "NT=" << data[0] << endl;
+
+	if (argc == 2)
+	  {
+	    if (strcmp (argv[1], "status") == 0)
+	      {
+		switch (stlgeometry->GetStatus())
+		  {
+		  case STLGeometry::STL_GOOD:
+		    strcpy (buf, "GOOD"); break;
+		  case STLGeometry::STL_WARNING:
+		    strcpy (buf, "WARNING"); break;
+		  case STLGeometry::STL_ERROR:
+		    strcpy (buf, "ERROR"); break;
+		  }
+		Tcl_SetResult (interp, buf, TCL_STATIC);
+		return TCL_OK;
+	      }
+	    if (strcmp (argv[1], "statustext") == 0)
+	      {
+		Tcl_SetResult (interp, (char*)stlgeometry->GetStatusText().c_str(), TCL_STATIC);
+		return TCL_OK;
+	      }
+	    if (strcmp (argv[1], "topology_ok") == 0)
+	      {
+		sprintf (buf, "%d", stlgeometry->Topology_Ok());
+		Tcl_SetResult (interp, buf, TCL_STATIC);
+	      }
+	    if (strcmp (argv[1], "orientation_ok") == 0)
+	      {
+		sprintf (buf, "%d", stlgeometry->Orientation_Ok());
+		Tcl_SetResult (interp, buf, TCL_STATIC);
+	      }
+	  }
+      }
+    else
+      {
+	data[0] = 0;
+	data[1] = 0;
+	data[2] = 0;
+	data[3] = 0;
+	data[4] = 0;
+	data[5] = 0;
+	data[6] = 0;
+	data[7] = 0;
+      }
+
+
+
+
+    sprintf (buf, "%i", (int)data[0]);
+    Tcl_SetVar (interp, argv[1], buf, 0);
+
+    sprintf (buf, "%5.3g", data[1]);
+    Tcl_SetVar (interp, argv[2], buf, 0);
+    sprintf (buf, "%5.3g", data[2]);
+    Tcl_SetVar (interp, argv[3], buf, 0);
+    sprintf (buf, "%5.3g", data[3]);
+    Tcl_SetVar (interp, argv[4], buf, 0);
+
+    sprintf (buf, "%5.3g", data[4]);
+    Tcl_SetVar (interp, argv[5], buf, 0);
+    sprintf (buf, "%5.3g", data[5]);
+    Tcl_SetVar (interp, argv[6], buf, 0);
+    sprintf (buf, "%5.3g", data[6]);
+    Tcl_SetVar (interp, argv[7], buf, 0);
+
+    sprintf (buf, "%i", (int)data[7]);
+    Tcl_SetVar (interp, argv[8], buf, 0);
+
+    return TCL_OK;
+  }
+
+
+
+  extern int Ng_SetMeshingParameters  (ClientData clientData,
+				       Tcl_Interp * interp,
+				       int argc, tcl_const char *argv[]);
+
+  int Ng_STLCalcLocalH  (ClientData clientData,    
+			 Tcl_Interp * interp,
+			 int argc, tcl_const char *argv[])
+  {
+    for (int i = 0; i < geometryregister.Size(); i++)
+      geometryregister[i] -> SetParameters (interp);
+
+
+    Ng_SetMeshingParameters (clientData, interp, argc, argv);
+
+    STLGeometry * stlgeometry = dynamic_cast<STLGeometry*> (ng_geometry);
+    if (mesh.Ptr() && stlgeometry)
+      {
+	mesh -> SetLocalH (stlgeometry->GetBoundingBox().PMin() - Vec3d(10, 10, 10),
+			   stlgeometry->GetBoundingBox().PMax() + Vec3d(10, 10, 10),
+			   mparam.grading);
+	stlgeometry -> RestrictLocalH(*mesh, mparam.maxh);
+
+	if (stlparam.resthsurfmeshcurvenable)
+	  mesh -> CalcLocalHFromSurfaceCurvature (mparam.grading, 
+						  stlparam.resthsurfmeshcurvfac);
+      }
+
+    return TCL_OK;
+  }
+
+
+
+
+  VisualScene * STLGeometryRegister :: GetVisualScene (const NetgenGeometry * geom) const
+  {
+    STLGeometry * geometry = dynamic_cast<STLGeometry*> (ng_geometry);
+    if (geometry)
+      {
+	vsstlmeshing.SetGeometry (geometry);
+	return &vsstlmeshing;
+      }
+    return NULL;
+  }
+}
+
+
+using namespace netgen;
+
+extern "C" int Ng_stl_Init (Tcl_Interp * interp);
+int Ng_stl_Init (Tcl_Interp * interp)
+{
+  geometryregister.Append (new STLGeometryRegister);
+
+  Tcl_CreateCommand (interp, "Ng_SetSTLParameters", Ng_SetSTLParameters,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+  
+  Tcl_CreateCommand (interp, "Ng_STLDoctor", Ng_STLDoctor,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  Tcl_CreateCommand (interp, "Ng_STLInfo", Ng_STLInfo,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+  
+  Tcl_CreateCommand (interp, "Ng_STLCalcLocalH", Ng_STLCalcLocalH,
+		     (ClientData)NULL,
+		     (Tcl_CmdDeleteProc*) NULL);
+
+  
+  return TCL_OK;
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/stltool.cpp b/contrib/Netgen/libsrc/stlgeom/stltool.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ee06157b085bba9c724443e8997f347cdf79a817
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stltool.cpp
@@ -0,0 +1,1287 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+#include <meshing.hpp>
+
+#include "stlgeom.hpp"
+
+namespace netgen
+{
+
+
+//add a point into a pointlist, return pointnumber
+int AddPointIfNotExists(Array<Point3d>& ap, const Point3d& p, double eps)
+{
+  int i;
+  for (i = 1; i <= ap.Size(); i++)
+    {
+      if (Dist(ap.Get(i),p) <= eps ) {return i;}
+    }
+  return ap.Append(p);
+}
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+double GetDistFromLine(const Point<3> & lp1, const Point<3> & lp2, 
+		       Point<3> & p)
+{
+  Vec3d vn = lp2 - lp1;
+  Vec3d v1 = p - lp1;
+  Vec3d v2 = lp2 - p;
+
+  Point3d pold = p;
+
+  if (v2 * vn <= 0) {p = lp2; return (pold - p).Length();}
+  if (v1 * vn <= 0) {p = lp1; return (pold - p).Length();}
+    
+  double vnl = vn.Length();
+  if (vnl == 0) {return Dist(lp1,p);}
+
+  vn /= vnl;
+  p = lp1 + (v1 * vn) * vn;
+  return (pold - p).Length();
+};
+
+double GetDistFromInfiniteLine(const Point<3>& lp1, const Point<3>& lp2, const Point<3>& p)
+{
+  Vec3d vn(lp1, lp2);
+  Vec3d v1(lp1, p);
+
+  double vnl = vn.Length();
+
+  if (vnl == 0)
+    {
+      return Dist (lp1, p);
+    }
+  else
+    {
+      return Cross (vn, v1).Length() / vnl;
+    }
+};
+
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//Binary IO-Manipulation
+
+
+
+void FIOReadInt(istream& ios, int& i)
+{
+  const int ilen = sizeof(int);
+  
+  char buf[ilen];
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios.get(buf[j]);
+    }
+  memcpy(&i, &buf, ilen);
+}
+
+void FIOWriteInt(ostream& ios, const int& i)
+{
+  const int ilen = sizeof(int);
+  
+  char buf[ilen];
+  memcpy(&buf, &i, ilen);
+
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios << buf[j];
+    }
+}
+
+void FIOReadDouble(istream& ios, double& i)
+{
+  const int ilen = sizeof(double);
+  
+  char buf[ilen];
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios.get(buf[j]);
+    }
+  memcpy(&i, &buf, ilen);
+}
+
+void FIOWriteDouble(ostream& ios, const double& i)
+{
+  const int ilen = sizeof(double);
+  
+  char buf[ilen];
+  memcpy(&buf, &i, ilen);
+
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios << buf[j];
+    }
+}
+
+void FIOReadFloat(istream& ios, float& i)
+{
+  const int ilen = sizeof(float);
+  
+  char buf[ilen];
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios.get(buf[j]);
+    }
+  memcpy(&i, &buf, ilen);
+}
+
+void FIOWriteFloat(ostream& ios, const float& i)
+{
+  const int ilen = sizeof(float);
+  
+  char buf[ilen];
+  memcpy(&buf, &i, ilen);
+
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios << buf[j];
+     }
+}
+
+void FIOReadString(istream& ios, char* str, int len)
+{
+  int j;
+  for (j = 0; j < len; j++)
+    {
+      ios.get(str[j]);
+    }
+}
+
+//read string and add terminating 0
+void FIOReadStringE(istream& ios, char* str, int len)
+{
+  int j;
+  for (j = 0; j < len; j++)
+    {
+      ios.get(str[j]);
+    }
+  str[len] = 0;
+}
+
+void FIOWriteString(ostream& ios, char* str, int len)
+{
+  int j;
+  for (j = 0; j < len; j++)
+    {
+      ios << str[j];
+    }
+}
+
+
+/*
+void FIOReadInt(istream& ios, int& i)
+{
+  const int ilen = sizeof(int);
+  
+  char buf[ilen];
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios.get(buf[ilen-j-1]);
+    }
+  memcpy(&i, &buf, ilen);
+}
+
+void FIOWriteInt(ostream& ios, const int& i)
+{
+  const int ilen = sizeof(int);
+  
+  char buf[ilen];
+  memcpy(&buf, &i, ilen);
+
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios << buf[ilen-j-1];
+    }
+}
+
+void FIOReadDouble(istream& ios, double& i)
+{
+  const int ilen = sizeof(double);
+  
+  char buf[ilen];
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios.get(buf[ilen-j-1]);
+    }
+  memcpy(&i, &buf, ilen);
+}
+
+void FIOWriteDouble(ostream& ios, const double& i)
+{
+  const int ilen = sizeof(double);
+  
+  char buf[ilen];
+  memcpy(&buf, &i, ilen);
+
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios << buf[ilen-j-1];
+    }
+}
+
+void FIOReadFloat(istream& ios, float& i)
+{
+  const int ilen = sizeof(float);
+  
+  char buf[ilen];
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios.get(buf[ilen-j-1]);
+    }
+  memcpy(&i, &buf, ilen);
+}
+
+void FIOWriteFloat(ostream& ios, const float& i)
+{
+  const int ilen = sizeof(float);
+  
+  char buf[ilen];
+  memcpy(&buf, &i, ilen);
+
+  int j;
+  for (j = 0; j < ilen; j++)
+    {
+      ios << buf[ilen-j-1];
+    }
+}
+
+void FIOReadString(istream& ios, char* str, int len)
+{
+  int j;
+  for (j = 0; j < len; j++)
+    {
+      ios.get(str[j]);
+    }
+}
+
+//read string and add terminating 0
+void FIOReadStringE(istream& ios, char* str, int len)
+{
+  int j;
+  for (j = 0; j < len; j++)
+    {
+      ios.get(str[j]);
+    }
+  str[len] = 0;
+}
+
+void FIOWriteString(ostream& ios, char* str, int len)
+{
+  int j;
+  for (j = 0; j < len; j++)
+    {
+      ios << str[j];
+    }
+}
+*/
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+STLReadTriangle :: STLReadTriangle (const Point<3> * apts,
+				    const Vec<3> & anormal)
+{
+  pts[0] = apts[0];
+  pts[1] = apts[1];
+  pts[2] = apts[2]; 
+  normal = anormal;
+}
+
+
+
+STLTriangle :: STLTriangle(const int * apts)
+{
+  pts[0] = apts[0];
+  pts[1] = apts[1];
+  pts[2] = apts[2];
+
+  facenum = 0;
+}
+
+int STLTriangle :: IsNeighbourFrom(const STLTriangle& t) const
+{
+  //triangles must have same orientation!!!
+  int i, j;
+  for(i = 0; i <= 2; i++)
+    {
+      for(j = 0; j <= 2; j++)
+	{
+	  if (t.pts[(i+1)%3] == pts[j] &&
+	      t.pts[i] == pts[(j+1)%3])
+	    {return 1;}
+	}
+    }
+  return 0;      
+}
+
+int STLTriangle :: IsWrongNeighbourFrom(const STLTriangle& t) const
+{
+  //triangles have not same orientation!!!
+  int i, j;
+  for(i = 0; i <= 2; i++)
+    {
+      for(j = 0; j <= 2; j++)
+	{
+	  if (t.pts[(i+1)%3] == pts[(j+1)%3] &&
+	      t.pts[i] == pts[j])
+	    {return 1;}
+	}
+    }
+  return 0;      
+}
+
+void STLTriangle :: GetNeighbourPoints(const STLTriangle& t, int& p1, int& p2) const
+{
+  int i, j;
+  for(i = 1; i <= 3; i++)
+    {
+      for(j = 1; j <= 3; j++)
+	{
+	  if (t.PNumMod(i+1) == PNumMod(j) &&
+	      t.PNumMod(i) == PNumMod(j+1))
+	    {p1 = PNumMod(j); p2 = PNumMod(j+1); return;}
+	}
+    }
+  PrintSysError("Get neighbourpoints failed!");
+}
+
+int STLTriangle :: GetNeighbourPointsAndOpposite(const STLTriangle& t, int& p1, int& p2, int& po) const
+{
+  int i, j;
+  for(i = 1; i <= 3; i++)
+    {
+      for(j = 1; j <= 3; j++)
+	{
+	  if (t.PNumMod(i+1) == PNumMod(j) &&
+	      t.PNumMod(i) == PNumMod(j+1))
+	    {p1 = PNumMod(j); p2 = PNumMod(j+1); po = PNumMod(j+2); return 1;}
+	}
+    }
+  return 0;
+}
+
+Vec<3> STLTriangle :: GeomNormal(const Array<Point<3> >& ap) const
+{
+  const Point<3> & p1 = ap.Get(PNum(1));
+  const Point<3> & p2 = ap.Get(PNum(2));
+  const Point<3> & p3 = ap.Get(PNum(3));
+  
+  return Cross(p2-p1, p3-p1);
+}
+
+
+void STLTriangle :: SetNormal (const Vec<3> & n)
+{
+  double len = n.Length();
+  if (len > 0)
+    {
+      normal = n;
+      normal.Normalize();
+    }
+  else
+    {
+      normal = Vec<3> (1, 0, 0);
+    }
+}
+
+
+void STLTriangle :: ChangeOrientation()
+{ 
+  normal *= -1;
+  Swap(pts[0],pts[1]); 
+}
+
+
+
+double STLTriangle :: Area(const Array<Point<3> >& ap) const
+{
+  return 0.5 * Cross(ap.Get(PNum(2))-ap.Get(PNum(1)), 
+		     ap.Get(PNum(3))-ap.Get(PNum(1))).Length();
+}
+
+double STLTriangle :: MinHeight(const Array<Point<3> >& ap) const
+{
+  double ml = MaxLength(ap);
+  if (ml != 0) {return 2.*Area(ap)/ml;}
+  PrintWarning("max Side Length of a triangle = 0!!!");
+  return 0;
+}
+
+double STLTriangle :: MaxLength(const Array<Point<3> >& ap) const
+{
+  return max3(Dist(ap.Get(PNum(1)),ap.Get(PNum(2))),
+	      Dist(ap.Get(PNum(2)),ap.Get(PNum(3))),
+	      Dist(ap.Get(PNum(3)),ap.Get(PNum(1))));
+}
+
+void STLTriangle :: ProjectInPlain(const Array<Point<3> >& ap, 
+				   const Vec<3> & n, Point<3> & pp) const
+{
+  const Point<3> & p1 = ap.Get(PNum(1));
+  const Point<3> & p2 = ap.Get(PNum(2));
+  const Point<3> & p3 = ap.Get(PNum(3));
+  
+  Vec<3> v1 = p2 - p1;
+  Vec<3> v2 = p3 - p1;
+  Vec<3> nt = Cross(v1, v2);
+
+  double c = - (p1(0)*nt(0) + p1(1)*nt(1) + p1(2)*nt(2));
+
+  double prod = n * nt;  
+
+  if (fabs(prod) == 0) 
+    {
+      pp = Point<3>(1.E20,1.E20,1.E20); 
+      return; 
+    }
+
+  double nfact = -(pp(0)*nt(0) + pp(1)*nt(1) + pp(2)*nt(2) + c) / (prod);
+  pp = pp + (nfact) * n;
+
+}
+
+
+int STLTriangle :: ProjectInPlain (const Array<Point<3> >& ap, 
+				   const Vec<3> & nproj, 
+				   Point<3> & pp, Vec<3> & lam) const
+{
+  const Point<3> & p1 = ap.Get(PNum(1));
+  const Point<3> & p2 = ap.Get(PNum(2));
+  const Point<3> & p3 = ap.Get(PNum(3));
+  
+  Vec<3> v1 = p2-p1;
+  Vec<3> v2 = p3-p1;
+
+  Mat<3> mat;
+  for (int i = 0; i < 3; i++)
+    {
+      mat(i,0) = v1(i);
+      mat(i,1) = v2(i);
+      mat(i,2) = nproj(i);
+    }
+
+  int err = 0;
+  mat.Solve (pp-p1, lam);
+  //  int err = SolveLinearSystem (v1, v2, nproj, pp-p1, lam);
+
+  if (!err)
+    {
+      //      pp = p1 + lam(0) * v1 + lam(1) * v2;
+
+      pp(0) = p1(0) + lam(0) * v1(0) + lam(1) * v2(0);
+      pp(1) = p1(1) + lam(0) * v1(1) + lam(1) * v2(1);
+      pp(2) = p1(2) + lam(0) * v1(2) + lam(1) * v2(2);
+    }
+  return err;
+}
+
+
+
+
+
+void STLTriangle :: ProjectInPlain(const Array<Point<3> >& ap, 
+				   Point<3> & pp) const
+{
+  const Point<3> & p1 = ap.Get(PNum(1));
+  const Point<3> & p2 = ap.Get(PNum(2));
+  const Point<3> & p3 = ap.Get(PNum(3));
+  
+  Vec<3> v1 = p2 - p1;
+  Vec<3> v2 = p3 - p1;
+  Vec<3> nt = Cross(v1, v2);
+
+  double c = - (p1(0)*nt(0) + p1(1)*nt(1) + p1(2)*nt(2));
+  
+  double prod = nt * nt;  
+
+  double nfact = -(pp(0)*nt(0) + pp(1)*nt(1) + pp(2)*nt(2) + c) / (prod);
+
+  pp = pp + (nfact) * nt;
+}
+
+int STLTriangle :: PointInside(const Array<Point<3> > & ap, 
+			       const Point<3> & pp) const
+{
+  const Point<3> & p1 = ap.Get(PNum(1));
+  const Point<3> & p2 = ap.Get(PNum(2));
+  const Point<3> & p3 = ap.Get(PNum(3));
+  
+  Vec<3> v1 = p2 - p1;
+  Vec<3> v2 = p3 - p1;
+  Vec<3> v  = pp - p1;
+  double det, l1, l2;
+  Vec<3> ex, ey, ez;
+
+
+  ez = GeomNormal(ap);
+  ez /= ez.Length();
+  ex = v1;
+  ex /= ex.Length();
+  ey = Cross (ez, ex);
+  
+  Vec<2> v1p(v1*ex, v1*ey);
+  Vec<2> v2p(v2*ex, v2*ey);
+  Vec<2> vp(v*ex, v*ey);
+
+  det = v2p(1) * v1p(0) - v2p(0) * v1p(1);
+
+  if (fabs(det) == 0) {return 0;}
+  
+  l2 = (vp(1) * v1p(0) - vp(0) * v1p(1)) / det;
+  
+  if (v1p(0) != 0.)
+    {
+      l1 = (vp(0) - l2 * v2p(0)) / v1p(0);
+    }
+  else if (v1p(1) != 0.)
+    {
+      l1 = (vp(1) - l2 * v2p(1)) / v1p(1);
+    }
+  else {return 0;}
+  
+  if (l1 >= -1E-10 && l2 >= -1E-10 && l1 + l2 <= 1.+1E-10) {return 1;}
+  return 0; 
+}
+
+double STLTriangle :: GetNearestPoint(const Array<Point<3> >& ap, 
+				      Point<3> & p3d) const
+{
+  Point<3> p = p3d;
+  ProjectInPlain(ap, p);
+  double dist = (p - p3d).Length();
+
+  if (PointInside(ap, p)) {p3d = p; return dist;}
+  else
+    {
+      Point<3> pf = 0.0;
+      double nearest = 1E50;
+      //int fi = 0;
+      for (int j = 1; j <= 3; j++)
+	{
+	  p = p3d;
+	  dist = GetDistFromLine(ap.Get(PNum(j)), ap.Get(PNumMod(j+1)), p);
+	  if (dist < nearest)
+	    {
+	      nearest = dist; 
+	      pf = p;
+	    }
+	}
+      p3d = pf;
+      return nearest;
+    }
+}
+
+int STLTriangle :: HasEdge(int p1, int p2) const
+{
+  int i;
+  for (i = 1; i <= 3; i++)
+    {
+      if (p1 == PNum(i) && p2 == PNumMod(i+1)) {return 1;}
+    }
+  return 0;
+}
+
+ostream& operator<<(ostream& os, const STLTriangle& t)
+{
+  os << "[";
+  os << t[0] << ",";
+  os << t[1] << ",";
+  os << t[2] << "]";
+
+  return os;
+};
+
+
+
+STLTopEdge :: STLTopEdge ()
+{
+  pts[0] = pts[1] = 0;
+  trigs[0] = trigs[1] = 0;
+  cosangle = 1;
+  status = ED_UNDEFINED;
+}
+
+STLTopEdge :: STLTopEdge (int p1, int p2, int trig1, int trig2)
+{ 
+  pts[0] = p1; 
+  pts[1] = p2; 
+  trigs[0] = trig1; 
+  trigs[1] = trig2; 
+  cosangle = 1;
+  status = ED_UNDEFINED;
+}
+
+
+
+
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//+++++++++++++++++++   STL CHART   +++++++++++++++++++++++++++++++
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+STLChart :: STLChart(STLGeometry * ageometry)
+{
+  charttrigs = new Array<int> (0,0);
+  outertrigs = new Array<int> (0,0);
+  ilimit = new Array<twoint> (0,0);
+  olimit = new Array<twoint> (0,0);
+
+  geometry = ageometry;
+
+  if ( (stlparam.usesearchtree == 1))
+    searchtree = new Box3dTree (geometry->GetBoundingBox().PMin() - Vec3d(1,1,1),
+				geometry->GetBoundingBox().PMax() + Vec3d(1,1,1));
+  else
+    searchtree = NULL;
+}
+
+void STLChart :: AddChartTrig(int i)
+{
+  charttrigs->Append(i);
+  
+  const STLTriangle & trig = geometry->GetTriangle(i);
+  const Point3d & p1 = geometry->GetPoint (trig.PNum(1));
+  const Point3d & p2 = geometry->GetPoint (trig.PNum(2));
+  const Point3d & p3 = geometry->GetPoint (trig.PNum(3));
+
+  Point3d pmin(p1), pmax(p1);
+  pmin.SetToMin (p2);
+  pmin.SetToMin (p3);
+  pmax.SetToMax (p2);
+  pmax.SetToMax (p3);
+  
+  if (!geomsearchtreeon && (stlparam.usesearchtree == 1))
+    {searchtree->Insert (pmin, pmax, i);}
+}
+
+void STLChart :: AddOuterTrig(int i)
+{
+  outertrigs->Append(i);
+
+  const STLTriangle & trig = geometry->GetTriangle(i);
+  const Point3d & p1 = geometry->GetPoint (trig.PNum(1));
+  const Point3d & p2 = geometry->GetPoint (trig.PNum(2));
+  const Point3d & p3 = geometry->GetPoint (trig.PNum(3));
+
+  Point3d pmin(p1), pmax(p1);
+  pmin.SetToMin (p2);
+  pmin.SetToMin (p3);
+  pmax.SetToMax (p2);
+  pmax.SetToMax (p3);
+  
+  if (!geomsearchtreeon && (stlparam.usesearchtree==1))
+    {searchtree->Insert (pmin, pmax, i);}
+}
+
+int STLChart :: IsInWholeChart(int nr) const
+{
+  int i;
+  for (i = 1; i <= charttrigs->Size(); i++)
+    {
+      if (charttrigs->Get(i) == nr) {return 1;}
+    }
+  for (i = 1; i <= outertrigs->Size(); i++)
+    {
+      if (outertrigs->Get(i) == nr) {return 1;}
+    }
+  return 0;
+}
+
+void STLChart :: GetTrianglesInBox (const Point3d & pmin,
+				    const Point3d & pmax,
+				    Array<int> & trias) const
+{
+  if (geomsearchtreeon) {PrintMessage(5,"geomsearchtreeon is set!!!");}
+
+  if (searchtree)
+    searchtree -> GetIntersecting (pmin, pmax, trias);
+  else
+    {
+      int i;
+      Box3d box1(pmin, pmax);
+      box1.Increase (1e-4);
+      Box3d box2;
+
+      trias.SetSize(0);
+      
+      int nt = GetNT();
+      for (i = 1; i <= nt; i++)
+	{
+
+	  int trignum = GetTrig(i);
+	  const STLTriangle & trig = geometry->GetTriangle(trignum);
+	  box2.SetPoint (geometry->GetPoint (trig.PNum(1)));
+	  box2.AddPoint (geometry->GetPoint (trig.PNum(2)));
+	  box2.AddPoint (geometry->GetPoint (trig.PNum(3)));
+	  
+	  if (box1.Intersect (box2))
+	    {
+	      trias.Append (trignum);
+	    }
+	}
+    }
+}
+
+//trigs may contain the same triangle double
+void STLChart :: MoveToOuterChart(const Array<int>& trigs)
+{
+  if (!trigs.Size()) {return;}
+  int i;
+  for (i = 1; i <= trigs.Size(); i++)
+    {
+      if (charttrigs->Get(trigs.Get(i)) != -1) 
+	{AddOuterTrig(charttrigs->Get(trigs.Get(i)));}
+      charttrigs->Elem(trigs.Get(i)) = -1;
+    }
+  DelChartTrigs(trigs);
+}
+
+//trigs may contain the same triangle double
+void STLChart :: DelChartTrigs(const Array<int>& trigs)
+{
+  if (!trigs.Size()) {return;}
+
+  int i;
+  for (i = 1; i <= trigs.Size(); i++)
+    {
+      charttrigs->Elem(trigs.Get(i)) = -1;
+    }
+
+  int cnt = 0;
+  for (i = 1; i <= charttrigs->Size(); i++)
+    {
+      if (charttrigs->Elem(i) == -1)
+	{
+	  cnt++;
+	}
+      if (cnt != 0 && i < charttrigs->Size())
+	{
+	  charttrigs->Elem(i-cnt+1) = charttrigs->Get(i+1);
+	}
+    }
+  i = charttrigs->Size() - trigs.Size();
+  charttrigs->SetSize(i);
+
+  if (!geomsearchtreeon && stlparam.usesearchtree == 1)
+    {
+      PrintMessage(7, "Warning: unsecure routine due to first use of searchtrees!!!");
+      //bould new searchtree!!!
+      searchtree = new Box3dTree (geometry->GetBoundingBox().PMin() - Vec3d(1,1,1),
+				  geometry->GetBoundingBox().PMax() + Vec3d(1,1,1));
+
+      for (i = 1; i <= charttrigs->Size(); i++)
+	{
+	  const STLTriangle & trig = geometry->GetTriangle(i);
+	  const Point3d & p1 = geometry->GetPoint (trig.PNum(1));
+	  const Point3d & p2 = geometry->GetPoint (trig.PNum(2));
+	  const Point3d & p3 = geometry->GetPoint (trig.PNum(3));
+	  
+	  Point3d pmin(p1), pmax(p1);
+	  pmin.SetToMin (p2);
+	  pmin.SetToMin (p3);
+	  pmax.SetToMax (p2);
+	  pmax.SetToMax (p3);
+	  
+	  searchtree->Insert (pmin, pmax, i);	  
+	}
+    }
+}
+
+
+void STLChart :: SetNormal (const Point<3> & apref, const Vec<3> & anormal)
+{
+  pref = apref;
+  normal = anormal;
+  double len = normal.Length();
+  if (len) normal /= len;
+  else normal = Vec<3> (1, 0, 0);
+
+  t1 = normal.GetNormal ();
+  t2 = Cross (normal, t1);
+}
+
+Point<2> STLChart :: Project2d (const Point<3> & p3d) const
+{
+  Vec<3> v = p3d-pref;
+  return Point<2> (t1 * v, t2 * v);
+}
+
+
+
+/*
+  Point3d p1, p2, center;
+  double rad;
+  int i1, i2;
+public:
+*/
+STLBoundarySeg :: 
+STLBoundarySeg (int ai1, int ai2, const Array<Point<3> > & points,
+		const STLChart * chart)
+{
+  i1 = ai1;
+  i2 = ai2; 
+  p1 = points.Get(i1);
+  p2 = points.Get(i2);
+  center = ::netgen::Center (p1, p2);
+  rad = Dist (p1, center);
+
+  p2d1 = chart->Project2d (p1);
+  p2d2 = chart->Project2d (p2);
+  
+  boundingbox.Set (p2d1);
+  boundingbox.Add (p2d2);
+}
+
+void STLBoundarySeg :: Swap ()
+{
+  ::netgen::Swap (i1, i2);
+  ::netgen::Swap (p1, p2);
+}
+
+
+
+STLBoundary :: STLBoundary (STLGeometry * ageometry)
+  : // boundary(), 
+    geometry(ageometry)
+{
+  ;
+}
+
+
+void STLBoundary :: AddOrDelSegment(const STLBoundarySeg & seg)
+{
+  int i;
+  int found = 0;
+  for (i = 1; i <= boundary.Size(); i++)
+    {
+      if (found) {boundary.Elem(i-1) = boundary.Get(i);}
+      if (boundary.Get(i) == seg) {found = 1;}
+    }
+  if (!found) 
+    {
+      boundary.Append(seg);
+    }
+  else 
+    {
+      boundary.SetSize(boundary.Size()-1);
+    }
+}
+
+void STLBoundary ::AddTriangle(const STLTriangle & t)
+{
+  int i;
+  int found1 = 0;
+  int found2 = 0;
+  int found3 = 0;
+  //int offset = 0;
+  
+
+  STLBoundarySeg seg1(t[0],t[1], geometry->GetPoints(), chart);
+  STLBoundarySeg seg2(t[1],t[2], geometry->GetPoints(), chart);
+  STLBoundarySeg seg3(t[2],t[0], geometry->GetPoints(), chart);
+
+  seg1.SetSmoothEdge (geometry->IsSmoothEdge (seg1.I1(), seg1.I2()));
+  seg2.SetSmoothEdge (geometry->IsSmoothEdge (seg2.I1(), seg2.I2()));
+  seg3.SetSmoothEdge (geometry->IsSmoothEdge (seg3.I1(), seg3.I2()));
+
+  /*
+  for (i = 1; i <= boundary.Size(); i++)
+    {
+      if (offset) {boundary.Elem(i-offset) = boundary.Get(i);}
+      if (boundary.Get(i) == seg1) {found1 = 1; offset++;}
+      if (boundary.Get(i) == seg2) {found2 = 1; offset++;}
+      if (boundary.Get(i) == seg3) {found3 = 1; offset++;}
+    }
+
+  if (offset)
+    {
+      boundary.SetSize(boundary.Size()-offset);
+    }    
+  */
+  for (i = boundary.Size(); i >= 1; i--)
+    {
+      if (boundary.Get(i) == seg1) 
+	{ boundary.DeleteElement (i); found1 = 1; } 
+      else if (boundary.Get(i) == seg2) 
+	{ boundary.DeleteElement (i); found2 = 1; } 
+      else if (boundary.Get(i) == seg3) 
+	{ boundary.DeleteElement (i); found3 = 1; } 
+    }
+
+  if (!found1) {seg1.Swap(); boundary.Append(seg1);}
+  if (!found2) {seg2.Swap(); boundary.Append(seg2);}
+  if (!found3) {seg3.Swap(); boundary.Append(seg3);}
+}
+
+int STLBoundary :: TestSeg(const Point<3>& p1, const Point<3> & p2, const Vec<3> & sn, 
+			   double sinchartangle, int divisions, Array<Point<3> >& points, double eps)
+{
+
+  if (usechartnormal)
+    return TestSegChartNV (p1, p2, sn);
+
+  // for statistics
+  {
+    int i;
+    static Array<int> cntclass;
+    static int cnt = 0;
+    static int cnti = 0, cnto = 0;
+    static long int cntsegs = 0;
+    if (cntclass.Size() == 0)
+      {
+	cntclass.SetSize (20);
+	for (i = 1; i <= cntclass.Size(); i++)
+	  cntclass.Elem(i) = 0;
+      }
+    
+    cntsegs += NOSegments();
+    int cla = int (log (double(NOSegments()+1)) / log(2.0));
+    if (cla < 1) cla = 1;
+    if (cla > cntclass.Size()) cla = cntclass.Size();
+    cntclass.Elem(cla)++;
+    cnt++;
+    if (divisions)
+      cnti++;
+    else
+      cnto++;
+    if (cnt > 100000) 
+      {
+	cnt = 0;
+	/*
+	(*testout) << "TestSeg-calls for classes:" << endl;
+	(*testout) << cnti << " inner calls, " << cnto << " outercalls" << endl;
+	(*testout) << "total testes segments: " << cntsegs << endl;
+	for (i = 1; i <= cntclass.Size(); i++)
+	  {
+	    (*testout) << int (exp (i * log(2.0))) << " bnd segs: " << cntclass.Get(i) << endl;
+	  }
+	*/
+      }
+  }
+
+
+  int i,j,k;
+  Point<3> seg1p/*, seg2p*/;
+  Point<3> sp1,sp2;
+  double lambda1, lambda2, vlen2;
+  Vec<3> vptpl;
+  double sinchartangle2 = sqr(sinchartangle);
+  double scal;
+  int possible;
+
+  //double maxval = -1;
+  //double maxvalnew = -1;
+
+
+
+  double scalp1 = p1(0) * sn(0) + p1(1) * sn(1) + p1(2) * sn(2);
+  double scalp2 = p2(0) * sn(0) + p2(1) * sn(1) + p2(2) * sn(2);
+  double minl = min2(scalp1, scalp2);
+  double maxl = max2(scalp1, scalp2);
+  Point<3> c = Center (p1, p2);
+  double dist1 = Dist (c, p1);
+ 
+  int nseg = NOSegments();
+  for (j = 1; j <= nseg; j++)
+    {
+      const STLBoundarySeg & seg = GetSegment(j);
+
+
+      if (seg.IsSmoothEdge())
+	continue;
+
+
+      sp1 = seg.P1();
+      sp2 = seg.P2();
+
+      // Test, ob Spiral Konfikt moeglich
+      
+      possible = 1;
+
+      double scalsp1 = sp1(0) * sn(0) + sp1(1) * sn(1) + sp1(2) * sn(2);
+      double scalsp2 = sp2(0) * sn(0) + sp2(1) * sn(1) + sp2(2) * sn(2);
+
+      double minsl = min2(scalsp1, scalsp2);
+      double maxsl = max2(scalsp1, scalsp2);
+      
+      double maxdiff = max2 (maxsl - minl, maxl - minsl);
+      
+      /*
+      Point3d sc = Center (sp1, sp2);
+      double mindist = Dist(c, sc) - dist1 - GetSegment(j).Radius();
+      if (maxdiff < sinchartangle * mindist)
+	{
+	  possible = 0;
+	}
+      */
+       
+      double hscal = maxdiff + sinchartangle * (dist1 + seg.Radius());
+      if (hscal * hscal < sinchartangle * Dist2(c, seg.center ))
+	possible = 0;
+
+
+      /*      
+      if (possible)
+	{
+	  double mindist2ex = MinDistLL2 (p1, p2, sp1, sp2);
+	  if (maxdiff * maxdiff < sinchartangle2 * mindist2ex)
+	    possible = 0;
+	}
+      */
+
+      if (possible)
+      	{
+	  LinearPolynomial2V lp (scalp1 - scalsp1,
+				 scalp2 - scalp1,
+				 -(scalsp2 - scalsp1));
+	  QuadraticPolynomial2V slp;
+	  slp.Square (lp);
+	  
+      
+	  Vec3d v (p1, sp1);
+	  Vec3d vl (p1, p2);
+	  Vec3d vsl (sp1, sp2);
+      
+	  QuadraticPolynomial2V qp (v.Length2(),
+				    -2 * (v * vl),
+				    2 * (v * vsl),
+				    vl.Length2(),
+				    -2 * (vl * vsl),
+				    vsl.Length2());
+	  
+	  slp.Add (-sinchartangle2, qp);
+
+	  double hv = slp.MaxUnitSquare();
+
+	  if (hv > eps) return 0;
+	  /*
+	  if (hv > maxvalnew)
+	    maxvalnew = hv;
+	  */
+	}
+      
+
+      if (possible && 0)
+
+	for (i = 0; i <= divisions; i++)
+	  {
+	    
+	    lambda1 = (double)i/(double)divisions;
+	    seg1p = Point3d(p1(0)*lambda1+p2(0)*(1.-lambda1),
+			    p1(1)*lambda1+p2(1)*(1.-lambda1),
+			    p1(2)*lambda1+p2(2)*(1.-lambda1));
+	    
+
+	    
+	    for (k = 0; k <= divisions; k++)
+	      {
+		lambda2 = (double)k/(double)divisions;
+		vptpl = Vec3d(sp1(0)*lambda2+sp2(0)*(1.-lambda2)-seg1p(0),
+			      sp1(1)*lambda2+sp2(1)*(1.-lambda2)-seg1p(1),
+			      sp1(2)*lambda2+sp2(2)*(1.-lambda2)-seg1p(2));
+		
+		vlen2 = vptpl.Length2();
+
+		//		if (vlen2 > 0)
+		  {
+		    scal = vptpl * sn;
+		    double hv = scal*scal - sinchartangle2*vlen2;
+
+
+
+		    /*
+		    if (hv > maxval)
+		      maxval = hv;
+		    */
+		    if (hv > eps) return 0;
+		  }
+	      } 
+	  }
+    }
+  
+  return 1;
+  //  return (maxvalnew < eps);
+}
+
+
+
+// checks, whether 2d projection intersects
+int STLBoundary :: TestSegChartNV(const Point3d & p1, const Point3d& p2, 
+				  const Vec3d& sn)
+{
+  int j;
+  int nseg = NOSegments();
+
+  Point<2> p2d1 = chart->Project2d (p1);
+  Point<2> p2d2 = chart->Project2d (p2);
+
+  Box<2> box2d;
+  box2d.Set (p2d1);
+  box2d.Add (p2d2);
+  /*
+  Point2d pmin(p2d1);
+  pmin.SetToMin (p2d2);
+  Point2d pmax(p2d1);
+  pmax.SetToMax (p2d2);
+  */
+
+  Line2d l1 (p2d1, p2d2);
+
+  double lam1, lam2;
+  double eps = 1e-3;
+  
+  for (j = 1; j <= nseg; j++)
+    {
+      const STLBoundarySeg & seg = GetSegment(j);
+
+      if (!box2d.Intersect (seg.BoundingBox()))
+	continue;
+      /*
+      if (seg.P2DMin()(0) > pmax(0)) continue;
+      if (seg.P2DMin()(1) > pmax(1)) continue;
+      if (seg.P2DMax()(0) < pmin(0)) continue;
+      if (seg.P2DMax()(1) < pmin(1)) continue;
+      */
+
+      if (seg.IsSmoothEdge()) continue;
+
+      const Point<2> & sp1 = seg.P2D1();
+      const Point<2> & sp2 = seg.P2D2();
+      
+
+      Line2d l2 (sp1, sp2);
+      
+      int err =
+	CrossPointBarycentric (l1, l2, lam1, lam2);
+      /*
+      if (chartdebug)
+	{
+	  
+	  (*testout) << "lam1 = " << lam1 << ", lam2 = " << lam2 << endl;
+	  (*testout) << "p2d = " << p2d1 << ", " << p2d2 << endl;
+	  (*testout) << "sp2d = " << sp1 << ", " << sp2 << endl;
+	  (*testout) << "i1,2 = " << seg.I1() << ", " << seg.I2() << endl;
+	  
+	}
+      */
+      if (!err && lam1 > eps && lam1 < 1-eps &&
+	  lam2 > eps && lam2 < 1-eps)
+	return 0;
+    }
+  return 1;
+}
+
+
+
+STLDoctorParams :: STLDoctorParams()
+{
+  drawmeshededges = 1;
+  geom_tol_fact = 1E-6;
+  longlinefact = 0;
+  showexcluded = 1;
+
+  selectmode = 0;
+  edgeselectmode = 0;
+  useexternaledges = 0;
+  showfaces = 0;
+  showtouchedtrigchart = 1;
+  showedgecornerpoints = 1;
+  conecheck = 1;
+  spiralcheck = 1;
+  selecttrig = 0;
+  nodeofseltrig = 1;
+  selectwithmouse = 1;
+  showmarkedtrigs = 1;
+  dirtytrigfact = 0.001;
+  smoothangle = 90;
+  smoothnormalsweight = 0.2;
+  vicinity = 0;
+  showvicinity = 0;
+}
+
+
+
+STLDoctorParams stldoctor;
+
+void STLDoctorParams :: Print (ostream & ost) const
+{
+  ost << "STL doctor parameters:" << endl
+      << "selecttrig = " << selecttrig << endl
+      << "selectlocalpoint = " << nodeofseltrig << endl
+      << "selectwithmouse = " << selectwithmouse << endl
+      << "showmarkedtrigs = " << showmarkedtrigs << endl
+      << "dirtytrigfact = " << dirtytrigfact << endl
+      << "smoothangle = " << smoothangle << endl;
+}
+
+
+STLParameters ::   STLParameters()
+{
+  yangle = 30;
+  contyangle = 20;
+  edgecornerangle = 60;
+  chartangle = 15;
+  outerchartangle = 70;
+     
+  usesearchtree = 0;
+  atlasminh = 1E-4;
+  resthsurfcurvfac = 2;
+  resthsurfcurvenable = 0;
+  resthatlasfac = 2;
+  resthatlasenable = 1;
+  resthchartdistfac = 1.2;
+  resthchartdistenable = 1;
+  resthlinelengthfac = 0.5;
+  resthlinelengthenable = 1;
+  resthcloseedgefac = 1;
+  resthcloseedgeenable = 1;
+  resthedgeanglefac = 1;
+  resthedgeangleenable = 0;
+  resthsurfmeshcurvfac = 1;
+  resthsurfmeshcurvenable = 0;
+  recalc_h_opt = 1;
+}
+
+void STLParameters :: Print (ostream & ost) const
+{
+  ost << "STL parameters:" << endl
+      << "yellow angle = " << yangle << endl
+      << "continued yellow angle = " << contyangle << endl
+      << "edgecornerangle = " << edgecornerangle << endl
+      << "chartangle = " << chartangle << endl
+      << "outerchartangle = " << outerchartangle << endl
+      << "restrict h due to ..., enable and safety factor: " << endl
+      << "surface curvature: " << resthsurfcurvenable
+      << ", fac = " << resthsurfcurvfac << endl
+      << "atlas surface curvature: " << resthatlasenable
+      << ", fac = " << resthatlasfac << endl
+      << "chart distance: " << resthchartdistenable
+      << ", fac = " << resthchartdistfac << endl
+      << "line length: " << resthlinelengthenable
+      << ", fac = " << resthlinelengthfac << endl
+      << "close edges: " << resthcloseedgeenable
+      << ", fac = " << resthcloseedgefac << endl
+      << "edge angle: " << resthedgeangleenable
+      << ", fac = " << resthedgeanglefac << endl;
+}
+
+
+STLParameters stlparam;
+
+
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/stltool.hpp b/contrib/Netgen/libsrc/stlgeom/stltool.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ca3d6e2f14b546740c6552a88de6dff575144eaf
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stltool.hpp
@@ -0,0 +1,271 @@
+#ifndef FILE_STLTOOL
+#define FILE_STLTOOL
+
+
+//#include "gprim/gprim.hh"
+
+/**************************************************************************/
+/* File:   stlgeom.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Author2: Johannes Gerstmayr                                            */
+/* Date:   20. Nov. 99                                                    */
+/**************************************************************************/
+
+
+
+// use one normal vector for whole chart
+extern int usechartnormal;
+extern int chartdebug;
+
+extern int geomsearchtreeon;
+extern int AddPointIfNotExists(Array<Point3d>& ap, const Point3d& p, double eps = 1e-8);
+//get distance from line lp1-lp2 to point p
+extern double GetDistFromLine(const Point<3>& lp1, const Point<3>& lp2, Point<3>& p);
+extern double GetDistFromInfiniteLine(const Point<3>& lp1, const Point<3>& lp2, const Point<3>& p);
+
+
+extern void FIOReadInt(istream& ios, int& i);
+extern void FIOWriteInt(ostream& ios, const int& i);
+extern void FIOReadDouble(istream& ios, double& i);
+extern void FIOWriteDouble(ostream& ios, const double& i);
+extern void FIOReadFloat(istream& ios, float& i);
+extern void FIOWriteFloat(ostream& ios, const float& i);
+extern void FIOReadString(istream& ios, char* str, int len);
+extern void FIOReadStringE(istream& ios, char* str, int len);
+extern void FIOWriteString(ostream& ios, char* str, int len);
+
+
+typedef Array <int> * ArrayINTPTR;
+
+class STLGeometry;
+
+class STLChart
+{
+private:
+  STLGeometry * geometry;
+  Array<int>* charttrigs; // trigs which only belong to this chart
+  Array<int>* outertrigs; // trigs which belong to other charts
+  Box3dTree * searchtree; // ADT containing outer trigs
+
+  Array<twoint>* olimit; //outer limit of outer chart
+  Array<twoint>* ilimit; //outer limit of inner chart
+
+
+public:
+  
+  STLChart(STLGeometry * ageometry);
+  void AddChartTrig(int i);
+  void AddOuterTrig(int i);
+  
+  int IsInWholeChart(int nr) const;
+
+  int GetChartTrig(int i) const {return charttrigs->Get(i);}
+  int GetOuterTrig(int i) const {return outertrigs->Get(i);}
+  //get all trigs:
+  int GetTrig(int i) const
+    {
+      if (i <= charttrigs->Size()) {return charttrigs->Get(i);}
+      else {return outertrigs->Get(i-charttrigs->Size());}
+    }
+  
+  int GetNChartT() const {return charttrigs->Size();}
+  int GetNOuterT() const {return outertrigs->Size();}
+  int GetNT() const {return charttrigs->Size()+outertrigs->Size(); }
+
+  void GetTrianglesInBox (const Point3d & pmin,
+			  const Point3d & pmax,
+			  Array<int> & trias) const;
+  void AddOLimit(twoint l) {olimit->Append(l);}
+  void AddILimit(twoint l) {ilimit->Append(l);}
+
+  void ClearOLimit() {olimit->SetSize(0);}
+  void ClearILimit() {ilimit->SetSize(0);}
+
+  int GetNOLimit() const {return olimit->Size();}
+  int GetNILimit() const {return ilimit->Size();}
+
+  twoint GetOLimit(int i) const {return olimit->Get(i);}
+  twoint GetILimit(int i) const {return ilimit->Get(i);}
+
+  //move triangles trigs (local chart-trig numbers) to outer chart
+  void MoveToOuterChart(const Array<int>& trigs);
+  void DelChartTrigs(const Array<int>& trigs);
+
+
+  // define local coordinate system, JS:
+private:
+  Vec<3> normal;
+  Point<3> pref;
+  Vec<3> t1, t2;
+public:
+  void SetNormal (const Point<3> & apref, const Vec<3> & anormal);
+  const Vec<3> & GetNormal () const { return normal; }
+  Point<2> Project2d (const Point<3> & p3d) const;
+};
+
+class STLBoundarySeg
+{
+  Point<3> p1, p2, center;
+  Point<2> p2d1, p2d2;
+  Box<2> boundingbox;
+  //  Point<2> p2dmin, p2dmax;
+
+  double rad;
+  int i1, i2;
+  int smoothedge;
+public:
+  STLBoundarySeg () { ; }
+  STLBoundarySeg (int ai1, int ai2, const Array<Point<3> > & points,
+		  const STLChart * achart);
+
+  int operator== (const STLBoundarySeg & s2) const
+    { return i1 == s2.i1 && i2 == s2.i2; }
+  void Swap ();
+  int I1() const { return i1; }
+  int I2() const { return i2; }
+  const Point<3> & P1() const { return p1; }
+  const Point<3> & P2() const { return p2; }
+  const Point<2> & P2D1() const { return p2d1; }
+  const Point<2> & P2D2() const { return p2d2; }
+  const Point<2> & P2DMin() const { return boundingbox.PMin(); }
+  const Point<2> & P2DMax() const { return boundingbox.PMax(); }
+  const Point<3> & Center() const { return center; }
+  const Box<2> & BoundingBox() const { return boundingbox; }
+  double Radius () const { return rad; }
+
+  void SetSmoothEdge (int se) { smoothedge = se; }
+  int IsSmoothEdge () const { return smoothedge; }
+  friend class STLBoundary;
+};
+
+class STLBoundary
+{
+private:
+  STLGeometry * geometry;
+  const STLChart * chart;
+  Array<STLBoundarySeg> boundary;
+public:
+  STLBoundary(STLGeometry * ageometry);
+  // : boundary() {};
+
+  void Clear() {boundary.SetSize(0);};
+  void SetChart (const STLChart * achart) { chart = achart; }
+  //don't check, if already exists!
+  void AddNewSegment(const STLBoundarySeg & seg) {boundary.Append(seg);};
+  //check if segment exists
+  void AddOrDelSegment(const STLBoundarySeg & seg);
+  //addordelsegment for all 3 triangle segments!
+  void AddTriangle(const STLTriangle & t);
+  int NOSegments() {return boundary.Size();};
+  const STLBoundarySeg & GetSegment(int i) {return boundary.Get(i);}
+
+  int TestSeg(const Point<3> & p1, const Point<3> & p2, const Vec<3> & sn, 
+	      double sinchartangle, int divisions, Array<Point<3> >& points,
+	      double eps);
+
+  int TestSegChartNV(const Point3d& p1, const Point3d& p2, const Vec3d& sn);
+};
+
+
+class STLDoctorParams
+{
+public:
+  int drawmeshededges;
+  double geom_tol_fact;
+
+  double longlinefact;
+  int showexcluded;
+
+  int selectmode; //0==trig, 1==edge, 2==point, 3==multiedge, 4==line cluster
+  int edgeselectmode;
+
+  int useexternaledges;
+  int showfaces;
+  int showedgecornerpoints;
+  int showtouchedtrigchart;
+  int conecheck;
+  int spiralcheck;
+  int selecttrig;
+  int nodeofseltrig;
+  int selectwithmouse;
+  int showmarkedtrigs;
+  double dirtytrigfact;
+  double smoothangle;
+
+  double smoothnormalsweight;
+
+  int showvicinity;
+  int vicinity;
+  ///
+  STLDoctorParams();
+  ///
+  void Print (ostream & ost) const;
+};
+
+extern STLDoctorParams stldoctor;
+
+
+
+class STLParameters
+{
+public:
+  /// angle for edge detection
+  double yangle;
+  double contyangle; //edges continued with contyangle
+  /// angle of geometry edge at which the mesher should set a point
+  double edgecornerangle;
+  /// angle inside on chart
+  double chartangle;
+  /// angle for overlapping parts of char
+  double outerchartangle;
+  /// 0 .. no, 1 .. local, (2 .. global)
+  int usesearchtree;
+  ///
+  double resthatlasfac; 
+  int resthatlasenable;
+  double atlasminh;
+
+  double resthsurfcurvfac; 
+  int resthsurfcurvenable;
+
+  double resthchartdistfac;
+  int resthchartdistenable;
+
+  double resthcloseedgefac;
+  int resthcloseedgeenable;
+  
+  double resthedgeanglefac;
+  int resthedgeangleenable;
+  
+  double resthsurfmeshcurvfac;
+  int resthsurfmeshcurvenable;
+  
+  double resthlinelengthfac;
+  int resthlinelengthenable;
+
+  ///
+  int recalc_h_opt;
+  ///
+  STLParameters();
+  ///
+  void Print (ostream & ost) const;
+};
+
+extern STLParameters stlparam;
+
+
+void STLMeshing (STLGeometry & geom,
+		 class Mesh & mesh);
+
+
+int STLSurfaceMeshing (STLGeometry & geom,
+			class Mesh & mesh);
+
+void STLSurfaceOptimization (STLGeometry & geom,
+			     class Mesh & mesh,
+			     class MeshingParameters & mparam);
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/stlgeom/stltopology.cpp b/contrib/Netgen/libsrc/stlgeom/stltopology.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8547034f080c1a310b2137cc4b7fa872fcd58e9a
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stltopology.cpp
@@ -0,0 +1,1067 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+#include <meshing.hpp>
+
+#include "stlgeom.hpp"
+
+namespace netgen
+{
+
+
+  STLTopology :: STLTopology()
+  : trias(), topedges(), points(), ht_topedges(NULL), 
+    trigsperpoint(), neighbourtrigs()
+{
+  ;
+}
+
+STLTopology :: ~STLTopology()
+{
+  ;
+}
+
+
+
+
+STLGeometry *  STLTopology :: LoadBinary (istream & ist)
+{
+  STLGeometry * geom = new STLGeometry();
+  Array<STLReadTriangle> readtrigs;
+
+  PrintMessage(1,"Read STL binary file");
+  
+  if (sizeof(int) != 4 || sizeof(float) != 4) 
+    {
+      PrintWarning("for stl-binary compatibility only use 32 bit compilation!!!");
+    }
+
+  //specific settings for stl-binary format
+  const int namelen = 80; //length of name of header in file
+  const int nospaces = 2; //number of spaces after a triangle
+
+  //read header: name
+  char buf[namelen+1];
+  FIOReadStringE(ist,buf,namelen);
+  PrintMessage(5,"header = ",buf);
+
+  //Read Number of facets
+  int nofacets;
+  FIOReadInt(ist,nofacets);
+  PrintMessage(5,"NO facets = ",nofacets);
+
+  Point<3> pts[3];
+  Vec<3> normal;
+
+  int cntface, j;
+  //int vertex = 0;
+  float f;
+  char spaces[nospaces+1];
+
+  for (cntface = 0; cntface < nofacets; cntface++)
+    {
+      if (cntface % 10000 == 9999) { PrintDot(); } 
+
+      FIOReadFloat(ist,f); normal(0) = f;
+      FIOReadFloat(ist,f); normal(1) = f;
+      FIOReadFloat(ist,f); normal(2) = f;
+      
+      for (j = 0; j < 3; j++)
+	{
+	  FIOReadFloat(ist,f); pts[j](0) = f;
+	  FIOReadFloat(ist,f); pts[j](1) = f;
+	  FIOReadFloat(ist,f); pts[j](2) = f;	  
+	} 
+
+      readtrigs.Append (STLReadTriangle (pts, normal));
+      FIOReadString(ist,spaces,nospaces);
+    }	    
+  
+
+  geom->InitSTLGeometry(readtrigs);
+
+  return geom;
+}
+
+
+void STLTopology :: SaveBinary (const char* filename, const char* aname) const
+{
+  ofstream ost(filename);
+  PrintFnStart("Write STL binary file '",filename,"'");
+
+  if (sizeof(int) != 4 || sizeof(float) != 4) 
+    {PrintWarning("for stl-binary compatibility only use 32 bit compilation!!!");}
+
+  //specific settings for stl-binary format
+  const int namelen = 80; //length of name of header in file
+  const int nospaces = 2; //number of spaces after a triangle
+
+  //write header: aname
+  int i, j;
+  char buf[namelen+1];
+  int strend = 0;
+  for(i = 0; i <= namelen; i++) 
+    {
+      if (aname[i] == 0) {strend = 1;}
+      if (!strend) {buf[i] = aname[i];}
+      else {buf[i] = 0;}
+    }
+
+  FIOWriteString(ost,buf,namelen);
+  PrintMessage(5,"header = ",buf);
+
+  //RWrite Number of facets
+  int nofacets = GetNT();
+  FIOWriteInt(ost,nofacets);
+  PrintMessage(5,"NO facets = ", nofacets);
+
+  float f;
+  char spaces[nospaces+1];
+  for (i = 0; i < nospaces; i++) {spaces[i] = ' ';}
+  spaces[nospaces] = 0;
+
+  for (i = 1; i <= GetNT(); i++)
+    {
+      const STLTriangle & t = GetTriangle(i);
+
+      const Vec<3> & n = t.Normal();
+      f = n(0); FIOWriteFloat(ost,f);
+      f = n(1); FIOWriteFloat(ost,f);
+      f = n(2); FIOWriteFloat(ost,f);
+
+      for (j = 1; j <= 3; j++)
+	{
+	  const Point3d p = GetPoint(t.PNum(j));
+	  
+	  f = p.X(); FIOWriteFloat(ost,f);
+	  f = p.Y(); FIOWriteFloat(ost,f);
+	  f = p.Z(); FIOWriteFloat(ost,f);
+	}
+      FIOWriteString(ost,spaces,nospaces);
+    }
+  PrintMessage(5,"done");
+}
+
+
+void STLTopology :: SaveSTLE (const char* filename) const
+{
+  ofstream outf (filename);
+  int i, j;
+  
+  outf << GetNT() << endl;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      const STLTriangle & t = GetTriangle(i);
+      for (j = 1; j <= 3; j++)
+	{
+	  const Point3d p = GetPoint(t.PNum(j));
+	  outf << p.X() << " " << p.Y() << " " << p.Z() << endl;
+	}
+    }
+
+
+  int ned = 0;
+  for (i = 1; i <= GetNTE(); i++)
+    {
+      if (GetTopEdge (i).GetStatus() == ED_CONFIRMED)
+	ned++;
+    }
+  
+  outf << ned << endl;
+
+  for (i = 1; i <= GetNTE(); i++)
+    {
+      const STLTopEdge & edge = GetTopEdge (i);
+      if (edge.GetStatus() == ED_CONFIRMED)
+	for (j = 1; j <= 2; j++)
+	  {
+	    const Point3d p = GetPoint(edge.PNum(j));
+	    outf << p.X() << " " << p.Y() << " " << p.Z() << endl;
+	  }
+    }      
+}
+
+
+
+STLGeometry *  STLTopology :: LoadNaomi (istream & ist)
+{
+  int i;
+  STLGeometry * geom = new STLGeometry();
+  Array<STLReadTriangle> readtrigs;
+
+  PrintFnStart("read NAOMI file format");
+  
+  char buf[100];
+  Vec<3> normal;
+
+  //int cntface = 0;
+  //int cntvertex = 0;
+  double px, py, pz;
+    
+
+  int noface, novertex;
+  Array<Point<3> > readpoints;
+
+  ist >> buf;
+  if (strcmp (buf, "NODES") == 0)
+    {
+      ist >> novertex;
+      PrintMessage(5,"nuber of vertices = ", novertex);
+      for (i = 0; i < novertex; i++)
+	{
+	  ist >> px;
+	  ist >> py;
+	  ist >> pz;
+	  readpoints.Append(Point<3> (px,py,pz));
+	}
+    }
+  else
+    {
+      PrintFileError("no node information");
+    }
+
+
+  ist >> buf;
+  if (strcmp (buf, "2D_EDGES") == 0)
+    {
+      ist >> noface;
+      PrintMessage(5,"number of faces=",noface);
+      int dummy, p1, p2, p3;
+      Point<3> pts[3];
+
+      for (i = 0; i < noface; i++)
+	{
+	  ist >> dummy; //2
+	  ist >> dummy; //1
+	  ist >> p1;
+	  ist >> p2;
+	  ist >> p3;
+	  ist >> dummy; //0
+
+	  pts[0] = readpoints.Get(p1);
+	  pts[1] = readpoints.Get(p2);
+	  pts[2] = readpoints.Get(p3);
+	  
+	  normal = Cross (pts[1]-pts[0], pts[2]-pts[0]) . Normalize();
+
+	  readtrigs.Append (STLReadTriangle (pts, normal));
+
+	}
+      PrintMessage(5,"read ", readtrigs.Size(), " triangles");
+    }
+  else
+    {
+      PrintMessage(5,"read='",buf,"'\n");
+      PrintFileError("ERROR: no Triangle information");
+    }
+
+  geom->InitSTLGeometry(readtrigs);
+
+  return geom;
+}
+
+void STLTopology :: Save (const char* filename) const
+{ 
+  PrintFnStart("Write stl-file '",filename, "'");
+
+  ofstream fout(filename);
+  fout << "solid\n";
+
+  char buf1[50];
+  char buf2[50];
+  char buf3[50];
+
+  int i, j;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      const STLTriangle & t = GetTriangle(i);
+
+      fout << "facet normal ";
+      const Vec3d& n = GetTriangle(i).Normal();
+
+      sprintf(buf1,"%1.9g",n.X());
+      sprintf(buf2,"%1.9g",n.Y());
+      sprintf(buf3,"%1.9g",n.Z());
+
+      fout << buf1 << " " << buf2 << " " << buf3 << "\n";
+      fout << "outer loop\n";
+
+      for (j = 1; j <= 3; j++)
+	{
+	  const Point3d p = GetPoint(t.PNum(j));
+	  
+	  sprintf(buf1,"%1.9g",p.X());
+	  sprintf(buf2,"%1.9g",p.Y());
+	  sprintf(buf3,"%1.9g",p.Z());
+
+	  fout << "vertex " << buf1 << " " << buf2 << " " << buf3 << "\n";
+	}
+
+      fout << "endloop\n";
+      fout << "endfacet\n"; 
+    }
+  fout << "endsolid\n";
+
+  
+  // write also NETGEN surface mesh:
+  ofstream fout2("geom.surf");
+  fout2 << "surfacemesh" << endl;
+  fout2 << GetNP() << endl;
+  for (i = 1; i <= GetNP(); i++)
+    {
+      for (j = 0; j < 3; j++)
+	{
+	  fout2.width(8);
+	  fout2 << GetPoint(i)(j);
+	}
+
+      fout2 << endl;
+    }
+
+  fout2 << GetNT() << endl;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      const STLTriangle & t = GetTriangle(i);  
+      for (j = 1; j <= 3; j++)
+	{
+	  fout2.width(8);
+	  fout2 << t.PNum(j);
+	}
+      fout2 << endl;
+    }
+}
+
+
+STLGeometry *  STLTopology ::Load (istream & ist)
+{
+  size_t i;
+  STLGeometry * geom = new STLGeometry();
+
+  Array<STLReadTriangle> readtrigs;
+
+  char buf[100];
+  Point<3> pts[3];
+  Vec<3> normal;
+
+  int cntface = 0;
+  int vertex = 0;
+  bool badnormals = 0;
+
+  while (ist.good())
+    {
+      ist >> buf;
+
+      size_t n = strlen (buf);
+      for (i = 0; i < n; i++)
+	buf[i] = tolower (buf[i]);
+
+      if (strcmp (buf, "facet") == 0)
+	{
+	  cntface++;
+	}
+
+      if (strcmp (buf, "normal") == 0)
+	{
+	  ist >> normal(0)
+	      >> normal(1)
+	      >> normal(2);
+	  normal.Normalize();
+	}
+      
+      if (strcmp (buf, "vertex") == 0)
+	{
+	  ist >> pts[vertex](0)
+	      >> pts[vertex](1)
+	      >> pts[vertex](2);
+
+	  vertex++;
+
+	  if (vertex == 3)
+	    {
+	      if (normal.Length() <= 1e-5)
+
+		{
+		  normal = Cross (pts[1]-pts[0], pts[2]-pts[0]);
+		  normal.Normalize();
+		}
+
+	      else
+
+		{
+		  Vec<3> hnormal;
+		  hnormal = Cross (pts[1]-pts[0], pts[2]-pts[0]);
+		  hnormal.Normalize();
+
+		  if (normal * hnormal < 0.5)
+		    {
+		      badnormals = 1;
+		    }
+		}
+
+	      vertex = 0;
+
+	      if ( (Dist2 (pts[0], pts[1]) > 1e-16) &&
+		   (Dist2 (pts[0], pts[2]) > 1e-16) &&
+		   (Dist2 (pts[1], pts[2]) > 1e-16) )
+		
+		readtrigs.Append (STLReadTriangle (pts, normal));
+	    }
+	}
+    }
+  
+  if (badnormals) 
+    {
+      PrintWarning("File has normal vectors which differ extremly from geometry->correct with stldoctor!!!");
+    }
+
+  geom->InitSTLGeometry(readtrigs);
+  return geom;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+void STLTopology :: InitSTLGeometry(const Array<STLReadTriangle> & readtrigs)
+{
+  int i, k;
+  
+  // const double geometry_tol_fact = 1E6; 
+  // distances lower than max_box_size/tol are ignored
+
+  trias.SetSize(0);
+  points.SetSize(0);
+
+  PrintMessage(3,"number of triangles = ", readtrigs.Size());
+
+  if (!readtrigs.Size())
+    return;
+  
+
+  boundingbox.Set (readtrigs[0][0]);
+  for (i = 0; i < readtrigs.Size(); i++)
+    for (k = 0; k < 3; k++)
+      boundingbox.Add (readtrigs[i][k]);
+  
+  PrintMessage(5,"boundingbox: ", Point3d(boundingbox.PMin()), " - ", 
+	       Point3d(boundingbox.PMax()));
+
+  Box<3> bb = boundingbox;
+  bb.Increase (1);
+
+  pointtree = new Point3dTree (bb.PMin(), bb.PMax());
+
+
+
+  Array<int> pintersect;
+
+  pointtol = boundingbox.Diam() * stldoctor.geom_tol_fact;
+  PrintMessage(5,"point tolerance = ", pointtol);
+
+  for(i = 0; i < readtrigs.Size(); i++)
+    {
+      const STLReadTriangle & t = readtrigs[i];
+      STLTriangle st;
+      // Vec<3> n = t.Normal();
+      st.SetNormal (t.Normal());
+
+      for (k = 0; k < 3; k++)
+	{
+	  Point<3> p = t[k];
+
+	  Point<3> pmin = p - Vec<3> (pointtol, pointtol, pointtol);
+	  Point<3> pmax = p + Vec<3> (pointtol, pointtol, pointtol);
+	  
+	  pointtree->GetIntersecting (pmin, pmax, pintersect);
+	  
+	  if (pintersect.Size() > 1)
+	    PrintError("too many close points");
+	  int foundpos = -1;
+	  if (pintersect.Size())
+	    foundpos = pintersect[0];
+	  
+	  if (foundpos == -1)
+	    {
+	      foundpos = AddPoint(p);
+	      pointtree->Insert (p, foundpos);
+	    }
+	  st[k] = foundpos;
+	}
+
+      if ( (st[0] == st[1]) ||
+	   (st[0] == st[2]) || 
+	   (st[1] == st[2]) )
+	{
+	  PrintError("STL Triangle degenerated");
+	}
+      else
+	{
+	  AddTriangle(st);
+	}
+      
+    } 
+
+  FindNeighbourTrigs();
+}
+
+
+
+
+int STLTopology :: GetPointNum (const Point<3> & p)
+{
+  Point<3> pmin = p - Vec<3> (pointtol, pointtol, pointtol);
+  Point<3> pmax = p + Vec<3> (pointtol, pointtol, pointtol);
+  
+  Array<int> pintersect;
+
+  pointtree->GetIntersecting (pmin, pmax, pintersect);
+  if (pintersect.Size() == 1)
+    return pintersect[0];
+  else 
+    return 0;
+}
+
+
+
+void STLTopology :: FindNeighbourTrigs()
+{
+  //  if (topedges.Size()) return;
+
+  PushStatusF("Find Neighbour Triangles");
+
+  int i, j, k, l;
+
+  // build up topology tables
+
+  //int np = GetNP();
+  int nt = GetNT();
+
+  INDEX_2_HASHTABLE<int> * oldedges = ht_topedges;
+  ht_topedges = new INDEX_2_HASHTABLE<int> (GetNP()+1);
+  topedges.SetSize(0);
+  
+  for (i = 1; i <= nt; i++)
+    {
+      STLTriangle & trig = GetTriangle(i);
+
+
+      for (j = 1; j <= 3; j++)
+	{
+	  int pi1 = trig.PNumMod (j+1);
+	  int pi2 = trig.PNumMod (j+2);
+	  
+	  INDEX_2 i2(pi1, pi2);
+	  i2.Sort();
+
+	  int enr;
+	  int othertn;
+
+	  if (ht_topedges->Used(i2))
+	    {
+	      enr = ht_topedges->Get(i2);
+	      topedges.Elem(enr).TrigNum(2) = i;
+
+	      othertn = topedges.Get(enr).TrigNum(1);
+	      STLTriangle & othertrig = GetTriangle(othertn);
+
+	      trig.NBTrigNum(j) = othertn;
+	      trig.EdgeNum(j) = enr;
+	      for (k = 1; k <= 3; k++)
+		if (othertrig.EdgeNum(k) == enr)
+		  othertrig.NBTrigNum(k) = i;
+	    }
+	  else
+	    {
+	      enr = topedges.Append (STLTopEdge (pi1, pi2, i, 0));
+	      ht_topedges->Set (i2, enr);
+	      trig.EdgeNum(j) = enr;
+	    }
+	}
+    }
+
+  
+  PrintMessage(5,"topology built, checking");
+
+  topology_ok = 1;
+  int ne = GetNTE();
+
+  for (i = 1; i <= nt; i++)
+    GetTriangle(i).flags.toperror = 0;
+
+  for (i = 1; i <= nt; i++)
+    for (j = 1; j <= 3; j++)
+      {
+	const STLTopEdge & edge = GetTopEdge (GetTriangle(i).EdgeNum(j));
+	if (edge.TrigNum(1) != i && edge.TrigNum(2) != i)
+	  {
+	    topology_ok = 0;
+	    GetTriangle(i).flags.toperror = 1;
+	  }
+      }
+
+  for (i = 1; i <= ne; i++)
+    {
+      const STLTopEdge & edge = GetTopEdge (i);
+      if (!edge.TrigNum(2))
+	{
+	  topology_ok = 0;
+	  GetTriangle(edge.TrigNum(1)).flags.toperror = 1;
+	}
+    }
+ 
+  if (topology_ok)
+    {
+      orientation_ok = 1;
+      for (i = 1; i <= nt; i++)
+	{
+	  const STLTriangle & t = GetTriangle (i);
+	  for (j = 1; j <= 3; j++)
+	    {
+	      const STLTriangle & nbt = GetTriangle (t.NBTrigNum(j));
+	      if (!t.IsNeighbourFrom (nbt))
+		orientation_ok = 0;
+	    }
+	}
+    }
+  else
+    orientation_ok = 0;
+  
+
+
+  status = STL_GOOD;
+  statustext = "";
+  if (!topology_ok || !orientation_ok)
+    {
+      status = STL_ERROR;
+      if (!topology_ok)
+	statustext = "Topology not ok";
+      else
+	statustext = "Orientation not ok";
+    }
+
+
+  PrintMessage(3,"topology_ok = ",topology_ok);
+  PrintMessage(3,"orientation_ok = ",orientation_ok);
+  PrintMessage(3,"topology found");
+
+  // generate point -> trig table
+
+  trigsperpoint.SetSize(GetNP());
+  for (i = 1; i <= GetNT(); i++)
+    for (j = 1; j <= 3; j++)
+      trigsperpoint.Add1(GetTriangle(i).PNum(j),i);
+
+
+  //check trigs per point:
+  /*
+  for (i = 1; i <= GetNP(); i++)
+    {
+      if (trigsperpoint.EntrySize(i) < 3)
+	{
+	  (*testout) << "ERROR: Point " << i << " has " << trigsperpoint.EntrySize(i) << " triangles!!!" << endl;
+	}
+    }
+  */
+  topedgesperpoint.SetSize (GetNP());
+  for (i = 1; i <= ne; i++)
+    for (j = 1; j <= 2; j++)
+      topedgesperpoint.Add1 (GetTopEdge (i).PNum(j), i);
+
+  PrintMessage(5,"point -> trig table generated");
+
+
+
+  // transfer edge data:
+  // .. to be done
+  delete oldedges;
+
+
+
+  for (STLTrigIndex ti = 0; ti < GetNT(); ti++)
+    {
+      STLTriangle & trig = trias[ti];
+      for (k = 0; k < 3; k++)
+	{
+	  STLPointIndex pi = trig[k] - STLBASE;
+	  STLPointIndex pi2 = trig[(k+1)%3] - STLBASE;
+	  STLPointIndex pi3 = trig[(k+2)%3] - STLBASE;
+	  
+	  // vector along edge
+	  Vec<3> ve = points[pi2] - points[pi];
+	  ve.Normalize();
+
+	  // vector along third point
+	  Vec<3> vt = points[pi3] - points[pi];
+	  vt -= (vt * ve) * ve;
+	  vt.Normalize();
+
+	  Vec<3> vn = trig.GeomNormal (points);
+	  vn.Normalize();
+
+	  double phimin = 10, phimax = -1; // out of (0, 2 pi)
+
+	  for (j = 0; j < trigsperpoint[pi].Size(); j++)
+	    {
+	      STLTrigIndex ti2 = trigsperpoint[pi][j] - STLBASE;
+	      const STLTriangle & trig2 = trias[ti2];
+
+	      if (ti == ti2) continue;
+	      
+	      bool hasboth = 0;
+	      for (l = 0; l < 3; l++)
+		if (trig2[l] - STLBASE == pi2)
+		  {
+		    hasboth = 1;
+		    break;
+		  }
+	      if (!hasboth) continue;
+
+	      STLPointIndex pi4(0);
+	      for (l = 0; l < 3; l++)
+		if (trig2[l] - STLBASE != pi && trig2[l] - STLBASE != pi2)
+		  pi4 = trig2[l] - STLBASE;
+
+	      Vec<3> vt2 = points[pi4] - points[pi];
+	      
+	      double phi = atan2 (vt2 * vn, vt2 * vt);
+	      if (phi < 0) phi += 2 * M_PI;
+	      
+	      if (phi < phimin)
+		{
+		  phimin = phi;
+		  trig.NBTrig (0, (k+2)%3) = ti2 + STLBASE;
+		}
+	      if (phi > phimax)
+		{
+		  phimax = phi;
+		  trig.NBTrig (1, (k+2)%3) = ti2 + STLBASE;
+		}
+	    }
+	}
+    }
+
+
+
+
+  if (status == STL_GOOD)
+    {
+      // for compatibility:
+      neighbourtrigs.SetSize(GetNT());
+      for (i = 1; i <= GetNT(); i++)
+	for (k = 1; k <= 3; k++)
+	  AddNeighbourTrig (i, GetTriangle(i).NBTrigNum(k));
+    }
+  else
+    {
+      // assemble neighbourtrigs (should be done only for illegal topology):
+      
+      neighbourtrigs.SetSize(GetNT());
+
+      int tr, found;
+      int wrongneighbourfound = 0;
+      for (i = 1; i <= GetNT(); i++)
+	{
+	  SetThreadPercent((double)i/(double)GetNT()*100.);
+	  if (multithread.terminate)
+	    {
+	      PopStatus();
+	      return;
+	    }
+	  
+	  for (k = 1; k <= 3; k++)
+	    {
+	      for (j = 1; j <= trigsperpoint.EntrySize(GetTriangle(i).PNum(k)); j++)
+		{
+		  tr = trigsperpoint.Get(GetTriangle(i).PNum(k),j);
+		  if (i != tr && (GetTriangle(i).IsNeighbourFrom(GetTriangle(tr))
+				  || GetTriangle(i).IsWrongNeighbourFrom(GetTriangle(tr))))
+		    {
+		      if (GetTriangle(i).IsWrongNeighbourFrom(GetTriangle(tr)))
+			{
+			  /*(*testout) << "ERROR: triangle " << i << " has a wrong neighbour triangle!!!" << endl;*/
+			  wrongneighbourfound ++;
+			}
+		      
+		      found = 0;
+		      for (int ii = 1; ii <= NONeighbourTrigs(i); ii++) 
+			{if (NeighbourTrig(i,ii) == tr) {found = 1;break;};}
+		      if (! found) {AddNeighbourTrig(i,tr);}
+		    }
+		}
+	    }
+	  if (NONeighbourTrigs(i) != 3) 
+	    {
+	      PrintError("TRIG ",i," has ",NONeighbourTrigs(i)," neighbours!!!!");
+	      for (int kk=1; kk <= NONeighbourTrigs(i); kk++)
+		{
+		  PrintMessage(5,"neighbour-trig",kk," = ",NeighbourTrig(i,kk));
+		}
+	    };
+	}
+      if (wrongneighbourfound)
+	{
+	  PrintError("++++++++++++++++++++\n");
+	  PrintError(wrongneighbourfound, " wrong oriented neighbourtriangles found!");
+	  PrintError("try to correct it (with stldoctor)!");
+	  PrintError("++++++++++++++++++++\n");
+	  
+	  status = STL_ERROR;
+	  statustext = "STL Mesh not consistent";
+
+	  multithread.terminate = 1;
+#ifdef STAT_STREAM
+	  (*statout) << "non-conform stl geometry \\hline" << endl;
+#endif
+	}
+    }
+
+  TopologyChanged();
+
+  PopStatus();
+}
+
+
+
+
+
+
+
+void STLTopology :: GetTrianglesInBox (/* 
+					  const Point<3> & pmin,
+					  const Point<3> & pmax,
+				       */
+				       const Box<3> & box,
+				       Array<int> & btrias) const
+{
+  if (searchtree)
+
+    searchtree -> GetIntersecting (box.PMin(), box.PMax(), btrias);
+  
+  else
+    {    
+      int i;
+      Box<3> box1 = box;
+      box1.Increase (1e-4);
+
+      btrias.SetSize(0);
+   
+      int nt = GetNT();
+      for (i = 1; i <= nt; i++)
+	{
+	  if (box1.Intersect (GetTriangle(i).box))
+	    {
+	      btrias.Append (i);
+	    }
+	}    
+    }
+}
+
+
+
+void STLTopology :: AddTriangle(const STLTriangle& t)
+{
+  trias.Append(t);
+  
+  const Point<3> & p1 = GetPoint (t.PNum(1));
+  const Point<3> & p2 = GetPoint (t.PNum(2));
+  const Point<3> & p3 = GetPoint (t.PNum(3));
+
+  Box<3> box;
+  box.Set (p1);
+  box.Add (p2);
+  box.Add (p3);
+  /*
+  //  Point<3> pmin(p1), pmax(p1);
+  pmin.SetToMin (p2);
+  pmin.SetToMin (p3);
+  pmax.SetToMax (p2);
+  pmax.SetToMax (p3);
+  */
+
+  trias.Last().box = box; 
+  trias.Last().center = Center (p1, p2, p3);
+  double r1 = Dist (p1, trias.Last().center);
+  double r2 = Dist (p2, trias.Last().center);
+  double r3 = Dist (p3, trias.Last().center);
+  trias.Last().rad = max2 (max2 (r1, r2), r3);
+
+  if (geomsearchtreeon)
+    {searchtree->Insert (box.PMin(), box.PMax(), trias.Size());}
+}
+
+
+
+
+int STLTopology :: GetLeftTrig(int p1, int p2) const
+{
+  int i;
+  for (i = 1; i <= trigsperpoint.EntrySize(p1); i++)
+    {
+      if (GetTriangle(trigsperpoint.Get(p1,i)).HasEdge(p1,p2)) {return trigsperpoint.Get(p1,i);}
+    }
+  PrintSysError("ERROR in GetLeftTrig !!!");
+
+  return 0;
+}
+
+int STLTopology :: GetRightTrig(int p1, int p2) const
+{
+  return GetLeftTrig(p2,p1);
+}
+
+
+int STLTopology :: NeighbourTrigSorted(int trig, int edgenum) const
+{
+  int i, p1, p2;
+  int psearch = GetTriangle(trig).PNum(edgenum);
+
+  for (i = 1; i <= 3; i++)
+    {
+      GetTriangle(trig).GetNeighbourPoints(GetTriangle(NeighbourTrig(trig,i)),p1,p2);
+      if (p1 == psearch) {return NeighbourTrig(trig,i);}
+    }
+
+  PrintSysError("ERROR in NeighbourTrigSorted");
+  return 0;
+}
+
+
+
+
+
+
+int STLTopology :: GetTopEdgeNum (int pi1, int pi2) const
+{
+  if (!ht_topedges) return 0;
+
+  INDEX_2 i2(pi1, pi2);
+  i2.Sort();
+
+  if (!ht_topedges->Used(i2)) return 0;
+  return ht_topedges->Get(i2);
+}
+
+
+
+
+void STLTopology :: InvertTrig (int trig)
+{
+  if (trig >= 1 && trig <= GetNT())
+    {
+      GetTriangle(trig).ChangeOrientation();
+      FindNeighbourTrigs();
+    }
+  else
+    {
+      PrintUserError("no triangle selected!");
+    }
+}
+
+
+
+
+void STLTopology :: DeleteTrig (int trig)
+{
+  if (trig >= 1 && trig <= GetNT())
+    {
+      trias.DeleteElement(trig);
+      FindNeighbourTrigs();
+    }
+  else
+    {
+      PrintUserError("no triangle selected!");
+    }
+}
+
+
+
+void STLTopology :: OrientAfterTrig (int trig)
+{
+  int starttrig = trig;
+
+  if (starttrig >= 1 && starttrig <= GetNT())
+    {
+
+      Array <int> oriented;
+      oriented.SetSize(GetNT());
+      int i;
+      for (i = 1; i <= oriented.Size(); i++)
+	{
+	  oriented.Elem(i) = 0;
+	}
+ 
+      oriented.Elem(starttrig) = 1;
+  
+      int k;
+      
+      Array <int> list1;
+      list1.SetSize(0);
+      Array <int> list2;
+      list2.SetSize(0);
+      list1.Append(starttrig);
+
+      int cnt = 1;
+      int end = 0;
+      int nt;
+      while (!end)
+	{
+	  end = 1;
+	  for (i = 1; i <= list1.Size(); i++)
+	    {
+	      const STLTriangle& tt = GetTriangle(list1.Get(i));
+	      for (k = 1; k <= 3; k++)
+		{
+		  nt = tt.NBTrigNum (k); // NeighbourTrig(list1.Get(i),k);
+		  if (oriented.Get(nt) == 0)
+		    {
+		      if (tt.IsWrongNeighbourFrom(GetTriangle(nt)))
+			{
+			  GetTriangle(nt).ChangeOrientation();
+			}
+		      oriented.Elem(nt) = 1;
+		      list2.Append(nt);
+		      cnt++;
+		      end = 0;
+		    }
+		}
+	    }
+	  list1.SetSize(0);
+	  for (i = 1; i <= list2.Size(); i++)
+	    {
+	      list1.Append(list2.Get(i));
+	    }
+	  list2.SetSize(0);
+	}
+
+      PrintMessage(5,"NO corrected triangles = ",cnt);
+      if (cnt == GetNT()) 
+	{
+	  PrintMessage(5,"ALL triangles oriented in same way!");
+	}
+      else
+	{
+	  PrintWarning("NOT ALL triangles oriented in same way!");
+	}
+
+      //      topedges.SetSize(0);
+      FindNeighbourTrigs();
+    }
+  else
+    {
+      PrintUserError("no triangle selected!");
+    }
+}
+
+
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/stltopology.hpp b/contrib/Netgen/libsrc/stlgeom/stltopology.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fbb2394fd8926029a0ef5a5939d4bb632934f4cb
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stltopology.hpp
@@ -0,0 +1,362 @@
+#ifndef FILE_STLTOPOLOGY
+#define FILE_STLTOPOLOGY
+
+/**************************************************************************/
+/* File:   stltopology.hpp                                                */
+/* Author: Joachim Schoeberl                                              */
+/* Author2: Johannes Gerstmayr                                            */
+/* Date:   26. Jul. 99                                                    */
+/**************************************************************************/
+
+/*
+  The STLTopology contains topologic information as
+  triangle->point, point->triangles, triangle->edge, 2-points->edge,...
+*/
+
+
+class STLGeometry;
+
+#define STLBASE 1
+
+class STLPointIndex
+{
+  int i;
+public:
+  STLPointIndex () { ; }
+  STLPointIndex (int ai) : i(ai) { ; }
+  STLPointIndex & operator= (const STLPointIndex & ai) { i = ai.i; return *this; }
+  STLPointIndex & operator= (int ai) { i = ai; return *this; }
+  operator int () const { return i; }
+  STLPointIndex operator++ (int) { return i++; }
+  STLPointIndex operator-- (int) { return i--; }
+};
+
+
+
+class STLTrigIndex
+{
+  int i;
+public:
+  STLTrigIndex () { ; }
+  STLTrigIndex (int ai) : i(ai) { ; }
+  STLTrigIndex & operator= (const STLTrigIndex & ai) { i = ai.i; return *this; }
+  STLTrigIndex & operator= (int ai) { i = ai; return *this; }
+  operator int () const { return i; }
+  STLTrigIndex operator++ (int) { return i++; }
+  STLTrigIndex operator-- (int) { return i--; }
+};
+
+
+
+
+
+// triangle structure for loading stl files
+class STLReadTriangle
+{
+  Vec<3> normal;
+  Point<3> pts[3];
+public:
+  STLReadTriangle (const Point<3> * apts, const Vec<3> & anormal);
+  STLReadTriangle () {};
+  const Point<3> & operator[] (int i) const { return pts[i]; }
+  const Vec<3> & Normal() const { return normal; }
+};
+
+
+
+class STLTriangle
+{
+  // topology edges of triangle, edge[i] opposite to point[i]
+  int topedges[3];
+  // neighbour triangles, trig[i] opposite to point[i]
+  int nbtrigs[2][3]; 
+  // normalized stored normal vector ??
+  Vec<3> normal;
+  // point numbers of triangle
+  int pts[3];
+  // front-side and back-side domains
+  int domains[2];
+
+
+public:
+
+  Box<3> box;
+  Point<3> center;
+  double rad;
+  int facenum;
+
+  struct 
+  {
+    unsigned int toperror : 1;
+  } flags;
+
+
+
+
+  STLTriangle (const int * apts);
+  STLTriangle () {pts[0]=0;pts[1]=0;pts[2]=0;}
+
+  int operator[] (int i) const { return pts[i]; }
+  int & operator[] (int i) { return pts[i]; }
+
+  int EdgeNum(int i) const { return topedges[(i-1)]; }
+  int & EdgeNum(int i) { return topedges[(i-1)]; }
+
+  int NBTrig (bool side, int i) const { return nbtrigs[side][i]; }
+  int & NBTrig (bool side, int i) { return nbtrigs[side][i]; }
+
+  
+  int Domain (bool side) const { return domains[side]; }
+  int & Domain (bool side) { return domains[side]; }
+
+
+
+  // obsolete:
+  int PNum(int i) const { return pts[(i-1)]; }
+  int & PNum(int i) { return pts[(i-1)]; }
+  int PNumMod(int i) const { return pts[(i-1)%3]; }
+  int & PNumMod(int i)  { return pts[(i-1)%3]; }
+
+  int EdgeNumMod(int i) const { return topedges[(i-1)%3]; }
+  int & EdgeNumMod(int i)  { return topedges[(i-1)%3]; }
+
+  int NBTrigNum(int i) const { return nbtrigs[0][(i-1)]; }
+  int & NBTrigNum(int i) { return nbtrigs[0][(i-1)]; }
+  int NBTrigNumMod(int i) const { return nbtrigs[0][(i-1)%3]; }
+  int & NBTrigNumMod(int i)  { return nbtrigs[0][(i-1)%3]; }
+  
+
+  // consistently oriented neighbour:
+  int IsNeighbourFrom(const STLTriangle& t) const;
+  // opposite to consistently oriented neighbour:
+  int IsWrongNeighbourFrom(const STLTriangle& t) const;
+
+  ///Get the two points of neighbour-Triangles in orientation of this-Triangle
+  void GetNeighbourPoints(const STLTriangle& t, int& p1, int& p2) const;
+  int GetNeighbourPointsAndOpposite(const STLTriangle& t, int& p1, int& p2, int& po) const;
+
+
+
+  // NON-normalized geometry - normal vector
+  Vec<3> GeomNormal(const Array<Point<3> >& ap) const;
+  
+  // Stored normal vector, normalized
+  void SetNormal (const Vec<3> & n);
+  const Vec<3> & Normal () const { return normal; }
+
+
+  void ChangeOrientation(); 
+
+  //project with a certain normal vector in plane
+  void ProjectInPlain(const Array<Point<3> >& ap, 
+		      const Vec<3> & n, Point<3> & pp) const;
+  //project with the triangle's normal vector in plane
+  void ProjectInPlain(const Array<Point<3> > & ap, Point<3> & pp) const;
+
+
+  /*
+    Project the point pp along the nproj into the plane of
+    the triangle. The triangle normal is given by ntrig to 
+    avoid numerical instabilities.
+    The local coordinates lam are defined by
+
+    pp(input) = P1 + lam1 v1 + lam2 v2 + lam3 n
+
+    the result is
+    
+    pp(output) = P1 + lam1 v1 + lam2 v2
+  */
+  int ProjectInPlain (const Array<Point<3> >& ap, 
+		      const Vec<3> & nproj, 
+		      Point<3> & pp, Vec<3> & lam) const;
+
+  int PointInside(const Array<Point<3> >& ap, const Point<3> & pp) const;
+
+  //get nearest point on triangle and distance to it
+  double GetNearestPoint(const Array<Point<3> >& ap, 
+			 Point<3> & p3d) const;
+
+  double Area(const Array<Point<3> >& ap) const;
+
+  double MinHeight(const Array<Point<3> >& ap) const;
+  double MaxLength(const Array<Point<3> >& ap) const; 
+  //max length of a side of triangle
+
+  int GetFaceNum() {return facenum;}
+  void SetFaceNum(int i) {facenum = i;}
+
+  int HasEdge(int p1, int p2) const;
+};
+
+
+/**
+   Topology Edge:
+   Useful unside a face.
+   A edges sharing more than 2 faces: trigs are undefined 
+ */
+class STLTopEdge 
+{
+  int pts[2];  
+  int trigs[2];  
+  double cosangle;
+  int status;  // excluded, confirmed, candidate, undefined
+public:
+  STLTopEdge ();
+  STLTopEdge (int p1, int p2, int trig1, int trig2);
+
+  int operator[] (int i) const { return pts[i]; }
+  int & operator[] (int i) { return pts[i]; }
+
+
+  int PNum(int i) const { return pts[(i-1)]; }
+  int & PNum(int i) { return pts[(i-1)]; }
+  int PNumMod(int i) const { return pts[(i-1)%2]; }
+  int & PNumMod(int i)  { return pts[(i-1)%2]; }
+
+  int TrigNum(int i) const { return trigs[(i-1)]; }
+  int & TrigNum(int i) { return trigs[(i-1)]; }
+  int TrigNumMod(int i) const { return trigs[(i-1)%2]; }
+  int & TrigNumMod(int i)  { return trigs[(i-1)%2]; }
+
+  void SetCosAngle (double ca) { cosangle = ca; }
+  double CosAngle () const { return cosangle; }
+  double Angle () const { return acos (cosangle); }
+
+  void SetStatus (int stat) { status = stat; }
+  int GetStatus () const { return status; }
+};
+
+
+
+ostream& operator<<(ostream& os, const STLTriangle& t);
+
+
+
+
+
+
+
+class STLTopology
+{
+protected:
+  Array<STLTriangle> trias;
+  Array<STLTopEdge> topedges;
+  Array<Point<3> > points;
+
+  // mapping of sorted pair of points to topedge
+  INDEX_2_HASHTABLE<int> * ht_topedges;
+  // mapping of node to trigs
+  TABLE<int> trigsperpoint; 
+  // mapping of node to edges
+  TABLE<int> topedgesperpoint; 
+  
+  // searchtree for trigs and points
+
+  Box3dTree * searchtree; // ADT
+  Point3dTree * pointtree;
+
+  Box<3> boundingbox;
+  double pointtol;
+
+public:
+  enum STL_GEOM_STATUS { STL_GOOD, STL_WARNING, STL_ERROR };
+
+protected:
+  STL_GEOM_STATUS status;
+  string statustext;
+  
+  bool topology_ok;
+  bool orientation_ok;
+
+public:
+  STLTopology();
+  virtual ~STLTopology();
+
+  static STLGeometry * LoadNaomi (istream & ist);
+  static STLGeometry * Load (istream & ist);
+  static STLGeometry * LoadBinary (istream & ist);
+
+  void Save (const char* filename) const;
+  void SaveBinary (const char* filename, const char* aname) const;
+  void SaveSTLE (const char * filename) const; // stores trigs and edges
+  
+  virtual void InitSTLGeometry (const Array<STLReadTriangle> & readtrigs);
+
+  virtual void TopologyChanged() {}; //do some things, if topology changed!
+
+  /// Generate topology tables
+  void FindNeighbourTrigs();
+
+  
+  void GetTrianglesInBox (const Box<3> & box,
+			  Array<int> & trias) const;
+
+
+  int GetNP() const { return points.Size(); }
+  int AddPoint(const Point<3> & p) { return points.Append(p); }
+  const Point<3> & GetPoint(int nr) const { return points.Get(nr); }
+  int GetPointNum (const Point<3> & p);
+  void SetPoint(int nr, const Point<3> & p) { points.Elem(nr) = p; }
+  const Array<Point<3> >& GetPoints() const { return points; }
+
+  const Point<3> & operator[] (STLPointIndex i) const { return points[i]; }
+  Point<3> & operator[] (STLPointIndex i) { return points[i]; }
+
+
+
+
+  int GetNT() const { return trias.Size(); }
+  void AddTriangle(const STLTriangle& t);
+  const STLTriangle & GetTriangle (int nr) const { return trias.Get(nr); }
+  STLTriangle & GetTriangle (int nr) { return trias.Elem(nr); }
+  
+  const STLTriangle & operator[] (STLTrigIndex i) const { return trias[i]; }
+  STLTriangle & operator[] (STLTrigIndex i) { return trias[i]; }
+
+
+  int GetNTE() const { return topedges.Size(); }
+  const STLTopEdge & GetTopEdge (int nr) const { return topedges.Get(nr); }
+  STLTopEdge & GetTopEdge (int nr)  { return topedges.Elem(nr); }
+  int GetTopEdgeNum (int pi1, int pi2) const;
+
+
+  int NOTrigsPerPoint(int pn) { return trigsperpoint.EntrySize(pn); }
+  int TrigPerPoint(int pn, int i) { return trigsperpoint.Get(pn, i); }
+
+
+  int NTopEdgesPerPoint (int pn) const { return topedgesperpoint.EntrySize(pn); }
+  int TopEdgePerPoint (int pn, int ei) const { return topedgesperpoint.Get(pn, ei); }
+
+  
+  bool Topology_Ok() const { return topology_ok; }
+  bool Orientation_Ok() const { return orientation_ok; }
+
+  STL_GEOM_STATUS GetStatus () const { return status; }
+  const string & GetStatusText () const { return statustext; }
+
+  void InvertTrig (int trig);
+  void DeleteTrig (int trig);
+  void OrientAfterTrig (int trig);
+
+
+  // Table will be constructed, if topology is not ok
+  /// neighbourtrigs for surfacetrigs
+  TABLE<int> neighbourtrigs;
+
+  /// get nr-th neighbour Triangle for triangle trig
+  int NONeighbourTrigs(int trig) const { return neighbourtrigs.EntrySize(trig); }
+  int NeighbourTrig(int trig, int nr) const { return neighbourtrigs.Get(trig,nr); }
+  int NeighbourTrigSorted(int trig, int nr) const;
+  void AddNeighbourTrig(int i, int nt) { neighbourtrigs.Add1(i, nt); }
+
+
+
+
+  int GetLeftTrig (int p1, int p2) const;
+  int GetRightTrig (int p1, int p2) const;
+
+  const Box<3> & GetBoundingBox () const { return boundingbox; }
+};
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/stlgeom/vsstl.cpp b/contrib/Netgen/libsrc/stlgeom/vsstl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..338c305ddf8bb3150794d6829c2c6a60d67b4ce2
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/vsstl.cpp
@@ -0,0 +1,1212 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <stlgeom.hpp>
+
+#include <meshing.hpp>
+#include <visual.hpp>
+
+
+#include "vsstl.hpp"
+
+
+namespace netgen
+{
+
+/*
+//mmm
+#include "stlgeom/modeller.hpp"
+*/
+
+/* *********************** Draw STL Geometry **************** */
+
+extern STLGeometry * stlgeometry;
+extern AutoPtr<Mesh> mesh;
+
+
+// #include "../../ngtcltk/mvdraw.hpp"
+
+
+VisualSceneSTLMeshing :: VisualSceneSTLMeshing ()
+  : VisualScene()
+{
+  selecttrig = 0;
+  nodeofseltrig = 1;
+  stlgeometry->SetSelectTrig(selecttrig);
+  stlgeometry->SetNodeOfSelTrig(nodeofseltrig);
+}
+
+VisualSceneSTLMeshing :: ~VisualSceneSTLMeshing ()
+{
+  ;
+}
+
+void VisualSceneSTLMeshing :: DrawScene ()
+{
+  int i, j, k;
+
+  if (changeval != stlgeometry->GetNT())
+    BuildScene();
+  changeval = stlgeometry->GetNT();
+
+  int colormeshsize = vispar.colormeshsize;
+  
+  double hmin = 0.0, hmax = 1.0;
+
+  if (colormeshsize)
+    {
+      hmax = -1E50;
+      hmin = +1E50;
+      double ms;
+
+      for (i = 1; i <= stlgeometry->GetNP(); i++)
+	{
+	  ms = mesh->GetH (stlgeometry->GetPoint(i));
+	  hmin = min2(hmin,ms);
+	  hmax = max2(hmax,ms);
+	}
+
+      //hmax = mparam.maxh;
+      //hmin = mesh->GetMinH (stlgeometry->GetBoundingBox().PMin(),
+      //			    stlgeometry->GetBoundingBox().PMax());
+  
+      if (hmin == 0) hmin = 0.1 * hmax;
+      //hmax *= 1.1;
+    }
+  
+
+
+  glClearColor(backcolor, backcolor, backcolor, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+
+  SetLight();
+
+  glPushMatrix();
+  glMultMatrixf (transformationmat);
+
+  SetClippingPlane ();
+
+  glShadeModel (GL_SMOOTH);
+  glDisable (GL_COLOR_MATERIAL);
+  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+  glEnable (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  float mat_spec_col[] = { 1, 1, 1, 1 };
+  glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_spec_col);
+
+  double shine = vispar.shininess;
+  // double transp = vispar.transp;
+
+  glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine);
+  glLogicOp (GL_COPY);
+
+  float mat_colred[]    = { 0.9f, 0.0f, 0.0f, 1.0f };
+  float mat_colgreen[]  = { 0.0f, 0.9f, 0.0f, 1.0f };
+  float mat_colblue[]   = { 0.1f, 0.1f, 1.0f, 1.0f };
+
+  float mat_colbluegreen[] = { 0.1f, 0.5f, 0.9f, 1.0f };
+  // float mat_colpink[]      = { 1.0f, 0.1f, 0.5f, 1.0f };
+  float mat_colviolet[]    = { 1.0f, 0.1f, 1.0f, 1.0f };
+  float mat_colbrown[]     = { 0.8f, 0.6f, 0.1f, 1.0f };
+  // float mat_colorange[]    = { 0.9f, 0.7f, 0.1f, 1.0f };
+  // float mat_colturquis[]   = { 0.0f, 1.0f, 0.8f, 1.0f };
+
+  float mat_colgrey[] = { 0.3f, 0.3f, 0.3f, 1.0f };
+
+  float mat_collred[]   = { 1.0f, 0.5f, 0.5f, 1.0f };
+  float mat_collgreen[] = { 0.2f, 1.9f, 0.2f, 1.0f };
+  float mat_collbrown[] = { 1.0f, 0.8f, 0.3f, 1.0f };
+
+  float mat_collgrey[] = { 0.8f, 0.8f, 0.8f, 1.0f };
+  // float mat_colmgrey[] = { 0.4f, 0.4f, 0.4f, 1.0f };
+
+  float mat_colstlbody[] = { 0.0f, 0.0f, 0.8f, 1.0f };
+  float mat_colseltrig[] = { 0.7f, 0.7f, 0.3f, 1.0f };
+  float mat_colseledge[] = { 0.7f, 0.7f, 1.0f, 1.0f };
+
+  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colblue);
+
+  float pgoff = 0.5f;
+
+  glPolygonOffset (pgoff*1, pgoff*1);
+  glEnable (GL_POLYGON_OFFSET_FILL);
+
+  glEnable (GL_NORMALIZE);
+
+  /*
+  {
+    //mmm
+    //test modeller
+    Modeller model;
+    
+    //MoZylinder z1(Point3d(0,0,0),Vec3d(100,0,0),20,0.01);
+    //model.Add(&z1);
+    //MoZylinder z2(Point3d(50,50,0),Vec3d(0,-100,0),20,0.01);
+    //model.Add(&z2);
+    
+    MoZylinder z1(Point3d(0,0,0),Vec3d(100,0,0),20,0.01);
+    MoZylinder z2(Point3d(50,50,0),Vec3d(0,-100,0),20,0.01);
+    MoCombine cb1(&z1,&z2);
+    model.Add(&cb1);
+    
+    Array<MoTriangle> trigs;
+    model.GetTriangles(trigs);
+    int i, k;
+    glBegin (GL_TRIANGLES);
+    for (i = 1; i <= trigs.Size(); i++)
+      {
+	const MoTriangle & tria = trigs.Get(i);
+	glNormal3f (tria.normal.X(),
+		    tria.normal.Y(),
+		    tria.normal.Z());
+	
+	for (k = 0; k < 3; k++)
+	  {
+	    glVertex3f (tria.pts[k].X(),
+			tria.pts[k].Y(),
+			tria.pts[k].Z());
+	  }
+      }    
+    glEnd ();
+    
+
+  }
+
+*/
+
+
+
+  
+  if (!stlgeometry->trigsconverted)
+    {
+      glBegin (GL_TRIANGLES);
+      for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	{
+	  /*
+	  if (j % 10 == seltria)
+	    glMaterialfv (GL_FRONT_AND_BACK, 
+			  GL_AMBIENT_AND_DIFFUSE, mat_colred);
+	  */
+
+	  const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	  glNormal3f (n.X(), n.Y(), n.Z());
+	  /*
+	  const STLReadTriangle & tria = stlgeometry -> GetReadTriangle(j);
+	  glNormal3f (tria.normal.X(),
+		      tria.normal.Y(),
+		      tria.normal.Z());
+	  */
+
+	  
+	  for (k = 1; k <= 3; k++)
+	    {
+	      const Point3d & tp = stlgeometry->GetPoint(stlgeometry->GetTriangle(j).PNum(k));
+	      glVertex3f (tp.X(), tp.Y(), tp.Z());
+
+	    }
+	  /*
+	  if (j%10 == seltria)
+	    glMaterialfv (GL_FRONT_AND_BACK, 
+			  GL_AMBIENT_AND_DIFFUSE, mat_colblue);
+	  */
+	}    
+      glEnd ();
+  
+      glDisable (GL_POLYGON_OFFSET_FILL);
+
+      int showtrias = vispar.stlshowtrias;
+
+      if (showtrias)
+	{
+	  float mat_coll[] = { 0.2f, 0.2f, 0.2f, 1.f };
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_coll);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+      
+	  glEnable (GL_NORMALIZE);
+      
+	  glBegin (GL_TRIANGLES);
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	      /*
+	      const STLReadTriangle & tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */  
+
+	      for (k = 1; k <= 3; k++)
+		{
+		  const Point3d & tp = 
+		    stlgeometry->GetPoint(stlgeometry->GetTriangle(j).PNum(k));
+		  glVertex3f (tp.X(), tp.Y(), tp.Z());
+		  
+		}
+	      
+	      /*
+	      for (k = 0; k < 3; k++)
+		{
+		  glVertex3f (tria.pts[k].X(),
+			      tria.pts[k].Y(),
+			      tria.pts[k].Z());
+		}
+	      */
+	    }    
+	  glEnd ();
+	}
+    }
+  else
+    {
+      int showfilledtrias = vispar.stlshowfilledtrias;
+
+      //(*mycout) << "in " << showfilledtrias << ", NT=" << stlgeometry -> GetNT() << endl;
+
+      int chartnumber;
+      if (vispar.stlshowmarktrias)
+	chartnumber = vispar.stlchartnumber + vispar.stlchartnumberoffset;
+      else
+	chartnumber = stlgeometry->GetMeshChartNr();
+
+      if (showfilledtrias)
+	{
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+	  if (colormeshsize)
+	    glEnable (GL_COLOR_MATERIAL);
+	  
+	  glPolygonOffset (pgoff*4, pgoff*4);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  glEnable (GL_NORMALIZE);
+
+
+	  glBegin (GL_TRIANGLES);
+
+	  int selt = stlgeometry -> GetSelectTrig();
+	  if (stldoctor.selectmode != 0) 
+	    {selt = 0; } //do not show selected triangle!!!!
+
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colstlbody);
+
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {
+	      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) {continue;}
+
+	      if (j == selt)
+		{
+		  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colseltrig);
+		}
+	      else if (j == selt+1)
+		{
+		  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colstlbody);
+		}
+	      
+	      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	  
+	      /*
+	      const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */
+	      for (k = 0; k < 3; k++)
+		{
+		  const Point3d & p = stlgeometry->GetPoint(st[k]);
+		  if (colormeshsize)
+		    {
+		      SetOpenGlColor (mesh->GetH (p), hmin, hmax, 1);
+		    }
+
+		  glVertex3f (p.X(), p.Y(), p.Z());
+		}
+	    } 
+   
+	  glEnd ();
+	}
+      
+      int foundseltrig = stlgeometry -> GetSelectTrig();
+      if (foundseltrig == 0 || foundseltrig > stlgeometry->GetNT() ||
+	  (stldoctor.showvicinity && !stlgeometry->Vicinity(foundseltrig)))
+	{foundseltrig = 0;}
+
+      if (foundseltrig)
+	{
+
+	  glPolygonOffset (pgoff*0, 0);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+
+	  //glDisable (GL_POLYGON_OFFSET_FILL);      
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colseledge);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+	  
+	  glEnable (GL_NORMALIZE);
+
+	  if (stldoctor.selectmode == 2)
+	    {
+	      //point
+	      const STLTriangle& st = stlgeometry -> GetTriangle(foundseltrig);
+	      const Point3d & p1 = stlgeometry->GetPoint(st[0]);
+	      const Point3d & p2 = stlgeometry->GetPoint(st[1]);
+	      const Point3d & p3 = stlgeometry->GetPoint(st[2]);
+
+	      double cs = (Dist(p1,p2)+Dist(p2,p3)+Dist(p3,p1))/100.;
+
+	      const Point3d & p = stlgeometry->GetPoint(st[nodeofseltrig-1]);
+	      
+	      glLineWidth (4);
+	      glBegin (GL_LINES);
+	      glVertex3f(p.X()+cs, p.Y()+cs, p.Z()+cs);
+	      glVertex3f(p.X()-cs, p.Y()-cs, p.Z()-cs);
+	      
+	      glVertex3f(p.X()-cs, p.Y()+cs, p.Z()+cs);
+	      glVertex3f(p.X()+cs, p.Y()-cs, p.Z()-cs);
+
+	      glVertex3f(p.X()-cs, p.Y()+cs, p.Z()+cs);
+	      glVertex3f(p.X()+cs, p.Y()-cs, p.Z()-cs);
+	      
+	      glVertex3f(p.X()+cs, p.Y()-cs, p.Z()+cs);
+	      glVertex3f(p.X()-cs, p.Y()+cs, p.Z()-cs);
+	      
+	      glEnd ();	  
+	      glLineWidth (1);
+	    }
+	  else if (stldoctor.selectmode == 1 || 
+		   stldoctor.selectmode == 3 || 
+		   stldoctor.selectmode == 4)
+	    {
+	      //multiedge
+	      
+	      const Array<twoint>& me = stlgeometry->SelectedMultiEdge();
+	      if (stlgeometry->GetSelectTrig() > 0 && 
+		  stlgeometry->GetSelectTrig() <= stlgeometry->GetNT() &&
+		  me.Size())
+		{
+
+		  int en = stlgeometry->EdgeDataList().GetEdgeNum(me.Get(1).i1,me.Get(1).i2);
+		  int status = stlgeometry->EdgeDataList().Get(en).GetStatus();
+		  
+		  switch (status)
+		    {
+		    case ED_CONFIRMED:
+		      glMaterialfv (GL_FRONT_AND_BACK, 
+				    GL_AMBIENT_AND_DIFFUSE, mat_collgreen);
+		      break;
+		    case ED_CANDIDATE:
+		      glMaterialfv (GL_FRONT_AND_BACK, 
+				    GL_AMBIENT_AND_DIFFUSE, mat_collbrown);
+		      break;
+		    case ED_EXCLUDED:
+		      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_collred);
+		      break;
+		    }
+
+		  glLineWidth (2);
+		  glBegin (GL_LINES);
+		  for (j = 1; j <= me.Size(); j++)
+		    { 
+		      Point3d p1 = stlgeometry->GetPoint(me.Get(j).i1);
+		      Point3d p2 = stlgeometry->GetPoint(me.Get(j).i2);
+		      
+		      glVertex3f(p1.X(), p1.Y(), p1.Z());
+		      glVertex3f(p2.X(), p2.Y(), p2.Z());
+		    }
+		  glEnd ();
+		  glLineWidth (1);
+		}
+	    }
+	}
+
+      int showmarktrias = vispar.stlshowmarktrias || vispar.stlshowactivechart;
+ 
+      if (stldoctor.showmarkedtrigs)
+	{
+	  //(*mycout) << "marked" << endl;
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); //GL_LINE
+	  glPolygonOffset (pgoff*1, pgoff*1);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbluegreen);
+	  glEnable (GL_NORMALIZE);
+
+	  glBegin (GL_TRIANGLES);
+
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {
+	      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) 
+		{continue;}
+
+	      if (!stlgeometry->IsMarkedTrig(j)) 
+		{continue;}
+	      
+	      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	      /*
+	      const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */
+	      for (k = 0; k < 3; k++)
+		{
+		  const Point3d & p = stlgeometry->GetPoint(st[k]);
+		  glVertex3f (p.X(), p.Y(), p.Z());
+		}
+	    }    
+	  glEnd ();
+
+	  //show OpenSegments on original geometry
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colviolet);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+	  glPolygonOffset (pgoff*1, 1);
+      
+	  glEnable (GL_NORMALIZE);
+      
+	  glBegin (GL_LINES);
+
+	  if (stlgeometry->GetNMarkedSegs())
+	    {
+	      Point<3> p1,p2;	      
+	      for (j = 1; j <= stlgeometry -> GetNMarkedSegs(); j++)
+		{
+		  stlgeometry->GetMarkedSeg(j,p1,p2);
+		  glVertex3dv(&p1(0));
+		  glVertex3dv(&p2(0));
+		}
+	    }
+	  glEnd ();
+	}
+
+
+      if (stldoctor.showfaces)
+	{
+	  int facenumber = vispar.stlchartnumber + vispar.stlchartnumberoffset;
+
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+	  glPolygonOffset (pgoff*3, 3);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_collgrey);
+	  glEnable (GL_NORMALIZE);
+
+	  glBegin (GL_TRIANGLES);
+
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {
+	      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) 
+		{continue;}
+
+	      //(*mycout) << " facenum = " << stlgeometry->GetTriangle(j).GetFaceNum() << " ";
+	      if (stlgeometry->GetTriangle(j).GetFaceNum() != facenumber) 
+		{continue;}
+	      
+	      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	      /*
+	      const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */
+	      for (k = 0; k < 3; k++)
+		{
+		  Point3d p = stlgeometry->GetPoint(st[k]);
+		  glVertex3f (p.X(), p.Y(), p.Z());
+		}
+	    }    
+	  glEnd ();
+	}
+
+      if (showmarktrias && stlgeometry->AtlasMade())
+	{
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+	  glPolygonOffset (pgoff*3, 3);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+
+	  glBegin (GL_TRIANGLES);
+	  
+	  if (chartnumber >= 1 && chartnumber <= stlgeometry->GetNOCharts())
+	    {
+	      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbrown);
+	      const STLChart& chart = stlgeometry->GetChart(chartnumber);
+	      for (j = 1; j <= chart.GetNChartT(); j++)
+		{
+		  /*
+		  if (j == charttrignumber) 
+		    {glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred);}
+		  else
+		    {glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbrown);}
+		  */
+		  const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetChartTrig(j));
+
+		  
+		  const Vec3d & n = stlgeometry->GetTriangle(chart.GetChartTrig(j)).Normal();
+		  glNormal3f (n.X(), n.Y(), n.Z());
+		  /*
+		  const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(chart.GetChartTrig(j));
+		  glNormal3f (tria.normal.X(),
+			      tria.normal.Y(),
+			      tria.normal.Z());
+		  */
+		  for (k = 0; k < 3; k++)
+		    {
+		      glVertex3f (stlgeometry->GetPoint(st[k])(0),
+				  stlgeometry->GetPoint(st[k])(1),
+				  stlgeometry->GetPoint(st[k])(2));
+		    }
+		}
+	      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+	      
+	      for (j = 1; j <= chart.GetNOuterT(); j++)
+		{
+		  
+		  const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetOuterTrig(j));
+
+		  const Vec3d & n = stlgeometry->GetTriangle(chart.GetOuterTrig(j)).Normal();
+		  glNormal3f (n.X(), n.Y(), n.Z());
+
+
+		  /*
+		  const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(chart.GetOuterTrig(j));
+		  glNormal3f (tria.normal.X(),
+			      tria.normal.Y(),
+			      tria.normal.Z());
+		  */
+		  for (k = 0; k < 3; k++)
+		    {
+		      glVertex3f (stlgeometry->GetPoint(st[k])(0),
+				  stlgeometry->GetPoint(st[k])(1),
+				  stlgeometry->GetPoint(st[k])(2));
+		    }
+		}
+	    }
+	  glEnd ();
+	}
+
+      int showtrias = vispar.stlshowtrias;
+
+      if (showtrias)
+	{
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgrey);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+	  glPolygonOffset (pgoff*2, 2);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  glEnable (GL_NORMALIZE);
+
+	  glBegin (GL_TRIANGLES);
+	  
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {	  
+	      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) {continue;}
+
+	      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	      /*
+	      const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */	  
+	      for (k = 0; k < 3; k++)
+		{
+		  glVertex3f (stlgeometry->GetPoint(st[k])(0),
+			      stlgeometry->GetPoint(st[k])(1),
+			      stlgeometry->GetPoint(st[k])(2));
+		}
+	    }    
+	  glEnd ();
+	} 
+
+      int showedges = vispar.stlshowedges;
+      
+      if (showedges)
+	{
+	  glPolygonOffset (pgoff*1, 1);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  //glDisable (GL_POLYGON_OFFSET_FILL);      
+
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+      
+	  glEnable (GL_NORMALIZE);
+      
+	  glBegin (GL_LINES);
+
+	  /*
+	  if (stldoctor.useexternaledges)
+	    {
+	      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colorange);
+	      for (j = 1; j <= stlgeometry -> NOExternalEdges(); j++)
+		{
+		  twoint v = stlgeometry->GetExternalEdge(j);
+		  Point3d p1 = stlgeometry->GetPoint(v.i1);
+		  Point3d p2 = stlgeometry->GetPoint(v.i2);
+		  
+		  Vec3d n1 = stlgeometry->GetNormal(v.i1);
+		  Vec3d n2 = stlgeometry->GetNormal(v.i2);
+		  
+		  glNormal3f(n1.X(), n1.Y(), n1.Z());
+		  glVertex3f(p1.X(), p1.Y(), p1.Z());
+		  glNormal3f(n2.X(), n2.Y(), n2.Z());
+		  glVertex3f(p2.X(), p2.Y(), p2.Z());
+		}
+	    }
+	  */
+
+	  
+	  if (!stlgeometry->meshlines.Size() || !stldoctor.drawmeshededges)
+	    {
+	      /*
+	      for (j = 1; j <= stlgeometry -> GetNE(); j++)
+		{
+		  STLEdge v = stlgeometry->GetEdge(j);
+		  Point3d p1 = stlgeometry->GetPoint(v.pts[0]);
+		  Point3d p2 = stlgeometry->GetPoint(v.pts[1]);
+		  
+		  Vec3d n1 = stlgeometry->GetNormal(v.pts[0]);
+		  Vec3d n2 = stlgeometry->GetNormal(v.pts[1]);
+		  
+		  glNormal3f(n1.X(), n1.Y(), n1.Z());
+		  glVertex3f(p1.X(), p1.Y(), p1.Z());
+		  glNormal3f(n2.X(), n2.Y(), n2.Z());
+		  glVertex3f(p2.X(), p2.Y(), p2.Z());
+		}
+	      */
+	      const STLEdgeDataList& ed = stlgeometry->EdgeDataList();
+	      for (i = 1; i <= ed.Size(); i++)
+		{
+		  if (ed.Get(i).GetStatus() != ED_UNDEFINED)
+		    {
+		      switch (ed.Get(i).GetStatus())
+			{
+			case ED_CONFIRMED:
+			  glMaterialfv (GL_FRONT_AND_BACK, 
+					GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+			  break;
+			case ED_CANDIDATE:
+			  glMaterialfv (GL_FRONT_AND_BACK, 
+					GL_AMBIENT_AND_DIFFUSE, mat_colbrown);
+			  break;
+			case ED_EXCLUDED:
+			  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred);
+			  break;
+			}
+
+		      if (ed.Get(i).GetStatus() == ED_EXCLUDED && !stldoctor.showexcluded) continue;
+
+		      Point3d p1 = stlgeometry->GetPoint(ed.Get(i).PNum(1));
+		      Point3d p2 = stlgeometry->GetPoint(ed.Get(i).PNum(2));
+		      glVertex3f(p1.X(), p1.Y(), p1.Z());
+		      glVertex3f(p2.X(), p2.Y(), p2.Z());		   
+		    }
+		}
+	    }
+
+	  /*
+	  else     
+	  if (stlgeometry->meshlines.Size() == 0)
+	    {
+	      for (j = 1; j <= stlgeometry->GetNLines(); j++)
+		{
+		  STLLine* line = stlgeometry->GetLine(j);
+		  int pn1, pn2;
+		  for (int k = 1; k <= line->NP()-1; k++)
+		    {
+		      pn1 = line->PNum(k);
+		      pn2 = line->PNum(k+1);
+
+		      Point3d p1 = stlgeometry->GetPoint(pn1);
+		      Point3d p2 = stlgeometry->GetPoint(pn2);
+		  
+		      Vec3d n1 = stlgeometry->GetNormal(pn1);
+		      Vec3d n2 = stlgeometry->GetNormal(pn2);
+		  
+		      glNormal3f(n1.X(), n1.Y(), n1.Z());
+		      glVertex3f(p1.X(), p1.Y(), p1.Z());
+		      glNormal3f(n2.X(), n2.Y(), n2.Z());
+		      glVertex3f(p2.X(), p2.Y(), p2.Z());
+		    }
+		}    
+	    }
+	  */
+	    
+	  else if (stlgeometry->meshlines.Size() != 0)
+	    {
+	      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+	      for (j = 1; j <= stlgeometry->meshlines.Size(); j++)
+		{
+		  STLLine* line = stlgeometry->meshlines.Get(j);
+		  int pn1, pn2;
+		  for (int k = 1; k <= line->NP()-1; k++)
+		    {
+		      pn1 = line->PNum(k);
+		      pn2 = line->PNum(k+1);
+
+		      Point3d p1 = stlgeometry->meshpoints.Get(pn1);
+		      Point3d p2 = stlgeometry->meshpoints.Get(pn2);
+		  		  
+		      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+		      glVertex3f(p1.X(), p1.Y(), p1.Z());
+		      glVertex3f(p2.X(), p2.Y(), p2.Z());
+
+		      
+		      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred);
+		      double cs = 0.02*Dist(p1,p2);
+		      glVertex3f(p1.X()+cs, p1.Y()+cs, p1.Z()+cs);
+		      glVertex3f(p1.X()-cs, p1.Y()-cs, p1.Z()-cs);
+		      glVertex3f(p2.X()+cs, p2.Y()+cs, p2.Z()+cs);
+		      glVertex3f(p2.X()-cs, p2.Y()-cs, p2.Z()-cs);
+
+		      glVertex3f(p1.X()-cs, p1.Y()+cs, p1.Z()+cs);
+		      glVertex3f(p1.X()+cs, p1.Y()-cs, p1.Z()-cs);
+		      glVertex3f(p2.X()-cs, p2.Y()+cs, p2.Z()+cs);
+		      glVertex3f(p2.X()+cs, p2.Y()-cs, p2.Z()-cs);
+		      
+		    }
+		}
+	    }
+	    
+
+	  glEnd ();
+	}
+
+      if (stldoctor.showedgecornerpoints && stlgeometry->LineEndPointsSet())
+	{
+	  glPointSize (5);
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred);
+	  glBegin (GL_POINTS);
+	  for (i = 1; i <= stlgeometry->GetNP(); i++)
+	    {
+	      if (stlgeometry->IsLineEndPoint(i))
+		{
+		  const Point3d p = stlgeometry->GetPoint(i);
+		  glVertex3f (p.X(), p.Y(), p.Z());
+		}
+	    }
+	  glEnd();
+	  
+	}
+
+
+    }
+
+ 
+  glPopMatrix();
+
+  if (vispar.colormeshsize)
+    DrawColorBar (hmin, hmax, 1);
+
+  glFinish();  
+}
+
+
+void VisualSceneSTLMeshing :: BuildScene (int zoomall)
+{
+  if (selecttrig && zoomall == 2)
+    center = stlgeometry -> GetPoint ( stlgeometry->GetTriangle(selecttrig).PNum(nodeofseltrig));
+  else
+    center = stlgeometry -> GetBoundingBox().Center();
+
+  rad = stlgeometry -> GetBoundingBox().Diam() / 2;
+
+  CalcTransformationMatrices();
+}
+
+
+
+void VisualSceneSTLMeshing :: MouseDblClick (int px, int py)
+{
+  //  (*mycout) << "dblclick: " << px << " - " << py << endl;
+  
+
+  int i, j, k, hits;
+
+  // select surface triangle by mouse click
+
+  GLuint selbuf[10000];
+  glSelectBuffer (10000, selbuf);
+
+
+  glRenderMode (GL_SELECT);
+
+  GLint viewport[4];
+  glGetIntegerv (GL_VIEWPORT, viewport);
+
+  /*  
+  (*mycout) << "viewport = " << viewport[0] << " " 
+       << viewport[1] << " " << viewport[2] << " " << viewport[3] << endl;
+  */
+
+  glMatrixMode (GL_PROJECTION); 
+  glPushMatrix();
+
+
+  GLdouble projmat[16];
+  glGetDoublev (GL_PROJECTION_MATRIX, projmat);
+
+  glLoadIdentity(); 
+  gluPickMatrix (px, viewport[3] - py, 1, 1, viewport); 
+  glMultMatrixd (projmat);
+  
+
+
+  glClearColor(backcolor, backcolor, backcolor, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glMatrixMode (GL_MODELVIEW); 
+
+  glPushMatrix();
+  glMultMatrixf (transformationmat);
+
+
+  glInitNames();
+  glPushName (1);
+
+
+  glEnable (GL_POLYGON_OFFSET_FILL);
+  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+    {
+      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) {continue;}
+
+      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+			
+      //const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+      //glNormal3f (tria.normal.X(), tria.normal.Y(), tria.normal.Z());
+      
+      if (stldoctor.selectmode == 0)
+	{
+	  glLoadName (j);
+	  glBegin (GL_TRIANGLES);
+	  for (k = 0; k < 3; k++)
+	    {
+	      Point3d p = stlgeometry->GetPoint(st[k]);
+	      glVertex3f (p.X(), p.Y(), p.Z());
+	    }
+	  glEnd ();
+	} 
+      else if (stldoctor.selectmode == 1 || stldoctor.selectmode == 3
+	        || stldoctor.selectmode == 4)
+	{
+	  Point3d pm = Center(stlgeometry->GetPoint(st[0]),
+			      stlgeometry->GetPoint(st[1]),
+			      stlgeometry->GetPoint(st[2]));
+
+	  for (k = 0; k < 3; k++)
+	    {
+	      glLoadName (j*3+k-2);
+	      glBegin (GL_TRIANGLES);
+
+	      Point3d p1 = stlgeometry->GetPoint(st[k]);
+	      Point3d p2 = stlgeometry->GetPoint(st[(k+1)%3]);
+	      glVertex3f (p1.X(), p1.Y(), p1.Z());
+	      glVertex3f (p2.X(), p2.Y(), p2.Z());
+	      glVertex3f (pm.X(), pm.Y(), pm.Z());
+
+	      glEnd ();
+	    }
+	}
+      else
+	{
+	  Point3d pm1 = Center(stlgeometry->GetPoint(st[0]),
+			       stlgeometry->GetPoint(st[1]));
+	  Point3d pm2 = Center(stlgeometry->GetPoint(st[1]),
+			       stlgeometry->GetPoint(st[2]));
+	  Point3d pm3 = Center(stlgeometry->GetPoint(st[2]),
+			       stlgeometry->GetPoint(st[0]));
+
+	  Point3d p1 = stlgeometry->GetPoint(st[0]);
+	  Point3d p2 = stlgeometry->GetPoint(st[1]);
+	  Point3d p3 = stlgeometry->GetPoint(st[2]);
+
+	  glLoadName (j*4-3);
+	  glBegin (GL_TRIANGLES);
+	  glVertex3f (p1.X(), p1.Y(), p1.Z());
+	  glVertex3f (pm1.X(), pm1.Y(), pm1.Z());
+	  glVertex3f (pm3.X(), pm3.Y(), pm3.Z());
+	  glEnd ();
+
+	  glLoadName (j*4-2);
+	  glBegin (GL_TRIANGLES);
+	  glVertex3f (p2.X(), p2.Y(), p2.Z());
+	  glVertex3f (pm2.X(), pm2.Y(), pm2.Z());
+	  glVertex3f (pm1.X(), pm1.Y(), pm1.Z());
+	  glEnd ();
+
+	  glLoadName (j*4-1);
+	  glBegin (GL_TRIANGLES);
+	  glVertex3f (p3.X(), p3.Y(), p3.Z());
+	  glVertex3f (pm3.X(), pm3.Y(), pm3.Z());
+	  glVertex3f (pm2.X(), pm2.Y(), pm2.Z());
+	  glEnd ();
+
+	  glLoadName (j*4);
+	  glBegin (GL_TRIANGLES);
+	  glVertex3f (pm1.X(), pm1.Y(), pm1.Z());
+	  glVertex3f (pm2.X(), pm2.Y(), pm2.Z());
+	  glVertex3f (pm3.X(), pm3.Y(), pm3.Z());
+	  glEnd ();
+	}
+    }    
+
+  glPopName();
+
+  glMatrixMode (GL_PROJECTION); 
+  glPopMatrix();
+
+  glMatrixMode (GL_MODELVIEW); 
+  glPopMatrix();
+
+  glFlush();  
+
+	
+  hits = glRenderMode (GL_RENDER);
+
+  //  (*mycout) << "hits = " << hits << endl;
+
+  //int minrec = -1;
+  int minname = 0;
+  GLuint mindepth = 0;
+  for (i = 0; i < hits; i++)
+    {
+      int curname = selbuf[4*i+3];
+      GLuint curdepth = selbuf[4*i+1];
+
+      /*      
+      (*mycout) << selbuf[4*i] << " " << selbuf[4*i+1] << " " 
+	   << selbuf[4*i+2] << " " << selbuf[4*i+3] << endl;
+      */
+      if (curname &&
+	  (curdepth < mindepth || !minname))
+	{
+	  //minrec = i;
+	  mindepth = curdepth;
+	  minname = curname;
+	}
+    }
+
+  if (!minname) {return;}
+  
+  if (stldoctor.selectmode == 0)
+    {
+      int oldtrig = selecttrig;
+      selecttrig = minname;
+      if (selecttrig == oldtrig)
+	nodeofseltrig = (nodeofseltrig % 3) + 1;
+      else
+	nodeofseltrig = 1;
+
+      stlgeometry->SetSelectTrig(selecttrig);
+      stlgeometry->SetNodeOfSelTrig(nodeofseltrig);
+      stlgeometry->PrintSelectInfo();
+      
+    }
+  else if (stldoctor.selectmode == 1 || stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+    {
+      selecttrig = (minname-1) / 3 + 1;
+      nodeofseltrig = minname-selecttrig*3+3;
+
+      stlgeometry->SetSelectTrig(selecttrig);
+      stlgeometry->SetNodeOfSelTrig(nodeofseltrig);
+      stlgeometry->PrintSelectInfo();
+
+      if (stldoctor.selectmode == 1)
+	{
+	  stlgeometry->BuildSelectedEdge(twoint(stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig),
+						stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig+1)));
+	}
+      if (stldoctor.selectmode == 3)
+	{
+	  stlgeometry->BuildSelectedMultiEdge(twoint(stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig),
+						     stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig+1)));
+	}
+      else if (stldoctor.selectmode == 4)
+	{
+	  stlgeometry->BuildSelectedCluster(twoint(stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig),
+						   stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig+1)));
+	}
+ 
+      switch (stldoctor.edgeselectmode)
+	{
+	case 1: stlgeometry->STLDoctorUndefinedEdge(); break;
+	case 2: stlgeometry->STLDoctorConfirmEdge(); break;
+	case 3: stlgeometry->STLDoctorCandidateEdge(); break;
+	case 4: stlgeometry->STLDoctorExcludeEdge(); break;
+	default: break;
+	}
+    }
+  else if (stldoctor.selectmode == 2)
+    {
+      selecttrig = (minname-1) / 4 + 1;
+      nodeofseltrig = minname-selecttrig*4+4;
+      if (nodeofseltrig == 4) {nodeofseltrig = 1;}
+
+      stlgeometry->SetSelectTrig(selecttrig);
+      stlgeometry->SetNodeOfSelTrig(nodeofseltrig);
+      stlgeometry->PrintSelectInfo();
+
+    }
+
+  if (stldoctor.showtouchedtrigchart && stlgeometry->AtlasMade() && stlgeometry->GetSelectTrig())
+    {
+      vispar.stlchartnumber =  stlgeometry->GetChartNr(stlgeometry->GetSelectTrig());
+      vispar.stlchartnumberoffset = 0;
+    }
+  
+}
+
+
+
+
+// VisualSceneSTLMeshing vsstlmeshing;
+
+
+
+
+
+  /* *********************** Draw STL Geometry **************** */
+
+
+  VisualSceneSTLGeometry :: VisualSceneSTLGeometry ()
+    : VisualScene()
+  {
+    ;
+  }
+
+  VisualSceneSTLGeometry :: ~VisualSceneSTLGeometry ()
+  {
+    ;
+  }
+
+  void VisualSceneSTLGeometry :: DrawScene ()
+  {
+    if (changeval != stlgeometry->GetNT())
+      BuildScene();
+
+    changeval = stlgeometry->GetNT();
+
+
+    glClearColor(backcolor, backcolor, backcolor, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    SetLight();
+
+
+    glPushMatrix();
+    glMultMatrixf (transformationmat);
+
+
+
+
+    glShadeModel (GL_SMOOTH);
+    glDisable (GL_COLOR_MATERIAL);
+    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+    glEnable (GL_BLEND);
+    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+
+    double shine = vispar.shininess;
+    // double transp = vispar.transp;
+
+    glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine);
+    glLogicOp (GL_COPY);
+
+
+    float mat_col[] = { 0.2f, 0.2f, 0.8f, 1.0f};
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+    glPolygonOffset (1, 1);
+    glEnable (GL_POLYGON_OFFSET_FILL);
+
+    glCallList (trilists.Get(1));
+
+    glDisable (GL_POLYGON_OFFSET_FILL);
+
+
+    int showtrias = vispar.showstltrias;
+
+    if (showtrias)
+      {
+	float mat_coll[] = { 0.2f, 0.2f, 0.2f, 1.0f };
+	glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_coll);
+	glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+      
+	glCallList (trilists.Get(1));
+      }
+
+    /*
+
+    glBegin (GL_TRIANGLES);
+    for (j = 1; j <= stlgeometry -> GetNT(); j++)
+    {
+    const STLTriangle & tria = stlgeometry -> GetTriangle(j);
+    glNormal3f (tria.normal.X(),
+    tria.normal.Y(),
+    tria.normal.Z());
+		  
+    for (k = 0; k < 3; k++)
+    {
+    glVertex3f (tria.pts[k].X(),
+    tria.pts[k].Y(),
+    tria.pts[k].Z());
+    }
+    }    
+    glEnd ();
+    */  
+
+
+
+ 
+    glPopMatrix();
+    glFinish();  
+  }
+
+
+  void VisualSceneSTLGeometry :: BuildScene (int zoomall)
+  {
+    //  cout << "rebuild stl geometry scene" << endl;
+
+    center = stlgeometry -> GetBoundingBox().Center();
+    rad = stlgeometry -> GetBoundingBox().Diam() / 2;
+
+
+    CalcTransformationMatrices();
+
+    for (int i = 1; i <= trilists.Size(); i++)
+      glDeleteLists (trilists.Elem(i), 1);
+    trilists.SetSize(0);
+
+
+    trilists.Append (glGenLists (1));
+    glNewList (trilists.Last(), GL_COMPILE);
+
+    glEnable (GL_NORMALIZE);
+
+    glBegin (GL_TRIANGLES);
+    for (int j = 1; j <= stlgeometry -> GetNT(); j++)
+      {
+	const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	glNormal3f (n.X(), n.Y(), n.Z());
+      
+	for (int k = 1; k <= 3; k++)
+	  {
+	    const Point3d & p = 
+	      stlgeometry->GetPoint (stlgeometry -> GetTriangle(j).PNum(k));
+	    glVertex3f (p.X(),p.Y(), p.Z());
+	  }
+      }    
+    glEnd ();
+      
+    glEndList ();
+  }
+ 
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/vsstl.hpp b/contrib/Netgen/libsrc/stlgeom/vsstl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d3841c817424123f8197e4e1d7662c2aa7bc186b
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/vsstl.hpp
@@ -0,0 +1,53 @@
+#ifndef FILE_VSSTL
+#define FILE_VSSTL
+
+/**************************************************************************/
+/* File:   vsstl.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   05. Jan. 2011                                                  */
+/**************************************************************************/
+
+namespace netgen
+{
+
+ class VisualSceneSTLGeometry : public VisualScene
+  {
+    Array<int> trilists;
+    class STLGeometry * stlgeometry;
+
+  public:
+    VisualSceneSTLGeometry ();
+    virtual ~VisualSceneSTLGeometry ();
+    void SetGeometry (class STLGeometry * astlgeometry) { stlgeometry = astlgeometry; }
+
+    virtual void BuildScene (int zoomall = 0);
+    virtual void DrawScene ();
+  };
+
+
+  class VisualSceneSTLMeshing : public VisualScene
+  {
+    Array<int> trilists;
+    int selecttrig, nodeofseltrig;
+    class STLGeometry * stlgeometry;
+
+  public:
+    VisualSceneSTLMeshing ();
+    virtual ~VisualSceneSTLMeshing ();
+
+    void SetGeometry (class STLGeometry * astlgeometry) { stlgeometry = astlgeometry; }
+
+    virtual void BuildScene (int zoomall = 0);
+    virtual void DrawScene ();
+    virtual void MouseDblClick (int px, int py);
+
+    int seltria;
+  };
+
+
+
+}
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/visualization/Makefile.am b/contrib/Netgen/libsrc/visualization/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..5f2dff7ebaf54eadc6401c89006b04f77c64bc60
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/Makefile.am
@@ -0,0 +1,11 @@
+noinst_HEADERS = meshdoc.hpp mvdraw.hpp vispar.hpp \
+visual.hpp vssolution.hpp
+
+include_HEADERS = soldata.hpp
+
+AM_CPPFLAGS =  $(MPI_INCLUDES) -I$(top_srcdir)/libsrc/include  -DOPENGL -D$(TOGL_WINDOWINGSYSTEM) $(OCCFLAGS)    $(TCL_INCLUDES)
+METASOURCES = AUTO
+noinst_LIBRARIES = libvisual.a
+libvisual_a_SOURCES = meshdoc.cpp mvdraw.cpp \
+	vsfieldlines.cpp vsmesh.cpp vssolution.cpp importsolution.cpp
+AM_CXXFLAGS = -DOPENGL
diff --git a/contrib/Netgen/libsrc/visualization/importsolution.cpp b/contrib/Netgen/libsrc/visualization/importsolution.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cf182d7120229eb93ceabb3831b18c607b3705ac
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/importsolution.cpp
@@ -0,0 +1,129 @@
+//
+//  Read solution file
+//
+
+
+#include <mystdlib.h>
+
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+#include <nginterface.h>
+
+namespace netgen
+{
+
+
+
+void ImportSolution (const char * filename)
+{
+  ifstream inf (filename);
+  char buf[100], name[1000];
+  int i, size, comps, order;
+  bool iscomplex;
+  const char * type;
+  Flags flags;
+
+  while (1)
+    {
+      buf[0] = 0;
+      inf >> buf;
+      if (strcmp (buf, "solution") == 0)
+	{
+	  inf >> name;
+	  
+	  inf >> buf[0];
+	  flags.DeleteFlags ();
+	  while (buf[0] == '-')
+	    {
+	      inf >> buf[1];
+	      inf.putback (buf[1]);
+	      if (!isalpha (buf[1]))
+		{
+		  break;
+		}
+	      inf >> (buf+1);
+	      flags.SetCommandLineFlag (buf);
+	      buf[0] = 0;
+	      inf >> buf[0];
+	    }
+	  inf.putback (buf[0]);
+
+	  (*testout) << "Flags: " << endl;
+	  flags.PrintFlags (*testout);
+	  (*testout) << "done" << endl;
+
+	  size = int(flags.GetNumFlag ("size", Ng_GetNP()));
+	  comps = int(flags.GetNumFlag ("components", 1));
+	  type = flags.GetStringFlag ("type", "nodal");
+	  order = int(flags.GetNumFlag ("order", 1));
+	  iscomplex = flags.GetDefineFlag ("complex");
+
+	  double * sol = new double[size*comps];
+	  
+	  (*testout) << "import solution " << name << " size = " << size << " comps = " << comps << " order = " << order << endl;
+
+	  for (i = 0; i < size*comps; i++)
+	    {
+	      inf >> sol[i];
+	      //	      (*testout) << "sol: " << sol[i] << endl;
+	    }
+	  
+	  Ng_SolutionData soldata;
+	  Ng_InitSolutionData (&soldata);
+	  soldata.name = name;
+	  soldata.data = sol;
+	  soldata.dist = comps;
+	  soldata.components = comps;
+	  soldata.order = order;
+	  soldata.iscomplex = iscomplex;
+	  soldata.soltype = NG_SOLUTION_NODAL;
+          soldata.draw_surface = 1;
+          soldata.draw_volume = 1;
+	  if (strcmp (type, "element") == 0)
+            {
+              soldata.soltype = NG_SOLUTION_ELEMENT;
+              soldata.draw_surface = 0;
+            }
+	  if (strcmp (type, "surfaceelement") == 0)
+            {
+              soldata.soltype = NG_SOLUTION_SURFACE_ELEMENT;
+              soldata.draw_volume = 0;
+            }
+	  if (strcmp (type, "noncontinuous") == 0)
+	    soldata.soltype = NG_SOLUTION_NONCONTINUOUS;
+	  if (strcmp (type, "surfacenoncontinuous") == 0)
+	    soldata.soltype = NG_SOLUTION_SURFACE_NONCONTINUOUS;
+
+	  Ng_SetSolutionData (&soldata);
+	  }
+      else
+	{
+	  //	  cout << "kw = (" << buf << ")" << endl;
+	  (*testout) << "kw = (" << buf << ")" << endl;
+	  break;
+	}
+    }
+  /*
+  struct Ng_SolutionData
+    {
+      char * name;      // name of gridfunction
+      double * data;    // solution values
+      int components;   // used components in solution vector
+      int dist;         // num of doubles per entry (alignment!)
+      Ng_SolutionType soltype;  // type of solution function
+  };
+
+  // initialize solution data with default arguments
+  void Ng_InitSolutionData (Ng_SolutionData * soldata);
+  // set solution data
+  void Ng_SetSolutionData (Ng_SolutionData * soldata);
+  */
+}
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/visualization/meshdoc.cpp b/contrib/Netgen/libsrc/visualization/meshdoc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c2df277e1071365999ebe872da59bc53fcaf22ee
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/meshdoc.cpp
@@ -0,0 +1,614 @@
+#ifndef NOTCL
+
+#include <mystdlib.h>
+
+#include <meshing.hpp>
+
+// #include "incvis.hpp"
+
+
+#include <visual.hpp>
+
+
+namespace netgen
+{
+  // #include "meshdoc.hpp"
+
+
+MeshDoctorParameters meshdoctor;
+VisualSceneMeshDoctor vsmeshdoc;
+
+extern AutoPtr<Mesh> mesh;
+
+  int Ng_MeshDoctor (ClientData clientData,
+		     Tcl_Interp * interp,
+		     int argc, tcl_const char *argv[])
+{
+  cout << "Mesh Doctor:" << endl;
+  int i;
+  for (i = 0; i < argc; i++)
+    cout << argv[i] << " ";
+  cout << endl;
+
+  meshdoctor.active = 
+      atoi (Tcl_GetVar (interp, "::meshdoctor.active", 0)); 
+
+
+  if (argc >= 2)
+    {
+      if (strcmp (argv[1], "markedgedist") == 0)
+	{
+	  vsmeshdoc.SetMarkEdgeDist (atoi (argv[2]));
+	}
+
+      if (strcmp (argv[1], "deletemarkedsegments") == 0)
+	{
+	  for (i = 1; i <= mesh->GetNSeg(); i++)
+	    if (vsmeshdoc.IsSegmentMarked (i))
+	      mesh->DeleteSegment (i);
+
+	  //	  for (i = 1; i <= mesh->GetNSE(); i++)
+	  //	    mesh->SurfaceElement(i).SetIndex (1);
+	  mesh->Compress();
+	}
+    }
+
+
+  vsmeshdoc.UpdateTables ();
+  vsmeshdoc.BuildScene();
+  return TCL_OK;
+}
+
+
+
+
+
+VisualSceneMeshDoctor :: VisualSceneMeshDoctor ()
+  : VisualScene()
+{
+  filledlist = 0;
+  outlinelist = 0;
+  edgelist = 0;
+  selelement = 0;
+  locpi = 1;
+  selpoint = 0;
+  selpoint2 = 0;
+  markedgedist = 1;
+
+  UpdateTables ();
+}
+
+VisualSceneMeshDoctor :: ~VisualSceneMeshDoctor ()
+{
+  ;
+}
+
+void VisualSceneMeshDoctor :: DrawScene ()
+{
+  if (!mesh) return;
+
+  int hchval = mesh->GetNP() + mesh->GetNE() + mesh->GetNSE();
+  if (changeval != hchval)
+    {
+      changeval = hchval;
+      BuildScene();
+    }
+
+
+  glClearColor(backcolor, backcolor, backcolor, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glEnable (GL_COLOR_MATERIAL);
+  glColor3f (1.0f, 1.0f, 1.0f);
+  glLineWidth (1.0f);
+
+  SetLight();
+
+  glPushMatrix();
+  glMultMatrixf (transformationmat);
+  
+  glInitNames ();
+  glPushName (0);
+  
+  glPolygonOffset (1, 1);
+  glEnable (GL_POLYGON_OFFSET_FILL);
+
+  SetClippingPlane ();
+
+  if (vispar.drawfilledtrigs)
+    glCallList (filledlist);
+
+  glDisable (GL_POLYGON_OFFSET_FILL);
+  
+  if (vispar.drawoutline)
+    glCallList (outlinelist);
+  
+  glPolygonOffset (-1, -1);
+  glEnable (GL_POLYGON_OFFSET_LINE);
+
+  if (vispar.drawedges)
+    glCallList (edgelist);
+  
+
+  glDisable (GL_POLYGON_OFFSET_LINE);
+
+  
+  
+  glPopName();
+
+  if (selpoint > 0 && selpoint <= mesh->GetNP())
+    {
+      GLfloat matcolblue[] = { 0, 0, 1, 1 };
+
+      glPointSize (10);
+      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolblue);
+      glBegin (GL_POINTS);
+      
+      const Point3d p = mesh->Point(selpoint);
+      glVertex3f (p.X(), p.Y(), p.Z());
+      glEnd();
+    }
+
+  glDisable(GL_CLIP_PLANE0);
+
+
+  glPopMatrix();
+  glFinish();  
+}
+
+
+
+
+void VisualSceneMeshDoctor :: BuildScene (int zoomall)
+{
+  int i, j;
+ 
+  
+  if (zoomall)
+    {
+      Point3d pmin, pmax;
+      mesh->GetBox (pmin, pmax, -1);
+
+      if (vispar.centerpoint)
+	center = mesh->Point (vispar.centerpoint);
+      else
+	center = Center (pmin, pmax);
+  
+      rad = 0.5 * Dist (pmin, pmax);
+
+      glEnable (GL_NORMALIZE);
+  
+      CalcTransformationMatrices();
+    }
+
+
+
+
+  if (filledlist)
+    {
+      glDeleteLists (filledlist, 1);
+      glDeleteLists (outlinelist, 1);
+      glDeleteLists (edgelist, 1);
+    }
+
+  
+  filledlist = glGenLists (1);
+  glNewList (filledlist, GL_COMPILE);
+
+  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+  
+  glLineWidth (1.0f);
+  
+  glDisable (GL_COLOR_MATERIAL);
+    
+  for (i = 1; i <= mesh->GetNSE(); i++)
+    {
+      glLoadName (i);
+
+      // copy to be thread-safe
+      Element2d el = mesh->SurfaceElement (i);
+
+      int drawel = 1;
+      for (j = 1; j <= el.GetNP(); j++)
+	{
+	  if (!el.PNum(j))
+	    drawel = 0;
+	}
+
+      if (!drawel)
+	continue;
+
+      GLfloat matcol[] = { 0, 1, 0, 1 };
+      GLfloat matcolsel[] = { 1, 0, 0, 1 };
+
+      if (i == selelement)
+	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, matcolsel);
+      else
+	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, matcol);
+
+      if (el.GetNP() == 3)
+	{
+	  glBegin (GL_TRIANGLES);
+	  
+	  const Point3d & lp1 = mesh->Point (el.PNum(1));
+	  const Point3d & lp2 = mesh->Point (el.PNum(2));
+	  const Point3d & lp3 = mesh->Point (el.PNum(3));
+	  Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+	  n /= (n.Length()+1e-12);
+	  glNormal3d (n.X(), n.Y(), n.Z());
+
+	  if (!vispar.colormeshsize)
+	    {
+	      glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+	      glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+	      glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	    }
+	  else
+	    {
+	      double h1 = mesh->GetH (lp1);
+	      double h2 = mesh->GetH (lp2);
+	      double h3 = mesh->GetH (lp3);
+	      
+	      SetOpenGlColor  (h1, 0.1, 10);
+	      glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+
+	      SetOpenGlColor  (h2, 0.1, 10);
+	      glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+
+	      SetOpenGlColor  (h3, 0.1, 10);
+	      glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	    }	    
+	  glEnd();
+	}
+      else if (el.GetNP() == 4)
+	{
+	  glBegin (GL_QUADS);
+	  
+	  const Point3d & lp1 = mesh->Point (el.PNum(1));
+	  const Point3d & lp2 = mesh->Point (el.PNum(2));
+	  const Point3d & lp3 = mesh->Point (el.PNum(4));
+	  const Point3d & lp4 = mesh->Point (el.PNum(3));
+	  Vec3d n = Cross (Vec3d (lp1, lp2), 
+			   Vec3d (lp1, Center (lp3, lp4)));
+	  n /= (n.Length()+1e-12);
+	  glNormal3d (n.X(), n.Y(), n.Z()); 
+	  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+	  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+	  glVertex3d (lp4.X(), lp4.Y(), lp4.Z());
+	  glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	  glEnd();
+	}
+      else if (el.GetNP() == 6)
+	{
+	  glBegin (GL_TRIANGLES);
+	  static int trigs[4][3] = {
+	    { 1, 6, 5 },
+	    { 2, 4, 6 },
+	    { 3, 5, 4 },
+	    { 4, 5, 6 } };
+
+	  for (j = 0; j < 4; j++)
+	    {
+	      const Point3d & lp1 = mesh->Point (el.PNum(trigs[j][0]));
+	      const Point3d & lp2 = mesh->Point (el.PNum(trigs[j][1]));
+	      const Point3d & lp3 = mesh->Point (el.PNum(trigs[j][2]));
+	      Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+	      n /= (n.Length() + 1e-12);
+	      glNormal3d (n.X(), n.Y(), n.Z());
+	      glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+	      glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+	      glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	    }
+	  glEnd();
+	}
+    }
+  glLoadName (0);
+  
+  glEndList ();
+
+  
+  
+  outlinelist = glGenLists (1);
+  glNewList (outlinelist, GL_COMPILE);
+
+  glLineWidth (1.0f);
+  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+
+  glColor3f (0.0f, 0.0f, 0.0f);
+  glEnable (GL_COLOR_MATERIAL);
+  
+  for (i = 1; i <= mesh->GetNSE(); i++)
+    {
+      Element2d el = mesh->SurfaceElement(i);
+
+      int drawel = 1;
+      for (j = 1; j <= el.GetNP(); j++)
+	{
+	  if (!el.PNum(j))
+	    drawel = 0;
+	}
+
+      if (!drawel)
+	continue;
+
+
+      if (el.GetNP() == 3)
+	{
+	  glBegin (GL_TRIANGLES);
+	  
+	  const Point3d & lp1 = mesh->Point (el.PNum(1));
+	  const Point3d & lp2 = mesh->Point (el.PNum(2));
+	  const Point3d & lp3 = mesh->Point (el.PNum(3));
+	  Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+	  n /= (n.Length() + 1e-12);
+	  glNormal3d (n.X(), n.Y(), n.Z());
+	  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+	  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+	  glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	  glEnd();
+	}
+      else if (el.GetNP() == 4)
+	{
+	  glBegin (GL_QUADS);
+	  
+	  const Point3d & lp1 = mesh->Point (el.PNum(1));
+	  const Point3d & lp2 = mesh->Point (el.PNum(2));
+	  const Point3d & lp3 = mesh->Point (el.PNum(4));
+	  const Point3d & lp4 = mesh->Point (el.PNum(3));
+	  Vec3d n = Cross (Vec3d (lp1, lp2), 
+			   Vec3d (lp1, Center (lp3, lp4)));
+	  n /= (n.Length() + 1e-12);
+	  glNormal3d (n.X(), n.Y(), n.Z());
+	  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+	  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+	  glVertex3d (lp4.X(), lp4.Y(), lp4.Z());
+	  glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	  glEnd();
+	}
+      else if (el.GetNP() == 6)
+	{
+	  glBegin (GL_LINES);
+	  
+	  const Point3d & lp1 = mesh->Point (el.PNum(1));
+	  const Point3d & lp2 = mesh->Point (el.PNum(2));
+	  const Point3d & lp3 = mesh->Point (el.PNum(3));
+	  const Point3d & lp4 = mesh->Point (el.PNum(4));
+	  const Point3d & lp5 = mesh->Point (el.PNum(5));
+	  const Point3d & lp6 = mesh->Point (el.PNum(6));
+
+	  Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+	  n /= (n.Length()+1e-12);
+	  glNormal3d (n.X(), n.Y(), n.Z());
+
+	  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+	  glVertex3d (lp6.X(), lp6.Y(), lp6.Z());
+	  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+	  glVertex3d (lp6.X(), lp6.Y(), lp6.Z());
+
+	  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+	  glVertex3d (lp5.X(), lp5.Y(), lp5.Z());
+	  glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	  glVertex3d (lp5.X(), lp5.Y(), lp5.Z());
+
+	  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+	  glVertex3d (lp4.X(), lp4.Y(), lp4.Z());
+	  glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	  glVertex3d (lp4.X(), lp4.Y(), lp4.Z());
+	  glEnd();
+	}
+    }
+  glLoadName (0);  
+  glEndList ();
+
+
+
+
+
+  edgelist = glGenLists (1);
+  glNewList (edgelist, GL_COMPILE);
+
+  glDisable (GL_COLOR_MATERIAL);
+
+  GLfloat matcoledge[] = { 0, 0, 1, 1 };
+  GLfloat matcolseledge[] = { 1, 0, 1, 1 };
+
+  glLineWidth (2.0f);
+
+  for (i = 1; i <= mesh->GetNSeg(); i++)
+    {
+      const Segment & seg = mesh->LineSegment(i);
+      const Point3d & p1 = mesh->Point(seg[0]);
+      const Point3d & p2 = mesh->Point(seg[1]);
+
+      if (edgedist.Get(seg[0]) <= markedgedist &&
+	  edgedist.Get(seg[1]) <= markedgedist)
+	{
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 
+			matcolseledge);
+	  glLineWidth (4.0f);
+	}
+      else
+	{
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 
+			matcoledge);
+	  glLineWidth (2.0f);
+	}
+      glBegin (GL_LINES);
+      glVertex3f (p1.X(), p1.Y(), p1.Z());
+      glVertex3f (p2.X(), p2.Y(), p2.Z());
+      glEnd(); 
+    }
+
+  glLineWidth (1.0f);
+  glEndList ();
+}
+
+
+
+
+void VisualSceneMeshDoctor :: MouseDblClick (int px, int py)
+{
+  cout << "dblclick: " << px << " - " << py << endl;
+  
+  int i, hits;
+
+  // select surface triangle by mouse click
+  GLuint selbuf[10000];
+  glSelectBuffer (10000, selbuf);
+
+
+  glRenderMode (GL_SELECT);
+
+  GLint viewport[4];
+  glGetIntegerv (GL_VIEWPORT, viewport);
+
+  glMatrixMode (GL_PROJECTION); 
+  glPushMatrix();
+
+  GLdouble projmat[16];
+  glGetDoublev (GL_PROJECTION_MATRIX, projmat);
+
+  glLoadIdentity(); 
+  gluPickMatrix (px, viewport[3] - py, 1, 1, viewport); 
+  glMultMatrixd (projmat);
+  
+
+  glClearColor(backcolor, backcolor, backcolor, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glMatrixMode (GL_MODELVIEW); 
+
+  glPushMatrix();
+  glMultMatrixf (transformationmat);
+
+  glInitNames();
+  glPushName (1);
+
+  glPolygonOffset (1, 1);
+  glEnable (GL_POLYGON_OFFSET_FILL);
+
+  glCallList (filledlist);
+
+  glDisable (GL_POLYGON_OFFSET_FILL);
+  
+  glPopName();
+
+  glMatrixMode (GL_PROJECTION); 
+  glPopMatrix();
+
+  glMatrixMode (GL_MODELVIEW); 
+  glPopMatrix();
+
+  glFlush();  
+
+	
+  hits = glRenderMode (GL_RENDER);
+
+  cout << "hits = " << hits << endl;
+
+  int minname = 0;
+  GLuint mindepth = 0;
+  for (i = 0; i < hits; i++)
+    {
+      int curname = selbuf[4*i+3];
+      GLuint curdepth = selbuf[4*i+1];
+
+      if (curname &&
+	  (curdepth < mindepth || !minname))
+	{
+	  mindepth = curdepth;
+	  minname = curname;
+	}
+    }
+
+  cout << "clicked element: " << minname << endl;
+
+  ClickElement (minname);
+
+  BuildScene ();
+}
+
+
+
+
+void VisualSceneMeshDoctor :: SetMarkEdgeDist (int dist)
+{
+  markedgedist = dist;
+  BuildScene();
+}
+
+void VisualSceneMeshDoctor :: ClickElement (int elnr)
+{
+  selelement = elnr;
+
+  int oldlocpi = locpi;
+  locpi = locpi % 3 + 1;
+  
+  if (selelement > 0 && selelement <= mesh->GetNSE())
+    {
+      selpoint = mesh->SurfaceElement(selelement).PNum(locpi);
+      selpoint2 = mesh->SurfaceElement(selelement).PNum(oldlocpi);
+      cout << "selpts = " << selpoint << ", " << selpoint2 << endl;
+    }
+
+  UpdateTables();
+}
+
+
+void VisualSceneMeshDoctor :: UpdateTables ()
+{
+  if (!mesh) return;
+
+  edgedist.SetSize(mesh->GetNP());
+  int i, changed;
+
+  for (i = 1; i <= mesh->GetNP(); i++)
+    edgedist.Elem(i) = 10000;
+
+  for (i = 1; i <= mesh->GetNSeg(); i++)
+    {
+      const Segment & seg = mesh->LineSegment(i);
+      if ( (seg[0] == selpoint && seg[1] == selpoint2) ||
+           (seg[1] == selpoint && seg[0] == selpoint2) )
+	{
+	  edgedist.Elem(selpoint) = 1;
+	  edgedist.Elem(selpoint2) = 1;
+	}
+    }
+
+  do
+    {
+      changed = 0;
+
+      for (i = 1; i <= mesh->GetNSeg(); i++)
+	{
+	  const Segment & seg = mesh->LineSegment(i);
+	  
+	  int edist = min2 (edgedist.Get(seg[0]), edgedist.Get(seg[1]));
+	  edist++;
+
+	  if (edgedist.Get(seg[0]) > edist)
+	    {
+	      edgedist.Elem(seg[0]) = edist;
+	      changed = 1;
+	    }
+	  if (edgedist.Get(seg[1]) > edist)
+	    {
+	      edgedist.Elem(seg[1]) = edist;
+	      changed = 1;
+	    }
+	}	    
+    }
+  while (changed);
+}
+
+int VisualSceneMeshDoctor :: IsSegmentMarked (int segnr) const
+{
+  const Segment & seg = mesh->LineSegment(segnr);
+  return (edgedist.Get(seg[0]) <= markedgedist &&
+	  edgedist.Get(seg[1]) <= markedgedist);
+}
+}
+
+
+#endif // NOTCL
diff --git a/contrib/Netgen/libsrc/visualization/meshdoc.hpp b/contrib/Netgen/libsrc/visualization/meshdoc.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..65763385f113b7046481cf68c0ca3cd65bb3dfae
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/meshdoc.hpp
@@ -0,0 +1,37 @@
+
+class VisualSceneMeshDoctor : public VisualScene
+{
+  int filledlist;
+  int outlinelist;
+  int edgelist;
+
+  int selelement, locpi;
+  int selpoint, selpoint2;
+
+  // for edgemarking:
+  Array<int> edgedist;
+  int markedgedist;
+  
+
+public:
+  VisualSceneMeshDoctor ();
+  virtual ~VisualSceneMeshDoctor ();
+
+  virtual void BuildScene (int zoomall = 0);
+  virtual void DrawScene ();
+  virtual void MouseDblClick (int px, int py);
+
+  void SetMarkEdgeDist (int dist);
+  void ClickElement (int elnr);
+  void UpdateTables ();
+  int IsSegmentMarked (int segnr) const;
+};
+
+class MeshDoctorParameters 
+{
+public:
+  int active;
+};
+
+
+extern MeshDoctorParameters meshdoctor;
diff --git a/contrib/Netgen/libsrc/visualization/mvdraw.cpp b/contrib/Netgen/libsrc/visualization/mvdraw.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d348fc7b661d74bb112e003a7aae6f06254a8780
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/mvdraw.cpp
@@ -0,0 +1,806 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <meshing.hpp>
+
+#include <visual.hpp>
+// #include <parallel.hpp>
+
+
+
+#ifndef WIN32
+#define GLX_GLXEXT_LEGACY
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>  /* for XA_RGB_DEFAULT_MAP atom */
+// #include <GL/glx.h>    // for parallel GL ???
+#endif
+
+
+
+
+
+namespace netgen
+{
+  DLL_HEADER Point3d VisualScene :: center;
+  DLL_HEADER double VisualScene :: rad;
+  DLL_HEADER GLdouble VisualScene :: backcolor;
+
+  /*
+#if TOGL_MAJOR_VERSION!=2
+  GLuint VisualScene :: fontbase = 0;
+#else
+  Tcl_Obj * VisualScene :: fontbase = NULL;
+  Togl * VisualScene :: globtogl;
+#endif
+  */
+
+  // texture for color decoding
+  // GLubyte * VisualScene :: colortexture = NULL;
+  GLuint VisualScene :: coltexname = 1;
+  int VisualScene :: ntexcols = -1;
+
+
+  float VisualScene :: lookatmat[16];
+  float VisualScene :: transmat[16];
+  float VisualScene :: rotmat[16];
+  float VisualScene :: centermat[16];
+  float VisualScene :: transformationmat[16];
+
+  int VisualScene :: selface;
+  int VisualScene :: selelement;
+  int VisualScene :: selpoint;
+  int VisualScene :: selpoint2;
+  int VisualScene :: locpi;
+  int VisualScene :: seledge;
+
+  int VisualScene :: selecttimestamp;
+
+
+  VisualizationParameters :: VisualizationParameters()
+  {
+    lightamb = 0.3;
+    lightdiff = 0.7;
+    lightspec = 1;
+    shininess = 50;
+    transp = 0.3;
+    locviewer = 0;
+    showstltrias = 0;
+    centerpoint = 0;
+    usedispllists = 1;
+    strcpy (selectvisual, "cross");
+
+    use_center_coords = false;
+  };
+  VisualizationParameters vispar;
+
+
+
+  double dist = 0;
+  // double dist = 6;
+  // vorher: pnear = 2;
+  // double pnear = 0.1;
+  // double pfar = 10;
+
+
+
+  VisualScene :: VisualScene ()
+  {
+    changeval = -1;
+    backcolor = 0;
+  }
+
+
+  VisualScene :: ~VisualScene()
+  {
+    ;
+  }
+
+
+  extern DLL_HEADER void Render();
+  DLL_HEADER void Render ()
+  {
+    multithread.redraw = 1;
+  }
+
+
+  void VisualScene :: BuildScene (int zoomall)
+  {
+    center = Point3d (0,0,0);
+    rad = 1;
+
+    CalcTransformationMatrices();
+
+    glEnable(GL_DEPTH_TEST);
+    glDisable (GL_DITHER);
+  
+    GLfloat ambvals[] = { 0.4f, 0.4f, 0.4f, 1.0f };
+    GLfloat diffvals[] = { 0.5f, 0.5f, 0.5f, 1.0f };
+    GLfloat specvals[] =  { 0.7f, 0.7f, 0.7f, 1.0f };
+    glLightfv(GL_LIGHT0, GL_AMBIENT, ambvals);
+    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffvals);
+    glLightfv(GL_LIGHT0, GL_SPECULAR, specvals);
+  
+    GLfloat light_position[] = { 1, 3, 3, 0 };
+    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
+  
+    glLightModeli (GL_LIGHT_MODEL_TWO_SIDE, 0);
+    glEnable(GL_LIGHTING);
+    glEnable(GL_LIGHT0);
+  }
+
+
+  void VisualScene :: DrawScene ()
+  {
+    if (changeval == -1)
+      BuildScene();
+    changeval = 0;
+
+    glClearColor(backcolor, backcolor, backcolor, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glEnable (GL_COLOR_MATERIAL);
+    glColor3f (1.0f, 1.0f, 1.0f);
+    glLineWidth (1.0f);
+
+    DrawCoordinateCross ();
+    DrawNetgenLogo ();
+    glFinish();  
+  }
+
+
+  void VisualScene :: CalcTransformationMatrices()
+  {
+    // prepare model view matrix
+  
+    glPushMatrix();
+
+    glLoadIdentity();
+    gluLookAt (0, 0, 6, 0, 0, 0, 0, 1, 0);
+    glGetFloatv (GL_MODELVIEW_MATRIX, lookatmat);
+
+    glLoadIdentity();
+    glTranslatef(0.0f, 0.0f, -dist);
+    glGetFloatv (GL_MODELVIEW_MATRIX, transmat);
+  
+    glLoadIdentity();
+    glGetFloatv (GL_MODELVIEW_MATRIX, rotmat);
+
+    glScalef (1/rad, 1/rad, 1/rad);
+    glTranslated (-center.X(), -center.Y(), -center.Z());
+    glGetFloatv (GL_MODELVIEW_MATRIX, centermat);
+
+    glLoadIdentity();
+    glMultMatrixf (lookatmat);
+    glMultMatrixf (transmat);
+    glMultMatrixf (rotmat);
+    glMultMatrixf (centermat);
+    glGetFloatv (GL_MODELVIEW_MATRIX, transformationmat);
+
+    glPopMatrix();
+  }
+
+
+  void VisualScene :: ArbitraryRotation (const Array<double> & alpha, const Array<Vec3d> & vec)
+  {
+    glPushMatrix();
+
+    glLoadIdentity();
+
+    for(int i=0; i<alpha.Size() && i<vec.Size(); i++)
+      {
+	glRotatef(alpha[i], vec[i].X(), vec[i].Y(), vec[i].Z());
+      }
+
+    glGetFloatv (GL_MODELVIEW_MATRIX, rotmat);
+
+    glLoadIdentity();
+    glMultMatrixf (lookatmat);
+    glMultMatrixf (transmat);
+    glMultMatrixf (rotmat);
+    glMultMatrixf (centermat);
+    glGetFloatv (GL_MODELVIEW_MATRIX, transformationmat);
+  
+    glPopMatrix();
+  } 
+
+
+
+  void VisualScene :: ArbitraryRotation (const double alpha, const Vec3d & vec)
+  {
+    Array<double> a(1); a[0] = alpha;
+    Array<Vec3d> v(1); v[0] = vec;
+
+    ArbitraryRotation(a,v);
+  } 
+
+  void VisualScene :: StandardRotation (const char * dir)
+  {
+    glPushMatrix();
+
+    glLoadIdentity();
+  
+    if (strcmp (dir, "xy") == 0)
+      ;
+    else if (strcmp (dir, "yx") == 0)
+      glRotatef(180.0, 1.0f, 1.0f, 0.0f);    
+    else if (strcmp (dir, "xz") == 0)
+      glRotatef(-90.0, 1.0f, 0.0f, 0.0f);    
+    else if (strcmp (dir, "zx") == 0)
+      {
+	glRotatef(180.0, 1.0f, 1.0f, 0.0f);    
+	glRotatef(-90.0, 1.0f, 0.0f, 0.0f);    
+      }
+    else if (strcmp (dir, "yz") == 0)
+      {
+	glRotatef(-90.0, 0.0f, 0.0f, 1.0f);    
+	glRotatef(-90.0, 0.0f, 1.0f, 0.0f);    
+      }
+    else if (strcmp (dir, "zy") == 0)
+      glRotatef(90.0, 0.0f, 1.0f, 0.0f);    
+
+
+    glGetFloatv (GL_MODELVIEW_MATRIX, rotmat);
+
+    glLoadIdentity();
+    glMultMatrixf (lookatmat);
+    glMultMatrixf (transmat);
+    glMultMatrixf (rotmat);
+    glMultMatrixf (centermat);
+    glGetFloatv (GL_MODELVIEW_MATRIX, transformationmat);
+  
+    glPopMatrix();
+  }
+
+  void VisualScene :: MouseMove(int oldx, int oldy,
+				int newx, int newy,
+				char mode)
+  {
+    int deltax = newx - oldx;
+    int deltay = newy - oldy;
+  
+    glPushMatrix();
+    glLoadIdentity ();
+  
+    switch (mode)
+      {
+      case 'r':
+	{	
+	  glRotatef(float(deltax)/2, 0.0f, 1.0f, 0.0f);
+	  glRotatef(float(deltay)/2, 1.0f, 0.0f, 0.0f);
+	  glMultMatrixf (rotmat);
+	  glGetFloatv (GL_MODELVIEW_MATRIX, rotmat);
+	  break;
+	}
+      case 'm':
+	{
+	  GLdouble projmat[16], modelviewmat[16];
+	  GLint viewport[4];
+	  glGetDoublev (GL_PROJECTION_MATRIX, projmat);
+	  glGetDoublev (GL_MODELVIEW_MATRIX, modelviewmat);
+	  glGetIntegerv (GL_VIEWPORT, viewport);
+	
+	  // vorher pvz1/2 = 0
+	  GLdouble pvx1 = 0, pvy1 = 0, pvz1 = 0.99; //  0.95;
+	  GLdouble pvx2 = deltax, pvy2 = -deltay, pvz2 = 0.99; // 0.95;
+
+	  GLdouble px1, py1, pz1;
+	  GLdouble px2, py2, pz2;
+	
+	  gluUnProject (pvx1, pvy1, pvz1, 
+			modelviewmat, projmat, viewport,
+			&px1, &py1, &pz1);
+	  gluUnProject (pvx2, pvy2, pvz2, 
+			modelviewmat, projmat, viewport,
+			&px2, &py2, &pz2);
+	  /*
+	    gluUnProject (oldx, oldy, 1, 
+	    modelviewmat, projmat, viewport,
+	    &px1, &py1, &pz1);
+	    gluUnProject (newx, newy, 1, 
+	    modelviewmat, projmat, viewport,
+	    &px2, &py2, &pz2);
+	  */
+
+	  /*	
+	    cout << "pv1 = " << pvx1 << ", " << pvy1 << ", " << pvz1 << endl;
+	    cout << "p1 = " << px1 << ", " << py1 << ", " << pz1 << endl;
+	  */
+
+	  glTranslated (px2-px1, py2-py1, pz2-pz1);
+	
+	  glMultMatrixf (transmat);
+	  glGetFloatv (GL_MODELVIEW_MATRIX, transmat);
+	  break;
+	}
+      case 'z':
+	{
+	  // glTranslatef(0.0f, 0.0f, -dist);
+
+	  // cout << "deltay = " << deltay << endl;
+	  // cout << "float_bug = " << (float(deltay)/100) << endl;   gives wrong result with icc 9.0.021
+	  glScaled (exp (double (-deltay)/100), 
+		    exp (double (-deltay)/100), 
+		    exp (double (-deltay)/100));
+	  // glTranslatef(0.0f, 0.0f, dist);
+	  glMultMatrixf (transmat);
+	  glGetFloatv (GL_MODELVIEW_MATRIX, transmat);
+	  break;
+	}
+      }
+
+    glLoadIdentity();
+    glMultMatrixf (lookatmat);
+    glMultMatrixf (transmat);
+    glMultMatrixf (rotmat);
+    glMultMatrixf (centermat);
+    glGetFloatv (GL_MODELVIEW_MATRIX, transformationmat);
+  
+    glPopMatrix();
+  }
+
+
+  void VisualScene :: LookAt (const Point<3> & cam, const Point<3> & obj,
+			      const Point<3> & camup)
+  {
+    glPushMatrix();
+    glLoadIdentity ();
+    gluLookAt (cam(0), cam(1), cam(2), 
+	       obj(0), obj(1), obj(2),
+	       camup(0), camup(1), camup(2));
+    glMultMatrixf (centermat);
+    glGetFloatv (GL_MODELVIEW_MATRIX, transformationmat);
+    glPopMatrix();
+  }
+
+  
+  void VisualScene :: SetClippingPlane ()
+  {
+    if (vispar.clipenable)
+      {
+	Vec3d n = vispar.clipnormal;
+	n /= (n.Length()+1e-10);
+	clipplane[0] = n.X();
+	clipplane[1] = n.Y();
+	clipplane[2] = n.Z();
+	clipplane[3] = -(Vec3d(center) * n) + rad * vispar.clipdist;
+
+	glClipPlane(GL_CLIP_PLANE0, clipplane);
+	glEnable(GL_CLIP_PLANE0);
+      }
+    else
+      glDisable (GL_CLIP_PLANE0);
+  }
+
+
+
+
+  void VisualScene :: MouseDblClick (int /* px */, int /* py */)
+  {
+    ;
+  }
+
+
+
+  void VisualScene :: SetLight()
+  {
+    GLfloat vals[3];
+    double lightamb = vispar.lightamb;
+    vals[0] = vals[1] = vals[2] = lightamb;
+    glLightfv(GL_LIGHT0, GL_AMBIENT, vals);
+
+    double lightdiff = vispar.lightdiff;
+    vals[0] = vals[1] = vals[2] = lightdiff;
+    glLightfv(GL_LIGHT0, GL_DIFFUSE, vals);
+
+    double lightspec = vispar.lightspec;
+    vals[0] = vals[1] = vals[2] = lightspec;
+    glLightfv(GL_LIGHT0, GL_SPECULAR, vals);
+
+    glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, vispar.shininess);
+    glLightModeli (GL_LIGHT_MODEL_LOCAL_VIEWER, vispar.locviewer);
+
+    float mat_spec_col[] = { 1, 1, 1, 1 };
+    glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_spec_col);
+
+    glEnable (GL_LIGHTING);
+    glEnable (GL_LIGHT0);
+  }
+
+
+
+
+  void VisualScene :: SetOpenGlColor(double val, double valmin, double valmax,
+				     int logscale)
+  {
+    double value;
+
+    if (!logscale)
+      value = (val - valmin) / (valmax - valmin);
+    else
+      {
+	if (valmax <= 0) valmax = 1;
+	if (valmin <= 0) valmin = 1e-4 * valmax;
+	value = (log(fabs(val)) - log(valmin)) / (log(valmax) - log(valmin));
+      }
+
+    if (!invcolor)
+      value = 1 - value;
+
+    glTexCoord1f ( 0.998 * value + 0.001);
+    // glTexCoord1f ( val ); 
+
+    glTexCoord2f ( 0.998 * value + 0.001, 1.5);
+    // glTexCoord1f ( value ); 
+
+    if (value > 1) value = 1;
+    if (value < 0) value = 0;
+
+    value *= 4;
+
+    static const double colp[][3] =
+      {
+	{ 1, 0, 0 },
+	{ 1, 1, 0 },
+	{ 0, 1, 0 },
+	{ 0, 1, 1 },
+	{ 0, 0, 1 },
+	//	{ 1, 0, 1 },
+	//	{ 1, 0, 0 },
+      };
+  
+    int i = int(value);
+    double r = value - i;
+
+    GLdouble col[3];
+    for (int j = 0; j < 3; j++)
+      col[j] = (1-r) * colp[i][j] + r * colp[i+1][j];
+  
+    glColor3d (col[0], col[1], col[2]);
+  }
+
+
+
+  void VisualScene :: CreateTexture (int ncols, int linear, int typ)
+  {
+    if (linear) ncols = 32;
+    else   ncols = 8;
+
+
+    if (ntexcols != ncols) 
+      {
+	ntexcols = ncols;
+      
+	GLubyte colortexture[4*32];
+
+	const double colp[][3] =
+	  {
+	    { 1, 0, 0 },
+	    { 1, 1, 0 },
+	    { 0, 1, 0 },
+	    { 0, 1, 1 },
+	    { 0, 0, 1 },
+	  };
+  
+	for (int i = 0; i < ncols; i++)
+	  {
+	    double value = 4.0 * i / (ncols-1);
+
+	    int iv = int(value);
+	    double r = value - iv;
+
+	    GLdouble col[3];
+
+	    if(r > 1e-3)
+	      for (int j = 0; j < 3; j++)
+		col[j] = (1.-r) * colp[iv][j] + r * colp[iv+1][j];
+	    else
+	      for (int j = 0; j < 3; j++)
+		col[j] = colp[iv][j];
+
+	    colortexture[4*i] = GLubyte (255 * col[0]);
+	    colortexture[4*i+1] = GLubyte (255 * col[1]);
+	    colortexture[4*i+2] = GLubyte (255 * col[2]);
+	    colortexture[4*i+3] = GLubyte(255);
+	  }
+
+	// glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+
+     	glTexImage1D (GL_TEXTURE_1D, 0, 4, ncols, 0, GL_RGBA, GL_UNSIGNED_BYTE, colortexture);
+	glTexImage2D (GL_TEXTURE_2D, 0, 4, ncols, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, colortexture);
+
+	glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, typ);  // DECAL or MODULATE
+	
+	GLfloat bcol[] = { 1, 1, 1, 1.0 };
+	glTexParameterfv (GL_TEXTURE_1D, GL_TEXTURE_BORDER_COLOR, bcol);
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+
+	glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bcol);
+	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+	
+	if (linear)
+	  {
+	    glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	    glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+	    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	  }
+	else
+	  {
+	    glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	    glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+	    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	  }
+      }
+  }
+  
+
+
+
+  void VisualScene :: DrawColorBar (double minval, double maxval, int logscale, bool linear)
+  {
+    if (!vispar.drawcolorbar) return;
+
+    CreateTexture (8, linear, GL_DECAL);
+
+    if (logscale && maxval <= 0) maxval = 1;
+    if (logscale && minval <= 0) minval = 1e-4 * maxval;
+
+    double minx = -1;
+    double maxx = 1;
+    double miny = 0.75;
+    double maxy = 0.8;
+
+    glDisable (GL_LIGHTING);
+    glEnable (GL_COLOR_MATERIAL);
+    glEnable (GL_TEXTURE_1D);
+    glNormal3d (0, 0, 1);
+    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+    
+    glDisable (GL_DEPTH_TEST);
+    glBegin (GL_QUAD_STRIP);
+
+    for (double x = minx; x <= maxx; x += (maxx - minx) / 50)
+      {
+	SetOpenGlColor (x, minx, maxx);
+	glVertex3d (x, miny, -5);
+	glVertex3d (x, maxy, -5);
+      }
+    glEnd();
+
+    glDisable (GL_TEXTURE_1D);
+    
+    glEnable (GL_COLOR_MATERIAL);
+    GLfloat textcol[3] = { 1 - backcolor, 1 - backcolor, 1 - backcolor };
+    glColor3fv (textcol);
+    
+    glPushAttrib (GL_LIST_BIT);
+    // glListBase (fontbase);
+
+    char buf[20];
+    for (int i = 0; i <= 4; i++)
+      {
+	double x = minx + i * (maxx-minx) / 4;
+	glRasterPos3d (x, 0.7,-5);
+      
+	double val;
+	if (logscale)
+	  val = minval * pow (maxval / minval, i / 4.0);
+	else
+	  val = minval + i * (maxval-minval) / 4;
+
+	sprintf (buf, "%8.3e", val);
+	// glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf);
+	MyOpenGLText (buf);
+      }
+
+    glPopAttrib ();
+    glEnable (GL_DEPTH_TEST);
+  }
+
+
+  void VisualScene :: DrawCoordinateCross ()
+  {
+    if (!vispar.drawcoordinatecross) return;
+
+    glDisable (GL_DEPTH_TEST);
+    glMatrixMode (GL_PROJECTION); 
+    glPushMatrix();
+    glLoadIdentity();
+
+    glMatrixMode (GL_MODELVIEW); 
+    glPushMatrix();
+    glLoadIdentity();
+
+    GLint viewport[4];
+    glGetIntegerv (GL_VIEWPORT, viewport);
+
+    glTranslatef (-1, -1, 0.0);
+    glScalef (40.0 / viewport[2], 40.0 / viewport[3], 1);
+    glTranslatef (2.0, 2.0, 0.0);
+    glMultMatrixf (rotmat);
+
+    glEnable (GL_COLOR_MATERIAL);
+    glDisable (GL_LIGHTING);
+
+    glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+
+    GLfloat textcol[3] = { 1 - backcolor,
+			   1 - backcolor,
+			   1 - backcolor };
+    glColor3fv (textcol);
+
+    glLineWidth (1.0f);
+
+    double len = 1;
+
+    glBegin(GL_LINES);
+    glVertex3d (0, 0, 0);
+    glVertex3d (len, 0, 0);
+    glVertex3d (0.0f, 0.0f, 0.0f);
+    glVertex3d (0.0f, len, 0.0f);
+    glVertex3d (0.0f, 0.0f, 0.0f);
+    glVertex3d (0.0f, 0.0f, len);
+    glEnd ();
+
+    glPushAttrib (GL_LIST_BIT);
+    // glListBase (fontbase);
+
+    char buf[20];
+
+    glRasterPos3d (len, 0.0f, 0.0f);
+    sprintf (buf, "x");
+    // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf);
+    MyOpenGLText (buf);
+    glRasterPos3d (0.0f, len, 0.0f);
+    sprintf (buf, "y");
+    // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf);
+    MyOpenGLText (buf);
+    glRasterPos3d (0.0f, 0.0f, len);
+    sprintf (buf, "z");
+    // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf);
+    MyOpenGLText (buf);
+
+    glPopAttrib ();
+
+    glEnable (GL_LIGHTING);
+
+    glMatrixMode (GL_PROJECTION); 
+    glPopMatrix();
+    glMatrixMode (GL_MODELVIEW); 
+    glPopMatrix();
+    glEnable (GL_DEPTH_TEST);
+  }
+
+
+  void VisualScene :: DrawNetgenLogo ()
+  {
+    if (!vispar.drawnetgenlogo) return;
+
+    glDisable (GL_DEPTH_TEST);
+    glMatrixMode (GL_PROJECTION); 
+    glPushMatrix();
+    glLoadIdentity();
+
+    glMatrixMode (GL_MODELVIEW); 
+    glPushMatrix();
+    glLoadIdentity();
+
+    GLint viewport[4];
+    glGetIntegerv (GL_VIEWPORT, viewport);
+
+    glTranslatef (1, -1, 0.0);
+    glScalef (40.0 / viewport[2], 40.0 / viewport[3], 1);
+    glTranslatef (-7.0, 2.0, 0.0);
+
+    glDisable (GL_CLIP_PLANE0);
+    glDisable (GL_LIGHTING);
+
+    glEnable (GL_COLOR_MATERIAL);
+    GLfloat textcol[3] = { 1 - backcolor,
+			   1 - backcolor,
+			   1 - backcolor };
+    glColor3fv (textcol);
+    glLineWidth (1.0f);
+
+    glPushAttrib (GL_LIST_BIT);
+    // glListBase (fontbase);
+
+    char buf[] = "Netgen " PACKAGE_VERSION;
+
+    glRasterPos3d (0.0f, 0.0f, 0.0f);
+    // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf);
+    MyOpenGLText (buf);
+
+    glPopAttrib ();
+
+    glEnable (GL_LIGHTING);
+    glMatrixMode (GL_PROJECTION); 
+    glPopMatrix();
+    glMatrixMode (GL_MODELVIEW); 
+    glPopMatrix();
+    glEnable (GL_DEPTH_TEST);
+  }
+
+
+
+
+
+
+
+#ifdef PARALLELGL
+  void VisualScene :: InitParallelGL ()
+  {
+    static int init = 0;
+
+    if (!init)
+      {
+	init = 1;
+
+	if (id == 0)
+	  {
+	    string displname;
+	    
+	    Display * dpy = glXGetCurrentDisplay();
+	    GLXDrawable drawable = glXGetCurrentDrawable();
+	    GLXContext ctx = glXGetCurrentContext();
+	    GLXContextID xid = glXGetContextIDEXT (ctx);
+	    
+	    displname = XDisplayName (0);
+	    /*
+
+	    cout << "Init Parallel GL" << endl;
+	    cout << "DisplayName = " << displname << endl;
+	    cout << "current display = " << dpy << endl;
+	    cout << "current drawable = " << drawable << endl;                  
+	    cout << "current context = " << ctx << endl;                  
+	    
+	    cout << "contextid = " << xid << endl;
+	    cout << "isdirect = " << glXIsDirect ( dpy, ctx ) << endl;                  
+	    cout << "extensionstring = " << glXQueryExtensionsString( dpy, 0 ) << endl;
+	    */
+
+	    MyMPI_SendCmd ("redraw");
+	    MyMPI_SendCmd ("init");
+		
+	    for (int dest = 1; dest < ntasks; dest++)
+	      {
+		MyMPI_Send (displname, dest, MPI_TAG_VIS);
+		MyMPI_Send (int (drawable), dest, MPI_TAG_VIS);
+		MyMPI_Send (int (xid), dest, MPI_TAG_VIS);
+	      } 
+	  }
+      }
+  }
+
+
+  void VisualScene :: Broadcast ()
+  {
+    if (ntasks == 1) return;
+
+    if (id == 0)
+      {
+	/*
+	for (int dest = 1; dest < ntasks; dest++)
+	  {
+	    MyMPI_Send ("redraw", dest, MPI_TAG_CMD);
+	    MyMPI_Send ("broadcast", dest, MPI_TAG_VIS);
+	  }
+	*/
+
+	MyMPI_SendCmd ("redraw");
+	MyMPI_SendCmd ("broadcast");
+      }
+
+    MyMPI_Bcast (selface);
+
+    vssolution.Broadcast ();
+  }
+#endif 
+
+}
diff --git a/contrib/Netgen/libsrc/visualization/mvdraw.hpp b/contrib/Netgen/libsrc/visualization/mvdraw.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..211227cdec3e9ea1378053544c6e5dc65db37a58
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/mvdraw.hpp
@@ -0,0 +1,245 @@
+#ifndef FILE_MVDRAW
+#define FILE_MVDRAW
+
+
+namespace netgen
+{
+
+  /*
+  extern void InitDrawMesh ();
+  extern void DrawMesh ();
+  extern void MouseMove(int oldx, int oldy,
+                        int newx, int newy,
+                        char mode);
+
+  extern void Render ();
+  */
+
+
+  class VisualScene
+  {
+  protected:
+    static DLL_HEADER Point3d center;
+    static DLL_HEADER double rad;
+
+    static float lookatmat[16];
+    static float transmat[16];
+    static float rotmat[16];
+    static float centermat[16];
+    static DLL_HEADER float transformationmat[16];
+
+    GLdouble clipplane[4];
+
+    int changeval;
+    static DLL_HEADER GLdouble backcolor;
+
+    static int selface;
+    static int selelement;
+    static int selpoint;
+    static int selpoint2;
+    static int locpi;
+    static int seledge;
+
+    static int selecttimestamp;
+
+  public:
+
+    // static GLubyte * colortexture;
+    static GLuint coltexname;
+    static int ntexcols;
+    // static bool linear_colors;
+    int invcolor;
+
+
+  public:
+    DLL_HEADER VisualScene ();
+    DLL_HEADER virtual ~VisualScene();
+
+    DLL_HEADER virtual void BuildScene (int zoomall = 0);
+    DLL_HEADER virtual void DrawScene ();
+  
+    DLL_HEADER void CalcTransformationMatrices();
+    DLL_HEADER void StandardRotation (const char * dir);
+    DLL_HEADER void ArbitraryRotation (const Array<double> & alpha, const Array<Vec3d> & vec);
+    DLL_HEADER void ArbitraryRotation (const double alpha, const Vec3d & vec);
+
+    DLL_HEADER void MouseMove(int oldx, int oldy,
+                   int newx, int newy,
+                   char mode);
+
+    DLL_HEADER void LookAt (const Point<3> & cam, const Point<3> & obj,
+                 const Point<3> & camup);
+
+    DLL_HEADER void SetClippingPlane ();
+
+    DLL_HEADER virtual void MouseDblClick (int px, int py);
+
+    DLL_HEADER void SetLight ();
+    static void SetBackGroundColor (double col)
+    { backcolor = col; }
+
+    DLL_HEADER void CreateTexture (int ncols, int linear, int typ = GL_DECAL);
+    DLL_HEADER void DrawColorBar (double minval, double maxval, int logscale = 0, bool linear = 1);
+    DLL_HEADER void DrawCoordinateCross ();
+    DLL_HEADER void DrawNetgenLogo ();
+    DLL_HEADER void SetOpenGlColor(double val, double valmin, double valmax, int logscale = 0);
+
+
+#ifdef PARALLELGL
+    DLL_HEADER void InitParallelGL ();
+    DLL_HEADER void Broadcast ();
+#endif 
+  };
+
+
+  extern void MyOpenGLText (const char * text);
+
+
+
+
+
+
+
+
+
+
+
+  class VisualSceneSurfaceMeshing : public VisualScene
+  {
+  public:
+    VisualSceneSurfaceMeshing ();
+    virtual ~VisualSceneSurfaceMeshing ();
+
+    virtual void BuildScene (int zoomall = 0);
+    virtual void DrawScene ();
+  };
+
+
+
+
+
+
+
+  class VisualSceneMesh : public VisualScene
+  {
+    int filledlist;
+    int linelist;
+    int edgelist;
+    int pointnumberlist;
+
+    int tetlist;
+    int prismlist;
+    int pyramidlist;
+    int hexlist;
+
+    int badellist;
+    int identifiedlist;
+    int domainsurflist;
+
+    int vstimestamp;//, selecttimestamp;
+    int filledtimestamp;
+    int linetimestamp;
+    int edgetimestamp;
+    int pointnumbertimestamp;
+
+    int tettimestamp;
+    int prismtimestamp;
+    int pyramidtimestamp;
+    int hextimestamp;
+
+    int badeltimestamp;
+    int identifiedtimestamp;
+    int domainsurftimestamp;
+
+
+#ifdef PARALLELGL
+    Array<int> par_linelists;
+    Array<int> par_filledlists;
+#endif
+
+
+    NgLock *lock;
+
+    //  int selface, selelement;
+    //  int selpoint, selpoint2, locpi;
+    //  int seledge;
+
+    double minh, maxh; // for meshsize coloring
+
+  public:
+    VisualSceneMesh ();
+    virtual ~VisualSceneMesh ();
+
+    virtual void BuildScene (int zoomall = 0);
+    virtual void DrawScene ();
+    virtual void MouseDblClick (int px, int py);
+
+    int SelectedFace () const
+    { return selface; }
+    void SetSelectedFace (int asf);
+    //    { selface = asf; selecttimestamp = GetTimeStamp(); }
+
+    int SelectedEdge () const
+    { return seledge; }
+    int SelectedElement () const
+    { return selelement; }
+    int SelectedPoint () const
+    { return selpoint; }
+    void BuildFilledList (bool names);
+    // private:
+    void BuildLineList();
+    void BuildEdgeList();
+    void BuildPointNumberList();
+
+    void BuildTetList();
+    void BuildPrismList();
+    void BuildPyramidList();
+    void BuildHexList();
+
+    void BuildBadelList();
+    void BuildIdentifiedList();
+    void BuildDomainSurfList();
+  };
+
+
+
+  class VisualSceneSpecPoints : public VisualScene
+  {
+  public:
+    VisualSceneSpecPoints ();
+    virtual ~VisualSceneSpecPoints ();
+
+    virtual void BuildScene (int zoomall = 0);
+    virtual void DrawScene ();
+
+    double len;
+  };
+
+
+
+
+
+
+
+  // extern struct Tcl_Interp * hinterp;
+
+
+  extern void AddVisualizationScene (const string & name, 
+                                     VisualScene * vs);
+
+
+  void MouseDblClickSelect (const int px, const int py,
+                            const GLdouble * clipplane, const GLdouble backcolor,
+                            const float * transformationmat,
+                            const Point3d & center,
+                            const double rad,
+                            const int displaylist,
+                            int & selelement, int & selface, int & seledge, int & selpoint,
+                            int & selpoint2, int & locpi);
+
+
+}
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/visualization/soldata.hpp b/contrib/Netgen/libsrc/visualization/soldata.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..65d05ddafc15a79243f14c8a1202da5a0fed75ef
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/soldata.hpp
@@ -0,0 +1,111 @@
+#ifndef FILE_SOLDATA
+#define FILE_SOLDATA
+
+
+namespace netgen
+{
+
+  using namespace std;
+
+  class DLL_HEADER SolutionData
+  {
+  protected:
+
+    string name;
+    int components;
+    bool iscomplex;
+
+    int multidimcomponent;
+
+  public:
+    SolutionData (const string & aname, 
+                  int acomponents = 1, bool aiscomplex = 0)
+      : name(aname), components(acomponents), iscomplex(aiscomplex)
+    { ; }
+
+    virtual ~SolutionData ()
+    { ; }
+
+    int GetComponents() 
+    { 
+      return components; 
+    }
+
+    bool IsComplex() 
+    {
+      return iscomplex; 
+    }
+
+    virtual bool GetValue (int /* elnr */, 
+                           double /* lam1 */, double /* lam2 */, double /* lam3 */,
+                           double * /* values */) 
+    { 
+      return false; 
+    }
+
+    virtual bool GetValue (int selnr,
+                           const double xref[], const double x[], const double dxdxref[],
+                           double * values)
+    {
+      return GetValue (selnr, xref[0], xref[1], xref[2], values); 
+    }
+
+    virtual bool GetMultiValue (int elnr, int npts,
+				const double * xref, int sxref,
+				const double * x, int sx,
+				const double * dxdxref, int sdxdxref,
+				double * values, int svalues)
+    {
+      bool res = false;
+      for (int i = 0; i < npts; i++)
+	res = GetValue (elnr, &xref[i*sxref], &x[i*sx], &dxdxref[i*sdxdxref], &values[i*svalues]);
+      return res;
+    }
+
+
+
+    virtual bool GetSurfValue (int /* selnr */,
+                               double /* lam1 */, double /* lam2 */, 
+                               double * /* values */)
+    { 
+      return false; 
+    }
+
+
+    virtual bool GetSurfValue (int selnr,
+                               const double xref[], const double x[], const double dxdxref[],
+                               double * values)
+    { 
+      return GetSurfValue (selnr, xref[0], xref[1], values); 
+    }
+
+
+    virtual bool GetMultiSurfValue (int selnr, int npts,
+                                    const double * xref, int sxref,
+                                    const double * x, int sx,
+                                    const double * dxdxref, int sdxdxref,
+                                    double * values, int svalues)
+    {
+      bool res = false;
+      for (int i = 0; i < npts; i++)
+	res = GetSurfValue (selnr, &xref[i*sxref], &x[i*sx], &dxdxref[i*sdxdxref], &values[i*svalues]);
+      return res;
+    }
+
+
+    virtual int GetNumMultiDimComponents ()
+    {
+      return 1;
+    }
+
+    void SetMultiDimComponent (int mc)
+    { 
+      if (mc >= GetNumMultiDimComponents()) mc = GetNumMultiDimComponents()-1;
+      if (mc < 0) mc = 0;
+      multidimcomponent = mc; 
+    }
+  };
+}
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/visualization/stlmeshing.cpp b/contrib/Netgen/libsrc/visualization/stlmeshing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..452dbd928b1c62863fffb63cd3501953a0aee299
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/stlmeshing.cpp
@@ -0,0 +1,1076 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <stlgeom.hpp>
+
+#include <meshing.hpp>
+#ifndef NOTCL
+#include <visual.hpp>
+#endif
+
+namespace netgen
+{
+
+/*
+//mmm
+#include "stlgeom/modeller.hpp"
+*/
+
+/* *********************** Draw STL Geometry **************** */
+
+extern STLGeometry * stlgeometry;
+extern AutoPtr<Mesh> mesh;
+
+
+#ifdef OPENGL
+
+// #include "../../ngtcltk/mvdraw.hpp"
+
+
+VisualSceneSTLMeshing :: VisualSceneSTLMeshing ()
+  : VisualScene()
+{
+  selecttrig = 0;
+  nodeofseltrig = 1;
+  stlgeometry->SetSelectTrig(selecttrig);
+  stlgeometry->SetNodeOfSelTrig(nodeofseltrig);
+}
+
+VisualSceneSTLMeshing :: ~VisualSceneSTLMeshing ()
+{
+  ;
+}
+
+void VisualSceneSTLMeshing :: DrawScene ()
+{
+  int i, j, k;
+
+  if (changeval != stlgeometry->GetNT())
+    BuildScene();
+  changeval = stlgeometry->GetNT();
+
+  int colormeshsize = vispar.colormeshsize;
+  
+  double hmin = 0.0, hmax = 1.0;
+
+  if (colormeshsize)
+    {
+      hmax = -1E50;
+      hmin = +1E50;
+      double ms;
+
+      for (i = 1; i <= stlgeometry->GetNP(); i++)
+	{
+	  ms = mesh->GetH (stlgeometry->GetPoint(i));
+	  hmin = min2(hmin,ms);
+	  hmax = max2(hmax,ms);
+	}
+
+      //hmax = mparam.maxh;
+      //hmin = mesh->GetMinH (stlgeometry->GetBoundingBox().PMin(),
+      //			    stlgeometry->GetBoundingBox().PMax());
+  
+      if (hmin == 0) hmin = 0.1 * hmax;
+      //hmax *= 1.1;
+    }
+  
+
+
+  glClearColor(backcolor, backcolor, backcolor, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+
+  SetLight();
+
+  glPushMatrix();
+  glMultMatrixf (transformationmat);
+
+  SetClippingPlane ();
+
+  glShadeModel (GL_SMOOTH);
+  glDisable (GL_COLOR_MATERIAL);
+  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+  glEnable (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  float mat_spec_col[] = { 1, 1, 1, 1 };
+  glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_spec_col);
+
+  double shine = vispar.shininess;
+  // double transp = vispar.transp;
+
+  glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine);
+  glLogicOp (GL_COPY);
+
+  float mat_colred[]    = { 0.9f, 0.0f, 0.0f, 1.0f };
+  float mat_colgreen[]  = { 0.0f, 0.9f, 0.0f, 1.0f };
+  float mat_colblue[]   = { 0.1f, 0.1f, 1.0f, 1.0f };
+
+  float mat_colbluegreen[] = { 0.1f, 0.5f, 0.9f, 1.0f };
+  // float mat_colpink[]      = { 1.0f, 0.1f, 0.5f, 1.0f };
+  float mat_colviolet[]    = { 1.0f, 0.1f, 1.0f, 1.0f };
+  float mat_colbrown[]     = { 0.8f, 0.6f, 0.1f, 1.0f };
+  // float mat_colorange[]    = { 0.9f, 0.7f, 0.1f, 1.0f };
+  // float mat_colturquis[]   = { 0.0f, 1.0f, 0.8f, 1.0f };
+
+  float mat_colgrey[] = { 0.3f, 0.3f, 0.3f, 1.0f };
+
+  float mat_collred[]   = { 1.0f, 0.5f, 0.5f, 1.0f };
+  float mat_collgreen[] = { 0.2f, 1.9f, 0.2f, 1.0f };
+  float mat_collbrown[] = { 1.0f, 0.8f, 0.3f, 1.0f };
+
+  float mat_collgrey[] = { 0.8f, 0.8f, 0.8f, 1.0f };
+  // float mat_colmgrey[] = { 0.4f, 0.4f, 0.4f, 1.0f };
+
+  float mat_colstlbody[] = { 0.0f, 0.0f, 0.8f, 1.0f };
+  float mat_colseltrig[] = { 0.7f, 0.7f, 0.3f, 1.0f };
+  float mat_colseledge[] = { 0.7f, 0.7f, 1.0f, 1.0f };
+
+  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colblue);
+
+  float pgoff = 0.5f;
+
+  glPolygonOffset (pgoff*1, pgoff*1);
+  glEnable (GL_POLYGON_OFFSET_FILL);
+
+  glEnable (GL_NORMALIZE);
+
+  /*
+  {
+    //mmm
+    //test modeller
+    Modeller model;
+    
+    //MoZylinder z1(Point3d(0,0,0),Vec3d(100,0,0),20,0.01);
+    //model.Add(&z1);
+    //MoZylinder z2(Point3d(50,50,0),Vec3d(0,-100,0),20,0.01);
+    //model.Add(&z2);
+    
+    MoZylinder z1(Point3d(0,0,0),Vec3d(100,0,0),20,0.01);
+    MoZylinder z2(Point3d(50,50,0),Vec3d(0,-100,0),20,0.01);
+    MoCombine cb1(&z1,&z2);
+    model.Add(&cb1);
+    
+    Array<MoTriangle> trigs;
+    model.GetTriangles(trigs);
+    int i, k;
+    glBegin (GL_TRIANGLES);
+    for (i = 1; i <= trigs.Size(); i++)
+      {
+	const MoTriangle & tria = trigs.Get(i);
+	glNormal3f (tria.normal.X(),
+		    tria.normal.Y(),
+		    tria.normal.Z());
+	
+	for (k = 0; k < 3; k++)
+	  {
+	    glVertex3f (tria.pts[k].X(),
+			tria.pts[k].Y(),
+			tria.pts[k].Z());
+	  }
+      }    
+    glEnd ();
+    
+
+  }
+
+*/
+
+
+
+  
+  if (!stlgeometry->trigsconverted)
+    {
+      glBegin (GL_TRIANGLES);
+      for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	{
+	  /*
+	  if (j % 10 == seltria)
+	    glMaterialfv (GL_FRONT_AND_BACK, 
+			  GL_AMBIENT_AND_DIFFUSE, mat_colred);
+	  */
+
+	  const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	  glNormal3f (n.X(), n.Y(), n.Z());
+	  /*
+	  const STLReadTriangle & tria = stlgeometry -> GetReadTriangle(j);
+	  glNormal3f (tria.normal.X(),
+		      tria.normal.Y(),
+		      tria.normal.Z());
+	  */
+
+	  
+	  for (k = 1; k <= 3; k++)
+	    {
+	      const Point3d & tp = stlgeometry->GetPoint(stlgeometry->GetTriangle(j).PNum(k));
+	      glVertex3f (tp.X(), tp.Y(), tp.Z());
+
+	    }
+	  /*
+	  if (j%10 == seltria)
+	    glMaterialfv (GL_FRONT_AND_BACK, 
+			  GL_AMBIENT_AND_DIFFUSE, mat_colblue);
+	  */
+	}    
+      glEnd ();
+  
+      glDisable (GL_POLYGON_OFFSET_FILL);
+
+      int showtrias = vispar.stlshowtrias;
+
+      if (showtrias)
+	{
+	  float mat_coll[] = { 0.2f, 0.2f, 0.2f, 1.f };
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_coll);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+      
+	  glEnable (GL_NORMALIZE);
+      
+	  glBegin (GL_TRIANGLES);
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	      /*
+	      const STLReadTriangle & tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */  
+
+	      for (k = 1; k <= 3; k++)
+		{
+		  const Point3d & tp = 
+		    stlgeometry->GetPoint(stlgeometry->GetTriangle(j).PNum(k));
+		  glVertex3f (tp.X(), tp.Y(), tp.Z());
+		  
+		}
+	      
+	      /*
+	      for (k = 0; k < 3; k++)
+		{
+		  glVertex3f (tria.pts[k].X(),
+			      tria.pts[k].Y(),
+			      tria.pts[k].Z());
+		}
+	      */
+	    }    
+	  glEnd ();
+	}
+    }
+  else
+    {
+      int showfilledtrias = vispar.stlshowfilledtrias;
+
+      //(*mycout) << "in " << showfilledtrias << ", NT=" << stlgeometry -> GetNT() << endl;
+
+      int chartnumber;
+      if (vispar.stlshowmarktrias)
+	chartnumber = vispar.stlchartnumber + vispar.stlchartnumberoffset;
+      else
+	chartnumber = stlgeometry->GetMeshChartNr();
+
+      if (showfilledtrias)
+	{
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+	  if (colormeshsize)
+	    glEnable (GL_COLOR_MATERIAL);
+	  
+	  glPolygonOffset (pgoff*4, pgoff*4);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  glEnable (GL_NORMALIZE);
+
+
+	  glBegin (GL_TRIANGLES);
+
+	  int selt = stlgeometry -> GetSelectTrig();
+	  if (stldoctor.selectmode != 0) 
+	    {selt = 0; } //do not show selected triangle!!!!
+
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colstlbody);
+
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {
+	      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) {continue;}
+
+	      if (j == selt)
+		{
+		  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colseltrig);
+		}
+	      else if (j == selt+1)
+		{
+		  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colstlbody);
+		}
+	      
+	      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	  
+	      /*
+	      const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */
+	      for (k = 0; k < 3; k++)
+		{
+		  const Point3d & p = stlgeometry->GetPoint(st[k]);
+		  if (colormeshsize)
+		    {
+		      SetOpenGlColor (mesh->GetH (p), hmin, hmax, 1);
+		    }
+
+		  glVertex3f (p.X(), p.Y(), p.Z());
+		}
+	    } 
+   
+	  glEnd ();
+	}
+      
+      int foundseltrig = stlgeometry -> GetSelectTrig();
+      if (foundseltrig == 0 || foundseltrig > stlgeometry->GetNT() ||
+	  (stldoctor.showvicinity && !stlgeometry->Vicinity(foundseltrig)))
+	{foundseltrig = 0;}
+
+      if (foundseltrig)
+	{
+
+	  glPolygonOffset (pgoff*0, 0);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+
+	  //glDisable (GL_POLYGON_OFFSET_FILL);      
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colseledge);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+	  
+	  glEnable (GL_NORMALIZE);
+
+	  if (stldoctor.selectmode == 2)
+	    {
+	      //point
+	      const STLTriangle& st = stlgeometry -> GetTriangle(foundseltrig);
+	      const Point3d & p1 = stlgeometry->GetPoint(st[0]);
+	      const Point3d & p2 = stlgeometry->GetPoint(st[1]);
+	      const Point3d & p3 = stlgeometry->GetPoint(st[2]);
+
+	      double cs = (Dist(p1,p2)+Dist(p2,p3)+Dist(p3,p1))/100.;
+
+	      const Point3d & p = stlgeometry->GetPoint(st[nodeofseltrig-1]);
+	      
+	      glLineWidth (4);
+	      glBegin (GL_LINES);
+	      glVertex3f(p.X()+cs, p.Y()+cs, p.Z()+cs);
+	      glVertex3f(p.X()-cs, p.Y()-cs, p.Z()-cs);
+	      
+	      glVertex3f(p.X()-cs, p.Y()+cs, p.Z()+cs);
+	      glVertex3f(p.X()+cs, p.Y()-cs, p.Z()-cs);
+
+	      glVertex3f(p.X()-cs, p.Y()+cs, p.Z()+cs);
+	      glVertex3f(p.X()+cs, p.Y()-cs, p.Z()-cs);
+	      
+	      glVertex3f(p.X()+cs, p.Y()-cs, p.Z()+cs);
+	      glVertex3f(p.X()-cs, p.Y()+cs, p.Z()-cs);
+	      
+	      glEnd ();	  
+	      glLineWidth (1);
+	    }
+	  else if (stldoctor.selectmode == 1 || 
+		   stldoctor.selectmode == 3 || 
+		   stldoctor.selectmode == 4)
+	    {
+	      //multiedge
+	      
+	      const Array<twoint>& me = stlgeometry->SelectedMultiEdge();
+	      if (stlgeometry->GetSelectTrig() > 0 && 
+		  stlgeometry->GetSelectTrig() <= stlgeometry->GetNT() &&
+		  me.Size())
+		{
+
+		  int en = stlgeometry->EdgeDataList().GetEdgeNum(me.Get(1).i1,me.Get(1).i2);
+		  int status = stlgeometry->EdgeDataList().Get(en).GetStatus();
+		  
+		  switch (status)
+		    {
+		    case ED_CONFIRMED:
+		      glMaterialfv (GL_FRONT_AND_BACK, 
+				    GL_AMBIENT_AND_DIFFUSE, mat_collgreen);
+		      break;
+		    case ED_CANDIDATE:
+		      glMaterialfv (GL_FRONT_AND_BACK, 
+				    GL_AMBIENT_AND_DIFFUSE, mat_collbrown);
+		      break;
+		    case ED_EXCLUDED:
+		      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_collred);
+		      break;
+		    }
+
+		  glLineWidth (2);
+		  glBegin (GL_LINES);
+		  for (j = 1; j <= me.Size(); j++)
+		    { 
+		      Point3d p1 = stlgeometry->GetPoint(me.Get(j).i1);
+		      Point3d p2 = stlgeometry->GetPoint(me.Get(j).i2);
+		      
+		      glVertex3f(p1.X(), p1.Y(), p1.Z());
+		      glVertex3f(p2.X(), p2.Y(), p2.Z());
+		    }
+		  glEnd ();
+		  glLineWidth (1);
+		}
+	    }
+	}
+
+      int showmarktrias = vispar.stlshowmarktrias || vispar.stlshowactivechart;
+ 
+      if (stldoctor.showmarkedtrigs)
+	{
+	  //(*mycout) << "marked" << endl;
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); //GL_LINE
+	  glPolygonOffset (pgoff*1, pgoff*1);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbluegreen);
+	  glEnable (GL_NORMALIZE);
+
+	  glBegin (GL_TRIANGLES);
+
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {
+	      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) 
+		{continue;}
+
+	      if (!stlgeometry->IsMarkedTrig(j)) 
+		{continue;}
+	      
+	      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	      /*
+	      const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */
+	      for (k = 0; k < 3; k++)
+		{
+		  const Point3d & p = stlgeometry->GetPoint(st[k]);
+		  glVertex3f (p.X(), p.Y(), p.Z());
+		}
+	    }    
+	  glEnd ();
+
+	  //show OpenSegments on original geometry
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colviolet);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+	  glPolygonOffset (pgoff*1, 1);
+      
+	  glEnable (GL_NORMALIZE);
+      
+	  glBegin (GL_LINES);
+
+	  if (stlgeometry->GetNMarkedSegs())
+	    {
+	      Point<3> p1,p2;	      
+	      for (j = 1; j <= stlgeometry -> GetNMarkedSegs(); j++)
+		{
+		  stlgeometry->GetMarkedSeg(j,p1,p2);
+		  glVertex3dv(&p1(0));
+		  glVertex3dv(&p2(0));
+		}
+	    }
+	  glEnd ();
+	}
+
+
+      if (stldoctor.showfaces)
+	{
+	  int facenumber = vispar.stlchartnumber + vispar.stlchartnumberoffset;
+
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+	  glPolygonOffset (pgoff*3, 3);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_collgrey);
+	  glEnable (GL_NORMALIZE);
+
+	  glBegin (GL_TRIANGLES);
+
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {
+	      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) 
+		{continue;}
+
+	      //(*mycout) << " facenum = " << stlgeometry->GetTriangle(j).GetFaceNum() << " ";
+	      if (stlgeometry->GetTriangle(j).GetFaceNum() != facenumber) 
+		{continue;}
+	      
+	      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	      /*
+	      const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */
+	      for (k = 0; k < 3; k++)
+		{
+		  Point3d p = stlgeometry->GetPoint(st[k]);
+		  glVertex3f (p.X(), p.Y(), p.Z());
+		}
+	    }    
+	  glEnd ();
+	}
+
+      if (showmarktrias && stlgeometry->AtlasMade())
+	{
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+	  glPolygonOffset (pgoff*3, 3);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+
+	  glBegin (GL_TRIANGLES);
+	  
+	  if (chartnumber >= 1 && chartnumber <= stlgeometry->GetNOCharts())
+	    {
+	      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbrown);
+	      const STLChart& chart = stlgeometry->GetChart(chartnumber);
+	      for (j = 1; j <= chart.GetNChartT(); j++)
+		{
+		  /*
+		  if (j == charttrignumber) 
+		    {glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred);}
+		  else
+		    {glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbrown);}
+		  */
+		  const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetChartTrig(j));
+
+		  
+		  const Vec3d & n = stlgeometry->GetTriangle(chart.GetChartTrig(j)).Normal();
+		  glNormal3f (n.X(), n.Y(), n.Z());
+		  /*
+		  const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(chart.GetChartTrig(j));
+		  glNormal3f (tria.normal.X(),
+			      tria.normal.Y(),
+			      tria.normal.Z());
+		  */
+		  for (k = 0; k < 3; k++)
+		    {
+		      glVertex3f (stlgeometry->GetPoint(st[k])(0),
+				  stlgeometry->GetPoint(st[k])(1),
+				  stlgeometry->GetPoint(st[k])(2));
+		    }
+		}
+	      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+	      
+	      for (j = 1; j <= chart.GetNOuterT(); j++)
+		{
+		  
+		  const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetOuterTrig(j));
+
+		  const Vec3d & n = stlgeometry->GetTriangle(chart.GetOuterTrig(j)).Normal();
+		  glNormal3f (n.X(), n.Y(), n.Z());
+
+
+		  /*
+		  const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(chart.GetOuterTrig(j));
+		  glNormal3f (tria.normal.X(),
+			      tria.normal.Y(),
+			      tria.normal.Z());
+		  */
+		  for (k = 0; k < 3; k++)
+		    {
+		      glVertex3f (stlgeometry->GetPoint(st[k])(0),
+				  stlgeometry->GetPoint(st[k])(1),
+				  stlgeometry->GetPoint(st[k])(2));
+		    }
+		}
+	    }
+	  glEnd ();
+	}
+
+      int showtrias = vispar.stlshowtrias;
+
+      if (showtrias)
+	{
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgrey);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+	  glPolygonOffset (pgoff*2, 2);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  glEnable (GL_NORMALIZE);
+
+	  glBegin (GL_TRIANGLES);
+	  
+	  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+	    {	  
+	      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) {continue;}
+
+	      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+
+	      const Vec3d & n = stlgeometry->GetTriangle(j).Normal();
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	      /*
+	      const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+	      glNormal3f (tria.normal.X(),
+			  tria.normal.Y(),
+			  tria.normal.Z());
+	      */	  
+	      for (k = 0; k < 3; k++)
+		{
+		  glVertex3f (stlgeometry->GetPoint(st[k])(0),
+			      stlgeometry->GetPoint(st[k])(1),
+			      stlgeometry->GetPoint(st[k])(2));
+		}
+	    }    
+	  glEnd ();
+	} 
+
+      int showedges = vispar.stlshowedges;
+      
+      if (showedges)
+	{
+	  glPolygonOffset (pgoff*1, 1);
+	  glEnable (GL_POLYGON_OFFSET_FILL);
+	  //glDisable (GL_POLYGON_OFFSET_FILL);      
+
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+	  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+      
+	  glEnable (GL_NORMALIZE);
+      
+	  glBegin (GL_LINES);
+
+	  /*
+	  if (stldoctor.useexternaledges)
+	    {
+	      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colorange);
+	      for (j = 1; j <= stlgeometry -> NOExternalEdges(); j++)
+		{
+		  twoint v = stlgeometry->GetExternalEdge(j);
+		  Point3d p1 = stlgeometry->GetPoint(v.i1);
+		  Point3d p2 = stlgeometry->GetPoint(v.i2);
+		  
+		  Vec3d n1 = stlgeometry->GetNormal(v.i1);
+		  Vec3d n2 = stlgeometry->GetNormal(v.i2);
+		  
+		  glNormal3f(n1.X(), n1.Y(), n1.Z());
+		  glVertex3f(p1.X(), p1.Y(), p1.Z());
+		  glNormal3f(n2.X(), n2.Y(), n2.Z());
+		  glVertex3f(p2.X(), p2.Y(), p2.Z());
+		}
+	    }
+	  */
+
+	  
+	  if (!stlgeometry->meshlines.Size() || !stldoctor.drawmeshededges)
+	    {
+	      /*
+	      for (j = 1; j <= stlgeometry -> GetNE(); j++)
+		{
+		  STLEdge v = stlgeometry->GetEdge(j);
+		  Point3d p1 = stlgeometry->GetPoint(v.pts[0]);
+		  Point3d p2 = stlgeometry->GetPoint(v.pts[1]);
+		  
+		  Vec3d n1 = stlgeometry->GetNormal(v.pts[0]);
+		  Vec3d n2 = stlgeometry->GetNormal(v.pts[1]);
+		  
+		  glNormal3f(n1.X(), n1.Y(), n1.Z());
+		  glVertex3f(p1.X(), p1.Y(), p1.Z());
+		  glNormal3f(n2.X(), n2.Y(), n2.Z());
+		  glVertex3f(p2.X(), p2.Y(), p2.Z());
+		}
+	      */
+	      const STLEdgeDataList& ed = stlgeometry->EdgeDataList();
+	      for (i = 1; i <= ed.Size(); i++)
+		{
+		  if (ed.Get(i).GetStatus() != ED_UNDEFINED)
+		    {
+		      switch (ed.Get(i).GetStatus())
+			{
+			case ED_CONFIRMED:
+			  glMaterialfv (GL_FRONT_AND_BACK, 
+					GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+			  break;
+			case ED_CANDIDATE:
+			  glMaterialfv (GL_FRONT_AND_BACK, 
+					GL_AMBIENT_AND_DIFFUSE, mat_colbrown);
+			  break;
+			case ED_EXCLUDED:
+			  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred);
+			  break;
+			}
+
+		      if (ed.Get(i).GetStatus() == ED_EXCLUDED && !stldoctor.showexcluded) continue;
+
+		      Point3d p1 = stlgeometry->GetPoint(ed.Get(i).PNum(1));
+		      Point3d p2 = stlgeometry->GetPoint(ed.Get(i).PNum(2));
+		      glVertex3f(p1.X(), p1.Y(), p1.Z());
+		      glVertex3f(p2.X(), p2.Y(), p2.Z());		   
+		    }
+		}
+	    }
+
+	  /*
+	  else     
+	  if (stlgeometry->meshlines.Size() == 0)
+	    {
+	      for (j = 1; j <= stlgeometry->GetNLines(); j++)
+		{
+		  STLLine* line = stlgeometry->GetLine(j);
+		  int pn1, pn2;
+		  for (int k = 1; k <= line->NP()-1; k++)
+		    {
+		      pn1 = line->PNum(k);
+		      pn2 = line->PNum(k+1);
+
+		      Point3d p1 = stlgeometry->GetPoint(pn1);
+		      Point3d p2 = stlgeometry->GetPoint(pn2);
+		  
+		      Vec3d n1 = stlgeometry->GetNormal(pn1);
+		      Vec3d n2 = stlgeometry->GetNormal(pn2);
+		  
+		      glNormal3f(n1.X(), n1.Y(), n1.Z());
+		      glVertex3f(p1.X(), p1.Y(), p1.Z());
+		      glNormal3f(n2.X(), n2.Y(), n2.Z());
+		      glVertex3f(p2.X(), p2.Y(), p2.Z());
+		    }
+		}    
+	    }
+	  */
+	    
+	  else if (stlgeometry->meshlines.Size() != 0)
+	    {
+	      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+	      for (j = 1; j <= stlgeometry->meshlines.Size(); j++)
+		{
+		  STLLine* line = stlgeometry->meshlines.Get(j);
+		  int pn1, pn2;
+		  for (int k = 1; k <= line->NP()-1; k++)
+		    {
+		      pn1 = line->PNum(k);
+		      pn2 = line->PNum(k+1);
+
+		      Point3d p1 = stlgeometry->meshpoints.Get(pn1);
+		      Point3d p2 = stlgeometry->meshpoints.Get(pn2);
+		  		  
+		      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen);
+		      glVertex3f(p1.X(), p1.Y(), p1.Z());
+		      glVertex3f(p2.X(), p2.Y(), p2.Z());
+
+		      
+		      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred);
+		      double cs = 0.02*Dist(p1,p2);
+		      glVertex3f(p1.X()+cs, p1.Y()+cs, p1.Z()+cs);
+		      glVertex3f(p1.X()-cs, p1.Y()-cs, p1.Z()-cs);
+		      glVertex3f(p2.X()+cs, p2.Y()+cs, p2.Z()+cs);
+		      glVertex3f(p2.X()-cs, p2.Y()-cs, p2.Z()-cs);
+
+		      glVertex3f(p1.X()-cs, p1.Y()+cs, p1.Z()+cs);
+		      glVertex3f(p1.X()+cs, p1.Y()-cs, p1.Z()-cs);
+		      glVertex3f(p2.X()-cs, p2.Y()+cs, p2.Z()+cs);
+		      glVertex3f(p2.X()+cs, p2.Y()-cs, p2.Z()-cs);
+		      
+		    }
+		}
+	    }
+	    
+
+	  glEnd ();
+	}
+
+      if (stldoctor.showedgecornerpoints && stlgeometry->LineEndPointsSet())
+	{
+	  glPointSize (5);
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred);
+	  glBegin (GL_POINTS);
+	  for (i = 1; i <= stlgeometry->GetNP(); i++)
+	    {
+	      if (stlgeometry->IsLineEndPoint(i))
+		{
+		  const Point3d p = stlgeometry->GetPoint(i);
+		  glVertex3f (p.X(), p.Y(), p.Z());
+		}
+	    }
+	  glEnd();
+	  
+	}
+
+
+    }
+
+ 
+  glPopMatrix();
+
+  if (vispar.colormeshsize)
+    DrawColorBar (hmin, hmax, 1);
+
+  glFinish();  
+}
+
+
+void VisualSceneSTLMeshing :: BuildScene (int zoomall)
+{
+  if (selecttrig && zoomall == 2)
+    center = stlgeometry -> GetPoint ( stlgeometry->GetTriangle(selecttrig).PNum(nodeofseltrig));
+  else
+    center = stlgeometry -> GetBoundingBox().Center();
+
+  rad = stlgeometry -> GetBoundingBox().Diam() / 2;
+
+  CalcTransformationMatrices();
+}
+
+
+
+void VisualSceneSTLMeshing :: MouseDblClick (int px, int py)
+{
+  //  (*mycout) << "dblclick: " << px << " - " << py << endl;
+  
+
+  int i, j, k, hits;
+
+  // select surface triangle by mouse click
+
+  GLuint selbuf[10000];
+  glSelectBuffer (10000, selbuf);
+
+
+  glRenderMode (GL_SELECT);
+
+  GLint viewport[4];
+  glGetIntegerv (GL_VIEWPORT, viewport);
+
+  /*  
+  (*mycout) << "viewport = " << viewport[0] << " " 
+       << viewport[1] << " " << viewport[2] << " " << viewport[3] << endl;
+  */
+
+  glMatrixMode (GL_PROJECTION); 
+  glPushMatrix();
+
+
+  GLdouble projmat[16];
+  glGetDoublev (GL_PROJECTION_MATRIX, projmat);
+
+  glLoadIdentity(); 
+  gluPickMatrix (px, viewport[3] - py, 1, 1, viewport); 
+  glMultMatrixd (projmat);
+  
+
+
+  glClearColor(backcolor, backcolor, backcolor, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glMatrixMode (GL_MODELVIEW); 
+
+  glPushMatrix();
+  glMultMatrixf (transformationmat);
+
+
+  glInitNames();
+  glPushName (1);
+
+
+  glEnable (GL_POLYGON_OFFSET_FILL);
+  for (j = 1; j <= stlgeometry -> GetNT(); j++)
+    {
+      if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) {continue;}
+
+      const STLTriangle& st = stlgeometry -> GetTriangle(j);
+			
+      //const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j);
+      //glNormal3f (tria.normal.X(), tria.normal.Y(), tria.normal.Z());
+      
+      if (stldoctor.selectmode == 0)
+	{
+	  glLoadName (j);
+	  glBegin (GL_TRIANGLES);
+	  for (k = 0; k < 3; k++)
+	    {
+	      Point3d p = stlgeometry->GetPoint(st[k]);
+	      glVertex3f (p.X(), p.Y(), p.Z());
+	    }
+	  glEnd ();
+	} 
+      else if (stldoctor.selectmode == 1 || stldoctor.selectmode == 3
+	        || stldoctor.selectmode == 4)
+	{
+	  Point3d pm = Center(stlgeometry->GetPoint(st[0]),
+			      stlgeometry->GetPoint(st[1]),
+			      stlgeometry->GetPoint(st[2]));
+
+	  for (k = 0; k < 3; k++)
+	    {
+	      glLoadName (j*3+k-2);
+	      glBegin (GL_TRIANGLES);
+
+	      Point3d p1 = stlgeometry->GetPoint(st[k]);
+	      Point3d p2 = stlgeometry->GetPoint(st[(k+1)%3]);
+	      glVertex3f (p1.X(), p1.Y(), p1.Z());
+	      glVertex3f (p2.X(), p2.Y(), p2.Z());
+	      glVertex3f (pm.X(), pm.Y(), pm.Z());
+
+	      glEnd ();
+	    }
+	}
+      else
+	{
+	  Point3d pm1 = Center(stlgeometry->GetPoint(st[0]),
+			       stlgeometry->GetPoint(st[1]));
+	  Point3d pm2 = Center(stlgeometry->GetPoint(st[1]),
+			       stlgeometry->GetPoint(st[2]));
+	  Point3d pm3 = Center(stlgeometry->GetPoint(st[2]),
+			       stlgeometry->GetPoint(st[0]));
+
+	  Point3d p1 = stlgeometry->GetPoint(st[0]);
+	  Point3d p2 = stlgeometry->GetPoint(st[1]);
+	  Point3d p3 = stlgeometry->GetPoint(st[2]);
+
+	  glLoadName (j*4-3);
+	  glBegin (GL_TRIANGLES);
+	  glVertex3f (p1.X(), p1.Y(), p1.Z());
+	  glVertex3f (pm1.X(), pm1.Y(), pm1.Z());
+	  glVertex3f (pm3.X(), pm3.Y(), pm3.Z());
+	  glEnd ();
+
+	  glLoadName (j*4-2);
+	  glBegin (GL_TRIANGLES);
+	  glVertex3f (p2.X(), p2.Y(), p2.Z());
+	  glVertex3f (pm2.X(), pm2.Y(), pm2.Z());
+	  glVertex3f (pm1.X(), pm1.Y(), pm1.Z());
+	  glEnd ();
+
+	  glLoadName (j*4-1);
+	  glBegin (GL_TRIANGLES);
+	  glVertex3f (p3.X(), p3.Y(), p3.Z());
+	  glVertex3f (pm3.X(), pm3.Y(), pm3.Z());
+	  glVertex3f (pm2.X(), pm2.Y(), pm2.Z());
+	  glEnd ();
+
+	  glLoadName (j*4);
+	  glBegin (GL_TRIANGLES);
+	  glVertex3f (pm1.X(), pm1.Y(), pm1.Z());
+	  glVertex3f (pm2.X(), pm2.Y(), pm2.Z());
+	  glVertex3f (pm3.X(), pm3.Y(), pm3.Z());
+	  glEnd ();
+	}
+    }    
+
+  glPopName();
+
+  glMatrixMode (GL_PROJECTION); 
+  glPopMatrix();
+
+  glMatrixMode (GL_MODELVIEW); 
+  glPopMatrix();
+
+  glFlush();  
+
+	
+  hits = glRenderMode (GL_RENDER);
+
+  //  (*mycout) << "hits = " << hits << endl;
+
+  //int minrec = -1;
+  int minname = 0;
+  GLuint mindepth = 0;
+  for (i = 0; i < hits; i++)
+    {
+      int curname = selbuf[4*i+3];
+      GLuint curdepth = selbuf[4*i+1];
+
+      /*      
+      (*mycout) << selbuf[4*i] << " " << selbuf[4*i+1] << " " 
+	   << selbuf[4*i+2] << " " << selbuf[4*i+3] << endl;
+      */
+      if (curname &&
+	  (curdepth < mindepth || !minname))
+	{
+	  //minrec = i;
+	  mindepth = curdepth;
+	  minname = curname;
+	}
+    }
+
+  if (!minname) {return;}
+  
+  if (stldoctor.selectmode == 0)
+    {
+      int oldtrig = selecttrig;
+      selecttrig = minname;
+      if (selecttrig == oldtrig)
+	nodeofseltrig = (nodeofseltrig % 3) + 1;
+      else
+	nodeofseltrig = 1;
+
+      stlgeometry->SetSelectTrig(selecttrig);
+      stlgeometry->SetNodeOfSelTrig(nodeofseltrig);
+      stlgeometry->PrintSelectInfo();
+      
+    }
+  else if (stldoctor.selectmode == 1 || stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+    {
+      selecttrig = (minname-1) / 3 + 1;
+      nodeofseltrig = minname-selecttrig*3+3;
+
+      stlgeometry->SetSelectTrig(selecttrig);
+      stlgeometry->SetNodeOfSelTrig(nodeofseltrig);
+      stlgeometry->PrintSelectInfo();
+
+      if (stldoctor.selectmode == 1)
+	{
+	  stlgeometry->BuildSelectedEdge(twoint(stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig),
+						stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig+1)));
+	}
+      if (stldoctor.selectmode == 3)
+	{
+	  stlgeometry->BuildSelectedMultiEdge(twoint(stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig),
+						     stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig+1)));
+	}
+      else if (stldoctor.selectmode == 4)
+	{
+	  stlgeometry->BuildSelectedCluster(twoint(stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig),
+						   stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig+1)));
+	}
+ 
+      switch (stldoctor.edgeselectmode)
+	{
+	case 1: stlgeometry->STLDoctorUndefinedEdge(); break;
+	case 2: stlgeometry->STLDoctorConfirmEdge(); break;
+	case 3: stlgeometry->STLDoctorCandidateEdge(); break;
+	case 4: stlgeometry->STLDoctorExcludeEdge(); break;
+	default: break;
+	}
+    }
+  else if (stldoctor.selectmode == 2)
+    {
+      selecttrig = (minname-1) / 4 + 1;
+      nodeofseltrig = minname-selecttrig*4+4;
+      if (nodeofseltrig == 4) {nodeofseltrig = 1;}
+
+      stlgeometry->SetSelectTrig(selecttrig);
+      stlgeometry->SetNodeOfSelTrig(nodeofseltrig);
+      stlgeometry->PrintSelectInfo();
+
+    }
+
+  if (stldoctor.showtouchedtrigchart && stlgeometry->AtlasMade() && stlgeometry->GetSelectTrig())
+    {
+      vispar.stlchartnumber =  stlgeometry->GetChartNr(stlgeometry->GetSelectTrig());
+      vispar.stlchartnumberoffset = 0;
+    }
+  
+}
+
+
+
+
+VisualSceneSTLMeshing vsstlmeshing;
+
+#endif
+
+
+
+ 
+}
diff --git a/contrib/Netgen/libsrc/visualization/vispar.hpp b/contrib/Netgen/libsrc/visualization/vispar.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..da139826330905f024f2acc3d9839fb00608222d
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vispar.hpp
@@ -0,0 +1,106 @@
+#ifndef FILE_VISPAR
+#define FILE_VISPAR
+
+namespace netgen
+{
+
+class VisualizationParameters
+{
+public:
+  double lightamb;
+  double lightdiff;
+  double lightspec;
+  double shininess;
+  double transp;
+  int locviewer;
+  char selectvisual[20];
+  int showstltrias;
+  
+  Vec3d clipnormal;
+  double clipdist;
+  int clipenable;
+  int clipplanetimestamp;
+
+  int colormeshsize;
+
+  int drawfilledtrigs;
+  int drawbadels;
+  int drawoutline;
+  int drawedges;
+  int subdivisions;
+
+  int drawprisms;
+  int drawpyramids;
+  int drawhexes;
+  double shrink;
+  int drawidentified;
+  int drawpointnumbers;
+  int drawedgenumbers;
+  int drawfacenumbers;
+  int drawelementnumbers;
+  int drawdomainsurf;
+  int drawtets;
+  int drawtetsdomain;
+
+  int clipdomain;
+  int donotclipdomain;
+
+  int drawededges;
+  int drawedpoints;
+  int drawedpointnrs;
+  int drawedtangents;
+  int drawededgenrs;
+  int drawmetispartition;
+
+  int drawcurveproj;
+  int drawcurveprojedge;
+  
+
+  int centerpoint;
+  int drawelement;
+
+  // stl:
+  int stlshowtrias;
+  int stlshowfilledtrias;
+  int stlshowedges;
+  int stlshowmarktrias;
+  int stlshowactivechart;
+  int stlchartnumber;
+  int stlchartnumberoffset;
+
+  // occ:
+  int occshowvolumenr;
+  bool occshowsurfaces;
+  bool occshowedges;
+  bool occvisproblemfaces;
+  bool occzoomtohighlightedentity;
+  double occdeflection;
+
+  // ACIS
+
+  bool ACISshowfaces;
+  bool ACISshowedges;
+  int ACISshowsolidnr;
+  int ACISshowsolidnr2;
+
+  bool whitebackground;
+  int stereo;
+  bool usedispllists;
+  bool drawcoordinatecross;
+  bool drawcolorbar;
+  bool drawnetgenlogo;
+
+  bool use_center_coords;
+  double centerx,centery,centerz;
+
+  bool drawspecpoint;
+  double specpointx,specpointy,specpointz;
+
+  
+public:
+  VisualizationParameters();
+};
+extern VisualizationParameters vispar;
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/visualization/visual.hpp b/contrib/Netgen/libsrc/visualization/visual.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..405c5b6be3b3982505eb0e4ba189bb93f3dfaba9
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/visual.hpp
@@ -0,0 +1,35 @@
+#ifndef FILE_VISUAL
+#define FILE_VISUAL
+
+/* *************************************************************************/
+/* File:   visual.hpp                                                       */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   02. Dec. 01                                                     */
+/* *************************************************************************/
+
+/* 
+
+Visualization
+
+*/
+
+#ifdef PARALLEL
+#define PARALLELGL
+#endif
+
+#include "../include/incvis.hpp"
+
+#include "vispar.hpp"
+#include "mvdraw.hpp"
+#include "soldata.hpp"
+
+#include <complex>
+
+namespace netgen
+{
+#include "vssolution.hpp"
+#include "meshdoc.hpp"
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/visualization/vscsg.cpp b/contrib/Netgen/libsrc/visualization/vscsg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f4ef67d7c5e235687f25fc27eee70588ab756e50
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vscsg.cpp
@@ -0,0 +1,272 @@
+#ifndef NOTCL
+
+#include <mystdlib.h>
+#include "incvis.hpp"
+
+#include <myadt.hpp>
+#include <meshing.hpp>
+#include <csg.hpp>
+#include <stlgeom.hpp>
+
+#include <visual.hpp>
+
+
+namespace netgen
+{
+
+/* *********************** Draw Geometry **************** */
+
+  extern Array<Point<3> > project1, project2;
+
+
+extern AutoPtr<CSGeometry> geometry;
+
+
+VisualSceneGeometry :: VisualSceneGeometry ()
+  : VisualScene()
+{
+  selsurf = 0;
+}
+
+VisualSceneGeometry :: ~VisualSceneGeometry ()
+{
+  ;
+}
+
+void VisualSceneGeometry :: SelectSurface (int aselsurf)
+{
+  selsurf = aselsurf;
+  DrawScene();
+}
+
+
+void VisualSceneGeometry :: DrawScene ()
+{
+  int i;
+
+  if (changeval != geometry->GetChangeVal())
+    BuildScene();
+  changeval = geometry->GetChangeVal();
+
+  glClearColor(backcolor, backcolor, backcolor, 1.0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  
+  SetLight();
+
+
+  glPushMatrix();
+  glMultMatrixf (transformationmat);
+
+  SetClippingPlane ();
+
+  glShadeModel (GL_SMOOTH);
+  glDisable (GL_COLOR_MATERIAL);
+  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+  glEnable (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  /*
+  float mat_spec_col[] = { 1, 1, 1, 1 };
+  glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_spec_col);
+  */
+
+  double shine = vispar.shininess;
+  double transp = vispar.transp;
+
+
+  glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine);
+  glLogicOp (GL_COPY);
+  
+  glEnable (GL_NORMALIZE);
+
+  for (i = 0; i < geometry->GetNTopLevelObjects(); i++)
+    {
+      const TopLevelObject * tlo = geometry -> GetTopLevelObject (i);
+      if (tlo->GetVisible() && !tlo->GetTransparent())
+	{
+	  float mat_col[] = { tlo->GetRed(), tlo->GetGreen(), tlo->GetBlue(), 1 };
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+	  
+	  glCallList (trilists[i]);
+	}
+    }
+
+
+  glPolygonOffset (1, 1);
+  glEnable (GL_POLYGON_OFFSET_FILL);
+
+  glLogicOp (GL_NOOP);
+  for (i = 0; i < geometry->GetNTopLevelObjects(); i++)
+    {
+      const TopLevelObject * tlo = geometry -> GetTopLevelObject (i);
+      if (tlo->GetVisible() && tlo->GetTransparent())
+	{
+	  float mat_col[] = { tlo->GetRed(), tlo->GetGreen(), tlo->GetBlue(), transp };
+
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+	  
+	  glCallList (trilists[i]);
+	}
+    }
+
+  glDisable (GL_POLYGON_OFFSET_FILL);
+
+  /*
+  cout << "draw " << project1.Size() << " lines " << endl;
+  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+  glLineWidth (1.0f);
+  glEnable (GL_COLOR_MATERIAL);
+
+  glColor3f (1.0f, 0.0f, 0.0f);
+
+  glBegin (GL_LINES);
+  for (int i = 0; i < project1.Size(); i++)
+    {
+      glVertex3dv (project1[i]);
+      glVertex3dv (project2[i]);
+    }
+  glEnd();
+  */
+
+
+  glPopMatrix();
+  glDisable(GL_CLIP_PLANE0);
+ 
+
+
+  /*
+  glFlush();
+  
+  int err;
+  do
+    {
+      err = glGetError();
+      // cout << "glerr,1 = " << err << endl;
+    }
+  while (err != GL_NO_ERROR);
+
+
+  // CreateTexture (0, 1, GL_DECAL);
+  CreateTexture (0, 1, GL_MODULATE);
+  glEnable (GL_TEXTURE_1D);
+
+  float mat_col[] = { 1.0, 1.0, 1.0 }; 
+  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+  glDisable (GL_BLEND);
+  glDisable (GL_COLOR_MATERIAL);
+  glEnable (GL_NORMALIZE);
+
+  if (geometry->GetNTopLevelObjects())
+    {
+      cout << "call list" << endl;
+      glCallList (trilists[0]);
+    }
+
+  glColor3d (1.0, 1.0, 1.0);
+  
+  glBegin (GL_TRIANGLES);
+  glNormal3f (0, 0, 1);
+  SetOpenGlColor  (-1.0, 0, 1, 0);
+  glVertex3f (0.0, 0.0, 0.0);
+  SetOpenGlColor  (0.5, 0, 1, 0);
+  glNormal3f (0, 0, 1);
+  glVertex3f (1.0, 0.0, 0.0);
+  SetOpenGlColor  (2.0, 0, 1, 0);
+  glNormal3f (0, 0, 1);
+  glVertex3f (0.0, 1.0, 0.0);
+
+  glEnd ();
+
+  cout << "trig drawn" << endl;
+
+  glDisable (GL_TEXTURE_1D);
+  glDisable (GL_COLOR_MATERIAL);
+  glFlush();
+
+  cout << "glerr,2 = " << glGetError() << endl;
+  */
+
+
+
+  DrawCoordinateCross ();
+  DrawNetgenLogo ();  
+
+  glFinish();  
+}
+
+
+void VisualSceneGeometry :: BuildScene (int zoomall)
+{
+  Box<3> box;
+  int hasp = 0;
+  for (int i = 0; i < geometry->GetNTopLevelObjects(); i++)
+    {
+      const TriangleApproximation & ta =
+	*geometry->GetTriApprox(i);
+      if (!&ta) continue;
+
+      for (int j = 0; j < ta.GetNP(); j++)      
+	{
+	  if (hasp)
+	    box.Add (ta.GetPoint(j));
+	  else
+	    {
+	      hasp = 1;
+	      box.Set (ta.GetPoint(j));
+	    }
+	}
+    }
+  if (hasp)
+    {
+      center = box.Center();
+      rad = box.Diam() / 2;
+    }
+  else
+    {
+      center = Point3d(0,0,0);
+      rad = 1;
+    }
+
+  CalcTransformationMatrices();
+
+  for (int i = 0; i < trilists.Size(); i++)
+    glDeleteLists (trilists[i], 1);
+  trilists.SetSize(0);
+
+  for (int i = 0; i < geometry->GetNTopLevelObjects(); i++)
+    {
+      trilists.Append (glGenLists (1));
+      glNewList (trilists.Last(), GL_COMPILE);
+
+      glEnable (GL_NORMALIZE);
+      const TriangleApproximation & ta =
+	*geometry->GetTriApprox(i);
+      if (&ta) 
+	{
+	  glBegin (GL_TRIANGLES);
+	  for (int j = 0; j < ta.GetNT(); j++)
+	    {
+	      for (int k = 0; k < 3; k++)
+		{
+		  int pi = ta.GetTriangle(j)[k];
+		  glNormal3dv (ta.GetNormal (pi));
+		  glVertex3dv (ta.GetPoint(pi));
+		}
+	    }
+	  glEnd ();
+	}
+      glEndList ();
+    }
+
+}
+
+
+
+
+
+}
+
+
+#endif // NOTCL
diff --git a/contrib/Netgen/libsrc/visualization/vsfieldlines.cpp b/contrib/Netgen/libsrc/visualization/vsfieldlines.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e7201a483dad8bc527fc94a47352e23ec8d21414
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vsfieldlines.cpp
@@ -0,0 +1,729 @@
+#ifndef NOTCL
+
+#include <mystdlib.h>
+#include "incvis.hpp"
+
+
+#include <myadt.hpp>
+#include <meshing.hpp>
+#include <csg.hpp>
+#include <stlgeom.hpp>
+
+#include <visual.hpp>
+
+
+namespace netgen
+{
+
+  extern AutoPtr<Mesh> mesh;
+
+
+  
+  RKStepper :: ~RKStepper() 
+  {
+    delete a;
+  }
+    
+  RKStepper :: RKStepper(int type) : a(NULL), tolerance(1e100)
+  {
+    notrestarted = 0;
+
+    if (type == 0) // explicit Euler
+      {
+	c.SetSize(1); c[0] = 0;
+	b.SetSize(1); b[0] = 1;
+	steps = order = 1;
+      }
+    else if (type == 1) // Euler-Cauchy
+      {
+	c.SetSize(2); c[0] = 0; c[1] = 0.5;
+	b.SetSize(2); b[0] = 0; b[1] = 1;
+	Array<int> size(2);
+	size[0] = 0; size[1] = 1;
+	a = new TABLE<double>(size);
+	a->Set(2,1,0.5);  // Set, Get: 1-based!
+	steps = order = 2;
+      }
+    else if (type == 2) // Simpson
+      {
+	c.SetSize(3); c[0] = 0; c[1] = 1; c[2] = 0.5;
+	b.SetSize(3); b[0] = b[1] = 1./6.; b[2] = 2./3.;
+	Array<int> size(3);
+	size[0] = 0; size[1] = 1; size[2] = 2;
+	a = new TABLE<double>(size);
+	a->Set(2,1,1);
+	a->Set(3,1,0.25); a->Set(3,2,0.25); 
+	steps = order = 3;
+      }
+    else if (type == 3) // classical Runge-Kutta
+      {
+	c.SetSize(4); c[0] = 0; c[1] = c[2] = 0.5; c[3] = 1;
+	b.SetSize(4); b[0] = b[3] = 1./6.; b[1] = b[2] = 1./3.;
+	Array<int> size(4);
+	size[0] = 0; size[1] = 1; size[2] = 2; size[3] = 3;
+	a = new TABLE<double>(size);
+	a->Set(2,1,0.5);
+	a->Set(3,1,0); a->Set(3,2,0.5); 
+	a->Set(4,1,0); a->Set(4,2,0); a->Set(4,3,1); 
+	steps = order = 4;
+      }
+    
+    K.SetSize(steps);
+  }
+
+  void RKStepper :: StartNextValCalc(const Point3d & astartval, const double astartt, const double ah, const bool aadaptive)
+  {
+    //cout << "Starting RK-Step with h=" << ah << endl;
+
+    stepcount = 0;
+    h = ah;
+    startt = astartt;
+    startval = astartval;
+    adaptive = aadaptive;
+    adrun = 0;
+  }
+
+  bool RKStepper :: GetNextData(Point3d & val, double & t, double & ah)
+  {
+    bool finished(false);
+    
+    
+    //cout << "stepcount " << stepcount << endl;
+    
+    if(stepcount <= steps)
+      {
+	t = startt + c[stepcount-1]*h;
+	val = startval;
+	for(int i=0; i<stepcount-1; i++)
+	  val += h * a->Get(stepcount,i+1) * K[i];
+      }
+    
+    
+    if(stepcount == steps)
+      {
+	val = startval;
+	for(int i=0; i<steps; i++)
+	  val += h * b[i] * K[i];
+	
+	if(adaptive)
+	  {
+	    if(adrun == 0)
+	      {
+		stepcount = 0;
+		h *= 0.5;
+		adrun = 1;
+		valh = val;
+	      }
+	    else if (adrun == 1)
+	      {
+		stepcount = 0;
+		startval_bak = startval;
+		startval = val;
+		startt_bak = startt;
+		startt += h;//0.5*h;
+		adrun = 2;
+	      }
+	    else if (adrun == 2)
+	      {
+		Point3d valh2 = val;
+		val = valh2 + 1./(pow(2.,order)-1.) * (valh2 - valh);
+		Vec3d errvec = val - valh;
+		
+		double err = errvec.Length();
+		
+		double fac = 0.7 * pow(tolerance/err,1./(order+1.));
+		if(fac > 1.3) fac = 1.3;
+		
+		if(fac < 1 || notrestarted >= 2)
+		  ah = 2.*h * fac;
+		
+		if(err < tolerance) 
+		  {
+		    finished = true;
+		    notrestarted++;
+		    //(*testout) << "finished RK-Step, new h=" << ah << " tolerance " << tolerance << " err " << err << endl;
+		  }
+		else
+		  {
+		    //ah *= 0.9;
+		    notrestarted = 0;
+		    //(*testout) << "restarting h " << 2.*h << " ah " << ah << " tolerance " << tolerance << " err " << err << endl;
+		    StartNextValCalc(startval_bak,startt_bak, ah, adaptive);
+		  }
+	      }
+	  }
+	else 
+	  {
+	    t = startt + h;
+	    finished = true;
+	  }
+	
+      }
+    
+    if(stepcount == 0)
+      {
+	t = startt + c[stepcount]*h;
+	val = startval;
+	for(int i=0; i<stepcount; i++)
+	  val += h * a->Get(stepcount,i) * K[i];
+      }
+    
+    return finished;
+  }
+
+
+  bool RKStepper :: FeedNextF(const Vec3d & f)
+  {
+    K[stepcount] = f;
+    stepcount++;
+    return true;
+  }
+  
+
+
+  void FieldLineCalc :: GenerateFieldLines(Array<Point3d> & potential_startpoints, const int numlines, const int gllist,
+					   const double minval, const double maxval, const int logscale, double phaser, double phasei)
+  {
+
+    
+    Array<Point3d> points;
+    Array<double> values;
+    Array<bool> drawelems;
+    Array<int> dirstart;
+
+
+    if(vsol -> iscomplex)
+      SetPhase(phaser,phasei);
+
+    double crit = 1.0;
+
+    if(randomized)
+      {
+	double sum = 0;
+	double lami[3];
+	double values[6];
+	Vec3d v;
+	
+	for(int i=0; i<potential_startpoints.Size(); i++)
+	  {
+	    int elnr = mesh.GetElementOfPoint(potential_startpoints[i],lami,true) - 1;
+	    if(elnr == -1)
+	      continue;
+
+	    mesh.SetPointSearchStartElement(elnr);
+	    
+	    if (mesh.GetDimension()==3)
+              vss.GetValues ( vsol, elnr, lami[0], lami[1], lami[2], values);
+            else
+              vss.GetSurfValues ( vsol, elnr, lami[0], lami[1], values);
+              
+	    
+	    VisualSceneSolution::RealVec3d ( values, v, vsol->iscomplex, phaser, phasei);
+	    
+	    sum += v.Length();
+	  }
+
+	crit = sum/double(numlines);
+      }
+
+
+    int calculated = 0;
+
+    cout << endl;
+
+
+
+
+    for(int i=0; i<potential_startpoints.Size(); i++)
+      {
+	cout << "\rFieldline Calculation " << int(100.*i/potential_startpoints.Size()) << "%"; cout.flush();
+
+	if(randomized)
+	  SetCriticalValue((double(rand())/RAND_MAX)*crit);
+
+	if(calculated >= numlines) break;
+
+	Calc(potential_startpoints[i],points,values,drawelems,dirstart);
+
+	bool usable = false;
+
+	for(int j=1; j<dirstart.Size(); j++)
+	  for(int k=dirstart[j-1]; k<dirstart[j]-1; k++)
+	    {
+	      if(!drawelems[k] || !drawelems[k+1]) continue;
+	     
+	      usable = true;
+ 
+	      // vss.SetOpenGlColor  (0.5*(values[k]+values[k+1]), minval, maxval, logscale);
+              
+              /*
+              if (vss.usetexture == 1)
+                glTexCoord1f ( 0.5*(values[k]+values[k+1]) );
+              else
+              */
+              vss.SetOpenGlColor  (0.5*(values[k]+values[k+1]) );
+	      vss.DrawCylinder (points[k], points[k+1], thickness);
+	    }
+
+	if(usable) calculated++;
+      }
+    cout << "\rFieldline Calculation " << 100 << "%" << endl;
+    
+  }
+
+
+
+  FieldLineCalc :: FieldLineCalc(const Mesh & amesh, VisualSceneSolution & avss, const VisualSceneSolution::SolData * solution, 
+				 const double rel_length, const int amaxpoints, 
+				 const double rel_thickness, const double rel_tolerance, const int rk_type, const int adirection) :
+    mesh(amesh), vss(avss), vsol(solution), stepper(rk_type)
+  {
+    mesh.GetBox (pmin, pmax);
+    rad = 0.5 * Dist (pmin, pmax);
+    
+
+    maxlength = (rel_length > 0) ? rel_length : 0.5;
+    maxlength *= 2.*rad;
+
+    thickness = (rel_thickness > 0) ? rel_thickness : 0.0015;
+    thickness *= 2.*rad;
+    
+    double auxtolerance = (rel_tolerance > 0) ? rel_tolerance : 1.5e-3;
+    auxtolerance *= 2.*rad;
+
+    stepper.SetTolerance(auxtolerance);
+    
+    direction = adirection;
+    
+    
+    maxpoints = amaxpoints;
+
+    if(direction == 0)
+      {
+	maxlength *= 0.5;
+	maxpoints /= 2;
+      }
+    
+
+    phaser = 1;
+    phasei = 0;
+    
+    critical_value = -1;
+
+    randomized = false;
+    
+  }
+  
+
+
+  
+  void FieldLineCalc :: Calc(const Point3d & startpoint, Array<Point3d> & points, Array<double> & vals, Array<bool> & drawelems, Array<int> & dirstart)
+  {
+    double lami[3], startlami[3];
+    double values[6];
+    double dummyt(0);
+    Vec3d v;
+    Vec3d startv;
+    Point3d newp;
+    double h;
+    
+    double startval;
+    bool startdraw;
+    bool drawelem = false;
+    int elnr;
+
+    for (int i=0; i<6; i++) values[i]=0.0;
+    for (int i=0; i<3; i++) lami[i]=0.0;
+    for (int i=0; i<3; i++) startlami[i]=0.0;
+    
+    points.SetSize(0);
+    vals.SetSize(0);
+    drawelems.SetSize(0);
+
+    dirstart.SetSize(0);
+    dirstart.Append(0);
+
+
+    int startelnr = mesh.GetElementOfPoint(startpoint,startlami,true) - 1;
+    (*testout) << "p = " << startpoint << "; elnr = " << startelnr << endl;
+    if (startelnr == -1)
+      return;
+      
+    mesh.SetPointSearchStartElement(startelnr);
+
+    if (mesh.GetDimension()==3)
+      startdraw = vss.GetValues ( vsol, startelnr, startlami[0], startlami[1], startlami[2], values);
+    else
+      startdraw = vss.GetSurfValues ( vsol, startelnr, startlami[0], startlami[1], values);
+
+    VisualSceneSolution::RealVec3d ( values, startv, vsol->iscomplex, phaser, phasei);
+
+    startval = startv.Length();
+
+    if(critical_value > 0 && fabs(startval) < critical_value)
+      return;
+
+    //cout << "p = " << startpoint << "; elnr = " << startelnr << endl;
+
+
+      
+    for(int dir = 1; dir >= -1; dir -= 2)
+      {
+	if(dir*direction < 0) continue;
+	  
+	points.Append(startpoint);
+	vals.Append(startval);
+	drawelems.Append(startdraw);
+	  
+	h = 0.001*rad/startval; // otherwise no nice lines; should be made accessible from outside
+	
+	v = startv;
+	if(dir == -1) v *= -1.;
+
+	elnr = startelnr;
+	lami[0] = startlami[0]; lami[1] = startlami[1]; lami[2] = startlami[2]; 
+	  
+
+	for(double length = 0; length < maxlength; length += h*vals.Last())
+	  {
+	    if(v.Length() < 1e-12*rad)
+	      {
+		(*testout) << "Current fieldlinecalculation came to a stillstand at " << points.Last() << endl;
+		break;
+	      }
+
+	    stepper.StartNextValCalc(points.Last(),dummyt,h,true);
+	    stepper.FeedNextF(v);
+
+	    while(!stepper.GetNextData(newp,dummyt,h) && elnr != -1)
+	      {
+		elnr = mesh.GetElementOfPoint(newp,lami,true) - 1;
+		if(elnr != -1)
+		  {
+		    mesh.SetPointSearchStartElement(elnr);
+                    if (mesh.GetDimension()==3)
+                        drawelem = vss.GetValues (vsol, elnr, lami[0], lami[1], lami[2], values);
+                    else
+                      drawelem = vss.GetSurfValues (vsol, elnr, lami[0], lami[1], values);
+
+		    VisualSceneSolution::RealVec3d (values, v, vsol->iscomplex, phaser, phasei);
+		    if(dir == -1) v *= -1.;
+		    stepper.FeedNextF(v);
+		  }
+	      }
+
+	    if (elnr == -1)
+	      {
+		//cout << "direction " <<dir << " reached the wall." << endl;
+		break;
+	      }
+
+	    points.Append(newp);
+	    vals.Append(v.Length());
+	    drawelems.Append(drawelem);
+
+	    if(points.Size() % 40 == 0 && points.Size() > 1)
+	      (*testout) << "Points in current fieldline: " << points.Size() << ", current position: " << newp << endl;
+
+	    if(maxpoints > 0 && points.Size() >= maxpoints)
+	      {
+		break;
+	      }
+
+	    //cout << "length " << length << " h " << h << " vals.Last() " << vals.Last()  << " maxlength " << maxlength << endl;
+	  }
+	dirstart.Append(points.Size());
+      }
+  }
+
+
+
+
+  
+  void VisualSceneSolution :: BuildFieldLinesFromBox(Array<Point3d> & startpoints)
+  {
+    if(fieldlines_startarea_parameter[0] > fieldlines_startarea_parameter[3] ||
+       fieldlines_startarea_parameter[1] > fieldlines_startarea_parameter[4] ||
+       fieldlines_startarea_parameter[2] > fieldlines_startarea_parameter[5])
+      {
+	Point3d pmin, pmax;
+	mesh->GetBox (pmin, pmax);
+	
+	fieldlines_startarea_parameter[0] = pmin.X();	
+	fieldlines_startarea_parameter[1] = pmin.Y();
+	fieldlines_startarea_parameter[2] = pmin.Z();
+	fieldlines_startarea_parameter[3] = pmax.X();	
+	fieldlines_startarea_parameter[4] = pmax.Y();
+	fieldlines_startarea_parameter[5] = pmax.Z();
+      }
+    
+    for (int i = 1; i <= startpoints.Size(); i++)
+      {
+	Point3d p (fieldlines_startarea_parameter[0] + double (rand()) / RAND_MAX * (fieldlines_startarea_parameter[3]-fieldlines_startarea_parameter[0]),
+		   fieldlines_startarea_parameter[1] + double (rand()) / RAND_MAX * (fieldlines_startarea_parameter[4]-fieldlines_startarea_parameter[1]),
+		   fieldlines_startarea_parameter[2] + double (rand()) / RAND_MAX * (fieldlines_startarea_parameter[5]-fieldlines_startarea_parameter[2]));
+	
+	startpoints[i-1] = p;
+      }
+  }
+
+  void VisualSceneSolution :: BuildFieldLinesFromLine(Array<Point3d> & startpoints)
+  {
+    for (int i = 1; i <= startpoints.Size(); i++)
+      {
+	double s = double (rand()) / RAND_MAX;
+
+	Point3d p (fieldlines_startarea_parameter[0] + s * (fieldlines_startarea_parameter[3]-fieldlines_startarea_parameter[0]),
+		   fieldlines_startarea_parameter[1] + s * (fieldlines_startarea_parameter[4]-fieldlines_startarea_parameter[1]),
+		   fieldlines_startarea_parameter[2] + s * (fieldlines_startarea_parameter[5]-fieldlines_startarea_parameter[2]));
+	
+	startpoints[i-1] = p;
+      }
+  }
+
+
+  void VisualSceneSolution :: BuildFieldLinesFromFile(Array<Point3d> & startpoints)
+  {
+    ifstream * infile;
+
+    infile = new ifstream(fieldlines_filename.c_str());
+
+    //cout << "reading from file " << fieldlines_filename << endl;
+
+    int numpoints = 0;
+
+    string keyword;
+
+    
+    double dparam;
+    int iparam;
+
+    while(infile->good())
+      {
+	(*infile) >> keyword;
+
+	if(keyword == "point") numpoints++;
+	else if(keyword == "line" || keyword == "box")
+	  {
+	    for(int i=0; i<6; i++) (*infile) >> dparam;
+	    (*infile) >> iparam;
+	    numpoints += iparam;
+	  }
+      }
+
+    delete infile;
+
+
+    //cout << numpoints << " startpoints" << endl;
+
+    startpoints.SetSize(numpoints);
+    
+    infile = new ifstream(fieldlines_filename.c_str());
+
+    numpoints = 0;
+
+    while(infile->good())
+      {
+	(*infile) >> keyword;
+
+	if (keyword == "point")
+	  {
+	    (*infile) >> startpoints[numpoints].X(); (*infile) >> startpoints[numpoints].Y(); (*infile) >> startpoints[numpoints].Z();
+	    numpoints++;
+	  }
+	else if (keyword == "line" || keyword == "box")
+	  {
+	    for(int i=0; i<6; i++) (*infile) >> fieldlines_startarea_parameter[i];
+	    (*infile) >> iparam;
+
+	    Array<Point3d> auxpoints(iparam);
+	    
+	    if (keyword == "box")
+	      BuildFieldLinesFromBox(auxpoints);
+	    else if (keyword == "line")
+	      BuildFieldLinesFromLine(auxpoints);
+	    
+	    for(int i=0; i<iparam; i++)
+	      {
+		startpoints[numpoints] = auxpoints[i];
+		numpoints++;
+	      }
+	  }
+
+	//cout << "startpoints " << startpoints << endl;
+      }
+
+    delete infile;
+    
+    
+
+    
+  }
+
+  
+  void VisualSceneSolution :: BuildFieldLinesFromFace(Array<Point3d> & startpoints)
+  {
+    Array<SurfaceElementIndex> elements_2d;
+    
+    //cout << "fieldlines_startface " << fieldlines_startface << endl;
+    mesh->GetSurfaceElementsOfFace(fieldlines_startface,elements_2d);
+    if(elements_2d.Size() == 0)
+      {
+	cerr << "No Elements on selected face (?)" << endl;
+	return;
+      }
+    Vec3d v1,v2,cross;
+    
+    double area = 0;
+
+	int i;
+    for(i=0; i<elements_2d.Size(); i++)
+      {
+	const Element2d & elem = mesh->SurfaceElement(elements_2d[i]);
+	
+	v1 = mesh->Point(elem[1]) - mesh->Point(elem[0]);
+	v2 = mesh->Point(elem[2]) - mesh->Point(elem[0]);
+	cross = Cross(v1,v2);
+	area += cross.Length();
+	
+	if(elem.GetNV() == 4)
+	  {
+	    v1 = mesh->Point(elem[2]) - mesh->Point(elem[0]);
+	    v2 = mesh->Point(elem[3]) - mesh->Point(elem[0]);
+	    cross = Cross(v1,v2);
+	    area += cross.Length();
+	  }
+      }
+    
+    int startpointsp = 0;
+    i = 0;
+    
+    while(startpointsp < startpoints.Size())
+      {
+	const Element2d & elem = mesh->SurfaceElement(elements_2d[i]);
+	
+	int numtri = (elem.GetNV() == 3) ? 1 : 2;
+	
+	for(int tri = 0; startpointsp < startpoints.Size() && tri<numtri; tri++)
+	  {
+	    
+	    if(tri == 0)
+	      {
+		v1 = mesh->Point(elem[1]) - mesh->Point(elem[0]);
+		v2 = mesh->Point(elem[2]) - mesh->Point(elem[0]);
+		cross = Cross(v1,v2);
+	      }
+	    else if(tri == 1)
+	      {
+		v1 = mesh->Point(elem[2]) - mesh->Point(elem[0]);
+		v2 = mesh->Point(elem[3]) - mesh->Point(elem[0]);
+		cross = Cross(v1,v2);
+	      }
+	    
+	    double thisarea = cross.Length();
+	    
+	    int numloc = int(startpoints.Size()*thisarea/area);
+	    if(double (rand()) / RAND_MAX < startpoints.Size()*thisarea/area - numloc)
+	      numloc++;
+	    
+	    for(int j=0; startpointsp < startpoints.Size() && j<numloc; j++)
+	      {
+		double s = double (rand()) / RAND_MAX;
+		double t = double (rand()) / RAND_MAX;
+		if(s+t > 1)
+		  {
+		    s = 1.-s; t = 1.-t;
+		  }
+		startpoints[startpointsp] = mesh->Point(elem[0]) + s*v1 +t*v2;
+		startpointsp++;
+	      }
+	  }
+	i++;
+	if(i == elements_2d.Size()) i = 0;
+      } 
+    
+  }
+
+
+  void VisualSceneSolution :: BuildFieldLinesPlot ()
+  {
+    if (fieldlinestimestamp >= solutiontimestamp) 
+      return;
+    fieldlinestimestamp = solutiontimestamp;
+    
+
+    if (fieldlineslist)
+      glDeleteLists (fieldlineslist, num_fieldlineslists);
+
+    if (vecfunction == -1)
+      return;
+
+    const SolData * vsol = soldata[fieldlines_vecfunction];
+
+    num_fieldlineslists = (vsol -> iscomplex && !fieldlines_fixedphase) ? 100 : 1;
+   
+
+    FieldLineCalc linecalc(*mesh,*this,vsol,
+			   fieldlines_rellength,fieldlines_maxpoints,fieldlines_relthickness,fieldlines_reltolerance,fieldlines_rktype);
+
+    if(fieldlines_randomstart) 
+      linecalc.Randomized();
+
+    fieldlineslist = glGenLists (num_fieldlineslists);
+
+    int num_startpoints = num_fieldlines / num_fieldlineslists;
+    if (num_fieldlines % num_fieldlineslists != 0) num_startpoints++;
+
+    if(fieldlines_randomstart)
+      num_startpoints *= 10;
+
+    
+    Array<Point3d> startpoints(num_startpoints);
+    
+
+    for (int ln = 0; ln < num_fieldlineslists; ln++)
+      {
+	if(fieldlines_startarea == 0)
+	  BuildFieldLinesFromBox(startpoints);
+	else if(fieldlines_startarea == 1)
+	  BuildFieldLinesFromFile(startpoints);
+	else if(fieldlines_startarea == 2)
+	  BuildFieldLinesFromFace(startpoints);
+
+
+	    
+	double phi;
+	
+	if(vsol -> iscomplex)
+	  {
+	    if(fieldlines_fixedphase)
+	      phi = fieldlines_phase;
+	    else
+	      phi = 2*M_PI*ln / num_fieldlineslists;
+	  }
+	else
+	  phi = 0;
+
+	cout << "phi = " << phi << endl;
+
+	double phaser = cos(phi), phasei = sin(phi);
+	
+
+        glNewList(fieldlineslist+ln, GL_COMPILE);
+        
+        SetTextureMode (usetexture);
+	linecalc.GenerateFieldLines(startpoints,num_fieldlines / num_fieldlineslists+1,
+				    fieldlineslist+ln,minval,maxval,logscale,phaser,phasei);
+
+        glEndList ();
+
+      }
+  }
+
+
+
+  
+}
+
+
+#endif // NOTCL
diff --git a/contrib/Netgen/libsrc/visualization/vsmesh.cpp b/contrib/Netgen/libsrc/visualization/vsmesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..240a7e738a31906073f20964e386137878480b73
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vsmesh.cpp
@@ -0,0 +1,3445 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <meshing.hpp>
+// #include <csg.hpp>
+
+#ifdef STLGEOM
+#include <stlgeom.hpp>
+#endif
+
+
+// #include <parallel.hpp>
+
+#include <visual.hpp>
+
+namespace netgen
+{
+  extern AutoPtr<Mesh> mesh;
+  extern NetgenGeometry * ng_geometry;
+
+  VisualSceneMesh vsmesh;
+
+  VisualSceneMesh :: VisualSceneMesh ()
+    : VisualScene()
+  {
+    filledlist = 0;
+    linelist = 0;
+    edgelist = 0;
+    badellist = 0;
+    tetlist = 0;
+    prismlist = 0;
+    hexlist = 0;
+    pyramidlist = 0;
+    identifiedlist = 0;
+    pointnumberlist = 0;
+    domainsurflist = 0;
+
+    vstimestamp = GetTimeStamp();
+    selecttimestamp = GetTimeStamp();
+    filledtimestamp = GetTimeStamp();
+    linetimestamp = GetTimeStamp();
+    edgetimestamp = GetTimeStamp();
+    pointnumbertimestamp = GetTimeStamp();
+
+    tettimestamp = GetTimeStamp();
+    prismtimestamp = GetTimeStamp();
+    hextimestamp = GetTimeStamp();
+    pyramidtimestamp = GetTimeStamp();
+
+    badeltimestamp = GetTimeStamp();
+    identifiedtimestamp = GetTimeStamp();
+    domainsurftimestamp = GetTimeStamp();
+
+
+    selface = -1;
+    selelement = -1;
+    locpi = 1;
+    selpoint = -1;
+    selpoint2 = -1;
+    seledge = -1;
+
+    minh = 0.0;
+    maxh = 0.0;
+  }
+
+  VisualSceneMesh :: ~VisualSceneMesh ()
+  {
+    ;
+  }
+
+
+  void VisualSceneMesh :: DrawScene ()
+  {
+    if (!mesh)
+      {
+	VisualScene::DrawScene();
+	return;
+      }
+
+    lock = NULL;
+
+    static int timer = NgProfiler::CreateTimer ("VSMesh::DrawScene");
+
+    NgProfiler::RegionTimer reg (timer);
+
+    BuildScene();
+
+    glClearColor(backcolor, backcolor, backcolor, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glEnable (GL_COLOR_MATERIAL);
+    glColor3f (1.0f, 1.0f, 1.0f);
+    glLineWidth (1.0f);
+
+    SetLight();
+
+    glPushMatrix();
+    glMultMatrixf (transformationmat);
+
+    GLdouble projmat[16];
+    glGetDoublev (GL_PROJECTION_MATRIX, projmat);
+
+
+#ifdef PARALLEL
+    glEnable (GL_BLEND);
+    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+#endif
+
+
+    glInitNames ();
+    glPushName (0);
+
+    //    glEnable (GL_LINE_SMOOTH);
+    //         glEnable (GL_BLEND);
+    //         glEnable (GL_POLYGON_SMOOTH);
+    //         glDisable (GL_DEPTH_TEST);
+    //         glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    //    glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
+
+    glDisable (GL_COLOR_MATERIAL);
+
+    GLfloat matcol0[] = { 0, 0, 0, 1 };
+    GLfloat matcol1[] = { 1, 1, 1, 1 };
+    GLfloat matcolf[] = { 0, 1, 0, 1 };
+    GLfloat matcolb[] = { 0.5, 0, 0, 1 };
+    // GLfloat matcolblue[] = { 0, 0, 1, 1 };
+
+    glMatrixMode (GL_MODELVIEW);
+
+    glMaterialfv(GL_FRONT, GL_EMISSION, matcol0);
+    glMaterialfv(GL_BACK, GL_EMISSION, matcol0);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matcol1);
+    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, matcolf);
+    glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, matcolb);
+
+    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+    // glPolygonOffset (1,10);
+    glPolygonOffset (2,2);
+    glEnable (GL_POLYGON_OFFSET_FILL);
+
+    SetClippingPlane ();
+
+    if (vispar.drawfilledtrigs)
+      {
+	if (filledtimestamp < mesh->GetTimeStamp () ||
+            filledtimestamp < selecttimestamp)
+	  {
+            BuildFilledList (false);
+	  }
+
+
+#ifdef PARALLELGL
+	if (ntasks > 1 && vispar.drawtetsdomain > 0 && vispar.drawtetsdomain < ntasks)
+	  glCallList (par_filledlists[vispar.drawtetsdomain]);
+	else
+#endif
+	  glCallList (filledlist);
+      }
+
+    if (vispar.drawbadels)
+      glCallList (badellist);
+
+    if (vispar.drawprisms)
+      {
+	BuildPrismList ();
+	glCallList (prismlist);
+      }
+
+    if (vispar.drawpyramids)
+      {
+	BuildPyramidList ();
+	glCallList (pyramidlist);
+      }
+
+    if (vispar.drawhexes)
+      {
+	BuildHexList ();
+	glCallList (hexlist);
+      }
+
+    if (vispar.drawtets)
+      {
+	BuildTetList ();
+	glCallList (tetlist);
+      }
+
+    if (vispar.drawdomainsurf)
+      {
+	BuildDomainSurfList();
+	glCallList (domainsurflist);
+      }
+
+    glDisable (GL_POLYGON_OFFSET_FILL);
+
+    // draw lines
+
+    glMatrixMode (GL_MODELVIEW);
+
+    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcol0);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, matcol0);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matcol0);
+
+    glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+    glLineWidth (1.0f);
+    glColor3f (0.0f, 0.0f, 0.0f);
+    glDisable (GL_LINE_SMOOTH);
+
+
+    if (vispar.drawoutline)
+      {
+	glPolygonOffset (1, 1);
+	glEnable (GL_POLYGON_OFFSET_LINE);
+
+	if (linetimestamp < mesh->GetTimeStamp ())
+	  BuildLineList ();
+
+#ifdef PARALLELGL
+	if (ntasks > 1 && vispar.drawtetsdomain > 0 && vispar.drawtetsdomain < ntasks)
+	  // for (int dest = 1; dest < ntasks; dest++)
+	  // if (vispar.drawtetsdomain == dest)
+	  glCallList (par_linelists[vispar.drawtetsdomain]);
+	else
+#endif
+	  glCallList (linelist);
+
+
+	glDisable (GL_POLYGON_OFFSET_LINE);
+      }
+
+
+    if (vispar.drawidentified)
+      {
+	glPolygonOffset (1, -1);
+	glEnable (GL_POLYGON_OFFSET_LINE);
+	glCallList (identifiedlist);
+	glDisable (GL_POLYGON_OFFSET_LINE);
+      }
+
+    if (vispar.drawpointnumbers ||
+	vispar.drawedgenumbers ||
+	vispar.drawfacenumbers ||
+	vispar.drawelementnumbers)
+      glCallList (pointnumberlist);
+
+
+    glPopName();
+
+    if (vispar.drawedges)
+      {
+	BuildEdgeList();
+	glCallList (edgelist);
+      }
+
+    if (selpoint > 0 && selpoint <= mesh->GetNP())
+      {
+	/*
+	  glPointSize (3.0);
+	  glColor3d (0, 0, 1);
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolblue);
+	  glBegin (GL_POINTS);
+
+	  const Point3d p = mesh->Point(selpoint);
+	  glVertex3f (p.X(), p.Y(), p.Z());
+	  glEnd();
+	*/
+
+	glColor3d (0, 0, 1);
+
+	static GLubyte cross[] =
+	  {
+            0xc6, 0xee, 0x7c, 0x38, 0x7c, 0xee, 0xc6
+	  };
+	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+	glDisable (GL_COLOR_MATERIAL);
+	glDisable (GL_LIGHTING);
+	glDisable (GL_CLIP_PLANE0);
+
+	const Point3d p = mesh->Point(selpoint);
+	glRasterPos3d (p.X(), p.Y(), p.Z());
+	glBitmap (7, 7, 3, 3, 0, 0, &cross[0]);
+      }
+
+
+    glDisable(GL_CLIP_PLANE0);
+
+    glPopMatrix();
+
+    if (vispar.colormeshsize)
+      DrawColorBar (minh, maxh, 1);
+
+    DrawCoordinateCross ();
+    DrawNetgenLogo ();
+
+
+    if (lock)
+      {
+	lock -> UnLock();
+	delete lock;
+	lock = NULL;
+      }
+    
+    glFinish();
+  }
+
+
+  void VisualSceneMesh :: BuildScene (int zoomall)
+  {
+    if (!mesh)
+      {
+	VisualScene::BuildScene (zoomall);
+	return;
+      }
+
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    static int timer = NgProfiler::CreateTimer ("VSMesh::BuildScene");
+    NgProfiler::RegionTimer reg (timer);
+
+
+
+    Point3d pmin, pmax;
+    static double oldrad = 0;
+
+    Array<Element2d> faces;
+
+    int meshtimestamp = mesh->GetTimeStamp();
+    if (meshtimestamp > vstimestamp || zoomall)
+      {
+	if (mesh->GetDimension() == 2)
+	  {
+            // works in NGSolve, mesh view
+            mesh->GetBox (pmin, pmax);
+	  }
+	else
+	  {
+            // otherwise strange zooms douring mesh generation
+            mesh->GetBox (pmin, pmax, SURFACEPOINT);
+	  }
+
+	if (vispar.use_center_coords && zoomall == 2)
+	  {
+            center.X() = vispar.centerx; center.Y() = vispar.centery; center.Z() = vispar.centerz;
+	  }
+	else if (selpoint >= 1 && zoomall == 2)
+	  center = mesh->Point (selpoint);
+	else if (vispar.centerpoint >= 1 && zoomall == 2)
+	  center = mesh->Point (vispar.centerpoint);
+	else
+	  center = Center (pmin, pmax);
+	rad = 0.5 * Dist (pmin, pmax);
+	if(rad == 0) rad = 1e-6;
+
+	if (rad > 1.2 * oldrad ||
+            mesh->GetMajorTimeStamp() > vstimestamp ||
+            zoomall)
+	  {
+            CalcTransformationMatrices();
+            oldrad = rad;
+	  }
+      }
+
+    glEnable (GL_NORMALIZE);
+
+    if (pointnumberlist)
+      {
+	glDeleteLists (pointnumberlist, 1);
+	pointnumberlist = 0;
+      }
+
+    if (badellist)
+      {
+	glDeleteLists (badellist, 1);
+	badellist = 0;
+      }
+    /*
+      if (prismlist)
+      {
+      glDeleteLists (prismlist, 1);
+      prismlist = 0;
+      }
+
+      if (pyramidlist)
+      {
+      glDeleteLists (pyramidlist, 1);
+      pyramidlist = 0;
+      }
+
+      if (hexlist)
+      {
+      glDeleteLists (hexlist, 1);
+      hexlist = 0;
+      }
+    */
+    if (identifiedlist)
+      {
+	glDeleteLists (identifiedlist, 1);
+	identifiedlist = 0;
+      }
+
+    pointnumberlist = glGenLists (1);
+    glNewList (pointnumberlist, GL_COMPILE);
+
+    if (vispar.drawpointnumbers ||
+	vispar.drawedgenumbers ||
+	vispar.drawfacenumbers ||
+	vispar.drawelementnumbers)
+      {
+	//     	glEnable (GL_COLOR_MATERIAL);
+	GLfloat textcol[3] = { 1 - backcolor,
+			       1 - backcolor,
+			       1 - backcolor };
+	glColor3fv (textcol);
+	glNormal3d (0, 0, 1);
+	glPushAttrib (GL_LIST_BIT);
+	// glListBase (fontbase);
+
+	char buf[30];
+
+	if (vispar.drawpointnumbers)
+	  for (int i = 1; i <= mesh->GetNP(); i++)
+            {
+	      const Point3d & p = mesh->Point(i);
+	      glRasterPos3d (p.X(), p.Y(), p.Z());
+
+	      sprintf (buf, "%d", i);
+
+	      // glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf);
+	      MyOpenGLText (buf);
+            }
+
+	if (vispar.drawedgenumbers)
+	  {
+	    /*
+	      for (SegmentIndex i = 0; i < mesh->GetNSeg(); i++)
+	      {
+	      const Segment & seg = (*mesh)[i];
+
+	      const Point3d & p1 = mesh->Point(seg[0]);
+	      const Point3d & p2 = mesh->Point(seg[1]);
+	      const Point3d p = Center (p1, p2);
+	      glRasterPos3d (p.X(), p.Y(), p.Z());
+
+	      sprintf (buf, "%d", seg.edgenr);
+	      glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf);
+	      }
+	    */
+
+	    const MeshTopology & top = mesh->GetTopology();
+	    for (int i = 1; i <= top.GetNEdges(); i++)
+	      {
+		int v1, v2;
+		top.GetEdgeVertices (i, v1, v2);
+		const Point3d & p1 = mesh->Point(v1);
+		const Point3d & p2 = mesh->Point(v2);
+		const Point3d p = Center (p1, p2);
+		glRasterPos3d (p.X(), p.Y(), p.Z());
+
+		sprintf (buf, "%d", i);
+		// glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf);
+		MyOpenGLText (buf);
+
+	      }
+
+	  }
+
+
+	if (vispar.drawfacenumbers)
+	  {
+	    const MeshTopology & top = mesh->GetTopology();
+	    Array<int> v;
+	    for (int i = 1; i <= top.GetNFaces(); i++)
+	      {
+		top.GetFaceVertices (i, v);
+		const Point3d & p1 = mesh->Point(v.Elem(1));
+		const Point3d & p2 = mesh->Point(v.Elem(2));
+		const Point3d & p3 = mesh->Point(v.Elem(3));
+		Point3d p;
+		if (v.Elem(4) == 0)
+                  {
+		    p = Center (p1, p2, p3);
+                  }
+		else
+                  {
+		    const Point3d & p4 = mesh->Point(v.Elem(4));
+		    Point3d hp1 = Center (p1, p2);
+		    Point3d hp2 = Center (p3, p4);
+		    p = Center (hp1, hp2);
+                  }
+
+		glRasterPos3d (p.X(), p.Y(), p.Z());
+		sprintf (buf, "%d", i);
+		// glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf);
+		MyOpenGLText (buf);
+	      }
+	  }
+
+
+
+	if (vispar.drawelementnumbers)
+	  {
+	    Array<int> v;
+	    for (int i = 1; i <= mesh->GetNE(); i++)
+	      {
+		// const ELEMENTTYPE & eltype = mesh->ElementType(i);
+		Array<int> pnums;
+
+		Point3d p;
+		const Element & el = mesh->VolumeElement (i);
+
+		if ( ! el.PNum(5)) //  eltype == TET )
+                  {
+
+		    pnums.SetSize(4);
+		    for( int j = 0; j < pnums.Size(); j++)
+		      pnums[j] = mesh->VolumeElement(i).PNum(j+1);
+
+
+		    const Point3d & p1 = mesh->Point(pnums[0]);
+		    const Point3d & p2 = mesh->Point(pnums[1]);
+		    const Point3d & p3 = mesh->Point(pnums[2]);
+		    const Point3d & p4 = mesh->Point(pnums[3]);
+		    p = Center (p1, p2, p3, p4);
+                  }
+		else if ( ! el.PNum(6)) // eltype == PYRAMID
+                  {
+		    pnums.SetSize(5);
+		    for( int j = 0; j < pnums.Size(); j++)
+		      pnums[j] = mesh->VolumeElement(i).PNum(j+1);
+
+
+		    const Point3d & p1 = mesh->Point(pnums[0]);
+		    const Point3d & p2 = mesh->Point(pnums[1]);
+		    const Point3d & p3 = mesh->Point(pnums[2]);
+		    const Point3d & p4 = mesh->Point(pnums[3]);
+		    const Point3d & p5 = mesh->Point(pnums[4]);
+
+		    p.X()  = 0.3 * p5.X() + 0.7 * Center ( Center(p1, p3) , Center(p2, p4) ) . X();
+		    p.Y()  = 0.3 * p5.Y() + 0.7 * Center ( Center(p1, p3) , Center(p2, p4) ) . Y();
+		    p.Z()  = 0.3 * p5.Z() + 0.7 * Center ( Center(p1, p3) , Center(p2, p4) ) . Z();
+
+                  }
+		else if ( ! el.PNum(7) ) // eltype == PRISM
+                  {
+		    pnums.SetSize(6);
+		    for( int j = 0; j < pnums.Size(); j++)
+		      pnums[j] = mesh->VolumeElement(i).PNum(j+1);
+
+		    const Point3d & p1 = mesh->Point(pnums[0]);
+		    const Point3d & p2 = mesh->Point(pnums[1]);
+		    const Point3d & p3 = mesh->Point(pnums[2]);
+		    const Point3d & p11 = mesh->Point(pnums[3]);
+		    const Point3d & p12 = mesh->Point(pnums[4]);
+		    const Point3d & p13 = mesh->Point(pnums[5]);
+		    p = Center (  Center (p1, p2, p3) , Center(p11, p12, p13) )  ;
+
+                  }
+		else if (! el.PNum(9) ) // eltype == HEX
+                  {
+		    pnums.SetSize(8);
+		    for( int j = 0; j < pnums.Size(); j++)
+		      pnums[j] = mesh->VolumeElement(i).PNum(j+1);
+
+		    const Point3d & p1 = mesh->Point(pnums[0]);
+		    const Point3d & p2 = mesh->Point(pnums[1]);
+		    const Point3d & p3 = mesh->Point(pnums[2]);
+		    const Point3d & p4 = mesh->Point(pnums[3]);
+		    const Point3d & p5 = mesh->Point(pnums[4]);
+		    const Point3d & p6 = mesh->Point(pnums[5]);
+		    const Point3d & p7 = mesh->Point(pnums[6]);
+		    const Point3d & p8 = mesh->Point(pnums[7]);
+
+		    p = Center ( Center ( Center(p1, p3), Center(p2, p4) ) , Center( Center(p5, p7) , Center(p6, p8 ) ) );
+                  }
+
+		glRasterPos3d (p.X(), p.Y(), p.Z());
+		sprintf (buf, "%d", i);
+		// glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf);
+		MyOpenGLText (buf);
+
+	      }
+	  }
+
+
+	glPopAttrib ();
+	//      	glDisable (GL_COLOR_MATERIAL);
+      }
+    glEndList ();
+
+
+
+
+
+
+    badellist = glGenLists (1);
+    glNewList (badellist, GL_COMPILE);
+
+    if (vispar.drawbadels)
+      {
+	//  SetClippingPlane ();
+
+	static float badelcol[] = { 1.0f, 0.0f, 1.0f, 1.0f };
+	glLineWidth (1.0f);
+
+	for (int i = 1; i <= mesh->GetNE(); i++)
+	  {
+            if (mesh->VolumeElement(i).flags.badel ||
+		mesh->VolumeElement(i).flags.illegal ||
+		(i == vispar.drawelement))
+	      {
+		// copy to be thread-safe
+		Element el = mesh->VolumeElement (i);
+		el.GetSurfaceTriangles (faces);
+
+		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, badelcol);
+
+
+		//	  if ( (el.GetNP() == 4) || (el.GetNP() == 10))
+		if (el.PNum(1))
+		  {
+		    glBegin (GL_TRIANGLES);
+
+		    for (int j = 1; j <= faces.Size(); j++)
+		      {
+			Element2d & face = faces.Elem(j);
+			const Point3d & lp1 = mesh->Point (el.PNum(face.PNum(1)));
+			const Point3d & lp2 = mesh->Point (el.PNum(face.PNum(2)));
+			const Point3d & lp3 = mesh->Point (el.PNum(face.PNum(3)));
+			Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+			n /= (n.Length()+1e-12);
+			glNormal3d (n.X(), n.Y(), n.Z());
+			glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+			glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+			glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+		      }
+
+		    glEnd();
+		  }
+	      }
+	  }
+
+
+
+	for (int i = 1; i <= mesh->GetNE(); i++)
+	  {
+            if (mesh->VolumeElement(i).flags.badel)
+	      {
+		// copy to be thread-safe
+		Element el = mesh->VolumeElement (i);
+		if ( (el.GetNP() == 4) || (el.GetNP() == 10))
+		  {
+		    glBegin (GL_LINES);
+		    glVertex3d (0,0,0);
+		    const Point3d & p = mesh->Point(el.PNum(1));
+		    glVertex3d (p.X(), p.Y(), p.Z());
+		    glEnd();
+		  }
+	      }
+	  }
+
+
+	for (int i = 1; i <= mesh->GetNE(); i++)
+	  {
+            Element el = mesh->VolumeElement (i);
+            int hascp = 0;
+            for (int j = 1; j <= el.GetNP(); j++)
+	      if (el.PNum(j) == vispar.centerpoint)
+		hascp = 1;
+
+            if (hascp)
+	      {
+		(*testout) << "draw el " << i << " : ";
+		for (int j = 1; j <= el.GetNP(); j++)
+                  (*testout) << el.PNum(j) << " ";
+		(*testout) << endl;
+
+		if (el.GetNP() == 4)
+		  {
+		    int et[6][2] =
+		      { { 1, 2 },
+			{ 1, 3 },
+			{ 1, 4 },
+			{ 2, 3 },
+			{ 2, 4 },
+			{ 3, 4 } } ;
+
+		    for (int j = 0; j < 6; j++)
+		      {
+			glBegin (GL_LINES);
+			const Point3d & p1 = mesh->Point (el.PNum(et[j][0]));
+			const Point3d & p2 = mesh->Point (el.PNum(et[j][1]));
+			glVertex3d (p1.X(), p1.Y(), p1.Z());
+			glVertex3d (p2.X(), p2.Y(), p2.Z());
+			glEnd ();
+		      }
+		  }
+
+
+		if (el.GetNP() == 10)
+		  {
+		    int et[12][2] =
+		      { { 1, 5 },
+			{ 2, 5 },
+			{ 1, 6 },
+			{ 3, 6 },
+			{ 1, 7 },
+			{ 4, 7 },
+			{ 2, 8 },
+			{ 3, 8 },
+			{ 2, 9 },
+			{ 4, 9 },
+			{ 3, 10 },
+			{ 4, 10 } };
+
+		    for (int j = 0; j < 12; j++)
+		      {
+			glBegin (GL_LINES);
+			const Point3d & p1 = mesh->Point (el.PNum(et[j][0]));
+			const Point3d & p2 = mesh->Point (el.PNum(et[j][1]));
+			glVertex3d (p1.X(), p1.Y(), p1.Z());
+			glVertex3d (p2.X(), p2.Y(), p2.Z());
+			glEnd ();
+		      }
+		  }
+	      }
+	  }
+
+
+	for (int i = 1; i <= mesh->GetNSE(); i++)
+	  {
+            Element2d el = mesh->SurfaceElement(i);
+            if (!el.BadElement())
+	      continue;
+
+            int drawel = 1;
+            for (int j = 1; j <= el.GetNP(); j++)
+	      if (!el.PNum(j))
+		drawel = 0;
+
+            if (!drawel)
+	      continue;
+
+            // cout << int (el.GetType()) << " " << flush;
+            switch (el.GetType())
+	      {
+	      case TRIG:
+		{
+                  glBegin (GL_TRIANGLES);
+
+                  Point3d lp1 = mesh->Point (el.PNum(1));
+                  Point3d lp2 = mesh->Point (el.PNum(2));
+                  Point3d lp3 = mesh->Point (el.PNum(3));
+                  Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+                  n /= (n.Length() + 1e-12);
+                  glNormal3dv (&n.X());
+                  glVertex3dv (&lp1.X());
+                  glVertex3dv (&lp2.X());
+                  glVertex3dv (&lp3.X());
+                  glEnd();
+                  break;
+		}
+	      case QUAD:
+		{
+                  glBegin (GL_QUADS);
+
+                  const Point3d & lp1 = mesh->Point (el.PNum(1));
+                  const Point3d & lp2 = mesh->Point (el.PNum(2));
+                  const Point3d & lp3 = mesh->Point (el.PNum(4));
+                  const Point3d & lp4 = mesh->Point (el.PNum(3));
+                  Vec3d n = Cross (Vec3d (lp1, lp2),
+				   Vec3d (lp1, Center (lp3, lp4)));
+                  n /= (n.Length() + 1e-12);
+                  glNormal3d (n.X(), n.Y(), n.Z());
+                  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+                  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+                  glVertex3d (lp4.X(), lp4.Y(), lp4.Z());
+                  glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+                  glEnd();
+                  break;
+		}
+	      case TRIG6:
+		{
+                  int lines[6][2] = {
+		    { 1, 6 }, { 2, 6 },
+		    { 1, 5 }, { 3, 5 },
+		    { 2, 4 }, { 3, 4 } };
+
+		  glBegin (GL_LINES);
+		  for (int j = 0; j < 6; j++)
+		    {
+		      glVertex3dv ( mesh->Point (el.PNum(lines[j][0])) );
+		      glVertex3dv ( mesh->Point (el.PNum(lines[j][0])) );
+		    }
+		  glEnd();
+		  break;
+		}
+
+	      case QUAD6:
+		{
+                  int lines[6][2] = {
+		    { 1, 5 }, { 2, 5 },
+		    { 3, 6 }, { 4, 6 },
+		    { 1, 4 }, { 2, 3 } };
+
+		  glBegin (GL_LINES);
+
+		  for (int j = 0; j < 6; j++)
+		    {
+		      const Point3d & lp1 = mesh->Point (el.PNum(lines[j][0]));
+		      const Point3d & lp2 = mesh->Point (el.PNum(lines[j][1]));
+
+		      glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+		      glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+		    }
+		  glEnd ();
+		  break;
+		}
+	      default:
+		PrintSysError ("Cannot draw surface element of type ",
+			       int(el.GetType()));
+	      }
+	  }
+	glLoadName (0);
+
+      }
+    glEndList ();
+
+
+    if (1)
+      {
+
+	identifiedlist = glGenLists (1);
+	glNewList (identifiedlist, GL_COMPILE);
+
+	GLfloat identifiedcol[] = { 1, 0, 1, 1 };
+
+	glLineWidth (3);
+
+	//  for (i = 1; i <= mesh->GetNSeg(); i++)
+
+	if (& mesh -> GetIdentifications() )
+	  {
+            INDEX_2_HASHTABLE<int> & idpts =
+	      mesh->GetIdentifications().GetIdentifiedPoints();
+            if (&idpts)
+	      {
+		for (int i = 1; i <= idpts.GetNBags(); i++)
+                  for (int j = 1; j <= idpts.GetBagSize(i); j++)
+		    {
+		      INDEX_2 pts;
+		      int val;
+
+		      idpts.GetData (i, j, pts, val);
+		      const Point3d & p1 = mesh->Point(pts.I1());
+		      const Point3d & p2 = mesh->Point(pts.I2());
+
+		      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
+				    identifiedcol);
+
+		      glBegin (GL_LINES);
+		      glVertex3f (p1.X(), p1.Y(), p1.Z());
+		      glVertex3f (p2.X(), p2.Y(), p2.Z());
+		      glEnd();
+		    }
+	      }
+	  }
+
+	glEndList ();
+      }
+
+    if (lock)
+      {
+	lock -> UnLock();
+	delete lock;
+	lock = NULL;
+      }
+
+    vstimestamp = meshtimestamp;
+  }
+
+
+
+
+  void VisualSceneMesh :: BuildFilledList (bool names)
+  {
+    static int timer = NgProfiler::CreateTimer ("Mesh::BuildFilledList");
+    NgProfiler::RegionTimer reg (timer);
+
+#ifdef PARALLELGL
+    if (id == 0 && ntasks > 1)
+      {
+	InitParallelGL();
+	par_filledlists.SetSize (ntasks);
+
+	MyMPI_SendCmd ("redraw");
+	MyMPI_SendCmd ("filledlist");
+	for ( int dest = 1; dest < ntasks; dest++ )
+	  MyMPI_Recv (par_filledlists[dest], dest, MPI_TAG_VIS);
+
+	if (filledlist)
+	  glDeleteLists (filledlist, 1);
+
+	filledlist = glGenLists (1);
+	glNewList (filledlist, GL_COMPILE);
+
+	for ( int dest = 1; dest < ntasks; dest++ )
+	  glCallList (par_filledlists[dest]);
+
+	glEndList();
+
+	filledtimestamp = NextTimeStamp();
+	return;
+      }
+
+#endif
+
+
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    filledtimestamp = NextTimeStamp();
+
+    if (filledlist)
+      glDeleteLists (filledlist, 1);
+
+    filledlist = glGenLists (1);
+    glNewList (filledlist, GL_COMPILE);
+
+      
+#ifdef STLGEOM
+    STLGeometry * stlgeometry = dynamic_cast<STLGeometry*> (ng_geometry);
+    bool checkvicinity = (stlgeometry != NULL) && stldoctor.showvicinity;
+#endif
+    glEnable (GL_NORMALIZE);
+
+    glLineWidth (1.0f);
+
+    Vector locms;
+
+    if (vispar.colormeshsize)
+      {
+	glEnable (GL_COLOR_MATERIAL);
+	glShadeModel (GL_SMOOTH);
+	locms.SetSize (mesh->GetNP());
+	maxh = -1;
+	minh = 1e99;
+	for (int i = 1; i <= locms.Size(); i++)
+	  {
+            Point3d p = mesh->Point(i);
+            locms(i-1) = mesh->GetH (p);
+            if (locms(i-1) > maxh) maxh = locms(i-1);
+            if (locms(i-1) < minh) minh = locms(i-1);
+	  }
+	if (!locms.Size())
+	  { 
+            minh = 1; 
+            maxh = 10; 
+	  }
+      }
+    else
+      glDisable (GL_COLOR_MATERIAL);
+
+
+    GLfloat matcol[] = { 0, 1, 0, 1 };
+    GLfloat matcolsel[] = { 1, 0, 0, 1 };
+
+    GLint rendermode;
+    glGetIntegerv (GL_RENDER_MODE, &rendermode);
+
+    CurvedElements & curv = mesh->GetCurvedElements();
+
+    int hoplotn = 1 << vispar.subdivisions;
+    
+    Array<SurfaceElementIndex> seia;
+
+
+    for (int faceindex = 1; faceindex <= mesh->GetNFD(); faceindex++)
+      {
+	mesh->GetSurfaceElementsOfFace (faceindex, seia);
+
+	// Philippose - 06/07/2009
+	// Modified the colour system to integrate the face colours into 
+	// the mesh data structure, rather than limit it to the OCC geometry 
+	// structure... allows other geometry types to use face colours too
+
+	matcol[0] = mesh->GetFaceDescriptor(faceindex).SurfColour().X();
+	matcol[1] = mesh->GetFaceDescriptor(faceindex).SurfColour().Y();
+	matcol[2] = mesh->GetFaceDescriptor(faceindex).SurfColour().Z();
+	matcol[3] = 1.0;
+
+	if (faceindex == selface)
+	  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolsel);
+	else
+	  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcol);
+	
+
+
+	for (int hi = 0; hi < seia.Size(); hi++)
+	  {
+	    SurfaceElementIndex sei = seia[hi];
+            const Element2d & el = (*mesh)[sei];
+
+            bool drawel = (!el.IsDeleted() & el.IsVisible());
+
+#ifdef STLGEOM
+            if (checkvicinity)
+	      for (int j = 0; j < el.GetNP(); j++)
+		if (!stlgeometry->Vicinity(el.GeomInfoPi(j+1).trignum))
+		  drawel = 0;
+#endif
+
+            if (!drawel)
+	      continue;
+	    
+	    if (names)
+	      glLoadName (sei+1);
+
+            switch (el.GetType())
+	      {
+	      case TRIG:
+		{
+                  if (curv.IsHighOrder()) //  && curv.IsSurfaceElementCurved(sei))
+		    {
+		      if (hoplotn > 128) hoplotn = 128;
+		      Point<3> xa[129];
+		      Vec<3> na[129];
+
+		      for (int i = 0; i < hoplotn; i++)
+			{
+			  glBegin (GL_TRIANGLE_STRIP);
+
+			  for (int j = 0; j <= hoplotn-i; j++)
+			    for (int k = 0; k < 2; k++)
+			      {
+				if (j == hoplotn-i && k == 1) continue;
+
+				if (i > 0 && k == 0)
+				  {
+				    glNormal3dv (na[j]);
+				    glVertex3dv (xa[j]);
+				    continue;
+				  }
+
+				Point<2> xref (double(j) / hoplotn, double(i+k) / hoplotn);
+				Point<3> xglob;
+				Mat<3,2> dxdxi;
+				Vec<3> dx, dy, n;
+
+				curv.CalcSurfaceTransformation (xref, sei, xglob, dxdxi);
+				for (int i = 0; i < 3; i++)
+				  {
+				    dx(i) = dxdxi(i,0);
+				    dy(i) = dxdxi(i,1);
+				  }
+				n = Cross (dx, dy);
+				glNormal3dv (n);
+				glVertex3dv (xglob);
+
+				if (k == 1)
+				  {
+				    na[j] = n;
+				    xa[j] = xglob;
+				  }
+			      }
+			  glEnd();
+			}
+		    }
+                  else // not high order
+		    {
+		      glBegin (GL_TRIANGLES);
+		      
+		      const Point<3> & lp0 = (*mesh) [el[0]];
+		      const Point<3> & lp1 = (*mesh) [el[1]];
+		      const Point<3> & lp2 = (*mesh) [el[2]];
+
+		      Vec<3> n = Cross (lp1-lp0, lp2-lp0).Normalize();
+		      glNormal3dv (n);
+
+		      for (int j = 0; j < 3; j++)
+			{
+			  if (vispar.colormeshsize)
+			    SetOpenGlColor  (locms(el[0]-1), minh, maxh, 0);
+			  glVertex3dv ( (*mesh)[el[j]] );
+			}
+		      
+		      glEnd();
+		    }
+		  
+                  break;
+		}
+	      case QUAD:
+		{
+                  if (curv.IsHighOrder()) //  && curv.IsSurfaceElementCurved(sei))
+		    {
+		      Point<2> xr[4];
+		      Point<3> xg;
+		      Vec<3> dx, dy, n;
+
+		      glBegin (GL_QUADS);
+
+		      for (int i = 0; i < hoplotn; i++)
+                        for (int j = 0; j < hoplotn; j++)
+			  {
+			    xr[0](0) = (double)    i/hoplotn; xr[0](1) = (double)    j/hoplotn;
+			    xr[1](0) = (double)(i+1)/hoplotn; xr[1](1) = (double)    j/hoplotn;
+			    xr[2](0) = (double)(i+1)/hoplotn; xr[2](1) = (double)(j+1)/hoplotn;
+			    xr[3](0) = (double)    i/hoplotn; xr[3](1) = (double)(j+1)/hoplotn;
+
+			    for (int l=0; l<4; l++)
+			      {
+				Mat<3,2> dxdxi;
+
+				curv.CalcSurfaceTransformation (xr[l], sei, xg, dxdxi);
+				for (int i = 0; i < 3; i++)
+				  {
+				    dx(i) = dxdxi(i,0);
+				    dy(i) = dxdxi(i,1);
+				  }
+
+				n = Cross (dx, dy);
+				n.Normalize();
+				glNormal3d (n(0), n(1), n(2));
+				glVertex3d (xg(0), xg(1), xg(2));
+			      }
+
+			  }
+
+		      glEnd();
+		    }
+
+                  else // not high order
+
+		    {
+		      glBegin (GL_QUADS);
+
+		      const Point<3> & lp1 = mesh->Point (el.PNum(1));
+		      const Point<3> & lp2 = mesh->Point (el.PNum(2));
+		      const Point<3> & lp3 = mesh->Point (el.PNum(4));
+		      const Point<3> & lp4 = mesh->Point (el.PNum(3));
+
+		      Vec<3> n = Cross (lp2-lp1,  Center (lp3, lp4)-lp1);
+		      n.Normalize();
+		      glNormal3dv (n);
+
+		      glVertex3dv (lp1);
+		      glVertex3dv (lp2);
+		      glVertex3dv (lp4);
+		      glVertex3dv (lp3);
+
+		      glEnd ();
+		    }
+                  break;
+		}
+
+	      case TRIG6:
+		{
+                  glBegin (GL_TRIANGLES);
+
+                  static int trigs[4][3] = {
+		    { 1, 6, 5 },
+		    { 2, 4, 6 },
+		    { 3, 5, 4 },
+		    { 4, 5, 6 } };
+
+		  for (int j = 0; j < 4; j++)
+		    {
+		      const Point<3> & lp1 = mesh->Point (el.PNum(trigs[j][0]));
+		      const Point<3> & lp2 = mesh->Point (el.PNum(trigs[j][1]));
+		      const Point<3> & lp3 = mesh->Point (el.PNum(trigs[j][2]));
+		      // Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+		      Vec<3> n = Cross (lp2-lp1, lp3-lp1);
+		      glNormal3dv (n);
+
+		      glVertex3dv (lp1);
+		      glVertex3dv (lp2);
+		      glVertex3dv (lp3);
+		    }
+		  glEnd();
+		  break;
+		}
+
+	      case QUAD6:
+		{
+                  glBegin (GL_QUADS);
+                  static int quads[2][4] = {
+		    { 1, 5, 6, 4 },
+		    { 5, 2, 3, 6 } };
+
+		  for (int j = 0; j < 2; j++)
+		    {
+		      Point3d lp1 = mesh->Point (el.PNum(quads[j][0]));
+		      Point3d lp2 = mesh->Point (el.PNum(quads[j][1]));
+		      Point3d lp3 = mesh->Point (el.PNum(quads[j][2]));
+		      Point3d lp4 = mesh->Point (el.PNum(quads[j][3]));
+		      Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+		      n /= (n.Length() + 1e-12);
+		      glNormal3dv (&n.X());
+		      glVertex3dv (&lp1.X());
+		      glVertex3dv (&lp2.X());
+		      glVertex3dv (&lp3.X());
+		      glVertex3dv (&lp4.X());
+		    }
+		  glEnd();
+		  break;
+		}
+
+	      case QUAD8:
+		{
+                  glBegin (GL_TRIANGLES);
+                  static int boundary[] =
+		    { 1, 5, 2, 8, 3, 6, 4, 7, 1 };
+
+                  Point3d c(0,0,0);
+                  for (int j = 0; j < 4; j++)
+		    {
+		      const Point3d & hp = mesh->Point (el[j]);
+		      c.X() -= 0.25 * hp.X();
+		      c.Y() -= 0.25 * hp.Y();
+		      c.Z() -= 0.25 * hp.Z();
+		    }
+                  for (int j = 4; j < 8; j++)
+		    {
+		      const Point3d & hp = mesh->Point (el[j]);
+		      c.X() += 0.5 * hp.X();
+		      c.Y() += 0.5 * hp.Y();
+		      c.Z() += 0.5 * hp.Z();
+		    }
+
+                  for (int j = 0; j < 8; j++)
+		    {
+		      Point3d lp1 = mesh->Point (el.PNum(boundary[j]));
+		      Point3d lp2 = mesh->Point (el.PNum(boundary[j+1]));
+
+		      Vec3d n = Cross (Vec3d (c, lp1), Vec3d (c, lp2));
+		      n /= (n.Length() + 1e-12);
+		      glNormal3dv (&n.X());
+		      glVertex3dv (&lp1.X());
+		      glVertex3dv (&lp2.X());
+		      glVertex3dv (&c.X());
+		    }
+                  glEnd();
+                  break;
+		}
+
+
+	      default:
+		PrintSysError ("Cannot draw (2) surface element of type ",
+			       int(el.GetType()));
+	      }
+	    
+
+	    
+	  }
+      }
+    
+
+    glLoadName (0);
+    glEndList ();
+
+
+#ifdef PARALLELGL
+    glFinish();
+    if (id > 0)
+      MyMPI_Send (filledlist, 0, MPI_TAG_VIS);
+#endif
+  }
+
+
+  void VisualSceneMesh :: BuildLineList()
+  {
+    static int timer = NgProfiler::CreateTimer ("Mesh::BuildLineList");
+    NgProfiler::RegionTimer reg (timer);
+
+#ifdef PARALLELGL
+
+    if (id == 0 && ntasks > 1)
+      {
+	InitParallelGL();
+
+	par_linelists.SetSize (ntasks);
+
+	MyMPI_SendCmd ("redraw");
+	MyMPI_SendCmd ("linelist");
+
+	for ( int dest = 1; dest < ntasks; dest++ )
+	  MyMPI_Recv (par_linelists[dest], dest, MPI_TAG_VIS);
+
+	if (linelist)
+	  glDeleteLists (linelist, 1);
+
+	linelist = glGenLists (1);
+	glNewList (linelist, GL_COMPILE);
+
+	for ( int dest = 1; dest < ntasks; dest++ )
+	  glCallList (par_linelists[dest]);
+
+	glEndList();
+
+
+	linetimestamp = NextTimeStamp();
+	return;
+      }
+
+#endif
+
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    linetimestamp = NextTimeStamp();
+
+#ifdef STLGEOM
+    STLGeometry * stlgeometry = dynamic_cast<STLGeometry*> (ng_geometry);
+    bool checkvicinity = (stlgeometry != NULL) && stldoctor.showvicinity;
+#endif
+
+    if (linelist)
+      glDeleteLists (linelist, 1);
+
+    linelist = glGenLists (1);
+    glNewList (linelist, GL_COMPILE);
+
+    // cout << "linelist = " << linelist << endl;
+
+    glLineWidth (1.0f);
+
+
+    int hoplotn = 1 << vispar.subdivisions;
+
+    // PrintMessage (3, "nse = ", mesh->GetNSE());
+    for (SurfaceElementIndex sei = 0; sei < mesh->GetNSE(); sei++)
+      {
+	const Element2d & el = (*mesh)[sei];
+
+	bool drawel = (!el.IsDeleted() & el.IsVisible());
+
+#ifdef STLGEOM
+	if (checkvicinity)
+	  for (int j = 0; j < el.GetNP(); j++)
+	    if (!stlgeometry->Vicinity(el.GeomInfoPi(j+1).trignum))
+	      drawel = 0;
+#endif
+
+	if (!drawel)
+	  continue;
+
+	switch (el.GetType())
+	  {
+	  case TRIG:
+            {
+	      CurvedElements & curv = mesh->GetCurvedElements();
+	      if (curv.IsHighOrder()) //  && curv.IsSurfaceElementCurved(sei))
+		{
+                  Point<3> xg;
+                  glBegin (GL_LINE_LOOP);
+                  for (int i = 0; i < hoplotn; i++)
+		    {
+		      Point<2> xr (double(i) / hoplotn, 0);
+		      curv.CalcSurfaceTransformation (xr, sei, xg);
+		      glVertex3dv (xg);
+		    }
+                  for (int i = 0; i < hoplotn; i++)
+		    {
+		      Point<2> xr (double(hoplotn-i) / hoplotn, double(i)/hoplotn);
+		      curv.CalcSurfaceTransformation (xr, sei, xg);
+		      glVertex3dv (xg);
+		    }
+                  for (int i = 0; i < hoplotn; i++)
+		    {
+		      Point<2> xr (0, double(hoplotn-i) / hoplotn);
+		      curv.CalcSurfaceTransformation (xr, sei, xg);
+		      glVertex3dv (xg);
+		    }
+
+                  glEnd();
+		}
+	      else
+		{
+                  glBegin (GL_TRIANGLES);
+
+		  for (int j = 0; j < 3; j++)
+		    glVertex3dv ( (*mesh) [el[j]] );
+		  /*
+                  const Point<3> & lp0 = (*mesh) [el[0]];
+                  const Point<3> & lp1 = (*mesh) [el[1]];
+                  const Point<3> & lp2 = (*mesh) [el[2]];
+
+                  glVertex3dv (lp0);
+                  glVertex3dv (lp1);
+                  glVertex3dv (lp2);
+		  */
+                  glEnd();
+		}
+
+	      break;
+
+            }
+
+	  case QUAD:
+            {
+	      CurvedElements & curv = mesh->GetCurvedElements();
+	      if (curv.IsHighOrder()) //  && curv.IsSurfaceElementCurved(sei))
+		{
+                  Point<2> xr;
+                  Point<3> xg;
+
+                  glBegin (GL_LINE_STRIP);
+
+                  for (int side = 0; side < 4; side++)
+		    {
+		      for (int i = 0; i <= hoplotn; i++)
+			{
+			  switch (side)
+			    {
+			    case 0:
+			      xr(0) = (double) i/hoplotn;
+			      xr(1) = 0.;
+			      break;
+			    case 1:
+			      xr(0) = 1.;
+			      xr(1) = (double) i/hoplotn;
+			      break;
+			    case 2:
+			      xr(0) = (double) (hoplotn-i)/hoplotn;
+			      xr(1) = 1.;
+			      break;
+			    case 3:
+			      xr(0) = 0.;
+			      xr(1) = (double) (hoplotn-i)/hoplotn;
+			      break;
+			    }
+
+			  curv.CalcSurfaceTransformation (xr, sei, xg);
+			  glVertex3d (xg(0), xg(1), xg(2));
+
+			}
+
+		    }
+                  glEnd();
+
+		} else {
+
+		glBegin (GL_QUADS);
+
+		const Point3d & lp1 = mesh->Point (el.PNum(1));
+		const Point3d & lp2 = mesh->Point (el.PNum(2));
+		const Point3d & lp3 = mesh->Point (el.PNum(4));
+		const Point3d & lp4 = mesh->Point (el.PNum(3));
+		Vec3d n = Cross (Vec3d (lp1, lp2),
+				 Vec3d (lp1, Center (lp3, lp4)));
+		glNormal3d (n.X(), n.Y(), n.Z());
+		glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+		glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+		glVertex3d (lp4.X(), lp4.Y(), lp4.Z());
+		glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+		glEnd();
+
+	      }
+
+	      break;
+
+            }
+
+	  case TRIG6:
+            {
+	      int lines[6][2] = {
+		{ 1, 6 }, { 2, 6 },
+		{ 1, 5 }, { 3, 5 },
+		{ 2, 4 }, { 3, 4 } };
+
+	      glBegin (GL_LINES);
+	      for (int j = 0; j < 6; j++)
+		{
+		  const Point3d & lp1 = mesh->Point (el.PNum(lines[j][0]));
+		  const Point3d & lp2 = mesh->Point (el.PNum(lines[j][1]));
+
+		  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+		  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+		}
+
+	      glEnd();
+	      break;
+            }
+
+	  case QUAD6:
+            {
+	      int lines[6][2] = {
+		{ 1, 5 }, { 2, 5 },
+		{ 3, 6 }, { 4, 6 },
+		{ 1, 4 }, { 2, 3 } };
+
+	      glBegin (GL_LINES);
+
+	      for (int j = 0; j < 6; j++)
+		{
+		  const Point3d & lp1 = mesh->Point (el.PNum(lines[j][0]));
+		  const Point3d & lp2 = mesh->Point (el.PNum(lines[j][1]));
+
+		  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+		  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+		}
+	      glEnd ();
+	      break;
+            }
+
+	  case QUAD8:
+            {
+	      int lines[8][2] = {
+		{ 1, 5 }, { 2, 5 }, { 3, 6 }, { 4, 6 },
+		{ 1, 7 }, { 4, 7 }, { 2, 8 }, { 3, 8 }
+	      };
+
+	      glBegin (GL_LINES);
+
+	      for (int j = 0; j < 8; j++)
+		{
+                  const Point3d & lp1 = mesh->Point (el.PNum(lines[j][0]));
+                  const Point3d & lp2 = mesh->Point (el.PNum(lines[j][1]));
+
+                  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+                  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+		}
+	      glEnd ();
+	      break;
+            }
+
+
+
+	  default:
+            PrintSysError ("Cannot draw (4) surface element of type ",
+			   int(el.GetType()));
+	  }
+      }
+
+    glEndList ();
+
+
+#ifdef PARALLELGL
+    glFinish();
+    if (id > 0)
+      MyMPI_Send (linelist, 0, MPI_TAG_VIS);
+#endif
+  }
+
+
+
+  void VisualSceneMesh :: BuildEdgeList()
+  {
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    if (edgetimestamp > mesh->GetTimeStamp () && vispar.shrink == 1)
+      return;
+
+    edgetimestamp = NextTimeStamp();
+
+    if (edgelist)
+      glDeleteLists (edgelist, 1);
+
+    edgelist = glGenLists (1);
+    glNewList (edgelist, GL_COMPILE);
+
+
+    GLfloat matcoledge[] = { 0, 0, 1, 1 };
+    GLfloat matcolsingedge[] = { 1, 0, 1, 1 };
+
+    glEnable (GL_POLYGON_OFFSET_LINE);
+    glPolygonOffset (1, -1);
+
+    glEnable (GL_COLOR_MATERIAL);
+    glDisable (GL_LIGHTING);
+
+    for (int i = 1; i <= mesh->GetNSeg(); i++)
+      {
+	const Segment & seg = mesh->LineSegment(i);
+	const Point3d & p1 = (*mesh)[seg[0]];
+	const Point3d & p2 = (*mesh)[seg[1]];
+
+	if (seg.singedge_left || seg.singedge_right)
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
+			matcolsingedge);
+	else
+	  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
+			matcoledge);
+
+	if (seg.singedge_left || seg.singedge_right)
+	  glColor3fv (matcolsingedge);
+	else
+	  glColor3fv (matcoledge);
+
+	if (seg.edgenr == seledge)
+	  glLineWidth(5);
+	else
+	  glLineWidth(2);
+
+	if (mesh->GetCurvedElements().IsHighOrder())
+	  {
+            int hoplotn = 1 << vispar.subdivisions;
+            // mesh->GetCurvedElements().GetNVisualSubsecs();
+
+            Point<3> x;
+            glBegin (GL_LINE_STRIP);
+
+            for (int j = 0; j <= hoplotn; j++)
+	      {
+		mesh->GetCurvedElements().CalcSegmentTransformation ((double) j/hoplotn, i-1, x);
+		glVertex3d (x(0), x(1), x(2));
+		/*
+		  cout << "x = " << x(0) << ", " << x(1) << ", " << x(2)
+		  << ", norm = 1+" << sqrt(x(0)*x(0)+x(1)*x(1))-1
+		  << ", phi = " << atan2(x(1), x(0))/M_PI << endl;
+		*/
+	      }
+
+            glEnd();
+
+	  }
+	else
+	  {
+            glBegin (GL_LINES);
+            Point<3> hp1 = p1;
+            Point<3> hp2 = p2;
+            Point<3> c = Center(p1, p2);
+            if (vispar.shrink < 1)
+	      {
+		hp1 = c + vispar.shrink * (hp1 - c);
+		hp2 = c + vispar.shrink * (hp2 - c);
+	      }
+            glVertex3dv (hp1);
+            glVertex3dv (hp2); // p2.X(), p2.Y(), p2.Z());
+            glEnd();
+	  }
+      }
+
+    glLineWidth (2);
+    glDisable (GL_POLYGON_OFFSET_LINE);
+
+    glDisable (GL_COLOR_MATERIAL);
+    glEnable (GL_LIGHTING);
+
+    glEndList();
+  }
+
+
+
+
+  void VisualSceneMesh :: BuildPointNumberList()
+  {
+    ;
+  }
+
+
+
+  // Bernstein Pol B_{n,i}(x) = n! / i! / (n-i)! (1-x)^{n-i} x^i
+  static inline double Bernstein (int n, int i, double x)
+  {
+    double val = 1;
+    for (int j = 1; j <= i; j++)
+      val *= x;
+    for (int j = 1; j <= n-i; j++)
+      val *= (1-x) * (j+i) / j;
+    return val;
+  }
+
+  void ToBernstein (int order, Point<3> * pts, int stride)
+  {
+    static DenseMatrix mat, inv;
+    static Vector vec1, vec2;
+
+    if (mat.Height () != order+1)
+      {
+	mat.SetSize (order+1);
+	inv.SetSize (order+1);
+	vec1.SetSize (order+1);
+	vec2.SetSize (order+1);
+	for (int i = 0; i <= order; i++)
+	  {
+            double x = double(i) / order;
+            for (int j = 0; j <= order; j++)
+	      mat(i,j) = Bernstein (order, j, x);
+	  }
+
+	CalcInverse (mat, inv);
+      }
+
+    for (int i = 0; i < 3; i++)
+      {
+	for (int j = 0; j <= order; j++)
+	  vec1(j) = pts[j*stride](i);
+
+	inv.Mult (vec1, vec2);
+
+	for (int j = 0; j <= order; j++)
+	  pts[j*stride](i) = vec2(j);
+      }
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  void VisualSceneMesh :: BuildTetList()
+  {
+
+    if (tettimestamp > mesh->GetTimeStamp () &&
+	tettimestamp > vispar.clipplanetimestamp )
+      return;
+
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    tettimestamp = NextTimeStamp();
+
+    if (tetlist)
+      glDeleteLists (tetlist, 1);
+
+
+    tetlist = glGenLists (1);
+    glNewList (tetlist, GL_COMPILE);
+
+
+    Vector locms;
+
+    // Philippose - 16/02/2010
+    // Add Mesh size based coloring of 
+    // meshes also for the volume elements
+    if (vispar.colormeshsize)
+      {
+	glEnable (GL_COLOR_MATERIAL);
+	locms.SetSize (mesh->GetNP());
+	maxh = -1;
+	minh = 1e99;
+	for (int i = 1; i <= locms.Size(); i++)
+	  {
+            Point3d p = mesh->Point(i);
+            locms(i-1) = mesh->GetH (p);
+            if (locms(i-1) > maxh) maxh = locms(i-1);
+            if (locms(i-1) < minh) minh = locms(i-1);
+	  }
+	if (!locms.Size())
+	  { 
+            minh = 1; 
+            maxh = 10; 
+	  }
+      }
+    else
+      glDisable (GL_COLOR_MATERIAL);
+
+
+
+    Array<Element2d> faces;
+
+    BitArray shownode(mesh->GetNP());
+    if (vispar.clipenable)
+      {
+	shownode.Clear();
+	for (int i = 1; i <= shownode.Size(); i++)
+	  {
+            Point<3> p = mesh->Point(i);
+
+            double val =
+	      p[0] * clipplane[0] +
+	      p[1] * clipplane[1] +
+	      p[2] * clipplane[2] +
+	      clipplane[3];
+
+            if (val > 0) shownode.Set (i);
+	  }
+      }
+    else
+      shownode.Set();
+
+
+    static float tetcols[][4] =
+      {
+	{ 1.0f, 1.0f, 0.0f, 1.0f },
+	{ 1.0f, 0.0f, 0.0f, 1.0f },
+	{ 0.0f, 1.0f, 0.0f, 1.0f },
+	{ 0.0f, 0.0f, 1.0f, 1.0f }
+	/*
+	{ 1.0f, 1.0f, 0.0f, 0.3f },
+	{ 1.0f, 0.0f, 0.0f, 0.3f },
+	{ 0.0f, 1.0f, 0.0f, 0.3f },
+	{ 0.0f, 0.0f, 1.0f, 0.3f }
+	*/
+      };
+
+    static float tetcols_ghost[4][4];
+
+    for (int j = 0; j < 4; j++)
+      {
+	for (int i = 0; i < 3; i++)
+	  tetcols_ghost[j][i] = tetcols[j][i];
+	tetcols_ghost[j][3] = 0.3; 
+      }
+
+
+    CurvedElements & curv = mesh->GetCurvedElements();
+
+
+    if (!curv.IsHighOrder())
+      glShadeModel (GL_FLAT);
+    else
+      glShadeModel (GL_SMOOTH);
+
+    int hoplotn = max (2, 1 << vispar.subdivisions);
+
+
+
+    for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++)
+      {
+	if (vispar.drawtetsdomain > 0)
+	  {
+	    int tetid = vispar.drawmetispartition ? 
+	      (*mesh)[ei].GetPartition() : (*mesh)[ei].GetIndex();
+	    
+	    if (vispar.drawtetsdomain != tetid) continue;
+	  }
+
+	const Element & el = (*mesh)[ei];
+
+	if ((el.GetType() == TET || el.GetType() == TET10) && !el.IsDeleted())
+	  {
+
+            bool drawtet = 1;
+            for (int j = 0; j < 4; j++)
+	      if (!shownode.Test(el[j]))
+		drawtet = 0;
+            if (!drawtet) continue;
+
+            int ind = el.GetIndex() % 4;
+
+            if (vispar.drawmetispartition && el.GetPartition()!=-1)
+	      ind = el.GetPartition() % 4;
+
+            if ( el.IsGhost() )
+	      glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, tetcols_ghost[ind]);
+            else
+	      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, tetcols[ind]);
+
+
+            if (curv.IsHighOrder()) //  && curv.IsElementCurved(ei))
+	      {
+		const ELEMENT_FACE * faces = MeshTopology :: GetFaces1 (TET);
+		const Point3d * vertices = MeshTopology :: GetVertices (TET);
+
+		/*
+		  Point<3> grid[11][11];
+		  Point<3> fpts[3];
+		  int order = vispar.subdivisions+1;
+
+		  for (int trig = 0; trig < 4; trig++)
+		  {
+		  for (int j = 0; j < 3; j++)
+		  fpts[j] = vertices[faces[trig][j]-1];
+
+		  static Point<3> c(0.25, 0.25, 0.25);
+		  if (vispar.shrink < 1)
+		  for (int j = 0; j < 3; j++)
+		  fpts[j] += (1-vispar.shrink) * (c-fpts[j]);
+
+		  for (int ix = 0; ix <= order; ix++)
+		  for (int iy = 0; iy <= order; iy++)
+		  {
+		  double lami[3] =
+		  { (1-double(ix)/order) * (1-double(iy)/order),
+		  (  double(ix)/order) * (1-double(iy)/order),
+		  double(iy)/order };
+
+		  Point<3> xl;
+		  for (int l = 0; l < 3; l++)
+		  xl(l) = lami[0] * fpts[0](l) + lami[1] * fpts[1](l) +
+		  lami[2] * fpts[2](l);
+
+		  curv.CalcElementTransformation (xl, i-1, grid[ix][iy]);
+		  }
+
+		  for (int j = 0; j <= order; j++)
+		  ToBernstein (order, &grid[j][0], &grid[0][1]-&grid[0][0]);
+		  for (int j = 0; j <= order; j++)
+		  ToBernstein (order, &grid[0][j], &grid[1][0]-&grid[0][0]);
+
+		  glMap2d(GL_MAP2_VERTEX_3,
+		  0.0, 1.0, &grid[0][1](0)-&grid[0][0](0), order+1,
+		  0.0, 1.0, &grid[1][0](0)-&grid[0][0](0), order+1,
+		  &grid[0][0](0));
+		  glEnable(GL_MAP2_VERTEX_3);
+		  glEnable(GL_AUTO_NORMAL);
+
+		  glMapGrid2f(8, 0.0, 0.999, 8, 0.0, 1.0);
+		  glEvalMesh2(GL_FILL, 0, 8, 0, 8);
+
+		  glDisable (GL_AUTO_NORMAL);
+		  glDisable (GL_MAP2_VERTEX_3);
+		  }
+		*/
+
+
+
+		int order = curv.GetOrder();
+
+		Array<Point<3> > ploc ( (order+1)*(order+1) );
+		Array<Point<3> > pglob ( (order+1)*(order+1) );
+		Point<3> fpts[3];
+
+		for (int trig = 0; trig < 4; trig++)
+		  {
+		    for (int j = 0; j < 3; j++)
+		      fpts[j] = vertices[faces[trig][j]-1];
+
+		    static Point<3> c(0.25, 0.25, 0.25);
+		    if (vispar.shrink < 1)
+		      for (int j = 0; j < 3; j++)
+                        fpts[j] += (1-vispar.shrink) * (c-fpts[j]);
+
+		    for (int ix = 0, ii = 0; ix <= order; ix++)
+		      for (int iy = 0; iy <= order; iy++, ii++)
+			{
+			  double lami[3] =
+			    { (1-double(ix)/order) * (1-double(iy)/order),
+			      (  double(ix)/order) * (1-double(iy)/order),
+			      double(iy)/order };
+
+			  Point<3> xl;
+			  for (int l = 0; l < 3; l++)
+			    xl(l) = lami[0] * fpts[0](l) + lami[1] * fpts[1](l) +
+			      lami[2] * fpts[2](l);
+
+			  ploc[ii] = xl;
+			}
+
+		    curv.CalcMultiPointElementTransformation (&ploc, ei, &pglob, 0);
+
+		    Point<3> grid[11][11];
+		    for (int ix = 0, ii = 0; ix <= order; ix++)
+		      for (int iy = 0; iy <= order; iy++, ii++)
+			grid[ix][iy] = pglob[ii];
+
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[j][0], &grid[0][1]-&grid[0][0]);
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[0][j], &grid[1][0]-&grid[0][0]);
+
+		    glMap2d(GL_MAP2_VERTEX_3,
+			    0.0, 1.0, &grid[0][1](0)-&grid[0][0](0), order+1,
+			    0.0, 1.0, &grid[1][0](0)-&grid[0][0](0), order+1,
+			    &grid[0][0](0));
+		    glEnable(GL_MAP2_VERTEX_3);
+		    glEnable(GL_AUTO_NORMAL);
+
+		    glMapGrid2f(hoplotn, 0.0, 0.9999f, hoplotn, 0.0, 1.0);
+		    glEvalMesh2(GL_FILL, 0, hoplotn, 0, hoplotn);
+
+		    glDisable (GL_AUTO_NORMAL);
+		    glDisable (GL_MAP2_VERTEX_3);
+		  }
+	      }
+
+            else // Not High Order
+
+	      {
+		Point<3> pts[4];
+		for (int j = 0; j < 4; j++)
+                  pts[j] = (*mesh)[el[j]];
+
+		if (vispar.shrink < 1)
+		  {
+		    Point<3> c = Center (pts[0], pts[1], pts[2], pts[3]);
+		    for (int j = 0; j < 4; j++)
+		      pts[j] = c + vispar.shrink * (pts[j]-c);
+		  }
+
+
+		Vec<3> n;
+
+		glBegin (GL_TRIANGLE_STRIP);
+
+		// Philippose - 16/02/2010
+		// Add Mesh size based coloring of 
+		// meshes also for the volume elements
+		if(vispar.colormeshsize)
+		  {
+		    n = Cross (pts[1]-pts[0], pts[2]-pts[0]);
+		    glNormal3dv (n);
+
+		    SetOpenGlColor (locms(el[0]-1), minh, maxh, 0);
+		    glVertex3dv (pts[0]);
+
+		    SetOpenGlColor (locms(el[1]-1), minh, maxh, 0);
+		    glVertex3dv (pts[1]);
+
+		    SetOpenGlColor (locms(el[2]-1), minh, maxh, 0);
+		    glVertex3dv (pts[2]);
+
+		    n = Cross (pts[3]-pts[1], pts[2]-pts[1]);
+		    glNormal3dv (n);
+
+		    SetOpenGlColor (locms(el[3]-1), minh, maxh, 0);
+		    glVertex3dv (pts[3]);
+
+		    n = Cross (pts[3]-pts[2], pts[0]-pts[2]);
+		    glNormal3dv (n);
+
+		    SetOpenGlColor (locms(el[0]-1), minh, maxh, 0);
+		    glVertex3dv (pts[0]);
+
+		    n = Cross (pts[1]-pts[3], pts[0]-pts[3]);
+		    glNormal3dv (n);
+
+		    SetOpenGlColor (locms(el[1]-1), minh, maxh, 0);
+		    glVertex3dv (pts[1]);
+		  }
+		else // Do not color mesh based on mesh size
+		  {
+		    glNormal3dv (Cross (pts[1]-pts[0], pts[2]-pts[0]));
+
+		    glVertex3dv (pts[0]);
+		    glVertex3dv (pts[1]);
+		    glVertex3dv (pts[2]);
+
+		    glNormal3dv (Cross (pts[3]-pts[1], pts[2]-pts[1]));
+		    glVertex3dv (pts[3]);
+
+		    glNormal3dv (Cross (pts[3]-pts[2], pts[0]-pts[2]));
+		    glVertex3dv (pts[0]);
+
+		    glNormal3dv (Cross (pts[1]-pts[3], pts[0]-pts[3]));
+		    glVertex3dv (pts[1]);
+		  }
+
+		glEnd();
+	      }
+	  }
+      }
+
+    glEndList ();
+  }
+
+
+
+
+  void VisualSceneMesh :: BuildPrismList()
+  {
+    if (prismtimestamp > mesh->GetTimeStamp () &&
+	prismtimestamp > vispar.clipplanetimestamp )
+      return;
+
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    prismtimestamp = NextTimeStamp();
+
+
+
+    if (prismlist)
+      glDeleteLists (prismlist, 1);
+
+    prismlist = glGenLists (1);
+    glNewList (prismlist, GL_COMPILE);
+
+    static float prismcol[] = { 0.0f, 1.0f, 1.0f, 1.0f };
+    glLineWidth (1.0f);
+
+    Array<Element2d> faces;
+
+
+    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, prismcol);
+
+    for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++)
+      {
+	const Element & el = (*mesh)[ei];
+	if (el.GetType() == PRISM && !el.IsDeleted())
+	  {
+            int j;
+            int i = ei + 1;
+
+            CurvedElements & curv = mesh->GetCurvedElements();
+            if (curv.IsHighOrder()) //  && curv.IsElementCurved(ei))
+	      {
+		const ELEMENT_FACE * faces = MeshTopology :: GetFaces1 (PRISM);
+		const Point3d * vertices = MeshTopology :: GetVertices (PRISM);
+
+		Point<3> grid[11][11];
+		Point<3> fpts[4];
+		int order = vispar.subdivisions+1;
+
+		for (int trig = 0; trig < 2; trig++)
+		  {
+		    for (int j = 0; j < 3; j++)
+		      fpts[j] = vertices[faces[trig][j]-1];
+
+		    static Point<3> c(1.0/3.0, 1.0/3.0, 0.5);
+		    if (vispar.shrink < 1)
+		      for (int j = 0; j < 3; j++)
+                        fpts[j] += (1-vispar.shrink) * (c-fpts[j]);
+
+		    for (int ix = 0; ix <= order; ix++)
+		      for (int iy = 0; iy <= order; iy++)
+			{
+			  double lami[3] =
+			    { (1-double(ix)/order) * (1-double(iy)/order),
+			      (  double(ix)/order) * (1-double(iy)/order),
+			      double(iy)/order };
+
+			  Point<3> xl;
+			  for (int l = 0; l < 3; l++)
+			    xl(l) = lami[0] * fpts[0](l) + lami[1] * fpts[1](l) +
+			      lami[2] * fpts[2](l);
+
+			  curv.CalcElementTransformation (xl, i-1, grid[ix][iy]);
+			}
+
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[j][0], &grid[0][1]-&grid[0][0]);
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[0][j], &grid[1][0]-&grid[0][0]);
+
+		    glMap2d(GL_MAP2_VERTEX_3,
+			    0.0, 1.0, &grid[0][1](0)-&grid[0][0](0), order+1,
+			    0.0, 1.0, &grid[1][0](0)-&grid[0][0](0), order+1,
+			    &grid[0][0](0));
+		    glEnable(GL_MAP2_VERTEX_3);
+		    glEnable(GL_AUTO_NORMAL);
+
+		    glMapGrid2f(8, 0.0, 0.999f, 8, 0.0, 1.0);
+		    glEvalMesh2(GL_FILL, 0, 8, 0, 8);
+
+		    glDisable (GL_AUTO_NORMAL);
+		    glDisable (GL_MAP2_VERTEX_3);
+		  }
+
+		for (int quad = 2; quad < 5; quad++)
+		  {
+		    for (int j = 0; j < 4; j++)
+		      fpts[j] = vertices[faces[quad][j]-1];
+
+		    static Point<3> c(1.0/3.0, 1.0/3.0, 0.5);
+		    if (vispar.shrink < 1)
+		      for (int j = 0; j < 4; j++)
+                        fpts[j] += (1-vispar.shrink) * (c-fpts[j]);
+
+		    for (int ix = 0; ix <= order; ix++)
+		      for (int iy = 0; iy <= order; iy++)
+			{
+			  double lami[4] =
+			    { (1-double(ix)/order) * (1-double(iy)/order),
+			      (  double(ix)/order) * (1-double(iy)/order),
+			      (  double(ix)/order) * (  double(iy)/order),
+			      (1-double(ix)/order) * (  double(iy)/order) };
+
+			  Point<3> xl;
+			  for (int l = 0; l < 3; l++)
+			    xl(l) =
+			      lami[0] * fpts[0](l) + lami[1] * fpts[1](l) +
+			      lami[2] * fpts[2](l) + lami[3] * fpts[3](l);
+
+			  curv.CalcElementTransformation (xl, ei, grid[ix][iy]);
+			}
+
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[j][0], &grid[0][1]-&grid[0][0]);
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[0][j], &grid[1][0]-&grid[0][0]);
+
+		    glMap2d(GL_MAP2_VERTEX_3,
+			    0.0, 1.0, &grid[0][1](0)-&grid[0][0](0), order+1,
+			    0.0, 1.0, &grid[1][0](0)-&grid[0][0](0), order+1,
+			    &grid[0][0](0));
+		    glEnable(GL_MAP2_VERTEX_3);
+		    glEnable(GL_AUTO_NORMAL);
+
+		    glMapGrid2f(8, 0.0, 1.0, 8, 0.0, 1.0);
+		    glEvalMesh2(GL_FILL, 0, 8, 0, 8);
+
+		    glDisable (GL_AUTO_NORMAL);
+		    glDisable (GL_MAP2_VERTEX_3);
+		  }
+
+
+
+
+
+		/*
+		  int hoplotn = 1 << vispar.subdivisions;
+		  // int hoplotn = curv.GetNVisualSubsecs();
+
+		  const Point3d * facepoint = MeshTopology :: GetVertices (TRIG);
+		  const ELEMENT_FACE * elface = MeshTopology :: GetFaces(TRIG);
+
+		  glBegin (GL_TRIANGLES);
+
+		  for (int trig = 0; trig<2; trig++)
+		  {
+
+		  Vec<3> x0,x1,d0,d1;
+		  x0 = facepoint[1] - facepoint[2];
+		  x1 = facepoint[0] - facepoint[2];
+		  x0.Normalize();
+		  x1.Normalize();
+		  if (trig == 1) swap (x0,x1);
+
+		  Point<3> xr[3];
+		  Point<3> xg;
+		  Vec<3> dx, dy, dz, n;
+
+		  for (int i1 = 0; i1 < hoplotn; i1++)
+		  for (int j1 = 0; j1 < hoplotn-i1; j1++)
+		  for (int k = 0; k < 2; k++)
+		  {
+		  if (k == 0)
+		  {
+		  xr[0](0) = (double)    i1/hoplotn; xr[0](1) = (double)    j1/hoplotn;
+		  xr[1](0) = (double)(i1+1)/hoplotn; xr[1](1) = (double)    j1/hoplotn;
+		  xr[2](0) = (double)    i1/hoplotn; xr[2](1) = (double)(j1+1)/hoplotn;
+		  } else
+		  {
+		  if (j1 == hoplotn-i1-1) continue;
+		  xr[0](0) = (double)(i1+1)/hoplotn; xr[0](1) = (double)    j1/hoplotn;
+		  xr[1](0) = (double)(i1+1)/hoplotn; xr[1](1) = (double)(j1+1)/hoplotn;
+		  xr[2](0) = (double)    i1/hoplotn; xr[2](1) = (double)(j1+1)/hoplotn;
+		  };
+
+		  for (int l=0; l<3; l++)
+		  {
+		  Mat<3,3> dxdxi;
+		  xr[l](2) = (double) trig;
+		  curv.CalcElementTransformation (xr[l], i-1, xg, dxdxi);
+		  for (int i = 0; i < 3; i++)
+		  {
+		  dx(i) = dxdxi(i,0);
+		  dy(i) = dxdxi(i,1);
+		  dz(i) = dxdxi(i,2);
+		  }
+
+		  Vec<3> d0 = x0(0)*dx + x0(1)*dy + x0(2)*dz;
+		  Vec<3> d1 = x1(0)*dx + x1(1)*dy + x1(2)*dz;
+		  n = Cross (d1, d0);
+		  glNormal3d (n(0), n(1), n(2));
+		  glVertex3d (xg(0), xg(1), xg(2));
+		  }
+		  }
+
+		  }
+
+		  glEnd ();
+
+		  glBegin (GL_QUADS);
+
+		  for (int quad = 0; quad<3; quad++)
+		  {
+		  const Point3d * facepoint = MeshTopology :: GetVertices (PRISM);
+
+		  Vec<3> x0,x1;
+		  int xyz;
+
+		  switch (quad)
+		  {
+		  case 0:
+		  x0 = facepoint[5] - facepoint[2];
+		  x1 = facepoint[0] - facepoint[2];
+		  xyz = 0;
+		  break;
+		  case 1:
+		  x0 = facepoint[4] - facepoint[0];
+		  x1 = facepoint[1] - facepoint[0];
+		  xyz = 0;
+		  break;
+		  case 2:
+		  x0 = facepoint[1] - facepoint[2];
+		  x1 = facepoint[5] - facepoint[2];
+		  xyz = 1;
+		  break;
+		  }
+
+		  x0.Normalize();
+		  x1.Normalize();
+
+		  swap (x0,x1);
+
+		  Point<3> xr[4];
+		  Point<3> xg;
+		  Vec<3> dx, dy, dz, n;
+
+		  for (int i1 = 0; i1 < hoplotn; i1++)
+		  for (int j1 = 0; j1 < hoplotn; j1++)
+		  {
+		  xr[0](xyz) = (double)    i1/hoplotn; xr[0](2) = (double)    j1/hoplotn;
+		  xr[1](xyz) = (double)(i1+1)/hoplotn; xr[1](2) = (double)    j1/hoplotn;
+		  xr[2](xyz) = (double)(i1+1)/hoplotn; xr[2](2) = (double)(j1+1)/hoplotn;
+		  xr[3](xyz) = (double)    i1/hoplotn; xr[3](2) = (double)(j1+1)/hoplotn;
+
+		  for (int l=0; l<4; l++)
+		  {
+		  switch (quad)
+		  {
+		  case 0: xr[l](1) = 0; break;
+		  case 1: xr[l](1) = 1-xr[l](0); break;
+		  case 2: xr[l](0) = 0; break;
+		  }
+
+		  Mat<3,3> dxdxi;
+		  curv.CalcElementTransformation (xr[l], i-1, xg, dxdxi);
+		  for (int i = 0; i < 3; i++)
+		  {
+		  dx(i) = dxdxi(i,0);
+		  dy(i) = dxdxi(i,1);
+		  dz(i) = dxdxi(i,2);
+		  }
+
+		  Vec<3> d0 = x0(0)*dx + x0(1)*dy + x0(2)*dz;
+		  Vec<3> d1 = x1(0)*dx + x1(1)*dy + x1(2)*dz;
+		  n = Cross (d1, d0);
+		  glNormal3d (n(0), n(1), n(2));
+		  glVertex3d (xg(0), xg(1), xg(2));
+		  }
+		  }
+		  }
+		  glEnd ();
+		*/
+	      }
+            else
+	      {
+		Point3d c(0,0,0);
+		if (vispar.shrink < 1)
+		  {
+		    for (j = 1; j <= 6; j++)
+		      {
+			Point3d p = mesh->Point(el.PNum(j));
+			c.X() += p.X() / 6;
+			c.Y() += p.Y() / 6;
+			c.Z() += p.Z() / 6;
+		      }
+		  }
+
+		el.GetSurfaceTriangles (faces);
+		glBegin (GL_TRIANGLES);
+		for (j = 1; j <= faces.Size(); j++)
+		  {
+		    Element2d & face = faces.Elem(j);
+		    Point3d lp1 = mesh->Point (el.PNum(face.PNum(1)));
+		    Point3d lp2 = mesh->Point (el.PNum(face.PNum(2)));
+		    Point3d lp3 = mesh->Point (el.PNum(face.PNum(3)));
+		    Vec3d n = Cross (Vec3d (lp1, lp3), Vec3d (lp1, lp2));
+		    n /= (n.Length()+1e-12);
+		    glNormal3d (n.X(), n.Y(), n.Z());
+		    if (vispar.shrink < 1)
+		      {
+			lp1 = c + vispar.shrink * (lp1 - c);
+			lp2 = c + vispar.shrink * (lp2 - c);
+			lp3 = c + vispar.shrink * (lp3 - c);
+		      }
+		    glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+		    glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+		    glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+		  }
+
+		glEnd();
+	      }
+	  }
+      }
+    glEndList ();
+  }
+
+
+
+
+  void VisualSceneMesh :: BuildHexList()
+  {
+    if (hextimestamp > mesh->GetTimeStamp () &&
+	hextimestamp > vispar.clipplanetimestamp )
+      return;
+
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    hextimestamp = NextTimeStamp();
+
+    if (hexlist) glDeleteLists (hexlist, 1);
+
+    hexlist = glGenLists (1);
+    glNewList (hexlist, GL_COMPILE);
+
+
+    static float hexcol[] = { 1.0f, 1.0f, 0.0f, 1.0f };
+    glLineWidth (1.0f);
+    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, hexcol);
+
+    Array<Element2d> faces;
+    // int hoplotn = 1 << vispar.subdivisions;
+
+    for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++)
+      {
+	const Element & el = (*mesh)[ei];
+	if (el.GetType() == HEX && !el.IsDeleted())
+	  {
+            CurvedElements & curv = mesh->GetCurvedElements();
+            if (curv.IsHighOrder()) //  && curv.IsElementCurved(ei))
+	      {
+		/* // classical
+		   glBegin (GL_QUADS);
+
+		   const ELEMENT_FACE * faces = MeshTopology :: GetFaces (HEX);
+		   const Point3d * vertices = MeshTopology :: GetVertices (HEX);
+
+		   Point<3> grid[33][33];
+		   Vec<3> gridn[33][33];
+		   Point<3> fpts[4];
+		   for (int quad = 0; quad<6; quad++)
+		   {
+		   for (int j = 0; j < 4; j++)
+		   fpts[j] = vertices[faces[quad][j]-1];
+
+		   static Point<3> c(0.5, 0.5, 0.5);
+		   if (vispar.shrink < 1)
+		   for (int j = 0; j < 4; j++)
+		   fpts[j] += (1-vispar.shrink) * (c-fpts[j]);
+
+		   Vec<3> taux = fpts[1]-fpts[0];
+		   Vec<3> tauy = fpts[3]-fpts[0];
+
+		   for (int ix = 0; ix <= hoplotn; ix++)
+		   for (int iy = 0; iy <= hoplotn; iy++)
+		   {
+		   Point<3> xl;
+		   Mat<3,3> dxdxi;
+		   double lami[4] =
+		   { (1-double(ix)/hoplotn) * (1-double(iy)/hoplotn),
+		   (  double(ix)/hoplotn) * (1-double(iy)/hoplotn),
+		   (  double(ix)/hoplotn) * (  double(iy)/hoplotn),
+		   (1-double(ix)/hoplotn) * (  double(iy)/hoplotn) };
+		   for (int l = 0; l < 3; l++)
+		   xl(l) = lami[0] * fpts[0](l) + lami[1] * fpts[1](l) +
+		   lami[2] * fpts[2](l) + lami[3] * fpts[3](l);
+
+		   curv.CalcElementTransformation (xl, ei, grid[ix][iy], dxdxi);
+
+		   Vec<3> gtaux = dxdxi * taux;
+		   Vec<3> gtauy = dxdxi * tauy;
+		   gridn[ix][iy] = Cross (gtauy, gtaux).Normalize();
+		   }
+
+		   for (int ix = 0; ix < hoplotn; ix++)
+		   for (int iy = 0; iy < hoplotn; iy++)
+		   {
+		   glNormal3dv (gridn[ix][iy]);
+		   glVertex3dv (grid[ix][iy]);
+
+		   glNormal3dv (gridn[ix+1][iy]);
+		   glVertex3dv (grid[ix+1][iy]);
+
+		   glNormal3dv (gridn[ix+1][iy+1]);
+		   glVertex3dv (grid[ix+1][iy+1]);
+
+		   glNormal3dv (gridn[ix][iy+1]);
+		   glVertex3dv (grid[ix][iy+1]);
+		   }
+		   }
+
+		   glEnd ();
+		*/
+
+		const ELEMENT_FACE * faces = MeshTopology :: GetFaces1 (HEX);
+		const Point3d * vertices = MeshTopology :: GetVertices (HEX);
+
+		Point<3> grid[11][11];
+		Point<3> fpts[4];
+		int order = vispar.subdivisions+1;
+
+		for (int quad = 0; quad<6; quad++)
+		  {
+		    for (int j = 0; j < 4; j++)
+		      fpts[j] = vertices[faces[quad][j]-1];
+
+		    static Point<3> c(0.5, 0.5, 0.5);
+		    if (vispar.shrink < 1)
+		      for (int j = 0; j < 4; j++)
+                        fpts[j] += (1-vispar.shrink) * (c-fpts[j]);
+
+		    for (int ix = 0; ix <= order; ix++)
+		      for (int iy = 0; iy <= order; iy++)
+			{
+			  double lami[4] =
+			    { (1-double(ix)/order) * (1-double(iy)/order),
+			      (  double(ix)/order) * (1-double(iy)/order),
+			      (  double(ix)/order) * (  double(iy)/order),
+			      (1-double(ix)/order) * (  double(iy)/order) };
+
+			  Point<3> xl;
+			  for (int l = 0; l < 3; l++)
+			    xl(l) = lami[0] * fpts[0](l) + lami[1] * fpts[1](l) +
+			      lami[2] * fpts[2](l) + lami[3] * fpts[3](l);
+
+			  curv.CalcElementTransformation (xl, ei, grid[ix][iy]);
+			}
+
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[j][0], &grid[0][1]-&grid[0][0]);
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[0][j], &grid[1][0]-&grid[0][0]);
+
+		    glMap2d(GL_MAP2_VERTEX_3,
+			    0.0, 1.0, &grid[0][1](0)-&grid[0][0](0), order+1,
+			    0.0, 1.0, &grid[1][0](0)-&grid[0][0](0), order+1,
+			    &grid[0][0](0));
+		    glEnable(GL_MAP2_VERTEX_3);
+		    glEnable(GL_AUTO_NORMAL);
+
+		    glMapGrid2f(8, 0.0, 1.0, 8, 0.0, 1.0);
+		    glEvalMesh2(GL_FILL, 0, 8, 0, 8);
+
+		    glDisable (GL_AUTO_NORMAL);
+		    glDisable (GL_MAP2_VERTEX_3);
+		  }
+	      }
+            else
+	      {
+		Point3d c(0,0,0);
+		if (vispar.shrink < 1)
+		  {
+		    for (int j = 1; j <= 8; j++)
+		      {
+			Point3d p = mesh->Point(el.PNum(j));
+			c.X() += p.X();
+			c.Y() += p.Y();
+			c.Z() += p.Z();
+		      }
+		    c.X() /= 8;
+		    c.Y() /= 8;
+		    c.Z() /= 8;
+		  }
+
+		glBegin (GL_TRIANGLES);
+
+		el.GetSurfaceTriangles (faces);
+		for (int j = 1; j <= faces.Size(); j++)
+		  {
+		    Element2d & face = faces.Elem(j);
+		    Point<3> lp1 = mesh->Point (el.PNum(face.PNum(1)));
+		    Point<3> lp2 = mesh->Point (el.PNum(face.PNum(2)));
+		    Point<3> lp3 = mesh->Point (el.PNum(face.PNum(3)));
+		    Vec<3> n = Cross (lp3-lp1, lp2-lp1);
+		    n.Normalize();
+		    glNormal3dv (n);
+
+		    if (vispar.shrink < 1)
+		      {
+			lp1 = c + vispar.shrink * (lp1 - c);
+			lp2 = c + vispar.shrink * (lp2 - c);
+			lp3 = c + vispar.shrink * (lp3 - c);
+		      }
+
+		    glVertex3dv (lp1);
+		    glVertex3dv (lp2);
+		    glVertex3dv (lp3);
+		  }
+
+		glEnd();
+	      }
+	  }
+      }
+    glEndList ();
+  }
+
+
+
+
+
+
+
+
+
+  void VisualSceneMesh :: BuildPyramidList()
+  {
+    if (pyramidtimestamp > mesh->GetTimeStamp () &&
+	pyramidtimestamp > vispar.clipplanetimestamp )
+      return;
+
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    pyramidtimestamp = NextTimeStamp();
+
+
+    if (pyramidlist)
+      glDeleteLists (pyramidlist, 1);
+
+
+    pyramidlist = glGenLists (1);
+    glNewList (pyramidlist, GL_COMPILE);
+
+    static float pyramidcol[] = { 1.0f, 0.0f, 1.0f, 1.0f };
+    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, pyramidcol);
+
+    glLineWidth (1.0f);
+    Array<Element2d> faces;
+
+    for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++)
+      {
+	const Element & el = (*mesh)[ei];
+	if (el.GetType() == PYRAMID && !el.IsDeleted())
+	  {
+            int i = ei + 1;
+
+            CurvedElements & curv = mesh->GetCurvedElements();
+            if (curv.IsHighOrder()) //  && curv.IsElementCurved(ei))
+	      {
+
+		const ELEMENT_FACE * faces = MeshTopology :: GetFaces1 (PYRAMID);
+		const Point3d * vertices = MeshTopology :: GetVertices (PYRAMID);
+
+		Point<3> grid[11][11];
+		Point<3> fpts[4];
+		int order = vispar.subdivisions+1;
+
+		for (int trig = 0; trig < 4; trig++)
+		  {
+		    for (int j = 0; j < 3; j++)
+		      fpts[j] = vertices[faces[trig][j]-1];
+
+		    static Point<3> c(0.375, 0.375, 0.25);
+		    if (vispar.shrink < 1)
+		      for (int j = 0; j < 3; j++)
+                        fpts[j] += (1-vispar.shrink) * (c-fpts[j]);
+
+		    for (int ix = 0; ix <= order; ix++)
+		      for (int iy = 0; iy <= order; iy++)
+			{
+			  double lami[3] =
+			    { (1-double(ix)/order) * (1-double(iy)/order),
+			      (  double(ix)/order) * (1-double(iy)/order),
+			      double(iy)/order };
+
+			  Point<3> xl;
+			  for (int l = 0; l < 3; l++)
+			    xl(l) = lami[0] * fpts[0](l) + lami[1] * fpts[1](l) +
+			      lami[2] * fpts[2](l);
+
+			  curv.CalcElementTransformation (xl, i-1, grid[ix][iy]);
+			}
+
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[j][0], &grid[0][1]-&grid[0][0]);
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[0][j], &grid[1][0]-&grid[0][0]);
+
+		    glMap2d(GL_MAP2_VERTEX_3,
+			    0.0, 1.0, &grid[0][1](0)-&grid[0][0](0), order+1,
+			    0.0, 1.0, &grid[1][0](0)-&grid[0][0](0), order+1,
+			    &grid[0][0](0));
+		    glEnable(GL_MAP2_VERTEX_3);
+		    glEnable(GL_AUTO_NORMAL);
+
+		    glMapGrid2f(8, 0.0, 0.999f, 8, 0.0, 1.0);
+		    glEvalMesh2(GL_FILL, 0, 8, 0, 8);
+
+		    glDisable (GL_AUTO_NORMAL);
+		    glDisable (GL_MAP2_VERTEX_3);
+		  }
+
+		for (int quad = 4; quad < 5; quad++)
+		  {
+		    for (int j = 0; j < 4; j++)
+		      fpts[j] = vertices[faces[quad][j]-1];
+
+		    static Point<3> c(0.375, 0.375, 0.25);
+		    if (vispar.shrink < 1)
+		      for (int j = 0; j < 4; j++)
+                        fpts[j] += (1-vispar.shrink) * (c-fpts[j]);
+
+		    for (int ix = 0; ix <= order; ix++)
+		      for (int iy = 0; iy <= order; iy++)
+			{
+			  double lami[4] =
+			    { (1-double(ix)/order) * (1-double(iy)/order),
+			      (  double(ix)/order) * (1-double(iy)/order),
+			      (  double(ix)/order) * (  double(iy)/order),
+			      (1-double(ix)/order) * (  double(iy)/order) };
+
+			  Point<3> xl;
+			  for (int l = 0; l < 3; l++)
+			    xl(l) =
+			      lami[0] * fpts[0](l) + lami[1] * fpts[1](l) +
+			      lami[2] * fpts[2](l) + lami[3] * fpts[3](l);
+
+			  curv.CalcElementTransformation (xl, ei, grid[ix][iy]);
+			}
+
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[j][0], &grid[0][1]-&grid[0][0]);
+		    for (int j = 0; j <= order; j++)
+		      ToBernstein (order, &grid[0][j], &grid[1][0]-&grid[0][0]);
+
+		    glMap2d(GL_MAP2_VERTEX_3,
+			    0.0, 1.0, &grid[0][1](0)-&grid[0][0](0), order+1,
+			    0.0, 1.0, &grid[1][0](0)-&grid[0][0](0), order+1,
+			    &grid[0][0](0));
+		    glEnable(GL_MAP2_VERTEX_3);
+		    glEnable(GL_AUTO_NORMAL);
+
+		    glMapGrid2f(8, 0.0, 1.0, 8, 0.0, 1.0);
+		    glEvalMesh2(GL_FILL, 0, 8, 0, 8);
+
+		    glDisable (GL_AUTO_NORMAL);
+		    glDisable (GL_MAP2_VERTEX_3);
+		  }
+
+
+
+
+
+
+		/*
+		  int hoplotn = 1 << vispar.subdivisions;
+
+		  const ELEMENT_FACE * faces = MeshTopology :: GetFaces (PYRAMID);
+		  const Point3d * vertices = MeshTopology :: GetVertices (PYRAMID);
+
+		  Point<3> grid[33][33];
+		  Vec<3> gridn[33][33];
+
+
+		  glBegin (GL_TRIANGLES);
+
+		  for (int trig = 0; trig < 4; trig++)
+		  {
+		  Point<3> p0 = vertices[faces[trig][0]-1];
+		  Point<3> p1 = vertices[faces[trig][1]-1];
+		  Point<3> p2 = vertices[faces[trig][2]-1];
+
+		  if (vispar.shrink < 1)
+		  {
+		  static Point<3> c(0.375, 0.375, 0.25);
+		  p0 = c + vispar.shrink * (p0 - c);
+		  p1 = c + vispar.shrink * (p1 - c);
+		  p2 = c + vispar.shrink * (p2 - c);
+		  }
+
+
+		  Vec<3> taux = p0-p2;
+		  Vec<3> tauy = p1-p2;
+		  Vec<3> gtaux, gtauy;
+
+		  Point<3> xl;
+		  Mat<3,3> dxdxi;
+
+		  for (int ix = 0; ix <= hoplotn; ix++)
+		  for (int iy = 0; iy <= hoplotn-ix; iy++)
+		  {
+		  for (int l = 0; l < 3; l++)
+		  xl(l) =
+		  (1-double(ix+iy)/hoplotn) * p2(l) +
+		  (double(ix)/hoplotn) * p0(l) +
+		  (double(iy)/hoplotn) * p1(l);
+
+		  curv.CalcElementTransformation (xl, i-1, grid[ix][iy], dxdxi);
+
+		  gtaux = dxdxi * taux;
+		  gtauy = dxdxi * tauy;
+		  gridn[ix][iy] = Cross (gtauy, gtaux).Normalize();
+		  }
+
+		  for (int ix = 0; ix < hoplotn; ix++)
+		  for (int iy = 0; iy < hoplotn-ix; iy++)
+		  {
+		  glNormal3dv (gridn[ix][iy]);
+		  glVertex3dv (grid[ix][iy]);
+
+		  glNormal3dv (gridn[ix+1][iy]);
+		  glVertex3dv (grid[ix+1][iy]);
+
+		  glNormal3dv (gridn[ix][iy+1]);
+		  glVertex3dv (grid[ix][iy+1]);
+
+		  if (iy < hoplotn-ix-1)
+		  {
+		  glNormal3dv (gridn[ix][iy+1]);
+		  glVertex3dv (grid[ix][iy+1]);
+
+		  glNormal3dv (gridn[ix+1][iy]);
+		  glVertex3dv (grid[ix+1][iy]);
+
+		  glNormal3dv (gridn[ix+1][iy+1]);
+		  glVertex3dv (grid[ix+1][iy+1]);
+		  }
+		  }
+		  }
+
+		  glEnd ();
+
+
+
+
+		  glBegin (GL_QUADS);
+
+		  for (int quad = 4; quad < 5; quad++)
+		  {
+		  Point<3> p0 = vertices[faces[quad][0]-1];
+		  Point<3> p1 = vertices[faces[quad][1]-1];
+		  Point<3> p2 = vertices[faces[quad][2]-1];
+		  Point<3> p3 = vertices[faces[quad][3]-1];
+
+		  if (vispar.shrink < 1)
+		  {
+		  static Point<3> c(0.375, 0.375, 0.25);
+		  p0 = c + vispar.shrink * (p0 - c);
+		  p1 = c + vispar.shrink * (p1 - c);
+		  p2 = c + vispar.shrink * (p2 - c);
+		  p3 = c + vispar.shrink * (p3 - c);
+		  }
+
+		  Vec<3> taux = p1-p0;
+		  Vec<3> tauy = p3-p0;
+		  Vec<3> gtaux, gtauy;
+
+		  Point<3> xl, xg;
+		  Mat<3,3> dxdxi;
+
+		  for (int ix = 0; ix <= hoplotn; ix++)
+		  for (int iy = 0; iy <= hoplotn; iy++)
+		  {
+		  Point<3> xl;
+		  for (int l = 0; l < 3; l++)
+		  xl(l) =
+		  (1-double(ix)/hoplotn)*(1-double(iy)/hoplotn) * p0(l) +
+		  (  double(ix)/hoplotn)*(1-double(iy)/hoplotn) * p1(l) +
+		  (  double(ix)/hoplotn)*(  double(iy)/hoplotn) * p2(l) +
+		  (1-double(ix)/hoplotn)*(  double(iy)/hoplotn) * p3(l);
+
+		  curv.CalcElementTransformation (xl, i-1, grid[ix][iy], dxdxi);
+
+		  gtaux = dxdxi * taux;
+		  gtauy = dxdxi * tauy;
+		  gridn[ix][iy] = Cross (gtauy, gtaux).Normalize();
+		  }
+
+		  for (int ix = 0; ix < hoplotn; ix++)
+		  for (int iy = 0; iy < hoplotn; iy++)
+		  {
+		  glNormal3dv (gridn[ix][iy]);
+		  glVertex3dv (grid[ix][iy]);
+
+		  glNormal3dv (gridn[ix+1][iy]);
+		  glVertex3dv (grid[ix+1][iy]);
+
+		  glNormal3dv (gridn[ix+1][iy+1]);
+		  glVertex3dv (grid[ix+1][iy+1]);
+
+		  glNormal3dv (gridn[ix][iy+1]);
+		  glVertex3dv (grid[ix][iy+1]);
+		  }
+		  }
+
+		  glEnd ();
+		*/
+
+
+	      }
+            else
+	      {
+
+
+
+		Point3d c(0,0,0);
+		if (vispar.shrink < 1)
+		  {
+		    for (int j = 1; j <= 5; j++)
+		      {
+			Point3d p = mesh->Point(el.PNum(j));
+			c.X() += p.X() / 5;
+			c.Y() += p.Y() / 5;
+			c.Z() += p.Z() / 5;
+		      }
+		  }
+
+
+		el.GetSurfaceTriangles (faces);
+
+		if (el.PNum(1))
+		  {
+		    glBegin (GL_TRIANGLES);
+
+		    for (int j = 1; j <= faces.Size(); j++)
+		      {
+			Element2d & face = faces.Elem(j);
+			Point3d lp1 = mesh->Point (el.PNum(face.PNum(1)));
+			Point3d lp2 = mesh->Point (el.PNum(face.PNum(2)));
+			Point3d lp3 = mesh->Point (el.PNum(face.PNum(3)));
+			Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+			n /= (n.Length()+1e-12);
+			n *= -1;
+			glNormal3d (n.X(), n.Y(), n.Z());
+
+			if (vispar.shrink < 1)
+			  {
+			    lp1 = c + vispar.shrink * (lp1 - c);
+			    lp2 = c + vispar.shrink * (lp2 - c);
+			    lp3 = c + vispar.shrink * (lp3 - c);
+			  }
+
+			glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+			glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+			glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+		      }
+
+		    glEnd();
+		  }
+	      }
+	  }
+      }
+    glEndList ();
+  }
+
+  void VisualSceneMesh :: BuildBadelList()
+  {
+    ;
+  }
+
+  void VisualSceneMesh :: BuildIdentifiedList()
+  {
+    ;
+  }
+
+  void VisualSceneMesh :: BuildDomainSurfList()
+  {
+    if (domainsurflist)
+      glDeleteLists (domainsurflist, 1);
+
+    domainsurflist = glGenLists (1);
+    glNewList (domainsurflist, GL_COMPILE);
+
+    int i, j;
+    glLineWidth (1.0f);
+
+    glDisable (GL_COLOR_MATERIAL);
+
+    for (i = 1; i <= mesh->GetNSE(); i++)
+      {
+	Element2d el = mesh->SurfaceElement (i);
+
+	int drawel = 1;
+	for (j = 1; j <= el.GetNP(); j++)
+	  {
+            if (!el.PNum(j))
+	      drawel = 0;
+	  }
+
+	if (!drawel)
+	  continue;
+
+	if (el.GetIndex() < 1 || el.GetIndex() > mesh->GetNFD())
+	  continue;
+	int domin = mesh->GetFaceDescriptor(el.GetIndex()).DomainIn();
+	int domout = mesh->GetFaceDescriptor(el.GetIndex()).DomainOut();
+
+	int fac;
+	if (domin == vispar.drawdomainsurf)
+	  fac = 1;
+	else if (domout == vispar.drawdomainsurf)
+	  fac = -1;
+	else
+	  continue;
+
+
+	GLfloat matcol[] = { 1, 0, 0, 1 };
+	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, matcol);
+
+
+	if (el.GetNP() == 3)
+	  {
+            glBegin (GL_TRIANGLES);
+
+            const Point3d & lp1 = mesh->Point (el.PNum(1));
+            const Point3d & lp2 = mesh->Point (el.PNum(2));
+            const Point3d & lp3 = mesh->Point (el.PNum(3));
+            Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+            n /= ( fac * (n.Length()+1e-12));
+            glNormal3d (n.X(), n.Y(), n.Z());
+
+            if (!vispar.colormeshsize)
+	      {
+		glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+		glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+		glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	      }
+            glEnd();
+	  }
+	else if (el.GetNP() == 4)
+	  {
+            glBegin (GL_QUADS);
+
+            const Point3d & lp1 = mesh->Point (el.PNum(1));
+            const Point3d & lp2 = mesh->Point (el.PNum(2));
+            const Point3d & lp3 = mesh->Point (el.PNum(4));
+            const Point3d & lp4 = mesh->Point (el.PNum(3));
+            Vec3d n = Cross (Vec3d (lp1, lp2),
+			     Vec3d (lp1, Center (lp3, lp4)));
+            n /= (fac * (n.Length()+1e-12));
+            glNormal3d (n.X(), n.Y(), n.Z());
+            glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+            glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+            glVertex3d (lp4.X(), lp4.Y(), lp4.Z());
+            glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+            glEnd();
+	  }
+	else if (el.GetNP() == 6)
+	  {
+            glBegin (GL_TRIANGLES);
+            static int trigs[4][3] = {
+	      { 1, 6, 5 },
+	      { 2, 4, 6 },
+	      { 3, 5, 4 },
+	      { 4, 5, 6 } };
+
+	    for (j = 0; j < 4; j++)
+	      {
+		const Point3d & lp1 = mesh->Point (el.PNum(trigs[j][0]));
+		const Point3d & lp2 = mesh->Point (el.PNum(trigs[j][1]));
+		const Point3d & lp3 = mesh->Point (el.PNum(trigs[j][2]));
+		Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+		n /= (fac * (n.Length() + 1e-12));
+		glNormal3d (n.X(), n.Y(), n.Z());
+		glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+		glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+		glVertex3d (lp3.X(), lp3.Y(), lp3.Z());
+	      }
+	    glEnd();
+	  }
+      }
+    glEndList ();
+  }
+
+
+
+
+
+
+
+
+  void VisualSceneMesh :: MouseDblClick (int px, int py)
+  {
+    BuildFilledList (true);
+
+    MouseDblClickSelect(px,py,clipplane,backcolor,transformationmat,center,rad,
+			filledlist,selelement,selface,seledge,selpoint,selpoint2,locpi);
+
+
+    selecttimestamp = NextTimeStamp();
+
+    if(lock)
+      {
+	lock->UnLock();
+	delete lock;
+	lock = NULL;
+      }
+
+    /*
+      int i, hits;
+
+      // select surface triangle by mouse click
+
+      GLuint selbuf[10000];
+      glSelectBuffer (10000, selbuf);
+
+
+      glRenderMode (GL_SELECT);
+
+      GLint viewport[4];
+      glGetIntegerv (GL_VIEWPORT, viewport);
+
+
+      glMatrixMode (GL_PROJECTION);
+      glPushMatrix();
+
+      GLdouble projmat[16];
+      glGetDoublev (GL_PROJECTION_MATRIX, projmat);
+
+      glLoadIdentity();
+      gluPickMatrix (px, viewport[3] - py, 1, 1, viewport);
+      glMultMatrixd (projmat);
+
+
+
+      glClearColor(backcolor, backcolor, backcolor, 1.0);
+      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+      glMatrixMode (GL_MODELVIEW);
+
+      glPushMatrix();
+      glMultMatrixf (transformationmat);
+
+
+      //  SetClippingPlane();
+
+      glInitNames();
+      glPushName (1);
+
+      glPolygonOffset (1, 1);
+      glEnable (GL_POLYGON_OFFSET_FILL);
+
+      glDisable(GL_CLIP_PLANE0);
+
+      if (vispar.clipenable)
+      {
+      Vec<3> n(clipplane[0], clipplane[1], clipplane[2]);
+      double len = Abs(n);
+      double mu = -clipplane[3] / (len*len);
+      Point<3> p (mu * n);
+      n /= len;
+      Vec<3> t1 = n.GetNormal ();
+      Vec<3> t2 = Cross (n, t1);
+
+      double xi1mid = (center - p) * t1;
+      double xi2mid = (center - p) * t2;
+
+      glLoadName (0);
+      glBegin (GL_QUADS);
+      glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid-rad) * t2);
+      glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid-rad) * t2);
+      glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid+rad) * t2);
+      glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid+rad) * t2);
+      glEnd ();
+      }
+
+      //  SetClippingPlane();
+
+      glCallList (filledlist);
+
+      glDisable (GL_POLYGON_OFFSET_FILL);
+
+      glPopName();
+
+      glMatrixMode (GL_PROJECTION);
+      glPopMatrix();
+
+      glMatrixMode (GL_MODELVIEW);
+      glPopMatrix();
+
+      glFlush();
+
+
+      hits = glRenderMode (GL_RENDER);
+
+      //  cout << "hits = " << hits << endl;
+
+      int minname = 0;
+      GLuint mindepth = 0;
+
+      // find clippingplane
+      GLuint clipdepth = 0; // GLuint(-1);
+
+      for (i = 0; i < hits; i++)
+      {
+      int curname = selbuf[4*i+3];
+      if (!curname) clipdepth = selbuf[4*i+1];
+      }
+
+      for (i = 0; i < hits; i++)
+      {
+      int curname = selbuf[4*i+3];
+      GLuint curdepth = selbuf[4*i+1];
+
+      if (curname && (curdepth > clipdepth) &&
+      (curdepth < mindepth || !minname))
+      {
+      mindepth = curdepth;
+      minname = curname;
+      }
+      }
+
+      seledge = -1;
+      if (minname)
+      {
+      const Element2d & sel = mesh->SurfaceElement(minname);
+
+
+      cout << "select element " << minname
+      << " on face " << sel.GetIndex() << endl;
+      cout << "Nodes: ";
+      for (i = 1; i <= sel.GetNP(); i++)
+      cout << sel.PNum(i) << " ";
+      cout << endl;
+
+      selelement = minname;
+      selface = mesh->SurfaceElement(minname).GetIndex();
+
+      locpi = (locpi % sel.GetNP()) + 1;
+      selpoint2 = selpoint;
+      selpoint = sel.PNum(locpi);
+      cout << "selected point " << selpoint
+      << ", pos = " << mesh->Point (selpoint)
+      << endl;
+
+      for (i = 1; i <= mesh->GetNSeg(); i++)
+      {
+      const Segment & seg = mesh->LineSegment(i);
+      if (seg[0] == selpoint && seg[1] == selpoint2 ||
+      seg[1] == selpoint && seg[0] == selpoint2)
+      {
+      seledge = seg.edgenr;
+      cout << "seledge = " << seledge << endl;
+      }
+      }
+
+      }
+      else
+      {
+      selface = -1;
+      selelement = -1;
+      selpoint = -1;
+      selpoint2 = -1;
+      }
+
+      glDisable(GL_CLIP_PLANE0);
+
+      selecttimestamp = NextTimeStamp();
+    */
+
+  }
+
+
+
+
+
+  void MouseDblClickSelect (const int px, const int py,
+			    const GLdouble * clipplane, const GLdouble backcolor,
+			    const float * transformationmat,
+			    const Point3d & center,
+			    const double rad,
+			    const int displaylist,
+			    int & selelement, int & selface, int & seledge, int & selpoint,
+			    int & selpoint2, int & locpi)
+  {
+    int i, hits;
+
+    // select surface triangle by mouse click
+
+    GLuint selbuf[10000];
+    glSelectBuffer (10000, selbuf);
+
+
+    glRenderMode (GL_SELECT);
+
+    GLint viewport[4];
+    glGetIntegerv (GL_VIEWPORT, viewport);
+
+
+    glMatrixMode (GL_PROJECTION);
+    glPushMatrix();
+
+    GLdouble projmat[16];
+    glGetDoublev (GL_PROJECTION_MATRIX, projmat);
+
+    glLoadIdentity();
+    gluPickMatrix (px, viewport[3] - py, 1, 1, viewport);
+    glMultMatrixd (projmat);
+
+
+
+    glClearColor(backcolor, backcolor, backcolor, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glMatrixMode (GL_MODELVIEW);
+
+    glPushMatrix();
+    glMultMatrixf (transformationmat);
+
+
+    //  SetClippingPlane();
+
+    glInitNames();
+    glPushName (1);
+
+    glPolygonOffset (1, 1);
+    glEnable (GL_POLYGON_OFFSET_FILL);
+
+    glDisable(GL_CLIP_PLANE0);
+
+    if (vispar.clipenable)
+      {
+	Vec<3> n(clipplane[0], clipplane[1], clipplane[2]);
+	double len = Abs(n);
+	double mu = -clipplane[3] / (len*len);
+	Point<3> p (mu * n);
+	n /= len;
+	Vec<3> t1 = n.GetNormal ();
+	Vec<3> t2 = Cross (n, t1);
+
+	double xi1mid = (center - p) * t1;
+	double xi2mid = (center - p) * t2;
+
+	glLoadName (0);
+	glBegin (GL_QUADS);
+	glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid-rad) * t2);
+	glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid-rad) * t2);
+	glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid+rad) * t2);
+	glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid+rad) * t2);
+	glEnd ();
+      }
+
+    //  SetClippingPlane();
+    glCallList (displaylist);
+
+    glDisable (GL_POLYGON_OFFSET_FILL);
+
+    glPopName();
+
+    glMatrixMode (GL_PROJECTION);
+    glPopMatrix();
+
+    glMatrixMode (GL_MODELVIEW);
+    glPopMatrix();
+
+    glFlush();
+
+
+    hits = glRenderMode (GL_RENDER);
+    //cout << "hits = " << hits << endl;
+
+    int minname = 0;
+    GLuint mindepth = 0;
+
+    // find clippingplane
+    GLuint clipdepth = 0; // GLuint(-1);
+
+    for (i = 0; i < hits; i++)
+      {
+	int curname = selbuf[4*i+3];
+	if (!curname) clipdepth = selbuf[4*i+1];
+      }
+
+    for (i = 0; i < hits; i++)
+      {
+	int curname = selbuf[4*i+3];
+	GLuint curdepth = selbuf[4*i+1];
+	/*
+	  cout << selbuf[4*i] << " " << selbuf[4*i+1] << " "
+	  << selbuf[4*i+2] << " " << selbuf[4*i+3] << endl;
+	*/
+	if (curname && (curdepth > clipdepth) &&
+            (curdepth < mindepth || !minname))
+	  {
+            mindepth = curdepth;
+            minname = curname;
+	  }
+      }
+
+    seledge = -1;
+    if (minname)
+      {
+	const Element2d & sel = mesh->SurfaceElement(minname);
+
+
+	cout << "select element " << minname
+	     << " on face " << sel.GetIndex() << endl;
+	cout << "Nodes: ";
+	for (i = 1; i <= sel.GetNP(); i++)
+	  cout << sel.PNum(i) << " ";
+	cout << endl;
+
+	selelement = minname;
+	selface = mesh->SurfaceElement(minname).GetIndex();
+
+	locpi = (locpi % sel.GetNP()) + 1;
+	selpoint2 = selpoint;
+	selpoint = sel.PNum(locpi);
+	cout << "selected point " << selpoint
+	     << ", pos = " << mesh->Point (selpoint)
+	     << endl;
+
+	for (i = 1; i <= mesh->GetNSeg(); i++)
+	  {
+            const Segment & seg = mesh->LineSegment(i);
+            if ( (seg[0] == selpoint && seg[1] == selpoint2) ||
+		 (seg[1] == selpoint && seg[0] == selpoint2) )
+	      {
+		seledge = seg.edgenr;
+		cout << "seledge = " << seledge << endl;
+	      }
+	  }
+
+      }
+    else
+      {
+	selface = -1;
+	selelement = -1;
+	selpoint = -1;
+	selpoint2 = -1;
+      }
+
+    glDisable(GL_CLIP_PLANE0);
+
+
+
+#ifdef PARALLELGL
+    vsmesh.Broadcast ();
+#endif
+  }
+
+
+  void VisualSceneMesh :: SetSelectedFace (int asf)
+  {
+    selface = asf;
+    selecttimestamp = NextTimeStamp();
+  }
+
+
+
+
+}
+
+
diff --git a/contrib/Netgen/libsrc/visualization/vsocc.cpp b/contrib/Netgen/libsrc/visualization/vsocc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c16984ccc78e550af744e927ed9f0c1934144d7f
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vsocc.cpp
@@ -0,0 +1,762 @@
+#ifndef NOTCL
+
+#ifdef OCCGEOMETRY
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <meshing.hpp>
+
+#include <occgeom.hpp>
+
+#include "TopoDS_Shape.hxx"
+#include "TopoDS_Vertex.hxx"
+#include "TopExp_Explorer.hxx"
+#include "BRep_Tool.hxx"
+#include "TopoDS.hxx"
+#include "gp_Pnt.hxx"
+#include "Geom_Curve.hxx"
+#include "Poly_Triangulation.hxx"
+#include "Poly_Array1OfTriangle.hxx"
+#include "TColgp_Array1OfPnt2d.hxx"
+#include "Poly_Triangle.hxx"
+#include "Poly_Polygon3D.hxx"
+#include "Poly_PolygonOnTriangulation.hxx"
+
+#include <visual.hpp>
+
+namespace netgen
+{
+   extern OCCGeometry * occgeometry;
+
+   /* *********************** Draw OCC Geometry **************** */
+
+   VisualSceneOCCGeometry :: VisualSceneOCCGeometry ()
+   : VisualScene()
+   {
+      trilists.SetSize(0);
+      linelists.SetSize(1);
+
+   }
+
+   VisualSceneOCCGeometry :: ~VisualSceneOCCGeometry ()
+   {
+      ;
+   }
+
+   void VisualSceneOCCGeometry :: DrawScene ()
+   {
+      if ( occgeometry->changed )
+      {
+         BuildScene();
+         occgeometry -> changed = 0;
+      }
+
+      glClearColor(backcolor, backcolor, backcolor, 1.0);
+      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+      SetLight();
+
+      glPushMatrix();
+      glMultMatrixf (transformationmat);
+
+      glShadeModel (GL_SMOOTH);
+      glDisable (GL_COLOR_MATERIAL);
+      glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+      glEnable (GL_BLEND);
+      glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      
+      //  glEnable (GL_LIGHTING);
+
+      double shine = vispar.shininess;
+      // double transp = vispar.transp;
+
+      glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine);
+      glLogicOp (GL_COPY);
+
+      glEnable (GL_NORMALIZE);
+
+      float mat_col[] = {  0.2f, 0.2f, 0.8f, 1.0f};
+      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+      glPolygonOffset (1, 1);
+      glEnable (GL_POLYGON_OFFSET_FILL);
+
+      // Philippose - 30/01/2009
+      // Added clipping planes to Geometry view
+      SetClippingPlane();
+
+      GLfloat matcoledge[] = {  0, 0, 1, 1};
+      GLfloat matcolhiedge[] = {  1, 0, 0, 1};
+
+      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcoledge);
+      glLineWidth (1.0f);
+
+      if (vispar.occshowedges) glCallList (linelists.Get(1));
+      if (vispar.occshowsurfaces) glCallList (trilists.Get(1));
+
+      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolhiedge);
+      glLineWidth (5.0f);
+
+      if (vispar.occshowedges) glCallList (linelists.Get(2));
+
+      for (int i = 1; i <= occgeometry->vmap.Extent(); i++)
+      if (occgeometry->vvispar[i-1].IsHighlighted())
+      {
+         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolhiedge);
+         glLineWidth (5.0f);
+
+         glBegin (GL_LINES);
+
+         gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(occgeometry->vmap(i)));
+         double d = rad/100;
+         glVertex3f (p.X()-d, p.Y(), p.Z());
+         glVertex3f (p.X()+d, p.Y(), p.Z());
+         glVertex3f (p.X(), p.Y()-d, p.Z());
+         glVertex3f (p.X(), p.Y()+d, p.Z());
+         glVertex3f (p.X(), p.Y(), p.Z()-d);
+         glVertex3f (p.X(), p.Y(), p.Z()+d);
+         glEnd();
+      }
+
+      glDisable (GL_POLYGON_OFFSET_FILL);
+
+      glPopMatrix();
+      //  DrawCoordinateCross ();
+      //  DrawNetgenLogo ();
+      glFinish();
+
+      glDisable (GL_POLYGON_OFFSET_FILL);
+   }
+
+   /*
+    void VisualSceneOCCGeometry :: BuildScene (int zoomall)
+    {
+    int i = 0, j, k;
+
+    TopExp_Explorer ex, ex_edge;
+
+    if (vispar.occvisproblemfaces || (occgeometry -> changed != 2))
+    {
+    Box<3> bb = occgeometry -> GetBoundingBox();
+
+    center = bb.Center();
+    rad = bb.Diam() / 2;
+
+
+
+    if (vispar.occvisproblemfaces)
+    {
+    for (i = 1; i <= occgeometry->fmap.Extent(); i++)
+    if (occgeometry->facemeshstatus[i-1] == -1)
+    {
+    GProp_GProps system;
+    BRepGProp::LinearProperties(occgeometry->fmap(i), system);
+    gp_Pnt pnt = system.CentreOfMass();
+    center = Point<3> (pnt.X(), pnt.Y(), pnt.Z());
+    cout << "Setting center to mid of face " << i << " = " << center << endl;
+    }
+    }
+
+
+    CalcTransformationMatrices();
+    }
+
+
+    for (i = 1; i <= linelists.Size(); i++)
+    glDeleteLists (linelists.Elem(i), 1);
+    linelists.SetSize(0);
+
+    linelists.Append (glGenLists (1));
+    glNewList (linelists.Last(), GL_COMPILE);
+
+    i = 0;
+    for (ex_edge.Init(occgeometry -> shape, TopAbs_EDGE);
+    ex_edge.More(); ex_edge.Next())
+    {
+    if (BRep_Tool::Degenerated(TopoDS::Edge(ex_edge.Current()))) continue;
+    i++;
+
+
+    TopoDS_Edge edge = TopoDS::Edge(ex_edge.Current());
+
+    Handle(Poly_PolygonOnTriangulation) aEdgePoly;
+    Handle(Poly_Triangulation) T;
+    TopLoc_Location aEdgeLoc;
+    BRep_Tool::PolygonOnTriangulation(edge, aEdgePoly, T, aEdgeLoc);
+
+    if(aEdgePoly.IsNull())
+    {
+    cout << "cannot visualize edge " << i << endl;
+    continue;
+    }
+
+    glBegin (GL_LINE_STRIP);
+
+    int nbnodes = aEdgePoly -> NbNodes();
+    for (j = 1; j <= nbnodes; j++)
+    {
+    gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc);
+    glVertex3f (p.X(), p.Y(), p.Z());
+    }
+
+    glEnd ();
+
+
+    }
+
+    glEndList ();
+
+    for (i = 1; i <= trilists.Size(); i++)
+    glDeleteLists (trilists.Elem(i), 1);
+    trilists.SetSize(0);
+
+
+    trilists.Append (glGenLists (1));
+    glNewList (trilists.Last(), GL_COMPILE);
+
+    i = 0;
+
+    TopExp_Explorer exp0, exp1, exp2, exp3;
+    int shapenr = 0;
+    for (exp0.Init(occgeometry -> shape, TopAbs_SOLID); exp0.More(); exp0.Next())
+    {
+    shapenr++;
+
+    if (vispar.occshowvolumenr != 0 &&
+    vispar.occshowvolumenr != shapenr) continue;
+
+    float mat_col[4];
+    mat_col[3] = 1;
+    switch (shapenr)
+    {
+    case 1:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.8;
+    break;
+    case 2:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.8;
+    break;
+    case 3:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.8;
+    break;
+    case 4:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.2;
+    break;
+    case 5:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.8;
+    break;
+    case 6:
+    mat_col[0] = 0.6;
+    mat_col[1] = 0.6;
+    mat_col[2] = 0.6;
+    break;
+    case 7:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.2;
+    break;
+    case 8:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.2;
+    break;
+    default:
+    //	  mat_col[0] = 1-(1.0/double(shapenr));
+    //	  mat_col[1] = 0.5;
+    mat_col[0] = 0.5+double((shapenr*shapenr*shapenr*shapenr) % 10)/20.0;
+    mat_col[1] = 0.5+double(int(shapenr*shapenr*shapenr*shapenr*sin(double(shapenr))) % 10)/20.0;
+    mat_col[2] = 0.5+double((shapenr*shapenr*shapenr) % 10)/20.0;
+    }
+
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+    for (exp1.Init(exp0.Current(), TopAbs_SHELL); exp1.More(); exp1.Next())
+    for (exp2.Init(exp1.Current().Composed(exp0.Current().Orientation()), TopAbs_FACE); exp2.More(); exp2.Next())
+    {
+    TopoDS_Face face = TopoDS::Face (exp2.Current().Composed(exp1.Current().Orientation()));
+
+    i = occgeometry->fmap.FindIndex(face);
+
+    TopLoc_Location loc;
+    Handle(Geom_Surface) surf = BRep_Tool::Surface (face);
+    BRepAdaptor_Surface sf(face, Standard_False);
+    BRepLProp_SLProps prop(sf, 1, 1e-5);
+    Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc);
+
+    if (triangulation.IsNull())
+    {
+    cout << "cannot visualize face " << i << endl;
+    continue;
+    }
+
+    if (vispar.occvisproblemfaces)
+    {
+    switch (occgeometry->facemeshstatus[i-1])
+    {
+    case 0:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.8;
+    break;
+    case 1:
+    mat_col[0] = 0.2;
+    mat_col[1] = 0.8;
+    mat_col[2] = 0.2;
+    break;
+    case -1:
+    mat_col[0] = 0.8;
+    mat_col[1] = 0.2;
+    mat_col[2] = 0.2;
+    break;
+    }
+    glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+    }
+    glBegin (GL_TRIANGLES);
+
+    int ntriangles = triangulation -> NbTriangles();
+    for (j = 1; j <= ntriangles; j++)
+    {
+    Poly_Triangle triangle = (triangulation -> Triangles())(j);
+    for (k = 1; k <= 3; k++)
+    {
+    gp_Pnt2d uv = (triangulation -> UVNodes())(triangle(k));
+    gp_Pnt pnt;
+    gp_Vec du, dv;
+    prop.SetParameters (uv.X(), uv.Y());
+    surf->D0 (uv.X(), uv.Y(), pnt);
+    gp_Vec n;
+
+    if (prop.IsNormalDefined())
+    n = prop.Normal();
+    else
+    n = gp_Vec (0,0,0);
+
+    if (face.Orientation() == TopAbs_REVERSED) n *= -1;
+    glNormal3f (n.X(), n.Y(), n.Z());
+    glVertex3f (pnt.X(), pnt.Y(), pnt.Z());
+    }
+    }
+    glEnd ();
+
+    }
+    }
+
+
+    glEndList ();
+
+    }
+    */
+
+   void VisualSceneOCCGeometry :: BuildScene (int zoomall)
+   {
+     if (occgeometry -> changed == OCCGEOMETRYVISUALIZATIONFULLCHANGE)
+       {
+         occgeometry -> BuildVisualizationMesh (vispar.occdeflection);
+
+         center = occgeometry -> Center();
+         rad = occgeometry -> GetBoundingBox().Diam() / 2;
+
+         if (vispar.occzoomtohighlightedentity)
+         {
+            bool hilite = false;
+            bool hiliteonepoint = false;
+            Bnd_Box bb;
+
+            for (int i = 1; i <= occgeometry->fmap.Extent(); i++)
+            if (occgeometry->fvispar[i-1].IsHighlighted())
+            {
+               hilite = true;
+               BRepBndLib::Add (occgeometry->fmap(i), bb);
+            }
+
+            for (int i = 1; i <= occgeometry->emap.Extent(); i++)
+            if (occgeometry->evispar[i-1].IsHighlighted())
+            {
+               hilite = true;
+               BRepBndLib::Add (occgeometry->emap(i), bb);
+            }
+
+            for (int i = 1; i <= occgeometry->vmap.Extent(); i++)
+            if (occgeometry->vvispar[i-1].IsHighlighted())
+            {
+               hiliteonepoint = true;
+               BRepBndLib::Add (occgeometry->vmap(i), bb);
+            }
+
+            if (hilite || hiliteonepoint)
+            {
+               double x1,y1,z1,x2,y2,z2;
+               bb.Get (x1,y1,z1,x2,y2,z2);
+               Point<3> p1 = Point<3> (x1,y1,z1);
+               Point<3> p2 = Point<3> (x2,y2,z2);
+               Box<3> boundingbox(p1,p2);
+
+               center = boundingbox.Center();
+               if (hiliteonepoint)
+               rad = occgeometry -> GetBoundingBox().Diam() / 100;
+               else
+               rad = boundingbox.Diam() / 2;
+            }
+         }
+
+         CalcTransformationMatrices();
+      }
+
+      // Clear lists
+
+      for (int i = 1; i <= linelists.Size(); i++)
+      glDeleteLists (linelists.Elem(i), 1);
+      linelists.SetSize(0);
+
+      for (int i = 1; i <= trilists.Size(); i++)
+      glDeleteLists (trilists.Elem(i), 1);
+      trilists.SetSize(0);
+
+      // Total wireframe
+
+      linelists.Append (glGenLists (1));
+      glNewList (linelists.Last(), GL_COMPILE);
+
+      for (int i = 1; i <= occgeometry->emap.Extent(); i++)
+      {
+         TopoDS_Edge edge = TopoDS::Edge(occgeometry->emap(i));
+         if (BRep_Tool::Degenerated(edge)) continue;
+         if (occgeometry->evispar[i-1].IsHighlighted()) continue;
+
+         Handle(Poly_PolygonOnTriangulation) aEdgePoly;
+         Handle(Poly_Triangulation) T;
+         TopLoc_Location aEdgeLoc;
+         BRep_Tool::PolygonOnTriangulation(edge, aEdgePoly, T, aEdgeLoc);
+
+         if(aEdgePoly.IsNull())
+         {
+            (*testout) << "visualizing edge " << occgeometry->emap.FindIndex (edge)
+            << " without using the occ visualization triangulation" << endl;
+
+            double s0, s1;
+            Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1);
+
+            glBegin (GL_LINE_STRIP);
+            for (int i = 0; i<=50; i++)
+            {
+               gp_Pnt p = c->Value (s0 + i*(s1-s0)/50.0);
+               glVertex3f (p.X(),p.Y(),p.Z());
+            }
+            glEnd ();
+
+            continue;
+         }
+
+         int nbnodes = aEdgePoly -> NbNodes();
+         glBegin (GL_LINE_STRIP);
+         for (int j = 1; j <= nbnodes; j++)
+         {
+            gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc);
+            glVertex3f (p.X(), p.Y(), p.Z());
+         }
+         glEnd ();
+      }
+
+      glEndList ();
+
+      // Highlighted edge list
+
+      linelists.Append (glGenLists (1));
+      glNewList (linelists.Last(), GL_COMPILE);
+
+      for (int i = 1; i <= occgeometry->emap.Extent(); i++)
+      if (occgeometry->evispar[i-1].IsHighlighted())
+      {
+         TopoDS_Edge edge = TopoDS::Edge(occgeometry->emap(i));
+         if (BRep_Tool::Degenerated(edge)) continue;
+
+         Handle(Poly_PolygonOnTriangulation) aEdgePoly;
+         Handle(Poly_Triangulation) T;
+         TopLoc_Location aEdgeLoc;
+         BRep_Tool::PolygonOnTriangulation(edge, aEdgePoly, T, aEdgeLoc);
+
+         if(aEdgePoly.IsNull())
+         {
+            (*testout) << "visualizing edge " << occgeometry->emap.FindIndex (edge)
+            << " without using the occ visualization triangulation" << endl;
+
+            double s0, s1;
+            Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1);
+
+            glBegin (GL_LINE_STRIP);
+            for (int i = 0; i<=50; i++)
+            {
+               gp_Pnt p = c->Value (s0 + i*(s1-s0)/50.0);
+               glVertex3f (p.X(),p.Y(),p.Z());
+            }
+            glEnd ();
+
+            continue;
+         }
+
+         int nbnodes = aEdgePoly -> NbNodes();
+         glBegin (GL_LINE_STRIP);
+         for (int j = 1; j <= nbnodes; j++)
+         {
+            gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc);
+            glVertex3f (p.X(), p.Y(), p.Z());
+         }
+         glEnd ();
+      }
+
+      glEndList ();
+
+      // display faces
+
+      trilists.Append (glGenLists (1));
+      glNewList (trilists.Last(), GL_COMPILE);
+
+      for (int i = 1; i <= occgeometry->fmap.Extent(); i++)
+      {
+         if (!occgeometry->fvispar[i-1].IsVisible()) continue;
+
+         glLoadName (i);
+         float mat_col[4];
+         mat_col[3] = 1;
+
+         TopoDS_Face face = TopoDS::Face(occgeometry->fmap(i));
+
+         if (!occgeometry->fvispar[i-1].IsHighlighted())
+         {
+            // Philippose - 30/01/2009
+            // OpenCascade XDE Support
+            Quantity_Color face_colour;
+            // Philippose - 23/02/2009
+            // Check to see if colours have been extracted first!!
+            // Forum bug-fox (Jean-Yves - 23/02/2009)
+            if(!(occgeometry->face_colours.IsNull())
+               && (occgeometry->face_colours->GetColor(face,XCAFDoc_ColorSurf,face_colour)))
+            {
+               mat_col[0] = face_colour.Red();
+               mat_col[1] = face_colour.Green();
+               mat_col[2] = face_colour.Blue();
+            }
+            else
+            {
+               mat_col[0] = 0.0;
+               mat_col[1] = 1.0;
+               mat_col[2] = 0.0;
+            }
+         }
+         else
+         {
+            mat_col[0] = 0.8;
+            mat_col[1] = 0.2;
+            mat_col[2] = 0.2;
+         }
+
+         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col);
+
+         TopLoc_Location loc;
+         Handle(Geom_Surface) surf = BRep_Tool::Surface (face);
+         BRepAdaptor_Surface sf(face, Standard_False);
+         BRepLProp_SLProps prop(sf, 1, 1e-5);
+         Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc);
+
+         if (triangulation.IsNull())
+         {
+            cout << "cannot visualize face " << i << endl;
+            occgeometry->fvispar[i-1].SetNotDrawable();
+            continue;
+         }
+
+         gp_Pnt2d uv;
+         gp_Pnt pnt;
+         gp_Vec n;
+
+         glBegin (GL_TRIANGLES);
+
+         int ntriangles = triangulation -> NbTriangles();
+         for (int j = 1; j <= ntriangles; j++)
+         {
+            Poly_Triangle triangle = (triangulation -> Triangles())(j);
+            gp_Pnt p[3];
+            for (int k = 1; k <= 3; k++)
+            p[k-1] = (triangulation -> Nodes())(triangle(k)).Transformed(loc);
+
+            for (int k = 1; k <= 3; k++)
+            {
+               uv = (triangulation -> UVNodes())(triangle(k));
+               prop.SetParameters (uv.X(), uv.Y());
+
+               //	      surf->D0 (uv.X(), uv.Y(), pnt);
+
+               if (prop.IsNormalDefined())
+               n = prop.Normal();
+               else
+               {
+                  (*testout) << "Visualization of face " << i
+                  << ": Normal vector not defined" << endl;
+                  //		  n = gp_Vec (0,0,0);
+                  gp_Vec a(p[0],p[1]);
+                  gp_Vec b(p[0],p[2]);
+                  n = b^a;
+               }
+
+               if (face.Orientation() == TopAbs_REVERSED) n *= -1;
+               glNormal3f (n.X(), n.Y(), n.Z());
+               glVertex3f (p[k-1].X(), p[k-1].Y(), p[k-1].Z());
+            }
+         }
+         glEnd ();
+
+      }
+      glEndList ();
+
+   }
+
+   void SelectFaceInOCCDialogTree (int facenr);
+
+   void VisualSceneOCCGeometry :: MouseDblClick (int px, int py)
+   {
+      int hits;
+
+      // select surface triangle by mouse click
+
+      GLuint selbuf[10000];
+      glSelectBuffer (10000, selbuf);
+
+      glRenderMode (GL_SELECT);
+
+      GLint viewport[4];
+      glGetIntegerv (GL_VIEWPORT, viewport);
+
+      glMatrixMode (GL_PROJECTION);
+      glPushMatrix();
+
+      GLdouble projmat[16];
+      glGetDoublev (GL_PROJECTION_MATRIX, projmat);
+
+      glLoadIdentity();
+      gluPickMatrix (px, viewport[3] - py, 1, 1, viewport);
+      glMultMatrixd (projmat);
+
+      glClearColor(backcolor, backcolor, backcolor, 1.0);
+      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+      glMatrixMode (GL_MODELVIEW);
+
+      glPushMatrix();
+      glMultMatrixf (transformationmat);
+
+      glInitNames();
+      glPushName (1);
+
+      glPolygonOffset (1, 1);
+      glEnable (GL_POLYGON_OFFSET_FILL);
+
+      glDisable(GL_CLIP_PLANE0);
+
+      // Philippose - 30/01/2009
+      // Enable clipping planes for Selection mode in OCC Geometry
+      if (vispar.clipenable)
+      {
+         Vec<3> n(clipplane[0], clipplane[1], clipplane[2]);
+         double len = Abs(n);
+         double mu = -clipplane[3] / (len*len);
+         Point<3> p (mu * n);
+         n /= len;
+         Vec<3> t1 = n.GetNormal ();
+         Vec<3> t2 = Cross (n, t1);
+
+         double xi1mid = (center - p) * t1;
+         double xi2mid = (center - p) * t2;
+
+         glLoadName (0);
+         glBegin (GL_QUADS);
+         glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid-rad) * t2);
+         glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid-rad) * t2);
+         glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid+rad) * t2);
+         glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid+rad) * t2);
+         glEnd ();
+      }
+
+      glCallList (trilists.Get(1));
+
+      glDisable (GL_POLYGON_OFFSET_FILL);
+
+      glPopName();
+
+      glMatrixMode (GL_PROJECTION);
+      glPopMatrix();
+
+      glMatrixMode (GL_MODELVIEW);
+      glPopMatrix();
+
+      glFlush();
+
+      hits = glRenderMode (GL_RENDER);
+
+      int minname = 0;
+      GLuint mindepth = 0;
+
+      // find clippingplane
+      GLuint clipdepth = 0; // GLuint(-1);
+
+      for (int i = 0; i < hits; i++)
+      {
+         int curname = selbuf[4*i+3];
+         if (!curname) clipdepth = selbuf[4*i+1];
+      }
+
+      for (int i = 0; i < hits; i++)
+      {
+         int curname = selbuf[4*i+3];
+         GLuint curdepth = selbuf[4*i+1];
+         if (curname && (curdepth> clipdepth) &&
+               (curdepth < mindepth || !minname))
+         {
+            mindepth = curdepth;
+            minname = curname;
+         }
+      }
+
+      occgeometry->LowLightAll();
+
+      if (minname)
+      {
+         occgeometry->fvispar[minname-1].Highlight();
+
+         if (vispar.occzoomtohighlightedentity)
+         occgeometry->changed = OCCGEOMETRYVISUALIZATIONFULLCHANGE;
+         else
+         occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+         cout << "Selected face: " << minname << endl;
+      }
+      else
+      {
+         occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE;
+      }
+
+      glDisable(GL_CLIP_PLANE0);
+
+      SelectFaceInOCCDialogTree (minname);
+
+      // Philippose - 30/01/2009
+      // Set the currently selected face in the array
+      // for local face mesh size definition
+      occgeometry->SetSelectedFace(minname);
+
+      //  selecttimestamp = NextTimeStamp();
+   }
+
+}
+
+#endif
+
+#endif // NOTCL
diff --git a/contrib/Netgen/libsrc/visualization/vssolution.cpp b/contrib/Netgen/libsrc/visualization/vssolution.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8ea46ddf0a53575717cb49b89e13584784f1c35a
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vssolution.cpp
@@ -0,0 +1,4537 @@
+#ifndef NOTCL
+#include <mystdlib.h>
+#include "incvis.hpp"
+
+
+#include <myadt.hpp>
+#include <meshing.hpp>
+#include <csg.hpp>
+#include <stlgeom.hpp>
+
+// #include <parallel.hpp>
+#include <visual.hpp>
+
+
+namespace netgen
+{
+  extern AutoPtr<Mesh> mesh;
+  extern VisualSceneMesh vsmesh;
+
+
+  VisualSceneSolution :: SolData :: SolData ()
+    : name (0), data (0), solclass(0)
+  { ; }
+
+  VisualSceneSolution :: SolData :: ~SolData ()
+  {
+    delete [] name;
+    delete data;
+    delete solclass;
+  }
+
+  
+  VisualSceneSolution :: VisualSceneSolution ()
+    : VisualScene()
+  {
+    surfellist = 0;
+    linelist = 0;
+    clipplanelist_scal = 0;
+    clipplanelist_vec = 0;
+    isolinelist = 0;
+    clipplane_isolinelist = 0;
+    surface_vector_list = 0;
+    isosurface_list = 0;
+
+    fieldlineslist = 0;
+    pointcurvelist = 0;
+
+    num_fieldlineslists = 0;
+
+
+    surfeltimestamp = GetTimeStamp();
+    surfellinetimestamp = GetTimeStamp();
+    clipplanetimestamp = GetTimeStamp();
+    solutiontimestamp = GetTimeStamp();
+    fieldlinestimestamp = GetTimeStamp();
+    pointcurve_timestamp = GetTimeStamp();
+    surface_vector_timestamp = GetTimeStamp();
+    isosurface_timestamp = GetTimeStamp();
+    timetimestamp = GetTimeStamp();
+    AddVisualizationScene ("solution", &vssolution);
+  }
+  
+  VisualSceneSolution :: ~VisualSceneSolution ()
+  {
+    ClearSolutionData();
+  }
+
+  void VisualSceneSolution :: AddSolutionData (SolData * sd)
+  {
+    NgLock meshlock1 (mesh->MajorMutex(), 1);
+
+    int funcnr = -1;
+    for (int i = 0; i < soldata.Size(); i++)
+      {
+        if (strcmp (soldata[i]->name, sd->name) == 0)
+          {
+            delete soldata[i];
+            soldata[i] = sd;
+            funcnr = i;
+            break;
+          }
+      }
+
+    if (funcnr == -1)
+      {
+        soldata.Append (sd);
+        funcnr = soldata.Size()-1;
+      }
+    
+    SolData * nsd = soldata[funcnr];
+
+    nsd->size = 0;
+    if (mesh)
+      {
+        switch (nsd->soltype)
+          {
+          case SOL_NODAL: nsd->size = mesh->GetNV(); break;
+          case SOL_ELEMENT: nsd->size = mesh->GetNE(); break;
+          case SOL_SURFACE_ELEMENT: nsd->size = mesh->GetNSE(); break;
+          case SOL_NONCONTINUOUS: 
+            {
+              switch (nsd->order)
+                {
+                case 0: nsd->size =      mesh->GetNE(); break;
+                case 1: nsd->size =  6 * mesh->GetNE(); break;
+                case 2: nsd->size = 18 * mesh->GetNE(); break;
+                }
+              break;
+            }
+          case SOL_SURFACE_NONCONTINUOUS: 
+            {
+              switch (nsd->order)
+                {
+                case 0: nsd->size =     mesh->GetNSE(); break;
+                case 1: nsd->size = 4 * mesh->GetNSE(); break;
+                case 2: nsd->size = 9 * mesh->GetNSE(); break;
+                }
+              break;
+            }
+          default:
+            nsd->size = 0;
+          }
+        solutiontimestamp = NextTimeStamp();
+      }
+  }
+
+  
+  void VisualSceneSolution :: ClearSolutionData ()
+  {
+    for (int i = 0; i < soldata.Size(); i++)
+      delete soldata[i];
+    soldata.SetSize (0);
+  }
+
+  void VisualSceneSolution :: UpdateSolutionTimeStamp ()
+  {
+    solutiontimestamp = NextTimeStamp();
+  }
+    
+  VisualSceneSolution::SolData * VisualSceneSolution :: GetSolData (int i)
+  { 
+    if (i >= 0 && i < soldata.Size())
+      return soldata[i];
+    else 
+      return NULL;
+  }
+  
+
+
+
+  void VisualSceneSolution :: SaveSolutionData (const char * filename) 
+  {
+    PrintMessage (1, "Write solution data to file ", filename);
+
+
+    if (strcmp (&filename[strlen(filename)-3], "sol") == 0)
+      {
+        ofstream ost(filename);
+        for (int i = 0; i < soldata.Size(); i++)
+          {
+            const SolData & sol = *soldata[i];
+      
+            ost << "solution " 
+                << sol.name
+                << " -size=" << sol.size 
+                << " -components=" << sol.components
+                << " -order=" << sol.order;
+            if (sol.iscomplex)
+              ost << " -complex";
+      
+            switch (sol.soltype)
+              {
+              case SOL_NODAL:
+                ost << " -type=nodal"; break;
+              case SOL_ELEMENT:
+                ost << " -type=element"; break;
+              case SOL_SURFACE_ELEMENT:
+                ost << " -type=surfaceelement"; break;
+              case SOL_NONCONTINUOUS:
+                ost << " -type=noncontinuous"; break;
+              case SOL_SURFACE_NONCONTINUOUS:
+                ost << " -type=surfacenoncontinuous"; break;
+              default:
+                cerr << "save solution data, case not handeld" << endl;
+              }
+      
+            ost << endl;
+            for (int j = 0; j < sol.size; j++)
+              {
+                for (int k = 0; k < sol.components; k++)
+                  ost << sol.data[j*sol.dist+k] << " ";
+                ost << "\n";
+              }
+          }
+      }
+
+
+    if (strcmp (&filename[strlen(filename)-3], "vtk") == 0)
+      {
+        string surf_fn = filename;
+        surf_fn.erase (strlen(filename)-4);
+        surf_fn += "_surf.vtk";
+
+        cout << "surface mesh = " << surf_fn << endl;
+        
+        ofstream surf_ost(surf_fn.c_str());
+
+        surf_ost << "# vtk DataFile Version 1.0\n"
+		 << "NGSolve surface mesh\n"
+		 << "ASCII\n"
+		 << "DATASET UNSTRUCTURED_GRID\n\n";
+
+        surf_ost << "POINTS " << mesh->GetNP() << " float\n";
+        for (PointIndex pi = PointIndex::BASE; pi < mesh->GetNP()+PointIndex::BASE; pi++)
+          {
+            const MeshPoint & mp = (*mesh)[pi];
+            surf_ost << mp(0) << " " << mp(1) << " " << mp(2) << "\n";
+          }
+
+        int cntverts = 0;
+        for (SurfaceElementIndex sei = 0; sei < mesh->GetNSE(); sei++)
+          cntverts += 1 + (*mesh)[sei].GetNP();
+
+        surf_ost << "\nCELLS " << mesh->GetNSE() << " " << cntverts << "\n";
+        for (SurfaceElementIndex sei = 0; sei < mesh->GetNSE(); sei++)
+          {
+            const Element2d & el = (*mesh)[sei];
+            surf_ost << el.GetNP();
+            for (int j = 0; j < el.GetNP(); j++)
+              surf_ost << " " << el[j] - PointIndex::BASE;
+            surf_ost << "\n";
+          }
+        surf_ost << "\nCELL_TYPES " << mesh->GetNSE() << "\n";
+        for (SurfaceElementIndex sei = 0; sei < mesh->GetNSE(); sei++)
+          {
+            const Element2d & el = (*mesh)[sei];
+            switch (el.GetType())
+              {
+              case QUAD: surf_ost << 9; break;
+              case TRIG: surf_ost << 5; break;
+              default:
+                cerr << "not implemented 2378" << endl;
+              }
+            surf_ost << "\n";
+          }
+
+
+       
+        ofstream ost(filename);
+
+        ost << "# vtk DataFile Version 1.0\n"
+            << "NGSolve solution\n"
+            << "ASCII\n"
+            << "DATASET UNSTRUCTURED_GRID\n\n";
+
+        ost << "POINTS " << mesh->GetNP() << " float\n";
+        for (PointIndex pi = PointIndex::BASE; pi < mesh->GetNP()+PointIndex::BASE; pi++)
+          {
+            const MeshPoint & mp = (*mesh)[pi];
+            ost << mp(0) << " " << mp(1) << " " << mp(2) << "\n";
+          }
+
+        cntverts = 0;
+        for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++)
+          cntverts += 1 + (*mesh)[ei].GetNP();
+
+        ost << "\nCELLS " << mesh->GetNE() << " " << cntverts << "\n";
+        for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++)
+          {
+            const Element & el = (*mesh)[ei];
+            ost << el.GetNP();
+            for (int j = 0; j < el.GetNP(); j++)
+              ost << " " << el[j] - PointIndex::BASE;
+            ost << "\n";
+          }
+        ost << "\nCELL_TYPES " << mesh->GetNE() << "\n";
+        for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++)
+          {
+            const Element & el = (*mesh)[ei];
+            switch (el.GetType())
+              {
+              case TET: ost << 10; break;
+              default:
+                cerr << "not implemented 67324" << endl;
+              }
+            ost << "\n";
+          }
+
+
+        ost << "CELL_DATA " << mesh->GetNE() << "\n";
+        for (int i = 0; i < soldata.Size(); i++)
+          {
+            ost << "VECTORS bfield float\n";
+            SolutionData & sol = *(soldata[i] -> solclass);
+            double values[3];
+
+            for (int elnr = 0; elnr < mesh->GetNE(); elnr++)
+              {
+                sol.GetValue (elnr, 0.25, 0.25, 0.25, values);
+                ost << values[0] << " "  << values[1] << " "  << values[2] << "\n";
+              }
+          }
+
+        /*
+	  ost << "POINT_DATA " << mesh->GetNP() << "\n";
+	  for (int i = 0; i < soldata.Size(); i++)
+          {
+	  ost << "VECTORS bfield float\n";
+	  SolutionData & sol = *(soldata[i] -> solclass);
+            
+	  for (PointIndex pi = PointIndex::BASE; 
+	  pi < mesh->GetNP()+PointIndex::BASE; pi++)
+	  {
+	  double values[3], sumvalues[3] = { 0, 0, 0 };
+
+	  FlatArray<int> els = mesh->GetTopology().GetVertexElements(pi);
+
+	  for (int j = 0; j < els.Size(); j++)
+	  {
+	  sol.GetValue (els[j]-1, 0.25, 0.25, 0.25, values);
+	  for (int k = 0; k < 3; k++)
+	  sumvalues[k] += values[k];
+	  }
+	  for (int k = 0; k < 3; k++)
+	  sumvalues[k] /= els.Size();
+                
+	  ost << sumvalues[0] << " "  << sumvalues[1] << " "  << sumvalues[2] << "\n";
+	  }
+          }
+        */
+      } 
+    
+  }
+  
+
+
+
+  void VisualSceneSolution :: DrawScene ()
+  {
+    if (!mesh) 
+      {
+        VisualScene::DrawScene();      
+        return;
+      }
+
+    // static NgLock mem_lock(mem_mutex);
+    // mem_lock.Lock();
+
+    NgLock meshlock1 (mesh->MajorMutex(), true);
+    NgLock meshlock (mesh->Mutex(), true);
+
+    BuildScene();
+
+    CreateTexture (numtexturecols, lineartexture, GL_MODULATE);
+
+    glClearColor(backcolor, backcolor, backcolor, 1);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    SetLight();
+    
+    glPushMatrix();
+    glMultMatrixf (transformationmat);
+
+    glMatrixMode (GL_MODELVIEW); 
+    
+    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+    
+    glPolygonOffset (1, 1);
+
+    glEnable (GL_POLYGON_OFFSET_FILL);
+
+    glEnable (GL_COLOR_MATERIAL);
+
+    if (usetexture)
+      {
+        SetTextureMode (usetexture);
+
+        glMatrixMode (GL_TEXTURE);
+        glLoadIdentity();
+        
+        if (usetexture == 1)
+          {
+            double hmax = maxval;
+            double hmin = minval;
+            if (invcolor) Swap (hmax, hmin);
+
+            if (fabs (hmax - hmin) > 1e-30) 
+              glScaled (1.0 / (hmin - hmax), 0, 0);
+            else
+              glScaled (1e30, 0, 0);
+            
+            glTranslatef (-hmax, 0, 0);
+          }
+        else
+          {
+            glTranslatef (0.5, 0, 0);
+            glRotatef(360 * vssolution.time, 0, 0, -1);
+            if (fabs (maxval) > 1e-10)
+              glScalef(0.5/maxval, 0.5/maxval, 0.5/maxval);
+            else
+              glScalef (1e10, 1e10, 1e10);
+          }
+        glMatrixMode (GL_MODELVIEW);
+      }
+
+    if (vispar.drawfilledtrigs || vispar.drawtetsdomain > 0 || vispar.drawdomainsurf > 0)
+      {
+        SetClippingPlane ();
+        
+        glCallList (surfellist);
+        glCallList (surface_vector_list);
+      
+        glDisable(GL_CLIP_PLANE0);
+      }
+
+    if (showclipsolution)
+      {
+	if (clipsolution == 1)
+	  glCallList (clipplanelist_scal);
+	if (clipsolution == 2)
+	  glCallList (clipplanelist_vec);
+      }
+
+
+    if (draw_fieldlines)
+      {
+	SetClippingPlane();
+        if (num_fieldlineslists <= 1)
+          glCallList (fieldlineslist);
+        else
+          {  // animated
+            int start = int (time / 10 * num_fieldlineslists);
+            for (int ln = 0; ln < 10; ln++)
+              {
+                int nr = fieldlineslist + (start + ln) % num_fieldlineslists;
+                glCallList (nr);
+              }
+          }
+        glDisable(GL_CLIP_PLANE0);
+      }
+
+    if(drawpointcurves)
+      {
+	glCallList(pointcurvelist);
+      }
+
+
+    glMatrixMode (GL_TEXTURE);
+    glLoadIdentity();
+    glMatrixMode (GL_MODELVIEW);
+
+    glDisable (GL_TEXTURE_1D);
+    glDisable (GL_TEXTURE_2D);
+
+    glDisable (GL_POLYGON_OFFSET_FILL);
+    glDisable (GL_COLOR_MATERIAL);
+
+    if (draw_isosurface)
+      glCallList (isosurface_list);
+    
+    
+    GLfloat matcol0[] = { 0, 0, 0, 1 };
+    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcol0);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, matcol0);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matcol0);
+    
+    glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+    glLineWidth (1.0f);
+    glColor3f (0.0f, 0.0f, 0.0f);
+    glDisable (GL_LINE_SMOOTH);
+
+
+    if (vispar.drawoutline && !numisolines)
+      {
+        SetClippingPlane ();
+        glCallList (linelist);
+        glDisable(GL_CLIP_PLANE0);
+      }
+
+    if (numisolines)
+      {
+        SetClippingPlane ();
+        glCallList (isolinelist);
+
+        glDisable(GL_CLIP_PLANE0);
+        glCallList (clipplane_isolinelist);
+      }
+
+    glPopMatrix();
+    
+    glDisable(GL_CLIP_PLANE0);
+    DrawColorBar (minval, maxval, logscale, lineartexture);
+    
+    if (vispar.drawcoordinatecross)
+      DrawCoordinateCross ();
+    DrawNetgenLogo ();
+    
+    glFinish();  
+
+    
+    // delete lock;
+    // mem_lock.UnLock();
+  }
+  
+
+
+  void VisualSceneSolution :: RealVec3d (const double * values, Vec3d & v, 
+                                         bool iscomplex, bool imag)
+  {
+    if (!iscomplex)
+      {
+        v.X() = values[0];
+        v.Y() = values[1];
+        v.Z() = values[2];
+      }
+    else
+      {
+        if (!imag)
+          {
+            v.X() = values[0];
+            v.Y() = values[2];
+            v.Z() = values[4];
+          }
+        else
+          {
+            v.X() = values[1];
+            v.Y() = values[3];
+            v.Z() = values[5];
+          }
+      }
+  }
+
+
+  void VisualSceneSolution :: RealVec3d (const double * values, Vec3d & v, 
+                                         bool iscomplex, double phaser, double phasei)
+  {
+    if (!iscomplex)
+      {
+        v.X() = values[0];
+        v.Y() = values[1];
+        v.Z() = values[2];
+      }
+    else
+      {
+        for (int i = 0; i < 3; i++)
+          v.X(i+1) = phaser * values[2*i] + phasei * values[2*i+1];
+      }
+  }
+
+
+  
+
+  void VisualSceneSolution :: BuildScene (int zoomall)
+  {
+    if (!mesh)
+      {
+        VisualScene::BuildScene (zoomall);
+        return;
+      }
+
+    /*
+      if (!cone_list)
+      {
+      cone_list = glGenLists (1);
+      glNewList (cone_list, GL_COMPILE);
+      DrawCone (Point<3> (0,0,0), Point<3> (0,0,1), 0.4);
+      glEndList();
+      }
+    */
+    
+    // vispar.colormeshsize = 1;
+    
+    // recalc clipping plane
+    SetClippingPlane ();
+    glDisable(GL_CLIP_PLANE0);
+    
+    
+    SolData * sol = NULL;
+    SolData * vsol = NULL;
+  
+    if (scalfunction != -1) 
+      sol = soldata[scalfunction];
+    if (vecfunction != -1)
+      vsol = soldata[vecfunction];
+
+    if (mesh->GetTimeStamp () > solutiontimestamp)
+      {
+        sol = NULL;
+        vsol = NULL;
+      }
+ 
+
+    if (sol && sol->solclass) sol->solclass->SetMultiDimComponent (multidimcomponent);
+    if (vsol && vsol->solclass) vsol->solclass->SetMultiDimComponent (multidimcomponent);
+
+    if (!autoscale || (!sol && !vsol) )
+      {
+        minval = mminval;
+        maxval = mmaxval;
+      }
+    else
+      {
+        if (mesh->GetTimeStamp () > surfeltimestamp ||
+            vispar.clipplanetimestamp > clipplanetimestamp ||
+            solutiontimestamp > surfeltimestamp)
+          {
+            GetMinMax (scalfunction, scalcomp, minval, maxval);
+          }
+      }
+ 
+    if (mesh->GetTimeStamp() > surfeltimestamp ||
+        solutiontimestamp > surfeltimestamp || 
+        zoomall)
+      {
+        if (mesh->GetTimeStamp() > surfeltimestamp || zoomall)
+          {
+            // mesh has changed
+          
+            Point3d pmin, pmax;
+            static double oldrad = 0;
+          
+            mesh->GetBox (pmin, pmax, -1);
+            center = Center (pmin, pmax);
+            rad = 0.5 * Dist (pmin, pmax);
+          
+            glEnable (GL_NORMALIZE);
+          
+            if (rad > 1.5 * oldrad ||
+                mesh->GetMajorTimeStamp() > surfeltimestamp ||
+                zoomall)
+              {
+                CalcTransformationMatrices();
+                oldrad = rad;
+              }
+          }
+      
+        DrawSurfaceElements();
+      
+        surfeltimestamp = max2 (solutiontimestamp, mesh->GetTimeStamp());
+      }
+
+    if (mesh->GetTimeStamp() > surfellinetimestamp ||
+        subdivision_timestamp > surfellinetimestamp ||
+        (deform && solutiontimestamp > surfellinetimestamp) || 
+        zoomall)
+      {
+        if (linelist)
+          glDeleteLists (linelist, 1);
+      
+        linelist = glGenLists (1);
+        glNewList (linelist, GL_COMPILE);
+      
+        DrawSurfaceElementLines();
+
+        glEndList ();
+      
+        surfellinetimestamp = max2 (solutiontimestamp, mesh->GetTimeStamp());
+      }
+
+  
+
+    if (mesh->GetTimeStamp() > surface_vector_timestamp ||
+        solutiontimestamp > surface_vector_timestamp ||
+        zoomall)
+      {
+        if (surface_vector_list)
+          glDeleteLists (surface_vector_list, 1);
+      
+        surface_vector_list = glGenLists (1);
+        glNewList (surface_vector_list, GL_COMPILE);
+
+        glEnable (GL_NORMALIZE);
+        DrawSurfaceVectors();
+
+        glEndList ();
+
+        surface_vector_timestamp = 
+          max2 (mesh->GetTimeStamp(), solutiontimestamp);
+      }
+
+
+    if (clipplanetimestamp < vispar.clipplanetimestamp ||
+        clipplanetimestamp < solutiontimestamp)
+      {
+
+        //      cout << "clipsolution = " << clipsolution << endl;
+        if (vispar.clipenable && clipsolution == 2)      
+          {
+            // lock->UnLock();
+            NgLock mlock (mesh->Mutex(), 0);
+            mlock.UnLock(); 
+            mesh->BuildElementSearchTree();
+            mlock.Lock();
+
+            // lock->Lock();
+          }
+
+      
+        if (vispar.clipenable && clipsolution == 1 && sol)
+	  DrawClipPlaneTrigs (); 
+
+        if (clipplanelist_vec)
+          glDeleteLists (clipplanelist_vec, 1);
+      
+        clipplanelist_vec = glGenLists (1);
+        glNewList (clipplanelist_vec, GL_COMPILE);
+
+        if (vispar.clipenable && clipsolution == 2 && vsol)
+          {
+            SetTextureMode (usetexture);
+
+            if (autoscale)
+              GetMinMax (vecfunction, 0, minval, maxval);
+
+            Array<ClipPlanePoint> cpp;
+            GetClippingPlaneGrid (cpp);
+
+            for (int i = 0; i < cpp.Size(); i++)
+              {
+                const ClipPlanePoint & p = cpp[i];
+                double values[6];
+                Vec3d v;
+
+                bool drawelem = 
+                  GetValues (vsol, p.elnr, p.lami(0), p.lami(1), p.lami(2), values);
+                RealVec3d (values, v, vsol->iscomplex, imag_part);
+
+                double val = v.Length();
+
+                if (drawelem && val > 1e-10 * maxval)
+                  {
+                    v *= (rad / val / gridsize * 0.5);
+                  
+                    SetOpenGlColor  (val);
+                    DrawCone (p.p, p.p+v, rad / gridsize * 0.2);
+                  }
+              }
+          }
+
+        glEndList ();
+      }
+
+
+    if (mesh->GetTimeStamp() > isosurface_timestamp ||
+        solutiontimestamp > isosurface_timestamp ||
+        zoomall)
+      {
+        if (isosurface_list)
+          glDeleteLists (isosurface_list, 1);
+      
+        isosurface_list = glGenLists (1);
+        glNewList (isosurface_list, GL_COMPILE);
+
+        glEnable (GL_NORMALIZE);
+        DrawIsoSurface(sol, vsol, scalcomp);
+
+        glEndList ();
+
+        isosurface_timestamp = 
+          max2 (mesh->GetTimeStamp(), solutiontimestamp);
+      }
+
+    if(mesh->GetTimeStamp() > pointcurve_timestamp ||
+       solutiontimestamp > pointcurve_timestamp)
+      {
+	if(pointcurvelist)
+	  glDeleteLists(pointcurvelist,1);
+	
+		
+	if(mesh->GetNumPointCurves() > 0)
+	  {
+	    pointcurvelist = glGenLists(1);
+	    glNewList(pointcurvelist,GL_COMPILE);
+	    //glColor3f (1.0f, 0.f, 0.f);
+	    
+	    for(int i=0; i<mesh->GetNumPointCurves(); i++)
+	      {
+		Box3d box;
+		box.SetPoint(mesh->GetPointCurvePoint(i,0));
+		for(int j=1; j<mesh->GetNumPointsOfPointCurve(i); j++)
+		  box.AddPoint(mesh->GetPointCurvePoint(i,j));
+		double diam = box.CalcDiam();
+			     
+		double thick = min2(0.1*diam, 0.001*rad);
+
+		double red,green,blue;
+		mesh->GetPointCurveColor(i,red,green,blue);
+		glColor3f (red, green, blue);
+		for(int j=0; j<mesh->GetNumPointsOfPointCurve(i)-1; j++)
+		  {
+		    DrawCylinder(mesh->GetPointCurvePoint(i,j),
+				 mesh->GetPointCurvePoint(i,j+1),
+				 thick);
+		  }
+	      }
+	    glEndList();
+	  }
+	
+      }
+
+
+    if (
+        numisolines && 
+        (clipplanetimestamp < vispar.clipplanetimestamp ||
+         clipplanetimestamp < solutiontimestamp) 
+        )
+      {
+        if (isolinelist) glDeleteLists (isolinelist, 1);
+      
+        isolinelist = glGenLists (1);
+        glNewList (isolinelist, GL_COMPILE);
+
+        Point<3> points[1100];
+        double values[1100];
+      
+        int nse = mesh->GetNSE();
+
+        CurvedElements & curv = mesh->GetCurvedElements();
+
+        if (sol)
+          {
+            glBegin (GL_LINES);
+          
+            for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+              {
+                const Element2d & el = (*mesh)[sei];
+
+#ifdef PARALLEL
+		// parallel visualization --> dont draw ghost elements
+		if ( el . IsGhost() ) continue;
+#endif
+                bool curved = curv.IsHighOrder(); //  && curv.IsSurfaceElementCurved(sei);
+              
+                if (el.GetType() == TRIG || el.GetType() == TRIG6)
+                  {
+                    Point<3> lp1, lp2, lp3;
+                    if (!curved)
+                      {
+                        GetPointDeformation (el[0]-1, lp1);
+                        GetPointDeformation (el[1]-1, lp2);
+                        GetPointDeformation (el[2]-1, lp3);
+                      }
+                  
+                    int n = 1 << subdivisions;
+                    int ii = 0;
+                    int ix, iy;
+                    for (iy = 0; iy <= n; iy++)
+                      for (ix = 0; ix <= n-iy; ix++)
+                        {
+                          double x = double(ix) / n;
+                          double y = double(iy) / n;
+                        
+                          // TODO: consider return value (bool: draw/don't draw element)
+                          GetSurfValue (sol, sei, x, y, scalcomp, values[ii]);
+                          Point<2> xref(x,y);
+                        
+                          if (curved)
+                            mesh->GetCurvedElements().
+                              CalcSurfaceTransformation (xref, sei, points[ii]);
+                          else
+                            points[ii] = lp3 + x * (lp1-lp3) + y * (lp2-lp3);
+                        
+                          if (deform)
+                            {
+                              points[ii] += GetSurfDeformation (sei, x, y);
+                            }
+                          ii++;
+                        }
+                  
+                    ii = 0;
+                    for (iy = 0; iy < n; iy++, ii++)
+                      for (ix = 0; ix < n-iy; ix++, ii++)
+                        {
+                          int index[] = { ii, ii+1, ii+n-iy+1,
+                                          ii+1, ii+n-iy+2, ii+n-iy+1 };
+                        
+                          DrawIsoLines (points[index[0]], points[index[1]], points[index[2]],
+                                        values[index[0]], values[index[1]], values[index[2]]);
+
+                          if (ix < n-iy-1) 
+                            DrawIsoLines (points[index[3]], points[index[4]], points[index[5]],
+                                          values[index[3]], values[index[4]], values[index[5]]);
+                        }    
+                  }
+              
+              
+                if (el.GetType() == QUAD || el.GetType() == QUAD6 || el.GetType() == QUAD8 )
+                  {
+                    Point<3> lpi[4];
+                    Vec<3> vx, vy, vtwist, def;
+                    if (!curved)
+                      {
+                        for (int j = 0; j < 4; j++)
+                          GetPointDeformation (el[j]-1, lpi[j]);
+                        vx = lpi[1]-lpi[0];
+                        vy = lpi[3]-lpi[0];
+                        vtwist = (lpi[0]-lpi[1]) + (lpi[2]-lpi[3]);
+                      }
+
+                    int n = 1 << subdivisions;
+                    int ix, iy, ii = 0;
+                    for (iy = 0; iy <= n; iy++)
+                      for (ix = 0; ix <= n; ix++, ii++)
+                        {
+                          double x = double(ix) / n;
+                          double y = double(iy) / n;
+                        
+                          // TODO: consider return value (bool: draw/don't draw element)
+                          GetSurfValue (sol, sei, x, y, scalcomp, values[ii]);
+                          Point<2> xref(x,y);
+                        
+                          if (curved)
+                            mesh->GetCurvedElements().
+                              CalcSurfaceTransformation (xref, sei, points[ii]);
+                          else
+                            points[ii] = lpi[0] + x * vx + y * vy + x*y * vtwist;
+                        
+                          if (deform)
+                            points[ii] += GetSurfDeformation (sei, x, y);
+                        }
+                  
+                    ii = 0;
+                    for (iy = 0; iy < n; iy++, ii++)
+                      for (ix = 0; ix < n; ix++, ii++)
+                        {
+                          DrawIsoLines (points[ii], points[ii+1], points[ii+n+1],
+                                        values[ii], values[ii+1], values[ii+n+1]);
+                          DrawIsoLines (points[ii+1], points[ii+n+2], points[ii+n+1],
+                                        values[ii+1], values[ii+n+2], values[ii+n+1]);
+                        }       
+                  }
+              }
+            glEnd();
+          }
+        glEndList ();
+
+        if (clipplane_isolinelist) glDeleteLists (clipplane_isolinelist, 1);
+            
+        if (vispar.clipenable && clipsolution == 1 && sol)
+          {
+            clipplane_isolinelist = glGenLists (1);
+            glNewList (clipplane_isolinelist, GL_COMPILE);
+
+            Array<ClipPlaneTrig> cpt;
+            Array<ClipPlanePoint> pts;
+            GetClippingPlaneTrigs (cpt, pts);  
+            bool drawelem;
+          
+            glNormal3d (-clipplane[0], -clipplane[1], -clipplane[2]);
+          
+            if (numisolines)
+              for (int i = 0; i < cpt.Size(); i++)
+                {
+                  const ClipPlaneTrig & trig = cpt[i];
+                  double vali[3];
+                  for (int j = 0; j < 3; j++)
+                    {
+                      Point<3> lami = pts[trig.points[j].pnr].lami;
+                      drawelem = GetValue (sol, trig.elnr, lami(0), lami(1), lami(2),
+                                           scalcomp, vali[j]);
+                    }
+                  if ( drawelem )
+                    DrawIsoLines (pts[trig.points[0].pnr].p,
+                                  pts[trig.points[1].pnr].p,
+                                  pts[trig.points[2].pnr].p,
+                                  // trig.points[1].p,
+                                  // trig.points[2].p,
+                                  vali[0], vali[1], vali[2]);
+                }
+            glEndList ();
+          }
+        glEnd();
+      }
+  
+    clipplanetimestamp = max2 (vispar.clipplanetimestamp, solutiontimestamp);
+  }
+  
+
+
+  void  VisualSceneSolution :: DrawSurfaceElements ()
+  {
+    static int timer = NgProfiler::CreateTimer ("Solution::DrawSurfaceElements");
+    NgProfiler::RegionTimer reg (timer);
+  
+    
+#ifdef PARALLELGL
+
+    if (id == 0 && ntasks > 1)
+      {
+	InitParallelGL();
+
+	par_surfellists.SetSize (ntasks);
+
+	MyMPI_SendCmd ("redraw");
+	MyMPI_SendCmd ("solsurfellist");
+
+	for ( int dest = 1; dest < ntasks; dest++ )
+	  MyMPI_Recv (par_surfellists[dest], dest, MPI_TAG_VIS);
+
+	if (surfellist)
+	  glDeleteLists (surfellist, 1);
+
+	surfellist = glGenLists (1);
+	glNewList (surfellist, GL_COMPILE);
+	
+	for ( int dest = 1; dest < ntasks; dest++ )
+	  glCallList (par_surfellists[dest]);
+	
+	glEndList();
+	return;
+      }
+#endif
+
+
+
+    if (surfellist)
+      glDeleteLists (surfellist, 1);
+    
+    surfellist = glGenLists (1);
+    glNewList (surfellist, GL_COMPILE);
+
+    
+    const SolData * sol = NULL;
+    
+    if (scalfunction != -1)
+      sol = soldata[scalfunction];
+    
+    if (mesh->GetTimeStamp () > solutiontimestamp)
+      sol = NULL;
+
+    if (sol && sol->solclass) sol->solclass->SetMultiDimComponent (multidimcomponent);
+
+
+
+    glLineWidth (1.0f);
+
+    GLfloat col_grey[] = { 0.6f, 0.6f, 0.6f };
+    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col_grey);
+        
+
+    int nse = mesh->GetNSE();
+
+    SetTextureMode (usetexture);
+
+    CurvedElements & curv = mesh->GetCurvedElements();
+
+    int n = 1 << subdivisions;
+    int npt = sqr(n+1);
+
+    Array<Point<2> > pref (npt);
+    Array<Point<3> > points (npt);
+    Array<Mat<3,2> > dxdxis (npt);
+    Array<Vec<3> > nvs(npt);
+    Array<double> values(npt);
+
+    Array<double> mvalues(npt);
+    if (sol && sol->draw_surface) mvalues.SetSize (npt * sol->components);
+
+    Array<complex<double> > valuesc(npt);
+
+    for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+      {
+        const Element2d & el = (*mesh)[sei];
+
+	if ( el . IsGhost() ) continue;
+
+        if (vispar.drawdomainsurf > 0)
+          {
+            if (mesh->GetDimension() == 3)
+              {
+                if (vispar.drawdomainsurf != mesh->GetFaceDescriptor(el.GetIndex()).DomainIn() &&
+                    vispar.drawdomainsurf != mesh->GetFaceDescriptor(el.GetIndex()).DomainOut())
+                  continue;
+              }
+            else
+              {
+                if (el.GetIndex() != vispar.drawdomainsurf) continue;
+              }
+          }
+
+
+
+        if ( el.GetType() == QUAD || el.GetType() == QUAD6 )
+          {
+            bool curved = curv.IsSurfaceElementCurved (sei);
+
+
+            for (int iy = 0, ii = 0; iy <= n; iy++)
+              for (int ix = 0; ix <= n; ix++, ii++)
+                pref[ii] = Point<2> (double(ix)/n, double(iy)/n);
+
+            int npt = (n+1)*(n+1);
+            if (curved)
+              for (int ii = 0; ii < npt; ii++)
+                {
+                  Point<2> xref = pref[ii];
+                  Mat<3,2> dxdxi;
+                  
+                  mesh->GetCurvedElements().
+                    CalcSurfaceTransformation (xref, sei, points[ii], dxdxi);
+                  nvs[ii] = Cross (dxdxi.Col(0), dxdxi.Col(1));
+                  nvs[ii].Normalize();
+                }
+            else
+              {
+		Point<3> lpi[4];
+		Vec<3> vx, vy, vtwist;
+		
+		for (int k = 0; k < 4; k++)
+		  GetPointDeformation (el[k]-1, lpi[k]);
+		
+		vx = lpi[1]-lpi[0];
+		vy = lpi[3]-lpi[0];
+		vtwist = (lpi[0]-lpi[1]) + (lpi[2]-lpi[3]);
+
+                for (int ii = 0; ii < npt; ii++)
+                  {
+                    double x = pref[ii](0);
+                    double y = pref[ii](1);
+                    points[ii] = lpi[0] + x * vx + y * vy + x*y * vtwist;
+                  }
+
+                Vec<3> nv = Cross (vx, vy);
+                nv.Normalize();
+                for (int ii = 0; ii < npt; ii++)
+                  nvs[ii] = nv;
+              }
+
+
+            bool drawelem = false;
+            if (sol && sol->draw_surface) 
+              {
+                if (usetexture == 2)
+                  for (int ii = 0; ii < npt; ii++)
+                    drawelem = GetSurfValueComplex (sol, sei, pref[ii](0), pref[ii](1), scalcomp, valuesc[ii]);
+                else
+                  for (int ii = 0; ii < npt; ii++)
+                    drawelem = GetSurfValue (sol, sei, pref[ii](0), pref[ii](1), scalcomp, values[ii]);
+              }
+            
+            if (deform)
+              for (int ii = 0; ii < npt; ii++)
+                points[ii] += GetSurfDeformation (sei, pref[ii](0), pref[ii](1));
+
+
+            int save_usetexture = usetexture;
+            if (!drawelem)
+              {
+                usetexture = 0;
+                SetTextureMode (0);
+              }
+
+            int ii = 0;
+
+            glBegin (GL_QUADS);
+
+            for (int iy = 0; iy < n; iy++, ii++)
+              for (int ix = 0; ix < n; ix++, ii++)
+                {
+                  int index[] = { ii, ii+1, ii+n+2, ii+n+1 };
+                  
+                  for (int j = 0; j < 4; j++)
+                    {
+                      if (drawelem)
+                        {
+                          if (usetexture != 2)
+                            SetOpenGlColor  (values[index[j]]);
+                          else
+                            glTexCoord2f ( valuesc[index[j]].real(),
+                                           valuesc[index[j]].imag() );
+                        }
+                      else
+                        glColor3fv (col_grey);
+                      
+                      glNormal3dv (nvs[index[j]]);
+                      glVertex3dv (points[index[j]]);
+                    }
+                }
+            glEnd();
+
+            if (!drawelem && (usetexture != save_usetexture))
+              {
+                usetexture = save_usetexture;
+                SetTextureMode (usetexture);
+              }
+
+          }
+      }
+
+    n = 1 << subdivisions;
+    double invn = 1.0 / n;
+    npt = (n+1)*(n+2)/2;
+
+    for(SurfaceElementIndex sei = 0; sei < nse; sei++)
+      {
+        const Element2d & el = (*mesh)[sei];
+
+	if ( el . IsGhost() ) continue;
+
+        if(vispar.drawdomainsurf > 0)
+	  {
+	    if (mesh->GetDimension() == 3)
+	      {
+		if (vispar.drawdomainsurf != mesh->GetFaceDescriptor(el.GetIndex()).DomainIn() &&
+		    vispar.drawdomainsurf != mesh->GetFaceDescriptor(el.GetIndex()).DomainOut())
+		  continue;
+	      }
+	    else
+	      {
+		if (el.GetIndex() != vispar.drawdomainsurf)
+		  continue;
+	      }
+	  }
+        
+        if ( el.GetType() == TRIG || el.GetType() == TRIG6 )
+          {
+	    bool curved = curv.IsSurfaceElementCurved(sei);
+
+            for (int iy = 0, ii = 0; iy <= n; iy++)
+              for (int ix = 0; ix <= n-iy; ix++, ii++)
+                pref[ii] = Point<2> (ix*invn, iy*invn);
+
+            if (curved)
+              {
+                mesh->GetCurvedElements().
+                  CalcMultiPointSurfaceTransformation (&pref, sei, &points, &dxdxis);
+
+                for (int ii = 0; ii < npt; ii++)
+                  nvs[ii] = Cross (dxdxis[ii].Col(0), dxdxis[ii].Col(1)).Normalize();
+              }
+            else
+              {
+		Point<3> p1 = mesh->Point (el[0]);
+		Point<3> p2 = mesh->Point (el[1]);
+		Point<3> p3 = mesh->Point (el[2]);
+
+                Vec<3> vx = p1-p3;
+                Vec<3> vy = p2-p3;
+                for (int ii = 0; ii < npt; ii++)
+                  {
+                    points[ii] = p3 + pref[ii](0) * vx + pref[ii](1) * vy;
+                    for (int j = 0; j < 3; j++)
+                      {
+                        dxdxis[ii](j,0) = vx(j);
+                        dxdxis[ii](j,1) = vy(j);
+                      }
+                  }
+
+                Vec<3> nv = Cross (vx, vy).Normalize();
+                for (int ii = 0; ii < npt; ii++)
+                  nvs[ii] = nv;
+              }
+
+            bool drawelem = false;
+            if (sol && sol->draw_surface) 
+              {
+		drawelem = GetMultiSurfValues (sol, sei, npt, 
+					       &pref[0](0), &pref[1](0)-&pref[0](0),
+					       &points[0](0), &points[1](0)-&points[0](0),
+					       &dxdxis[0](0), &dxdxis[1](0)-&dxdxis[0](0),
+					       &mvalues[0], sol->components);
+                if (usetexture == 2)
+		  for (int ii = 0; ii < npt; ii++)
+		    valuesc[ii] = ExtractValueComplex(sol, scalcomp, &mvalues[ii*sol->components]);
+                else
+		  for (int ii = 0; ii < npt; ii++)
+		    values[ii] = ExtractValue(sol, scalcomp, &mvalues[ii*sol->components]);
+              }
+            
+            if (deform)
+              for (int ii = 0; ii < npt; ii++)
+                points[ii] += GetSurfDeformation (sei, pref[ii](0), pref[ii](1));
+
+            int save_usetexture = usetexture;
+            if (!drawelem)
+              {
+                usetexture = 0;
+                SetTextureMode (usetexture);
+              }
+            
+            for (int iy = 0, ii = 0; iy < n; iy++)
+              {
+                glBegin (GL_TRIANGLE_STRIP);
+                for (int ix = 0; ix <= n-iy; ix++, ii++)
+                  for (int k = 0; k < 2; k++)
+                    {
+                      if (ix+iy+k > n) continue;
+                      int hi = (k == 0) ? ii : ii+n-iy+1;
+                      
+                      if (drawelem)
+                        {
+                          if (usetexture != 2)
+                            SetOpenGlColor (values[hi]); 
+                          else
+                            glTexCoord2f ( valuesc[hi].real(), valuesc[hi].imag() );
+                        }
+                      else
+                        glColor3fv (col_grey);
+                      
+                      glNormal3dv (nvs[hi]);
+                      glVertex3dv (points[hi]);
+                    }
+                glEnd();
+              }
+            if (!drawelem && (usetexture != save_usetexture))
+              {
+                usetexture = save_usetexture;
+                SetTextureMode (usetexture);
+              }
+	  }
+      }
+    glEndList ();
+
+
+#ifdef PARALLELGL
+    glFinish();
+    if (id > 0)
+      MyMPI_Send (surfellist, 0, MPI_TAG_VIS);
+#endif
+  }
+
+
+  void  VisualSceneSolution :: DrawSurfaceElementLines ()
+  {
+    glLineWidth (1.0f);
+    // glNormal3d (1, 0, 0);
+
+    int nse = mesh->GetNSE();
+
+    CurvedElements & curv = mesh->GetCurvedElements();
+
+    int n = 1 << subdivisions;
+    ArrayMem<Point<2>, 65> ptsloc(n+1);
+    ArrayMem<Point<3>, 65> ptsglob(n+1);
+
+    double trigpts[3][2] = { { 0, 0 }, { 1, 0 }, { 0, 1} };
+    double trigvecs[3][2] = { { 1, 0 }, { -1,1 }, { 0, -1} };
+
+    double quadpts[4][2] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1} };
+    double quadvecs[4][2] = { { 1, 0 }, { 0, 1 }, { -1, 0}, { 0, -1} };
+
+    for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+      {
+        Element2d & el = (*mesh)[sei];
+
+	if ( el . IsGhost() ) continue;
+
+        // bool curved = curv.IsSurfaceElementCurved (sei);
+
+        int nv = (el.GetType() == TRIG || el.GetType() == TRIG6) ? 3 : 4;
+        /*
+        Point<3> p1, p2, p3, p4;
+        if (!curved)
+	{
+            p1 = (*mesh)[el[0]];
+            p2 = (*mesh)[el[1]];
+            p3 = (*mesh)[el[2]];
+            if (nv == 4) p4 = (*mesh)[el[3]];
+          }
+	*/
+
+        for (int k = 0; k < nv; k++)
+          {
+            Point<2> p0;
+            Vec<2> vtau;
+            if (nv == 3)
+	      {
+		p0 = Point<2>(trigpts[k][0], trigpts[k][1]);
+		vtau = Vec<2>(trigvecs[k][0], trigvecs[k][1]);
+	      }
+            else
+	      {
+		p0 = Point<2>(quadpts[k][0], quadpts[k][1]);
+		vtau = Vec<2>(quadvecs[k][0], quadvecs[k][1]);
+	      }
+
+            glBegin (GL_LINE_STRIP);
+
+            // if (curved)
+              {
+                for (int ix = 0; ix <= n; ix++)
+                  ptsloc[ix] = p0 + (double(ix) / n) * vtau;
+                    
+                curv.CalcMultiPointSurfaceTransformation (&ptsloc, sei, &ptsglob, 0);
+
+                for (int ix = 0; ix <= n; ix++)
+                  {
+                    if (deform)
+                      ptsglob[ix] += GetSurfDeformation (sei, ptsloc[ix](0), ptsloc[ix](1));
+                    glVertex3dv (ptsglob[ix]);
+                  }
+              }
+	      /*
+            else
+              {
+                for (int ix = 0; ix <= n; ix++)
+                  {
+                    Point<2> p = p0 + (double(ix) / n) * vtau;
+
+                    Point<3> pnt;
+
+                    if (nv == 3)
+                      pnt = p3 + p(0) * (p1-p3) + p(1) * (p2-p3);
+                    else
+                      pnt = p1 + p(0) * (p2-p1) + p(1) * (p4-p1) + p(0)*p(1) * ( (p1-p2)+(p3-p4) );
+                
+                    if (deform)
+                      pnt += GetSurfDeformation (sei, p(0), p(1) );
+                    
+                    glVertex3dv (pnt);
+                  }
+              }
+	      */
+            glEnd ();
+          }
+      }
+  }
+
+
+
+
+
+
+
+
+
+  void VisualSceneSolution :: DrawIsoSurface(const SolData * sol, 
+                                             const SolData * vsol,
+                                             int comp)
+  {
+    if (!draw_isosurface) return;
+    if (!sol) return;
+
+   
+    SetTextureMode (0);
+    glColor3d (1.0, 0, 0);
+    glEnable (GL_COLOR_MATERIAL);
+
+
+    glBegin (GL_TRIANGLES);
+
+    int ne = mesh->GetNE();
+
+    const int edgei[6][2] =
+      { { 0, 1 }, { 0, 2 }, { 0, 3 },
+        { 1, 2 }, { 1, 3 }, { 2, 3 } };
+    
+    double edgelam[6];
+    Point<3> edgep[6];
+    Vec<3> normp[6];
+    double nodevali[4];
+    
+    int cntce;
+    int cpe1 = 0, cpe2 = 0, cpe3 = 0;
+    
+    int n = 1 << subdivisions;
+    int n3 = (n+1)*(n+1)*(n+1);
+    
+    Array<Point<3> > grid(n3);
+    Array<Point<3> > locgrid(n3);
+    Array<Mat<3,3> > trans(n3);
+    Array<double> val(n3);
+    Array<Vec<3> > grads(n3);
+    Array<int> compress(n3);
+    
+    MatrixFixWidth<3> pointmat(8);
+    grads = Vec<3> (0.0);
+
+    for (ElementIndex ei = 0; ei < ne; ei++)
+      {
+        // if(vispar.clipdomain > 0 && vispar.clipdomain != (*mesh)[ei].GetIndex()) continue;
+        // if(vispar.donotclipdomain > 0 && vispar.donotclipdomain == (*mesh)[ei].GetIndex()) continue;
+
+	if ( (*mesh)[ei] . IsGhost() ) continue;
+
+        ELEMENT_TYPE type = (*mesh)[ei].GetType();
+        if (type == HEX || type == PRISM || type == TET || type == PYRAMID)
+          {
+            const Element & el = (*mesh)[ei];
+            
+            int ii = 0;
+            int cnt_valid = 0;
+            
+            for (int ix = 0; ix <= n; ix++)
+              for (int iy = 0; iy <= n; iy++)
+                for (int iz = 0; iz <= n; iz++, ii++)
+                  {
+                    Point<3> ploc;
+                    compress[ii] = ii;
+                    
+                    switch (type)
+                      {
+                      case PRISM:
+                        if (ix+iy <= n)
+                          {
+                            ploc = Point<3> (double(ix) / n, double(iy) / n, double(iz) / n);
+                            compress[ii] = cnt_valid;
+                            cnt_valid++;
+                          }
+                        else
+                          compress[ii] = -1;
+                        break;
+                      case TET:
+                        if (ix+iy+iz <= n)
+                          {
+                            ploc = Point<3> (double(ix) / n, double(iy) / n, double(iz) / n);
+                            compress[ii] = cnt_valid;
+                            cnt_valid++;
+                          }
+                        else
+                          compress[ii] = -1;
+                        break;
+                      case HEX:
+                        ploc = Point<3> (double(ix) / n, double(iy) / n, double(iz) / n);
+                        break;
+                      case PYRAMID:
+                        ploc = Point<3> (double(ix) / n * (1-double(iz)/n),
+                                         double(iy) / n * (1-double(iz)/n),
+                                         double(iz)/n);
+                        break;
+                      default:
+                        cerr << "case not implementd 878234" << endl;
+                        ploc = 0.0;
+                      }
+                    if (compress[ii] != -1)
+                      locgrid[compress[ii]] = ploc;
+                  }
+            
+            if (type != TET && type != PRISM) cnt_valid = n3;
+            
+            
+            if (mesh->GetCurvedElements().IsHighOrder() || 1)
+              {
+                mesh->GetCurvedElements().
+                  CalcMultiPointElementTransformation (&locgrid, ei, &grid, &trans);
+              }
+            else
+              {
+                Vector shape(el.GetNP());
+                for (int k = 0; k < el.GetNP(); k++)
+                  for (int j = 0; j < 3; j++)
+                    pointmat(k,j) = (*mesh)[el[k]](j);
+                
+                for (int i = 0; i < cnt_valid; i++)
+                  {
+                    el.GetShapeNew (locgrid[i], shape);
+                    Point<3> pglob;
+                    for (int j = 0; j < 3; j++)
+                      {
+                        pglob(j) = 0;
+                        for (int k = 0; k < el.GetNP(); k++)
+                          pglob(j) += shape(k) * pointmat(k,j);
+                      }
+                    grid[i] = pglob;
+                  }
+              }
+
+            bool has_pos = 0, has_neg = 0;
+                
+            for (int i = 0; i < cnt_valid; i++)
+              {
+                GetValue (sol, ei, &locgrid[i](0), &grid[i](0), &trans[i](0), comp, val[i]);
+        
+                val[i] -= minval;
+
+                if (vsol)
+                  GetValues (vsol, ei, &locgrid[i](0), &grid[i](0), &trans[i](0), &grads[i](0));
+                grads[i] *= -1;
+
+
+                if (val[i] > 0)
+                  has_pos = 1;
+                else
+                  has_neg = 1;
+              }
+
+            if (!has_pos || !has_neg) continue;
+            
+            for (int ix = 0; ix < n; ix++)
+              for (int iy = 0; iy < n; iy++)
+                for (int iz = 0; iz < n; iz++)
+                  {
+                    int base = iz + (n+1)*iy + (n+1)*(n+1)*ix;
+                    int pi[8] = 
+                      { base, base+(n+1)*(n+1), base+(n+1)*(n+1)+(n+1), base+(n+1),
+                        base+1, base+(n+1)*(n+1)+1, base+(n+1)*(n+1)+(n+1)+1, base+(n+1)+1 };
+                    
+                    for (int j = 0; j < 8; j++)
+                      pi[j] = compress[pi[j]];
+                    
+                    int tets[6][4] = 
+                      { { 1, 2, 4, 5 },
+                        { 4, 5, 2, 8 },
+                        { 2, 8, 5, 6 },
+                        { 2, 3, 4, 8 },
+                        { 2, 3, 8, 6 },
+                        { 3, 8, 6, 7 } };
+                    
+                    for (int ii = 0; ii < 6; ii++)
+                      {
+                        int teti[4];
+                        for (int k = 0; k < 4; k++)
+                          teti[k] = pi[tets[ii][k]-1];
+                        
+                        bool is_valid = 1;
+                        for (int j = 0; j < 4; j++)
+                          if (teti[j] == -1) is_valid = 0;
+                        
+                        if (!is_valid) continue;
+                        
+                        for (int j = 0; j < 4; j++)
+                          nodevali[j] = val[teti[j]];
+                        
+                        cntce = 0;
+                        for (int j = 0; j < 6; j++)
+                          {
+                            int lpi1 = edgei[j][0];
+                            int lpi2 = edgei[j][1];
+                            if ( (nodevali[lpi1] > 0) !=
+                                 (nodevali[lpi2] > 0) )
+                              {
+                                Point<3> p1 = grid[teti[lpi1]];
+                                Point<3> p2 = grid[teti[lpi2]];
+
+                                edgelam[j] = nodevali[lpi2] / (nodevali[lpi2] - nodevali[lpi1]);
+                                edgep[j] = grid[teti[lpi1]] + (1-edgelam[j]) * (grid[teti[lpi2]]-grid[teti[lpi1]]);
+                                normp[j] = grads[teti[lpi1]] + (1-edgelam[j]) * (grads[teti[lpi2]]-grads[teti[lpi1]]);
+                                
+                                cntce++;
+                                cpe3 = cpe2;
+                                cpe2 = cpe1;
+                                cpe1 = j;
+                                if (cntce >= 3)
+                                  {
+                                    if (!vsol)
+                                      {
+                                        Point<3> points[3];
+                                        
+                                        points[0] = edgep[cpe1];
+                                        points[1] = edgep[cpe2];
+                                        points[2] = edgep[cpe3];
+
+                                        Vec<3> normal = Cross (points[2]-points[0], points[1]-points[0]);
+                                        if ( ( (normal * (p2-p1)) > 0 ) == ( nodevali[lpi1] < 0) )
+                                          normal *= -1;
+                                        glNormal3dv (normal);
+
+                                        glVertex3dv (points[0]);
+                                        glVertex3dv (points[1]);
+                                        glVertex3dv (points[2]);
+                                      }
+                                    else
+                                      {
+                                        glNormal3dv (normp[cpe1]);
+                                        glVertex3dv (edgep[cpe1]);
+                                        glNormal3dv (normp[cpe2]);
+                                        glVertex3dv (edgep[cpe2]);
+                                        glNormal3dv (normp[cpe3]);
+                                        glVertex3dv (edgep[cpe3]);
+                                      }
+                                  }
+                              }
+                          }
+                      }
+                  }
+          }
+      }
+    glEnd();
+  }
+    
+
+
+
+
+
+
+
+  void  VisualSceneSolution :: DrawTrigSurfaceVectors(const Array< Point<3> > & lp, 
+                                                      const Point<3> & pmin, const Point<3> & pmax,
+                                                      const int sei, const SolData * vsol)
+  {
+    int dir,dir1,dir2;
+    double s,t;
+
+    Vec<3> n = Cross (lp[1]-lp[0], lp[2]-lp[0]);
+    Vec<3> na (fabs (n(0)), fabs(n(1)), fabs(n(2)));
+    if (na(0) > na(1) && na(0) > na(2))
+      dir = 1;
+    else if (na(1) > na(2))
+      dir = 2;
+    else 
+      dir = 3;
+    
+    dir1 = (dir % 3) + 1;
+    dir2 = (dir1 % 3) + 1;
+
+    Point<2> p2d[3];
+
+    int k;
+
+    for (k = 0; k < 3; k++)
+      {
+        p2d[k] = Point<2> ((lp[k](dir1-1) - pmin(dir1-1)) / (2*rad),
+                           (lp[k](dir2-1) - pmin(dir2-1)) / (2*rad));
+      }
+
+    
+    double minx2d, maxx2d, miny2d, maxy2d;
+    minx2d = maxx2d = p2d[0](0);
+    miny2d = maxy2d = p2d[0](1);
+    for (k = 1; k < 3; k++)
+      {
+        minx2d = min2 (minx2d, p2d[k](0));
+        maxx2d = max2 (maxx2d, p2d[k](0));
+        miny2d = min2 (miny2d, p2d[k](1));
+        maxy2d = max2 (maxy2d, p2d[k](1));
+      }
+    
+    double mat11 = p2d[1](0) - p2d[0](0);
+    double mat21 = p2d[1](1) - p2d[0](1);
+    double mat12 = p2d[2](0) - p2d[0](0);
+    double mat22 = p2d[2](1) - p2d[0](1);
+    
+    double det = mat11*mat22-mat21*mat12;
+    double inv11 = mat22/det;
+    double inv21 = -mat21/det;
+    double inv12 = -mat12/det;
+    double inv22 = mat11/det;
+          
+    //    cout << "drawsurfacevectors. xoffset = " << xoffset << ", yoffset = ";
+    //    cout << yoffset << endl;
+    
+    for (s = xoffset/gridsize; s <= 1+xoffset/gridsize; s += 1.0 / gridsize)
+      if (s >= minx2d && s <= maxx2d)
+        for (t = yoffset/gridsize; t <= 1+yoffset/gridsize; t += 1.0 / gridsize)
+          if (t >= miny2d && t <= maxy2d)
+            {
+              double lam1 = inv11 * (s - p2d[0](0)) + inv12 * (t-p2d[0](1));
+              double lam2 = inv21 * (s - p2d[0](0)) + inv22 * (t-p2d[0](1));
+              
+              if (lam1 >= 0 && lam2 >= 0 && lam1+lam2 <= 1)
+                {
+                  Point<3> cp;
+                  for (k = 0; k < 3; k++)
+                    cp(k) = lp[0](k) + 
+                      lam1 * (lp[1](k)-lp[0](k)) + 
+                      lam2 * (lp[2](k)-lp[0](k));
+                  
+                  Vec<3> v;
+                  double values[6];
+                  bool drawelem = 
+                    GetSurfValues (vsol, sei, lam1, lam2, values);
+                  
+                  if (!vsol->iscomplex)
+                    for (k = 0; k < 3; k++)
+                      v(k) = values[k];
+                  else
+                    {
+                      if (!imag_part)
+                        for (k = 0; k < 3; k++)
+                          v(k) = values[2*k];
+                      else
+                        for (k = 0; k < 3; k++)
+                          v(k) = values[2*k+1];
+                    }
+                  
+                  if (mesh->GetDimension() == 2)
+                    if ( (!vsol->iscomplex && vsol->components != 3) ||
+                         (vsol->iscomplex && vsol->components != 6) )
+                      v(2) = 0;
+                  
+                  double val = v.Length();
+
+                  SetOpenGlColor  (val); // (val, minval, maxval, logscale);  // change JS
+
+                  if (val > 1e-10 * maxval)
+                    v *= (rad / val / gridsize * 0.5);
+                  else 
+                    drawelem = 0;
+
+                  if ( drawelem ) 
+                    DrawCone (cp, cp+4*v, 0.8*rad / gridsize);
+                }
+            }
+    
+  }
+
+
+
+  void  VisualSceneSolution :: DrawSurfaceVectors ()
+  {
+    SurfaceElementIndex sei;
+
+    const SolData * vsol = NULL;
+    // bool drawelem;
+
+    if (vecfunction != -1)
+      vsol = soldata[vecfunction];
+
+    if (mesh->GetTimeStamp () > solutiontimestamp)
+      vsol = NULL;
+     
+    if (!vsol) return;
+
+
+    Point<3> pmin = center - Vec3d (rad, rad, rad);
+    Point<3> pmax = center - Vec3d (rad, rad, rad);
+
+
+    // glColor3d (1.0, 1.0, 1.0);
+    // glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+    if (vsol->draw_surface && showsurfacesolution)
+      {
+        int nse = mesh->GetNSE();
+        for (sei = 0; sei < nse; sei++)
+          {
+            const Element2d & el = (*mesh)[sei];
+          
+	    if ( el . IsGhost() ) continue;
+
+            if (el.GetType() == TRIG || el.GetType() == TRIG6)
+              {
+          
+                Array< Point<3> > lp(3);
+
+                lp[0] = mesh->Point(el[2]);
+                lp[1] = mesh->Point(el[0]);
+                lp[2] = mesh->Point(el[1]);
+
+                DrawTrigSurfaceVectors(lp,pmin,pmax,sei,vsol);
+                
+                /*
+                  Vec<3> n = Cross (lp[1]-lp[0], lp[2]-lp[0]);
+                  Vec<3> na (fabs (n(0)), fabs(n(1)), fabs(n(2)));
+                  if (na(0) > na(1) && na(0) > na(2))
+                  dir = 1;
+                  else if (na(1) > na(2))
+                  dir = 2;
+                  else 
+                  dir = 3;
+
+                  dir1 = (dir % 3) + 1;
+                  dir2 = (dir1 % 3) + 1;
+          
+                  for (k = 0; k < 3; k++)
+                  {
+                  p2d[k] = Point<2> ((lp[k](dir1-1) - pmin(dir1-1)) / (2*rad),
+                  (lp[k](dir2-1) - pmin(dir2-1)) / (2*rad));
+                  }
+          
+                  double minx2d, maxx2d, miny2d, maxy2d;
+                  minx2d = maxx2d = p2d[0](0);
+                  miny2d = maxy2d = p2d[0](1);
+                  for (k = 1; k < 3; k++)
+                  {
+                  minx2d = min2 (minx2d, p2d[k](0));
+                  maxx2d = max2 (maxx2d, p2d[k](0));
+                  miny2d = min2 (miny2d, p2d[k](1));
+                  maxy2d = max2 (maxy2d, p2d[k](1));
+                  }
+
+                  double mat11 = p2d[1](0) - p2d[0](0);
+                  double mat21 = p2d[1](1) - p2d[0](1);
+                  double mat12 = p2d[2](0) - p2d[0](0);
+                  double mat22 = p2d[2](1) - p2d[0](1);
+
+                  double det = mat11*mat22-mat21*mat12;
+                  double inv11 = mat22/det;
+                  double inv21 = -mat21/det;
+                  double inv12 = -mat12/det;
+                  double inv22 = mat11/det;
+          
+                  //      cout << "drawsurfacevectors. xoffset = " << xoffset << ", yoffset = ";
+                  //      cout << yoffset << endl;
+          
+                  for (s = xoffset/gridsize; s <= 1+xoffset/gridsize; s += 1.0 / gridsize)
+                  if (s >= minx2d && s <= maxx2d)
+                  for (t = yoffset/gridsize; t <= 1+yoffset/gridsize; t += 1.0 / gridsize)
+                  if (t >= miny2d && t <= maxy2d)
+                  {
+                  double lam1 = inv11 * (s - p2d[0](0)) + inv12 * (t-p2d[0](1));
+                  double lam2 = inv21 * (s - p2d[0](0)) + inv22 * (t-p2d[0](1));
+                    
+                  if (lam1 >= 0 && lam2 >= 0 && lam1+lam2 <= 1)
+                  {
+                  Point<3> cp;
+                  for (k = 0; k < 3; k++)
+                  cp(k) = lp[0](k) + 
+                  lam1 * (lp[1](k)-lp[0](k)) + 
+                  lam2 * (lp[2](k)-lp[0](k));
+
+                  Vec<3> v;
+                  double values[6];
+                  drawelem = GetSurfValues (vsol, sei, lam1, lam2, values);
+
+                  if (!vsol->iscomplex)
+                  for (k = 0; k < 3; k++)
+                  v(k) = values[k];
+                  else
+                  {
+                  if (!imag_part)
+                  for (k = 0; k < 3; k++)
+                  v(k) = values[2*k];
+                  else
+                  for (k = 0; k < 3; k++)
+                  v(k) = values[2*k+1];
+                  }
+                        
+                  if (mesh->GetDimension() == 2)
+                  if ( (!vsol->iscomplex && vsol->components != 3) ||
+                  (vsol->iscomplex && vsol->components != 6) )
+                  v(2) = 0;
+                        
+                  double val = v.Length();
+                  SetOpenGlColor  (val, minval, maxval, logscale);
+
+                  if (val > 1e-10 * maxval)
+                  v *= (rad / val / gridsize * 0.5);
+                  else drawelem = 0;
+                  // "drawelem": added 07.04.2004 (FB)
+                  if ( drawelem ) DrawCone (cp, cp+4*v, 0.8*rad / gridsize);
+
+
+                  }
+                  }
+                */
+              }
+            else if (el.GetType() == QUAD)
+              {
+                /*
+		  Array < Point<3> > lp(3);
+
+		  lp[0] = mesh->Point(el[0]);
+		  lp[1] = mesh->Point(el[1]);
+		  lp[2] = mesh->Point(el[2]);
+
+		  DrawTrigSurfaceVectors(lp,pmin,pmax,sei,vsol);
+
+		  lp[0] = mesh->Point(el[0]);
+		  lp[1] = mesh->Point(el[2]);
+		  lp[2] = mesh->Point(el[3]);
+
+		  DrawTrigSurfaceVectors(lp,pmin,pmax,sei,vsol);
+                */
+                
+                Point<3> lp[4];
+                Point<2> p2d[4];
+                
+                for (int k = 0; k < 4; k++)
+                  lp[k] = mesh->Point (el[k]);
+                
+                
+                Vec<3> n = Cross (lp[1]-lp[0], lp[2]-lp[0]);
+                Vec<3> na (fabs (n(0)), fabs(n(1)), fabs(n(2)));
+                int dir, dir1, dir2;
+                if (na(0) > na(1) && na(0) > na(2))
+                  dir = 1;
+                else if (na(1) > na(2))
+                  dir = 2;
+                else 
+                  dir = 3;
+                
+                dir1 = (dir % 3) + 1;
+                dir2 = (dir1 % 3) + 1;
+                
+                for (int k = 0; k < 4; k++)
+                  {
+                    p2d[k] = Point<2> ((lp[k](dir1-1) - pmin(dir1-1)) / (2*rad),
+                                       (lp[k](dir2-1) - pmin(dir2-1)) / (2*rad));
+                  }
+                
+                double minx2d, maxx2d, miny2d, maxy2d;
+                minx2d = maxx2d = p2d[0](0);
+                miny2d = maxy2d = p2d[0](1);
+                for (int k = 1; k < 4; k++)
+                  {
+                    minx2d = min2 (minx2d, p2d[k](0));
+                    maxx2d = max2 (maxx2d, p2d[k](0));
+                    miny2d = min2 (miny2d, p2d[k](1));
+                    maxy2d = max2 (maxy2d, p2d[k](1));
+                  }
+                
+                for (double s = xoffset/gridsize; s <= 1+xoffset/gridsize; s += 1.0 / gridsize)
+                  if (s >= minx2d && s <= maxx2d)
+                    for (double t = yoffset/gridsize; t <= 1+yoffset/gridsize; t += 1.0 / gridsize)
+                      if (t >= miny2d && t <= maxy2d)
+                        {
+                          double lami[3];
+                          Point3d p3d(2*rad*s+pmin(0), 2*rad*t+pmin(1),0);
+                          
+                          if (mesh->PointContainedIn2DElement (p3d, lami, sei+1))
+                            {
+                              Point<3> cp = p3d;
+                              double lam1 = lami[0];
+                              double lam2 = lami[1];
+                              
+                              //for (k = 0; k < 3; k++)
+                              //cp(k) = lp[0](k) + 
+                              //lam1 * (lp[1](k)-lp[0](k)) + 
+                              //lam2 * (lp[2](k)-lp[0](k));
+                              
+                              
+                              Vec<3> v;
+                              double values[6];
+                              bool drawelem = GetSurfValues (vsol, sei, lam1, lam2, values);
+                              (*testout) << "sei " << sei << " lam1 " << lam1 << " lam2 " << lam2 << " drawelem " << drawelem << endl;
+                              
+                              if (!vsol->iscomplex)
+                                for (int k = 0; k < 3; k++)
+                                  v(k) = values[k];
+                              else
+                                {
+                                  if (!imag_part)
+                                    for (int k = 0; k < 3; k++)
+                                      v(k) = values[2*k];
+                                  else
+                                    for (int k = 0; k < 3; k++)
+                                      v(k) = values[2*k+1];
+                                }
+                              
+                              if (mesh->GetDimension() == 2)
+                                if ( (!vsol->iscomplex && vsol->components != 3) ||
+                                     (vsol->iscomplex && vsol->components != 6) )
+                                  v(2) = 0;
+                              
+                              double val = v.Length();
+                              SetOpenGlColor  (val); // , minval, maxval, logscale); july 09
+                              
+                              (*testout) << "v " << v << endl;
+                              
+                              if (val > 1e-10 * maxval)
+                                v *= (rad / val / gridsize * 0.5);
+                              
+                              (*testout) << "v " << v << endl;
+                              
+                              if ( drawelem )
+                                {
+                                  DrawCone (cp, cp+4*v, 0.8*rad / gridsize);
+                                  (*testout) << "cp " << cp << " rad " << rad << " gridsize " << gridsize << endl;
+                                }
+                              
+                            }
+                        }
+              }
+          }
+      }
+  }
+  
+  
+  
+  
+  void VisualSceneSolution :: 
+  DrawIsoLines (const Point<3> & p1, 
+                const Point<3> & p2, 
+                const Point<3> & p3,
+                double val1, double val2, double val3)
+  {
+    DrawIsoLines2 (p1, p2, p1, p3, val1, val2, val1, val3); // , minval, maxval, n);
+    DrawIsoLines2 (p2, p1, p2, p3, val2, val1, val2, val3); // , minval, maxval, n);
+    DrawIsoLines2 (p3, p1, p3, p2, val3, val1, val3, val2); // , minval, maxval, n);
+  }
+
+
+  void VisualSceneSolution :: 
+  DrawIsoLines2 (const Point<3> & hp1, 
+                 const Point<3> & hp2, 
+                 const Point<3> & hp3,
+                 const Point<3> & hp4,
+                 double val1, double val2, double val3, double val4)
+  {
+    int n = numisolines;
+    Point<3> p1, p2, p3, p4;
+    if (val1 < val2)
+      {
+        p1 = hp1; p2 = hp2;
+      }
+    else
+      {
+        p1 = hp2; p2 = hp1;
+        swap (val1, val2);
+      }
+
+    if (val3 < val4)
+      {
+        p3 = hp3; p4 = hp4;
+      }
+    else
+      {
+        p3 = hp4; p4 = hp3;
+        swap (val3, val4);
+      }
+
+    val2 += 1e-10;
+    val4 += 1e-10;
+
+    double fac = (maxval-minval) / n;
+    double idelta1 = 1.0 / (val2 - val1);
+    double idelta2 = 1.0 / (val4 - val3);
+
+    int mini = int ((max2 (val1, val3) - minval) / fac);
+    int maxi = int ((min2 (val2, val4) - minval) / fac);
+    if (mini < 0) mini = 0;
+    if (maxi > n-1) maxi = n-1;
+
+    for (int i = mini; i <= maxi; i++)
+      {
+        double val = minval + i * fac;
+        double lam1 = (val - val1) * idelta1;
+        double lam2 = (val - val3) * idelta2;
+        if (lam1 >= 0 && lam1 <= 1 && lam2 >= 0 && lam2 <= 1)
+          {
+            Point<3> lp1 = p1 + lam1 * (p2-p1);
+            Point<3> lp2 = p3 + lam2 * (p4-p3);
+            glVertex3dv (lp1 );
+            glVertex3dv (lp2 );
+            // glVertex3dv (lp2 );  // better ?
+            // glVertex3dv (lp1 );  
+          }
+      }
+  }
+
+
+
+  void VisualSceneSolution :: 
+  GetMinMax (int funcnr, int comp, double & minv, double & maxv) const
+  {
+#ifdef PARALLEL
+    if (id == 0)
+      {
+	MyMPI_SendCmd ("redraw");
+	MyMPI_SendCmd ("getminmax");
+      }
+    MyMPI_Bcast (funcnr);
+    MyMPI_Bcast (comp);
+#endif
+
+    const SolData * sol;
+    double val;
+    bool considerElem;
+
+    bool hasit = false;
+    minv = 0; maxv = 1;
+    if (funcnr != -1)
+      {
+        sol = soldata[funcnr];
+
+        if (sol->draw_volume)
+          {
+            int ne = mesh->GetNE();
+            for (int i = 0; i < ne; i++)
+              {
+                considerElem = GetValue (sol, i, 0.333, 0.333, 0.333, comp, val);
+                if (considerElem)
+                  {
+                    if (val > maxv || !hasit)
+                      maxv = val;
+                    if (val < minv || !hasit)
+                      minv = val;
+                    hasit = true;
+                  }
+              }
+          }
+        if (sol->draw_surface)
+          {
+            int nse = mesh->GetNSE();
+            for (int i = 0; i < nse; i++)
+              {
+                ELEMENT_TYPE type = mesh->SurfaceElement(i+1).GetType();
+                if (type == QUAD)
+                  considerElem = GetSurfValue (sol, i, 0.5, 0.5, comp, val);
+                else
+                  considerElem = GetSurfValue (sol, i, 0.3333333, 0.3333333, comp, val);
+                if (considerElem)
+                  {
+                    if (val > maxv || !hasit)
+                      maxv = val;
+                    if (val < minv || !hasit)
+                      minv = val;
+                    hasit = true;
+                  }
+              }
+          }
+      }
+    if (minv == maxv) maxv = minv+1e-6;
+
+#ifdef PARALLEL
+    if ((ntasks > 1) && (id == 0))
+      {
+	minv = 1e99;
+	maxv = -1e99;
+      }
+    double hmin, hmax;
+    MPI_Reduce (&minv, &hmin, 1, MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD);
+    MPI_Reduce (&maxv, &hmax, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
+    minv = hmin;
+    maxv = hmax;
+#endif
+  }
+
+
+
+
+
+  bool VisualSceneSolution :: 
+  GetValues (const SolData * data, ElementIndex elnr, 
+             double lam1, double lam2, double lam3,
+             double * values) const
+  {
+    bool ok = false;
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+          ok = data->solclass->GetValue (elnr, lam1, lam2, lam3, values);
+          break;
+        }
+      default:
+        {
+          for (int i = 0; i < data->components; i++)
+            ok = GetValue (data, elnr, lam1, lam2, lam3, i+1, values[i]);
+        }
+      }
+    return ok;
+  }
+
+  bool VisualSceneSolution :: 
+  GetValues (const SolData * data, ElementIndex elnr, 
+             const double xref[], const double x[], const double dxdxref[], 
+             double * values) const
+  {
+    bool ok = false;
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+          ok = data->solclass->GetValue (elnr, xref, x, dxdxref, values);
+          break;
+        }
+      default:
+        {
+          for (int i = 0; i < data->components; i++)
+            ok = GetValue (data, elnr, xref[0], xref[1], xref[2], i+1, values[i]);
+        }
+      }
+    return ok;
+  }
+
+
+  bool VisualSceneSolution :: 
+  GetValue (const SolData * data, ElementIndex elnr, 
+            const double xref[], const double x[], const double dxdxref[], 
+            int comp, double & val) const
+  {
+
+    double lam1 = xref[0];
+    double lam2 = xref[1];
+    double lam3 = xref[2];
+        
+    val = 0;
+    bool ok = 0;
+
+
+    if (comp == 0)
+      {
+        ArrayMem<double,20> values(data->components);
+        ok = GetValues (data, elnr, xref, x, dxdxref, &values[0]);
+
+	val = ExtractValue (data, 0, &values[0]);
+	return ok;
+      }
+
+
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+          double values[20];
+          ok = data->solclass->GetValue (elnr, xref, x, dxdxref, values);
+
+          val = values[comp-1];
+          return ok;
+        }
+      case SOL_NODAL:
+        {
+          const Element & el = (*mesh)[elnr];
+
+          double lami[8] = { 0.0 };
+          int np = 0;
+        
+          switch (el.GetType())
+            {
+            case TET:
+            case TET10:
+              {
+                lami[1] = lam1;
+                lami[2] = lam2;
+                lami[3] = lam3;
+                lami[0] = 1-lam1-lam2-lam3;
+                np = 4;
+                break;
+              }
+            case PRISM:
+            case PRISM12:
+              {
+                lami[0] = (1-lam3) * (1-lam1-lam2);
+                lami[1] = (1-lam3) * lam1;
+                lami[2] = (1-lam3) * lam2;
+                lami[3] = (lam3) * (1-lam1-lam2);
+                lami[4] = (lam3) * lam1;
+                lami[5] = (lam3) * lam2;
+                np = 6;
+                break;
+              }     
+            default:
+              cerr << "case not implementd 23523" << endl;
+            }
+
+          for (int i = 0; i < np; i++)
+            val += lami[i] * data->data[(el[i]-1) * data->dist + comp-1];
+
+          return 1;
+        }
+
+      case SOL_ELEMENT:
+        {
+          val = data->data[elnr * data->dist + comp-1];
+          return 1;
+        }
+
+      case SOL_SURFACE_ELEMENT:
+        return 0;
+
+      case SOL_NONCONTINUOUS:
+        {
+          const Element & el = (*mesh)[elnr];
+
+          double lami[8] = { 0.0 };
+          int np = 0;
+
+          switch (el.GetType())
+            {
+            case TET:
+            case TET10:
+              {
+                lami[1] = lam1;
+                lami[2] = lam2;
+                lami[3] = lam3;
+                lami[0] = 1-lam1-lam2-lam3;
+                np = 4;
+                break;
+              }
+            case PRISM:
+            case PRISM12:
+              {
+                lami[0] = (1-lam3) * (1-lam1-lam2);
+                lami[1] = (1-lam3) * lam1;
+                lami[2] = (1-lam3) * lam2;
+                lami[3] = (lam3) * (1-lam1-lam2);
+                lami[4] = (lam3) * lam1;
+                lami[5] = (lam3) * lam2;
+                np = 6;
+                break;
+              }
+            case PYRAMID:
+              {
+                if (lam3 > 1-1e-5)
+                  {
+                    lami[0] = lami[1] = lami[2] = lami[3] = 0;
+                    lami[4] = 1;
+                  }
+                else
+                  {
+                    double x0 = lam1 / (1-lam3);
+                    double y0 = lam2 / (1-lam3);
+                    lami[0] = (1-x0) * (1-y0) * (1-lam3);
+                    lami[1] = (  x0) * (1-y0) * (1-lam3);
+                    lami[2] = (  x0) * (  y0) * (1-lam3);
+                    lami[3] = (1-x0) * (  y0) * (1-lam3);
+                    lami[4] = lam3;
+                    np = 5;
+                  }
+                break;
+              }
+            default:
+              np = 0;
+            }
+
+          int base;
+          if (data->order == 1)
+            base = 6 * elnr;
+          else
+            base = 10 * elnr;
+
+
+          for (int i = 0; i < np; i++)
+            val += lami[i] * data->data[(base+i) * data->dist + comp-1];
+
+          return 1;
+        }
+
+      case SOL_MARKED_ELEMENTS:
+        {
+          val = (*mesh)[elnr].TestRefinementFlag();
+          return 1;
+        }
+      
+      case SOL_ELEMENT_ORDER:
+        {
+          val = (*mesh)[elnr].GetOrder();
+          return 1;
+        }
+
+      default:
+        cerr << "case not handled 7234" << endl;
+      }
+    return 0;
+  }
+
+
+
+  bool VisualSceneSolution :: 
+  GetValue (const SolData * data, ElementIndex elnr, 
+            double lam1, double lam2, double lam3,
+            int comp, double & val) const
+  {
+
+    val = 0;
+    bool ok = 0;
+
+    if (comp == 0)
+      {
+        ArrayMem<double,20> values(data->components);
+        ok = GetValues (data, elnr, lam1, lam2, lam3, &values[0]);
+	val = ExtractValue (data, 0, &values[0]);
+	return ok;
+      }
+
+
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+          double values[20];
+          ok = data->solclass->GetValue (elnr, lam1, lam2, lam3, values);
+
+          val = values[comp-1];
+          return ok;
+        }
+      case SOL_NODAL:
+        {
+          const Element & el = (*mesh)[elnr];
+
+          double lami[8] = { 0.0 };
+          int np = 0;
+        
+          switch (el.GetType())
+            {
+            case TET:
+            case TET10:
+              {
+                lami[1] = lam1;
+                lami[2] = lam2;
+                lami[3] = lam3;
+                lami[0] = 1-lam1-lam2-lam3;
+                np = 4;
+                break;
+              }
+            case PRISM:
+            case PRISM12:
+              {
+                lami[0] = (1-lam3) * (1-lam1-lam2);
+                lami[1] = (1-lam3) * lam1;
+                lami[2] = (1-lam3) * lam2;
+                lami[3] = (lam3) * (1-lam1-lam2);
+                lami[4] = (lam3) * lam1;
+                lami[5] = (lam3) * lam2;
+                np = 6;
+                break;
+              }     
+            default:
+              cerr << "case not implemented 234324" << endl;
+            }
+
+          for (int i = 0; i < np; i++)
+            val += lami[i] * data->data[(el[i]-1) * data->dist + comp-1];
+
+          return 1;
+        }
+
+      case SOL_ELEMENT:
+        {
+          val = data->data[elnr * data->dist + comp-1];
+          return 1;
+        }
+
+      case SOL_SURFACE_ELEMENT:
+        return 0;
+
+      case SOL_NONCONTINUOUS:
+        {
+          const Element & el = (*mesh)[elnr];
+
+          double lami[8] = { 0.0 };
+          int np = 0;
+
+          switch (el.GetType())
+            {
+            case TET:
+            case TET10:
+              {
+                lami[1] = lam1;
+                lami[2] = lam2;
+                lami[3] = lam3;
+                lami[0] = 1-lam1-lam2-lam3;
+                np = 4;
+                break;
+              }
+            case PRISM:
+            case PRISM12:
+              {
+                lami[0] = (1-lam3) * (1-lam1-lam2);
+                lami[1] = (1-lam3) * lam1;
+                lami[2] = (1-lam3) * lam2;
+                lami[3] = (lam3) * (1-lam1-lam2);
+                lami[4] = (lam3) * lam1;
+                lami[5] = (lam3) * lam2;
+                np = 6;
+                break;
+              }
+            case PYRAMID:
+              {
+                if (lam3 > 1-1e-5)
+                  {
+                    lami[0] = lami[1] = lami[2] = lami[3] = 0;
+                    lami[4] = 1;
+                  }
+                else
+                  {
+                    double x0 = lam1 / (1-lam3);
+                    double y0 = lam2 / (1-lam3);
+                    lami[0] = (1-x0) * (1-y0) * (1-lam3);
+                    lami[1] = (  x0) * (1-y0) * (1-lam3);
+                    lami[2] = (  x0) * (  y0) * (1-lam3);
+                    lami[3] = (1-x0) * (  y0) * (1-lam3);
+                    lami[4] = lam3;
+                    np = 5;
+                  }
+                break;
+              }
+            default:
+              np = 0;
+            }
+
+          int base;
+          if (data->order == 1)
+            base = 6 * elnr;
+          else
+            base = 10 * elnr;
+
+
+          for (int i = 0; i < np; i++)
+            val += lami[i] * data->data[(base+i) * data->dist + comp-1];
+
+          return 1;
+        }
+
+      case SOL_MARKED_ELEMENTS:
+        {
+          val = (*mesh)[elnr].TestRefinementFlag();
+          return 1;
+        }
+      
+      case SOL_ELEMENT_ORDER:
+        {
+          val = (*mesh)[elnr].GetOrder();
+          return 1;
+        }
+      default:
+        cerr << "case not implemented 234234" << endl;
+      }
+    return 0;
+  }
+
+
+
+
+
+
+
+  bool VisualSceneSolution :: 
+  GetValueComplex (const SolData * data, ElementIndex elnr, 
+                   double lam1, double lam2, double lam3,
+                   int comp, complex<double> & val) const
+  {
+    val = 0.0;
+    bool ok = 0;
+
+           
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+          double values[20];
+          ok = data->solclass->GetValue (elnr, lam1, lam2, lam3, values);
+          val = complex<double> (values[comp-1], values[comp]);
+          return ok;
+        }
+      default:
+        cerr << "case not handled 234234" << endl;
+      } 
+    return 0;
+  }
+  
+
+  bool VisualSceneSolution :: 
+  GetMultiValues (const SolData * data, ElementIndex elnr, int npt,
+		  const double * xref, int sxref,
+		  const double * x, int sx,
+		  const double * dxdxref, int sdxdxref,
+		  double * val, int sval) const
+  {
+    bool drawelem = false;
+    if (data->soltype == SOL_VIRTUALFUNCTION)
+      drawelem = data->solclass->GetMultiValue(elnr, npt, xref, sxref, x, sx, dxdxref, sdxdxref, val, sval);
+    else
+      for (int i = 0; i < npt; i++)
+        drawelem = GetValues (data, elnr, xref+i*sxref, x+i*sx, dxdxref+i*sdxdxref, val+i*sval);
+    return drawelem;
+  }
+
+
+
+
+
+
+  bool VisualSceneSolution :: 
+  GetSurfValues (const SolData * data, SurfaceElementIndex selnr, 
+                 double lam1, double lam2, 
+                 double * values) const
+  {
+    bool ok = false;
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+          ok = data->solclass->GetSurfValue (selnr, lam1, lam2, values);
+          // ok = 1;
+          // values[0] = 1.0;
+          break;
+        }
+      default:
+        {
+          for (int i = 0; i < data->components; i++)
+            ok = GetSurfValue (data, selnr, lam1, lam2, i+1, values[i]);
+        }
+      }
+    return ok;
+  }
+
+
+  bool VisualSceneSolution :: 
+  GetSurfValues (const SolData * data, SurfaceElementIndex selnr, 
+                 const double xref[], const double x[], const double dxdxref[], 
+                 double * values) const
+  {
+    bool ok = false;
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+          ok = data->solclass->GetSurfValue (selnr, xref, x, dxdxref, values);
+          break;
+        }
+      default:
+        {
+          for (int i = 0; i < data->components; i++)
+            ok = GetSurfValue (data, selnr, xref[0], xref[1], i+1, values[i]);
+        }
+      }
+    return ok;
+  }
+
+  bool VisualSceneSolution :: 
+  GetMultiSurfValues (const SolData * data, SurfaceElementIndex elnr, int npt,
+                      const double * xref, int sxref,
+                      const double * x, int sx,
+                      const double * dxdxref, int sdxdxref,
+                      double * val, int sval) const
+  {
+    bool drawelem = false;
+    if (data->soltype == SOL_VIRTUALFUNCTION)
+      drawelem = data->solclass->GetMultiSurfValue(elnr, npt, xref, sxref, x, sx, dxdxref, sdxdxref, val, sval);
+    else
+      for (int i = 0; i < npt; i++)
+        drawelem = GetSurfValues (data, elnr, xref+i*sxref, x+i*sx, dxdxref+i*sdxdxref, val+i*sval);
+    return drawelem;
+  }
+  
+  double VisualSceneSolution ::  ExtractValue (const SolData * data, int comp, double * values) const
+  {
+    double val = 0;
+    if (comp == 0)
+      {
+        switch (evalfunc)
+          {
+          case FUNC_ABS:
+            {
+              for (int ci = 0; ci < data->components; ci++)
+                val += sqr (values[ci]);
+              val = sqrt (val);
+              break;
+            }
+          case FUNC_ABS_TENSOR:
+            {
+              int d = 0;
+              switch (data->components)
+                {
+                case 1: d = 1; break;
+                case 3: d = 2; break;
+                case 6: d = 3; break;
+                }
+              for (int ci = 0; ci < d; ci++)
+                val += sqr (values[ci]);
+              for (int ci = d; ci < data->components; ci++)
+                val += 2*sqr (values[ci]);
+              val = sqrt (val);
+              break;
+            }
+
+          case FUNC_MISES:
+            {
+              int d = 0;
+              switch(data->components)
+                {
+                case 1: d = 1; break;
+                case 3: d = 2; break;
+                case 6: d = 3; break;
+                }
+              int ci;
+              double trace = 0.;
+              for (ci = 0; ci < d; ci++)
+                trace += 1./3.*(values[ci]);
+              for (ci = 0; ci < d; ci++)
+                val += sqr (values[ci]-trace);
+              for (ci = d; ci < data->components; ci++)
+                val += 2.*sqr (values[ci]);
+              val = sqrt (val);
+              break;
+            }
+          case FUNC_MAIN:
+            {
+              int d = 0;
+              switch(data->components)
+                {
+                case 1: d = 1; break;
+                case 3: d = 2; break;
+                case 6: d = 3; break;
+                }
+              Mat<3,3> m ;
+              Vec<3> ev;
+              int ci;
+              for (ci = 0; ci < d; ci++)
+                m(ci,ci) = (values[ci]);
+              m(0,1) = m(1,0) = values[3];
+              m(0,2) = m(2,0) = values[4];
+              m(1,2) = m(2,1) = values[5];
+
+              EigenValues (m, ev);
+              double help;
+              for (int i=0; i<d; i++)
+                {
+                  for (int j=d-1; i<j; j--)
+                    {
+                      if ( abs(ev(j)) > abs(ev(j-1)) )
+                        {
+                          help = ev(j);
+                          ev(j) = ev(j-1);
+                          ev(j-1) = help;
+                        }
+                    }
+                }
+              val = (ev(0));
+              break;
+            }
+          }
+        return val;
+      }
+
+    return values[comp-1];
+  }
+
+  complex<double> VisualSceneSolution ::  ExtractValueComplex (const SolData * data, int comp, double * values) const
+  {
+    if (!data->iscomplex)
+      return values[comp-1];
+    else
+      return complex<double> (values[comp-1], values[comp]);
+  }
+
+
+
+
+  bool VisualSceneSolution :: 
+  GetSurfValueComplex (const SolData * data, SurfaceElementIndex selnr, 
+                       double lam1, double lam2, 
+                       int comp, complex<double> & val) const
+  {
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+          ArrayMem<double,20> values(data->components);
+          bool ok;
+          
+          ok = data->solclass->GetSurfValue (selnr, lam1, lam2, &values[0]);
+          
+          if (ok)
+            {
+              if (!data->iscomplex)
+                val = values[comp-1];
+              else
+                val = complex<double> (values[comp-1], values[comp]);
+            }
+          
+          return ok;
+        }
+      default:
+        cerr << "case not implementd 6565" << endl;
+      }
+    return 0;
+  }
+  
+  bool VisualSceneSolution :: 
+  GetSurfValue (const SolData * data, SurfaceElementIndex selnr, 
+                double lam1, double lam2, 
+                int comp, double & val) const
+  {
+    bool ok;
+    if (comp == 0)
+      {
+        val = 0;
+        ArrayMem<double,20> values(data->components);
+        ok = GetSurfValues (data, selnr, lam1, lam2, &values[0]);
+	val = ExtractValue (data, 0, &values[0]);
+	return ok;
+      }
+
+
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+  
+          ArrayMem<double,20> values(data->components);
+          bool ok;
+
+          ok = data->solclass->GetSurfValue (selnr, lam1, lam2, &values[0]);
+
+          if (ok)
+            {
+              if (!data->iscomplex)
+                val =  values[comp-1];
+              else
+                {
+                  // cout << "time = " << time << ", cos = " << cos(time) << endl;
+     
+                  // old version: val = values[comp-1]*cos(3*time) + values[comp]*sin(3*time);
+                  // SZ: Sept 06 
+                  if(comp%2==0) 
+                    val =  values[comp-1]*cos(3*time) - values[comp-2]*sin(3*time);
+                  else
+                    val = values[comp-1]*cos(3*time) + values[comp]*sin(3*time);
+         
+         
+         
+                }
+            }
+
+          return ok;
+        }
+
+
+      case SOL_NODAL:
+        {
+          const Element2d & el = (*mesh)[selnr];
+
+          double lami[8];
+          int np, i;
+          val = 0;
+          double lam3 = 1-lam1-lam2;
+
+          switch (el.GetType())
+            {
+            case TRIG:
+              /*
+                lami[0] = lam3;
+                lami[1] = lam1;
+                lami[2] = lam2;
+              */
+              lami[0] = lam1;
+              lami[1] = lam2;
+              lami[2] = lam3;
+              np = 3;
+              break;
+
+            case TRIG6:
+              /*
+                lami[0] = lam3*(2*lam3-1);
+                lami[1] = lam1*(2*lam1-1);
+                lami[2] = lam2*(2*lam2-1);
+              */
+              // hierarchical basis:
+              lami[0] = lam3;
+              lami[1] = lam1;
+              lami[2] = lam2;
+              lami[3] = 4*lam1*lam2;
+              lami[4] = 4*lam2*lam3;
+              lami[5] = 4*lam1*lam3;
+              np = 6;
+              break;
+
+            case QUAD:
+            case QUAD6:
+              lami[0] = (1-lam1)*(1-lam2);
+              lami[1] = lam1 * (1-lam2);
+              lami[2] = lam1 * lam2;
+              lami[3] = (1-lam1) * lam2;
+              np = 4;
+              break;
+
+            default:
+              np = 0;
+            }
+
+          for (i = 0; i < np; i++)
+            val += lami[i] * data->data[(el[i]-1) * data->dist + comp-1];
+
+          return 1;
+        }
+
+      case SOL_ELEMENT:
+        {
+          int el1, el2;
+          mesh->GetTopology().GetSurface2VolumeElement (selnr+1, el1, el2);
+          el1--;
+
+          val = data->data[el1 * data->dist+comp-1];
+          return 1;
+        }
+
+      case SOL_NONCONTINUOUS:
+        {
+          val = 0;
+          // ?????
+          return 0;
+        }
+
+      case SOL_SURFACE_ELEMENT:
+        {
+          val = data->data[selnr * data->dist + comp-1];
+          return 1;
+        }
+
+      case SOL_SURFACE_NONCONTINUOUS:
+        {
+          const Element2d & el = (*mesh)[selnr];
+
+          double lami[8];
+          int np = 0;
+          val = 0;
+          int order = data->order;
+
+          switch (order)
+            {
+            case 0:
+              return data->data[selnr * data->dist + comp-1];
+            case 1:
+              {
+                switch (el.GetType())
+                  {
+                  case TRIG:
+                  case TRIG6:
+                    {
+                      lami[1] = lam1;
+                      lami[2] = lam2;
+                      lami[0] = 1-lam1-lam2;
+                      np = 3;
+                      break;
+                    }
+                  default:
+                    cerr << "case not implementd 2342" << endl;
+                  }
+                break;
+              }
+            case 2:
+              {
+                switch (el.GetType())
+                  {
+                  case TRIG:
+                    {
+                      lami[1] = lam1;
+                      lami[2] = lam2;
+                      lami[0] = 1-lam1-lam2;
+                      np = 3;
+                      break;
+                    }
+                  case TRIG6:
+                    {
+                      double lam3 = 1-lam1-lam2;
+                      lami[1] = 2*lam1 * (lam1-0.5);
+                      lami[2] = 2*lam2 * (lam2-0.5);
+                      lami[0] = 2*lam3 * (lam3-0.5);
+                      lami[3] = 4*lam1*lam2;
+                      lami[4] = 4*lam2*lam3;
+                      lami[5] = 4*lam1*lam3;
+                      np = 6;
+                      break;
+                    }
+                  default:
+                    cerr << "case not implemented 8712" << endl;
+                  }
+                break;
+              }
+            }
+        
+          int base;
+          if (order == 1)
+            base = 4 * selnr;
+          else 
+            base = 9 * selnr;
+
+          for (int i = 0; i < np; i++)
+            val += lami[i] * data->data[(base+i) * data->dist + comp-1];
+
+          return 1;
+        }
+
+      case SOL_MARKED_ELEMENTS:
+        {
+          val = (*mesh)[selnr].TestRefinementFlag();
+          return 1;
+        }
+      
+      case SOL_ELEMENT_ORDER:
+        {       
+          val = (*mesh)[selnr].GetOrder();
+          return 1;
+        }
+
+      }
+    return 0;
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+  bool VisualSceneSolution :: 
+  GetSurfValue (const SolData * data, SurfaceElementIndex selnr,
+                const double xref[], const double x[], const double dxdxref[], 
+                int comp, double & val) const
+  {
+    double lam1 = xref[0], lam2 = xref[1];
+
+    bool ok;
+    if (comp == 0)
+      {
+        val = 0;
+        ArrayMem<double,20> values(data->components);
+        ok = GetSurfValues (data, selnr, xref, x, dxdxref, &values[0]);
+	val = ExtractValue (data, 0, &values[0]);
+	return ok;
+      }
+
+
+    switch (data->soltype)
+      {
+      case SOL_VIRTUALFUNCTION:
+        {
+          ArrayMem<double,20> values(data->components);
+          bool ok;
+
+          // ok = data->solclass->GetSurfValue (selnr, lam1, lam2, &values[0]);
+          // cout << "data->solclass = " << flush << data->solclass << endl;
+          ok = data->solclass->GetSurfValue (selnr, xref, x, dxdxref, &values[0]);
+          // ok = 1;
+          // values[0] = 1.0;
+
+          if (ok)
+            {
+              if (!data->iscomplex)
+                val =  values[comp-1];
+              else
+                {
+                  // cout << "time = " << time << ", cos = " << cos(time) << endl;
+     
+                  // old version: val = values[comp-1]*cos(3*time) + values[comp]*sin(3*time);
+                  // SZ: Sept 06 
+                  if(comp%2==0) 
+                    val =  values[comp-1]*cos(3*time) - values[comp-2]*sin(3*time);
+                  else
+                    val = values[comp-1]*cos(3*time) + values[comp]*sin(3*time);
+                  
+                }
+            }
+
+          return ok;
+        }
+
+
+      case SOL_NODAL:
+        {
+          const Element2d & el = (*mesh)[selnr];
+
+          double lami[8];
+          int np, i;
+          val = 0;
+          double lam3 = 1-lam1-lam2;
+
+          switch (el.GetType())
+            {
+            case TRIG:
+              /*
+                lami[0] = lam3;
+                lami[1] = lam1;
+                lami[2] = lam2;
+              */
+              lami[0] = lam1;
+              lami[1] = lam2;
+              lami[2] = lam3;
+              np = 3;
+              break;
+
+            case TRIG6:
+              /*
+                lami[0] = lam3*(2*lam3-1);
+                lami[1] = lam1*(2*lam1-1);
+                lami[2] = lam2*(2*lam2-1);
+              */
+              // hierarchical basis:
+              lami[0] = lam3;
+              lami[1] = lam1;
+              lami[2] = lam2;
+              lami[3] = 4*lam1*lam2;
+              lami[4] = 4*lam2*lam3;
+              lami[5] = 4*lam1*lam3;
+              np = 6;
+              break;
+
+            case QUAD:
+            case QUAD6:
+              lami[0] = (1-lam1)*(1-lam2);
+              lami[1] = lam1 * (1-lam2);
+              lami[2] = lam1 * lam2;
+              lami[3] = (1-lam1) * lam2;
+              np = 4;
+              break;
+
+            default:
+              np = 0;
+            }
+
+          for (i = 0; i < np; i++)
+            val += lami[i] * data->data[(el[i]-1) * data->dist + comp-1];
+
+          return 1;
+        }
+
+      case SOL_ELEMENT:
+        {
+          int el1, el2;
+          mesh->GetTopology().GetSurface2VolumeElement (selnr+1, el1, el2);
+          el1--;
+
+          val = data->data[el1 * data->dist+comp-1];
+          return 1;
+        }
+
+      case SOL_NONCONTINUOUS:
+        {
+          val = 0;
+          // ?????
+          return 0;
+        }
+
+      case SOL_SURFACE_ELEMENT:
+        {
+          val = data->data[selnr * data->dist + comp-1];
+          return 1;
+        }
+
+      case SOL_SURFACE_NONCONTINUOUS:
+        {
+          const Element2d & el = (*mesh)[selnr];
+
+          double lami[8] = { 0.0 };
+          int np = 0;
+          val = 0;
+          int order = data->order;
+
+          switch (order)
+            {
+            case 0:
+              return data->data[selnr * data->dist + comp-1];
+            case 1:
+              {
+                switch (el.GetType())
+                  {
+                  case TRIG:
+                  case TRIG6:
+                    {
+                      lami[1] = lam1;
+                      lami[2] = lam2;
+                      lami[0] = 1-lam1-lam2;
+                      np = 3;
+                      break;
+                    }
+                  default:
+                    cerr << "case not impl 234234" << endl;
+                  }
+                break;
+              }
+            case 2:
+              {
+                switch (el.GetType())
+                  {
+                  case TRIG:
+                    {
+                      lami[1] = lam1;
+                      lami[2] = lam2;
+                      lami[0] = 1-lam1-lam2;
+                      np = 3;
+                      break;
+                    }
+                  case TRIG6:
+                    {
+                      double lam3 = 1-lam1-lam2;
+                      lami[1] = 2*lam1 * (lam1-0.5);
+                      lami[2] = 2*lam2 * (lam2-0.5);
+                      lami[0] = 2*lam3 * (lam3-0.5);
+                      lami[3] = 4*lam1*lam2;
+                      lami[4] = 4*lam2*lam3;
+                      lami[5] = 4*lam1*lam3;
+                      np = 6;
+                      break;
+                    }
+                  default:
+                    cerr << "case not implented 3234" << endl;
+                  }
+                break;
+              }
+            }
+        
+          int base;
+          if (order == 1)
+            base = 4 * selnr;
+          else 
+            base = 9 * selnr;
+
+          for (int i = 0; i < np; i++)
+            val += lami[i] * data->data[(base+i) * data->dist + comp-1];
+
+          return 1;
+        }
+
+      case SOL_MARKED_ELEMENTS:
+        {
+          val = (*mesh)[selnr].TestRefinementFlag();
+          return 1;
+        }
+      
+      case SOL_ELEMENT_ORDER:
+        {       
+          val = (*mesh)[selnr].GetOrder();
+          return 1;
+        }
+
+      }
+    return 0;
+  }
+
+
+
+
+
+
+
+
+
+  Vec<3> VisualSceneSolution :: 
+  GetDeformation (ElementIndex elnr, const Point<3> & p) const
+  {
+    Vec<3> def;
+    if (deform && vecfunction != -1)
+      {
+        GetValues (soldata[vecfunction], elnr, p(0), p(1), p(2), &def(0));
+        def *= scaledeform;
+
+        if (soldata[vecfunction]->components == 2) def(2) = 0;
+      }
+    else
+      def = 0;
+    return def;
+  }
+
+
+  Vec<3> VisualSceneSolution :: 
+  GetSurfDeformation (SurfaceElementIndex elnr, double lam1, double lam2) const
+  {
+    Vec<3> def;
+    if (deform && vecfunction != -1)
+      {
+        GetSurfValues (soldata[vecfunction], elnr, lam1, lam2,  &def(0));
+        def *= scaledeform;
+
+        if (soldata[vecfunction]->components == 2) def(2) = 0;
+      }
+    else if (deform && scalfunction != -1 && mesh->GetDimension()==2)
+      { // he: allow for 3d plots of 2d surfaces: usage: turn deformation on
+        def = 0;
+        GetSurfValue (soldata[scalfunction], elnr, lam1, lam2, scalcomp, def(2));
+        def *= scaledeform;
+      }
+    else
+      def = 0;
+    return def;
+  }
+
+  void VisualSceneSolution :: GetPointDeformation (int pnum, Point<3> & p, 
+                                                   SurfaceElementIndex elnr) const
+  {
+    p = mesh->Point (pnum+1);
+    if (deform && vecfunction != -1)
+      {
+        const SolData * vsol = soldata[vecfunction];
+      
+        Vec<3> v(0,0,0);
+        if (vsol->soltype == SOL_NODAL)
+          {
+            v = Vec3d(vsol->data[pnum * vsol->dist],
+                      vsol->data[pnum * vsol->dist+1],
+                      vsol->data[pnum * vsol->dist+2]);
+          }
+        else if (vsol->soltype == SOL_SURFACE_NONCONTINUOUS)
+          {
+            const Element2d & el = (*mesh)[elnr];
+            for (int j = 0; j < el.GetNP(); j++)
+              if (el[j] == pnum+1)
+                {
+                  int base = (4*elnr+j-1) * vsol->dist;
+                  v = Vec3d(vsol->data[base],
+                            vsol->data[base+1],
+                            vsol->data[base+2]);
+                }
+          }
+
+        if (vsol->dist == 2) v(2) = 0;
+      
+        v *= scaledeform;
+        p += v;
+      }
+  }
+
+
+
+
+  void VisualSceneSolution :: GetClippingPlaneTrigs (Array<ClipPlaneTrig> & trigs,
+                                                     Array<ClipPlanePoint> & pts)
+  {
+    static int timer1 = NgProfiler::CreateTimer ("ClipPlaneTrigs1");
+    static int timer2 = NgProfiler::CreateTimer ("ClipPlaneTrigs2");
+    static int timer3 = NgProfiler::CreateTimer ("ClipPlaneTrigs3");
+    static int timer4 = NgProfiler::CreateTimer ("ClipPlaneTrigs4");
+
+
+    NgProfiler::RegionTimer reg1 (timer1);
+
+    int ne = mesh->GetNE();
+
+    const int edgei[6][2] =
+      { { 0, 1 }, { 0, 2 }, { 0, 3 },
+        { 1, 2 }, { 1, 3 }, { 2, 3 } };
+
+    double edgelam[6];
+    Point<3> edgep[6];
+    double nodevali[4];
+
+    int cntce;
+    int cpe1 = 0, cpe2 = 0, cpe3 = 0;
+
+    // Array<Element> loctets;
+    // Array<Element> loctetsloc;
+    // Array<Point<3> > pointsloc;
+
+    int n = 1 << subdivisions;
+    int n3 = (n+1)*(n+1)*(n+1);
+
+    Array<Point<3> > grid(n3);
+    Array<Point<3> > locgrid(n3);
+    Array<Mat<3,3> > trans(n3);
+    Array<double> val(n3);
+    Array<int> compress(n3);
+
+
+    for (ElementIndex ei = 0; ei < ne; ei++)
+      {
+        int first_point_of_element = pts.Size();
+
+#ifdef PARALLEL
+	// parallel visualization --> dont draw ghost elements
+	if ( (*mesh)[ei] . IsGhost() ) continue;
+#endif
+
+	locgrid.SetSize(n3);
+        if(vispar.clipdomain > 0 && vispar.clipdomain != (*mesh)[ei].GetIndex()) continue;
+        if(vispar.donotclipdomain > 0 && vispar.donotclipdomain == (*mesh)[ei].GetIndex()) continue;
+
+        ELEMENT_TYPE type = (*mesh)[ei].GetType();
+        if (type == HEX || type == PRISM || type == TET || type == TET10 || type == PYRAMID)
+          {
+            const Element & el = (*mesh)[ei];
+
+            int ii = 0;
+            int cnt_valid = 0;
+
+            NgProfiler::StartTimer (timer2);
+
+
+            if (type == TET || type == TET10)
+              {
+                for (int ix = 0; ix <= n; ix++)
+                  for (int iy = 0; iy <= n; iy++)
+                    for (int iz = 0; iz <= n; iz++, ii++)
+                      {
+                        if (ix+iy+iz <= n)
+                          {
+                            compress[ii] = cnt_valid;
+                            locgrid[cnt_valid] = 
+                              Point<3> (double(ix) / n, double(iy) / n, double(iz) / n);
+                            cnt_valid++;
+                          }
+                        else
+                          compress[ii] = -1;
+                      }
+              }
+            
+            else
+              
+              for (int ix = 0; ix <= n; ix++)
+                for (int iy = 0; iy <= n; iy++)
+                  for (int iz = 0; iz <= n; iz++, ii++)
+                    {
+                      Point<3> ploc;
+                      compress[ii] = ii;
+                      
+                      switch (type)
+                        {
+                        case PRISM:
+                          if (ix+iy <= n)
+                            {
+                              ploc = Point<3> (double(ix) / n, double(iy) / n, double(iz) / n);
+                              compress[ii] = cnt_valid;
+                              cnt_valid++;
+                            }
+                          else
+                            compress[ii] = -1;
+                          break;
+                        case HEX:
+                          ploc = Point<3> (double(ix) / n, double(iy) / n, double(iz) / n);
+                          break;
+                        case PYRAMID:
+                          ploc = Point<3> (double(ix) / n * (1-double(iz)/n),
+                                           double(iy) / n * (1-double(iz)/n),
+                                           double(iz)/n);
+                          if (iz == n) ploc = Point<3> (0,0,1-1e-8);
+                          break;
+                        default:
+                          cerr << "clip plane trigs not implemented" << endl;
+                          ploc = Point<3> (0,0,0);
+                        }
+                      if (compress[ii] != -1)
+                        locgrid[compress[ii]] = ploc;
+                    }
+
+            if (type != TET && type != TET10 && type != PRISM) cnt_valid = n3;
+
+	    locgrid.SetSize(cnt_valid);
+
+            NgProfiler::StopTimer (timer2);
+            NgProfiler::RegionTimer reg4(timer4);
+
+            if (mesh->GetCurvedElements().IsHighOrder())
+              {
+                mesh->GetCurvedElements().
+                  CalcMultiPointElementTransformation (&locgrid, ei, &grid, 0);
+              }
+            else
+              {
+                Vector shape(el.GetNP());
+                MatrixFixWidth<3> pointmat(el.GetNP());
+
+                for (int k = 0; k < el.GetNP(); k++)
+                  for (int j = 0; j < 3; j++)
+                    pointmat(k,j) = (*mesh)[el[k]](j);
+                
+                for (int i = 0; i < cnt_valid; i++)
+                  {
+                    el.GetShapeNew (locgrid[i], shape);
+                    Point<3> pglob;
+                    for (int j = 0; j < 3; j++)
+                      {
+                        pglob(j) = 0;
+                        for (int k = 0; k < el.GetNP(); k++)
+                          pglob(j) += shape(k) * pointmat(k,j);
+                      }
+                    grid[i] = pglob;
+                  }
+              }
+
+            NgProfiler::RegionTimer reg3(timer3);
+
+            bool has_pos = 0, has_neg = 0;
+                
+            for (int i = 0; i < cnt_valid; i++)
+              {
+                val[i] = 
+                  grid[i](0) * clipplane[0] + 
+                  grid[i](1) * clipplane[1] + 
+                  grid[i](2) * clipplane[2] + 
+                  clipplane[3];
+                    
+                if (val[i] > 0)
+                  has_pos = 1;
+                else
+                  has_neg = 1;
+              }
+                
+            if (!has_pos || !has_neg) continue;
+                
+
+            for (int ix = 0; ix < n; ix++)
+              for (int iy = 0; iy < n; iy++)
+                for (int iz = 0; iz < n; iz++)
+                  {
+                    int base = iz + (n+1)*iy + (n+1)*(n+1)*ix;
+                    int pi[8] = 
+                      { base, base+(n+1)*(n+1), base+(n+1)*(n+1)+(n+1), base+(n+1),
+                        base+1, base+(n+1)*(n+1)+1, base+(n+1)*(n+1)+(n+1)+1, base+(n+1)+1 };
+
+                    for (int j = 0; j < 8; j++)
+                      pi[j] = compress[pi[j]];
+
+                    const int tets[6][4] = 
+                      { { 1, 2, 4, 5 },
+                        { 4, 5, 2, 8 },
+                        { 2, 8, 5, 6 },
+                        { 2, 3, 4, 8 },
+                        { 2, 3, 8, 6 },
+                        { 3, 8, 6, 7 } };
+
+                    for (int ii = 0; ii < 6; ii++)
+                      {
+                        int teti[4];
+                        for (int k = 0; k < 4; k++)
+                          teti[k] = pi[tets[ii][k]-1];
+
+                        bool is_valid = 1;
+                        for (int j = 0; j < 4; j++)
+                          if (teti[j] == -1) is_valid = 0;
+                        if (!is_valid) continue;
+
+                        for (int j = 0; j < 4; j++)
+                          nodevali[j] = val[teti[j]];
+          
+                        cntce = 0;
+                        for (int j = 0; j < 6; j++)
+                          {
+                            int lpi1 = edgei[j][0];
+                            int lpi2 = edgei[j][1];
+                            if ( (nodevali[lpi1] > 0) !=
+                                 (nodevali[lpi2] > 0) )
+                              {
+                                edgelam[j] = nodevali[lpi2] / (nodevali[lpi2] - nodevali[lpi1]);
+                                Point<3> p1 = grid[teti[lpi1]];
+                                Point<3> p2 = grid[teti[lpi2]];
+                  
+                                edgep[j] = p1 + (1-edgelam[j]) * (p2-p1);
+                  
+                                cntce++;
+                                cpe3 = cpe2;
+                                cpe2 = cpe1;
+                                cpe1 = j;
+                                if (cntce >= 3)
+                                  {
+                                    ClipPlaneTrig cpt;
+                                    cpt.elnr = ei;
+                                  
+                                    for (int k = 0; k < 3; k++)
+                                      {
+                                        int ednr;
+                                        switch (k)
+                                          {
+                                          case 0: ednr = cpe1; break;
+                                          case 1: ednr = cpe2; break;
+                                          case 2: ednr = cpe3; break;
+                                          }
+                                        // cpt.points[k].p = edgep[ednr];
+                                      
+                                        int pi1 = edgei[ednr][0];
+                                        int pi2 = edgei[ednr][1];
+                                        Point<3> p1 = locgrid[teti[pi1]];
+                                        Point<3> p2 = locgrid[teti[pi2]];
+
+                                        // cpt.points[k].lami = p2 + edgelam[ednr] * (p1-p2);
+
+                                        ClipPlanePoint cppt;
+                                        cppt.elnr = ei;
+                                        cppt.p = edgep[ednr];
+                                        cppt.lami =  p2 + edgelam[ednr] * (p1-p2);
+
+                                        int pnr = -1;
+
+                                        for (int l = first_point_of_element; l < pts.Size(); l++)
+                                          if (fabs (cppt.lami(0)-pts[l].lami(0)) < 1e-8 &&
+                                              fabs (cppt.lami(1)-pts[l].lami(1)) < 1e-8 &&
+                                              fabs (cppt.lami(2)-pts[l].lami(2)) < 1e-8)
+                                            {
+                                              pnr = l;
+                                              break;
+                                            }
+
+                                        if (pnr == -1)
+                                          pnr = pts.Append (cppt)-1;
+
+                                        cpt.points[k].pnr = pnr;
+                                        cpt.points[k].locpnr = pnr-first_point_of_element;
+                                      }
+                                  
+                                    trigs.Append (cpt);
+                                  }
+                              }
+                          }
+                      }
+                  }
+          }
+
+        else
+          {  // other elements not supported (JS, June 2007)
+            return;
+          }
+      
+      }
+  }
+
+  void VisualSceneSolution :: GetClippingPlaneGrid (Array<ClipPlanePoint> & pts)
+  {
+    Vec3d n(clipplane[0], clipplane[1], clipplane[2]);
+
+    double mu = -clipplane[3] / n.Length2();
+    Point3d p(mu*n.X(), mu * n.Y(), mu * n.Z());
+
+    // n /= n.Length();
+    n.Normalize();
+    Vec3d t1, t2;
+    n.GetNormal (t1);
+    t2 = Cross (n, t1);
+
+    double xi1, xi2;
+
+    double xi1mid = (center - p) * t1;
+    double xi2mid = (center - p) * t2;
+
+    pts.SetSize(0);
+
+    for (xi1 = xi1mid-rad+xoffset/gridsize; xi1 <= xi1mid+rad+xoffset/gridsize; xi1 += rad / gridsize)
+      for (xi2 = xi2mid-rad+yoffset/gridsize; xi2 <= xi2mid+rad+yoffset/gridsize; xi2 += rad / gridsize)
+        {
+          Point3d hp = p + xi1 * t1 + xi2 * t2;
+        
+          int cindex(-1);
+          bool allowindex(true);
+          if(vispar.clipdomain > 0)
+            {
+              cindex = vispar.clipdomain;
+            }
+          else if(vispar.donotclipdomain > 0)
+            {
+              allowindex = false;
+              cindex = vispar.donotclipdomain;
+            }
+
+          double lami[3];
+          int elnr = mesh->GetElementOfPoint (hp, lami,0,cindex,allowindex)-1;
+
+          if (elnr != -1)
+            {
+              ClipPlanePoint cpp;
+              cpp.p = hp;
+              cpp.elnr = elnr;
+              cpp.lami(0) = lami[0];
+              cpp.lami(1) = lami[1];
+              cpp.lami(2) = lami[2];
+              pts.Append (cpp);
+            }
+        }
+  };
+
+
+
+
+  void VisualSceneSolution :: DrawClipPlaneTrigs () 
+  {
+#ifdef PARALLELGL
+
+    if (id == 0 && ntasks > 1)
+      {
+	InitParallelGL();
+
+	Array<int> parlists (ntasks);
+
+	MyMPI_SendCmd ("redraw");
+	MyMPI_SendCmd ("clipplanetrigs");
+
+	for ( int dest = 1; dest < ntasks; dest++ )
+	  MyMPI_Recv (parlists[dest], dest, MPI_TAG_VIS);
+
+	if (clipplanelist_scal)
+	  glDeleteLists (clipplanelist_scal, 1);
+
+	clipplanelist_scal = glGenLists (1);
+	glNewList (clipplanelist_scal, GL_COMPILE);
+	
+	for ( int dest = 1; dest < ntasks; dest++ )
+	  glCallList (parlists[dest]);
+	
+	glEndList();
+	return;
+      }
+#endif
+
+
+
+
+
+    if (clipplanelist_scal)
+      glDeleteLists (clipplanelist_scal, 1);
+    
+    clipplanelist_scal = glGenLists (1);
+    glNewList (clipplanelist_scal, GL_COMPILE);
+
+
+    Array<ClipPlaneTrig> trigs;
+    Array<ClipPlanePoint> points;
+    GetClippingPlaneTrigs (trigs, points);
+	    
+    glNormal3d (-clipplane[0], -clipplane[1], -clipplane[2]);
+    glColor3d (1.0, 1.0, 1.0);
+    
+    SetTextureMode (usetexture);
+
+    SolData * sol = NULL;
+
+    if (scalfunction != -1) 
+      sol = soldata[scalfunction];
+
+
+
+    glBegin (GL_TRIANGLES);
+
+    int maxlpnr = 0;
+    for (int i = 0; i < trigs.Size(); i++)
+      for (int j = 0; j < 3; j++)
+        maxlpnr = max2 (maxlpnr, trigs[i].points[j].locpnr);
+
+    Array<double> vals(maxlpnr+1);
+    Array<complex<double> > valsc(maxlpnr+1);
+    Array<int> elnrs(maxlpnr+1);
+    Array<bool> trigok(maxlpnr+1);
+    Array<Point<3> > locpoints(maxlpnr+1);
+    Array<Point<3> > globpoints(maxlpnr+1);
+    Array<Mat<3> > jacobi(maxlpnr+1);
+    Array<double> mvalues( (maxlpnr+1) * sol->components);
+    trigok = false;
+    elnrs = -1;
+
+    Point<3> p[3];
+    // double val[3];
+    complex<double> valc[3];
+    int lastelnr = -1;
+    int nlp = -1;
+
+    for (int i = 0; i < trigs.Size(); i++)
+      {
+	bool ok = true;
+        const ClipPlaneTrig & trig = trigs[i];
+	if (trig.elnr != lastelnr)
+	  {
+	    lastelnr = trig.elnr;
+	    nlp = -1;
+
+	    for (int ii = i; ii < trigs.Size(); ii++)
+	      {
+		if (trigs[ii].elnr != trig.elnr) break;
+		for (int j = 0; j < 3; j++)
+		  nlp = max (nlp, trigs[ii].points[j].locpnr);
+	      }
+	    nlp++;
+	    locpoints.SetSize (nlp);
+
+	    for (int ii = i; ii < trigs.Size(); ii++)
+	      {
+		if (trigs[ii].elnr != trig.elnr) break;
+		for (int j = 0; j < 3; j++)
+		  locpoints[trigs[ii].points[j].locpnr] = points[trigs[ii].points[j].pnr].lami;
+	      }
+
+	    mesh->GetCurvedElements().
+	      CalcMultiPointElementTransformation (&locpoints, trig.elnr, 
+						   &globpoints, &jacobi);
+
+	    bool
+	      drawelem = GetMultiValues (sol, trig.elnr, nlp, 
+					 &locpoints[0](0), &locpoints[1](0)-&locpoints[0](0),
+					 &globpoints[0](0), &globpoints[1](0)-&globpoints[0](0),
+					 &jacobi[0](0), &jacobi[1](0)-&jacobi[0](0),
+					 &mvalues[0], sol->components);
+	    
+	    // cout << "have multivalues, comps = " << sol->components << endl;
+
+	    if (!drawelem) ok = false;
+	    if (usetexture != 2 || !sol->iscomplex)
+	      for (int ii = 0; ii < nlp; ii++)
+		vals[ii] = ExtractValue(sol, scalcomp, &mvalues[ii*sol->components]);
+	    else
+	      for (int ii = 0; ii < nlp; ii++)
+		valsc[ii] = complex<double> (mvalues[ii*sol->components],
+					     mvalues[ii*sol->components+1]);
+	  }
+	
+	if(ok)
+	  for(int j=0; j<3; j++)
+	    {
+	      if (usetexture != 2 || !sol->iscomplex)
+		SetOpenGlColor (vals[trig.points[j].locpnr]);
+	      else
+		glTexCoord2f ( valsc[trig.points[j].locpnr].real(), 
+			       valsc[trig.points[j].locpnr].imag() );
+
+	      p[j] = points[trig.points[j].pnr].p;
+
+	      if (deform)
+		{
+		  Point<3> ploc = points[trig.points[j].pnr].lami;
+		  p[j] += GetDeformation (trig.elnr, ploc);
+		}
+
+	      glVertex3dv (p[j]);
+	    }
+
+      }
+    glEnd();
+
+    glEndList ();
+
+
+#ifdef PARALLELGL
+    glFinish();
+    if (id > 0)
+      MyMPI_Send (clipplanelist_scal, 0, MPI_TAG_VIS);
+#endif
+  }
+
+
+
+
+
+
+
+
+
+
+  void VisualSceneSolution ::
+  SetOpenGlColor(double val)
+  {
+    if (usetexture == 1 && !logscale)
+      {
+        glTexCoord1f ( val );
+        return;
+      }
+
+    double valmin = minval;
+    double valmax = maxval;
+
+    double value;
+
+    if (!logscale)
+      value = (val - valmin) / (valmax - valmin);
+    else
+      {
+        if (valmax <= 0) valmax = 1;
+        if (valmin <= 0) valmin = 1e-4 * valmax;
+        value = (log(fabs(val)) - log(valmin)) / (log(valmax) - log(valmin));
+      }
+
+    if (!invcolor)
+      value = 1 - value;
+
+
+    if (value > 1) value = 1;
+    if (value < 0) value = 0;
+
+    value *= 4;
+
+    static const double colp[][3] =
+      {
+        { 1, 0, 0 },
+        { 1, 1, 0 },
+        { 0, 1, 0 },
+        { 0, 1, 1 },
+        { 0, 0, 1 },
+        { 1, 0, 1 },
+        { 1, 0, 0 },
+      };
+  
+    int i = int(value);
+    double r = value - i;
+
+    GLdouble col[3];
+    for (int j = 0; j < 3; j++)
+      col[j] = (1-r) * colp[i][j] + r * colp[i+1][j];
+  
+    glColor3dv (col);
+  }
+
+
+
+  void VisualSceneSolution ::
+  SetTextureMode (int texturemode) const
+  {
+    switch (texturemode)
+      {
+      case 0:
+        glDisable (GL_TEXTURE_1D);
+        glDisable (GL_TEXTURE_2D);
+        break;
+      case 1:
+        glEnable (GL_TEXTURE_1D);
+        glDisable (GL_TEXTURE_2D);
+        glColor3d (1.0, 1.0, 1.0);   
+        break;
+      case 2:
+        glDisable (GL_TEXTURE_1D);
+        glEnable (GL_TEXTURE_2D);
+        glColor3d (1.0, 1.0, 1.0);   
+        break;
+      }
+  }
+
+
+
+
+  void VisualSceneSolution ::
+  DrawCone (const Point<3> & p1, const Point<3> & p2, double r)
+  {
+    int n = 10, i;
+    Vec<3> p1p2 = p2 - p1;
+
+    p1p2.Normalize();
+    Vec<3> p2p1 = -p1p2;
+
+    Vec<3> t1 = p1p2.GetNormal();
+    Vec<3> t2 = Cross (p1p2, t1);
+
+    Point<3> oldp = p1 + r * t1;
+    Vec<3> oldn = t1;
+
+    Point<3> p;
+    Vec<3> normal;
+
+    Mat<2> rotmat;
+    Vec<2> cs, newcs;
+    cs(0) = 1;
+    cs(1) = 0;
+    rotmat(0,0) = rotmat(1,1) = cos(2*M_PI/n);
+    rotmat(1,0) = sin(2*M_PI/n);
+    rotmat(0,1) = -rotmat(1,0);
+
+    glBegin (GL_TRIANGLES);
+    for (i = 1; i <= n; i++)
+      {
+        /*
+          phi = 2 * M_PI * i / n;
+          normal = cos(phi) * t1 + sin(phi) * t2;
+        */
+        newcs = rotmat * cs;
+        cs = newcs;
+        normal = cs(0) * t1 + cs(1) * t2;
+
+        p = p1 + r * normal;
+
+        // cone
+        glNormal3dv (normal);
+        glVertex3dv (p);
+        glVertex3dv (p2);
+        glNormal3dv (oldn);
+        glVertex3dv (oldp);
+
+        // base-circle
+        glNormal3dv (p2p1);
+        glVertex3dv (p);
+        glVertex3dv (p1);
+        glVertex3dv (oldp);
+
+        oldp = p;
+        oldn = normal;
+      }
+    glEnd ();
+  }
+
+
+
+  void VisualSceneSolution ::
+  DrawCylinder (const Point<3> & p1, const Point<3> & p2, double r)
+  {
+    int n = 10, i;
+    Vec<3> p1p2 = p2 - p1;
+
+    p1p2.Normalize();
+    Vec<3> p2p1 = -p1p2;
+
+    Vec<3> t1 = p1p2.GetNormal();
+    Vec<3> t2 = Cross (p1p2, t1);
+
+    Point<3> oldhp1 = p1 + r * t1;
+    Point<3> oldhp2 = p2 + r * t1;
+    Vec<3> oldn = t1;
+
+    Point<3> hp1, hp2;
+    Vec<3> normal;
+
+    Mat<2> rotmat;
+    Vec<2> cs, newcs;
+    cs(0) = 1;
+    cs(1) = 0;
+    rotmat(0,0) = rotmat(1,1) = cos(2*M_PI/n);
+    rotmat(1,0) = sin(2*M_PI/n);
+    rotmat(0,1) = -rotmat(1,0);
+
+    glBegin (GL_QUADS);
+    for (i = 1; i <= n; i++)
+      {
+        newcs = rotmat * cs;
+        cs = newcs;
+        normal = cs(0) * t1 + cs(1) * t2;
+
+        hp1 = p1 + r * normal;
+        hp2 = p2 + r * normal;
+
+        // cylinder
+        glNormal3dv (normal);
+
+        glVertex3dv (hp1);
+        glVertex3dv (hp2);
+        glVertex3dv (oldhp2);
+        glVertex3dv (oldhp1);
+
+        oldhp1 = hp1;
+        oldhp2 = hp2;
+        oldn = normal;
+      }
+    glEnd ();
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+  void VisualSceneSolution :: MouseDblClick (int px, int py)
+  {
+    vsmesh.SetClippingPlane();
+    // vsmesh.BuildFilledList();
+    vsmesh.MouseDblClick(px,py);
+  }
+
+
+
+#ifdef PARALLELGL
+
+  void VisualSceneSolution :: Broadcast ()
+  {
+    MPI_Datatype type;
+    int blocklen[] = 
+      { 
+	1, 1, 1, 1,
+	1, 1, 1, 1, 
+	1, 1, 1, 1, 
+	1, 4, 1
+      };
+    MPI_Aint displ[] = { (char*)&usetexture - (char*)this,
+			 (char*)&clipsolution - (char*)this,
+			 (char*)&scalfunction - (char*)this,
+			 (char*)&scalcomp - (char*)this,
+
+			 (char*)&vecfunction - (char*)this,
+			 (char*)&gridsize - (char*)this,
+			 (char*)&autoscale - (char*)this,
+			 (char*)&logscale - (char*)this,
+
+			 (char*)&minval - (char*)this,
+			 (char*)&maxval - (char*)this,
+			 (char*)&numisolines - (char*)this,
+			 (char*)&subdivisions - (char*)this,
+
+			 (char*)&evalfunc - (char*)this,
+			 (char*)&clipplane[0] - (char*)this,
+			 (char*)&multidimcomponent - (char*)this 
+    };
+
+
+    MPI_Datatype types[] = { 
+      MPI_INT, MPI_INT, MPI_INT, MPI_INT,
+      MPI_INT, MPI_INT, MPI_INT, MPI_INT,
+      MPI_DOUBLE, MPI_DOUBLE, MPI_INT, MPI_INT,
+      MPI_INT, MPI_DOUBLE, MPI_INT
+    };
+
+    MPI_Type_create_struct (15, blocklen, displ, types, &type);
+    MPI_Type_commit ( &type );
+
+    MPI_Bcast (this, 1, type, 0, MPI_COMM_WORLD);
+    MPI_Type_free (&type);
+
+    /*
+    MyMPI_Bcast (usetexture);
+    MyMPI_Bcast (clipsolution);
+    MyMPI_Bcast (scalfunction);
+    MyMPI_Bcast (scalcomp);
+    MyMPI_Bcast (vecfunction);
+    MyMPI_Bcast (gridsize);
+
+    MyMPI_Bcast (autoscale);
+    MyMPI_Bcast (logscale);
+    MyMPI_Bcast (minval);
+    MyMPI_Bcast (maxval);
+    MyMPI_Bcast (numisolines);
+    MyMPI_Bcast (subdivisions);
+
+    MyMPI_Bcast (clipplane[0]);
+    MyMPI_Bcast (clipplane[1]);
+    MyMPI_Bcast (clipplane[2]);
+    MyMPI_Bcast (clipplane[3]);
+    */
+  }
+  
+#endif
+
+
+  int Ng_Vis_Set (ClientData clientData,
+                  Tcl_Interp * interp,
+                  int argc, tcl_const char *argv[])
+
+  {
+    if (argc >= 2)
+      {
+        if (strcmp (argv[1], "parameters") == 0)
+          {
+            vssolution.imag_part = 
+              atoi (Tcl_GetVar (interp, "::visoptions.imaginary", TCL_GLOBAL_ONLY));      
+            vssolution.usetexture = 
+              atoi (Tcl_GetVar (interp, "::visoptions.usetexture", TCL_GLOBAL_ONLY));
+            if (atoi (Tcl_GetVar (interp, "::visoptions.redrawperiodic", TCL_GLOBAL_ONLY)))
+              vssolution.usetexture = 2;
+                
+            vssolution.invcolor = 
+              atoi (Tcl_GetVar (interp, "::visoptions.invcolor", TCL_GLOBAL_ONLY));       
+
+            vssolution.clipsolution = 0;
+
+            if (strcmp (Tcl_GetVar (interp, "::visoptions.clipsolution", TCL_GLOBAL_ONLY), 
+                        "scal") == 0)
+              vssolution.clipsolution = 1;
+            if (strcmp (Tcl_GetVar (interp, "::visoptions.clipsolution", TCL_GLOBAL_ONLY), 
+                        "vec") == 0)
+              vssolution.clipsolution = 2;
+            
+            tcl_const char * scalname =  
+              Tcl_GetVar (interp, "::visoptions.scalfunction", TCL_GLOBAL_ONLY);
+            tcl_const char * vecname = 
+              Tcl_GetVar (interp, "::visoptions.vecfunction", TCL_GLOBAL_ONLY);
+            tcl_const char * fieldlines_vecname = 
+              Tcl_GetVar (interp, "::visoptions.fieldlinesvecfunction", TCL_GLOBAL_ONLY);
+                
+          
+            vssolution.scalfunction = -1;
+            vssolution.vecfunction = -1;
+            vssolution.fieldlines_vecfunction = -1;
+
+            int pointpos; // SZ 
+            const char * pch = strchr(scalname,'.');
+            pointpos = int(pch-scalname+1);
+
+            for (int i = 0; i < vssolution.soldata.Size(); i++)
+              {
+                if ( (strlen (vssolution.soldata[i]->name) == pointpos-1) &&
+                     (strncmp (vssolution.soldata[i]->name, scalname, pointpos-1) == 0) )
+                  {
+                    vssolution.scalfunction = i;
+                    vssolution.scalcomp = atoi (scalname + pointpos);
+		    if ( vssolution.scalcomp > vssolution.soldata[i]->components )
+                      vssolution.scalcomp = 1;
+		    char newscalname[100];
+		    for ( int ii = 0; ii < pointpos; ii++ )
+		      newscalname[ii] = scalname[ii];
+		    newscalname[pointpos] = '.';
+		    sprintf (newscalname+pointpos, "%i", vssolution.scalcomp);
+
+                    if (strcmp (scalname, newscalname) != 0)
+                      Tcl_SetVar ( interp, "::visoptions.scalfunction", newscalname, TCL_GLOBAL_ONLY );
+		    scalname = Tcl_GetVar (interp, "::visoptions.scalfunction", TCL_GLOBAL_ONLY);
+                  }
+                if (strcmp (vssolution.soldata[i]->name, vecname) == 0)
+                  {
+                    vssolution.vecfunction = i;
+                    //cout  << "set vecfunction to " << i << endl;
+                  }
+                if (strcmp (vssolution.soldata[i]->name, fieldlines_vecname) == 0)
+                  {
+                    vssolution.fieldlines_vecfunction = i;
+                    //cout  << "set fieldlines-vecfunction to " << i << endl;
+                  }
+              }
+
+            if(vssolution.fieldlines_vecfunction != -1 &&
+               vssolution.vecfunction == -1)
+              {
+                //cout << "WARNING: Setting vector function in Visualization toolbox to value from Fieldlines toolbox!" << endl;
+                vssolution.vecfunction = vssolution.fieldlines_vecfunction;
+              }
+               
+	    // reset visoptions.scalfunction and visoptions.vecfunction if not avialable 
+	    if ( vssolution.scalfunction == -1 && strcmp (scalname, "none") != 0)
+              Tcl_SetVar ( interp, "::visoptions.scalfunction", "none", TCL_GLOBAL_ONLY );
+	    if ( vssolution.vecfunction == -1  && strcmp (vecname, "none") != 0)
+              Tcl_SetVar ( interp, "::visoptions.vecfunction", "none", TCL_GLOBAL_ONLY );
+
+            tcl_const char * evalname = 
+              Tcl_GetVar (interp, "::visoptions.evaluate", TCL_GLOBAL_ONLY);
+          
+            if (strcmp(evalname, "abs") == 0) vssolution.evalfunc = VisualSceneSolution::FUNC_ABS;
+            if (strcmp(evalname, "abstens") == 0) vssolution.evalfunc = VisualSceneSolution::FUNC_ABS_TENSOR;
+            if (strcmp(evalname, "mises") == 0) vssolution.evalfunc = VisualSceneSolution::FUNC_MISES;
+            if (strcmp(evalname, "main") == 0) vssolution.evalfunc = VisualSceneSolution::FUNC_MAIN;
+
+            vssolution.gridsize = 
+              atoi (Tcl_GetVar (interp, "::visoptions.gridsize", TCL_GLOBAL_ONLY));
+
+            vssolution.xoffset = 
+              atof (Tcl_GetVar (interp, "::visoptions.xoffset", TCL_GLOBAL_ONLY));
+
+            //    cout << "x-offset:" << vssolution.xoffset << endl;
+
+            vssolution.yoffset = 
+              atof (Tcl_GetVar (interp, "::visoptions.yoffset", TCL_GLOBAL_ONLY));
+
+            vssolution.autoscale = 
+              atoi (Tcl_GetVar (interp, "::visoptions.autoscale", TCL_GLOBAL_ONLY));
+
+
+            /*
+              vssolution.linear_colors = 
+              atoi (Tcl_GetVar (interp, "::visoptions.lineartexture", TCL_GLOBAL_ONLY));
+            */
+            vssolution.logscale = 
+              atoi (Tcl_GetVar (interp, "::visoptions.logscale", TCL_GLOBAL_ONLY));
+
+            vssolution.mminval = 
+              atof (Tcl_GetVar (interp, "::visoptions.mminval", TCL_GLOBAL_ONLY));
+            vssolution.mmaxval = 
+              atof (Tcl_GetVar (interp, "::visoptions.mmaxval", TCL_GLOBAL_ONLY));
+
+            vssolution.showclipsolution = 
+              atoi (Tcl_GetVar (interp, "::visoptions.showclipsolution", TCL_GLOBAL_ONLY));
+            vssolution.showsurfacesolution = 
+              atoi (Tcl_GetVar (interp, "::visoptions.showsurfacesolution", TCL_GLOBAL_ONLY));
+            vssolution.lineartexture = 
+              atoi (Tcl_GetVar (interp, "::visoptions.lineartexture", TCL_GLOBAL_ONLY));
+            vssolution.numtexturecols = 
+              atoi (Tcl_GetVar (interp, "::visoptions.numtexturecols", TCL_GLOBAL_ONLY));
+
+            vssolution.multidimcomponent = 
+              atoi (Tcl_GetVar (interp, "::visoptions.multidimcomponent", TCL_GLOBAL_ONLY));
+
+	    vssolution.drawpointcurves = 
+	      atoi (Tcl_GetVar (interp, "::visoptions.drawpointcurves", TCL_GLOBAL_ONLY));	      
+
+            vssolution.draw_fieldlines = 
+	      atoi (Tcl_GetVar (interp, "::visoptions.drawfieldlines", TCL_GLOBAL_ONLY));
+            vssolution.num_fieldlines = 
+              atoi (Tcl_GetVar (interp, "::visoptions.numfieldlines", TCL_GLOBAL_ONLY));
+            vssolution.fieldlines_randomstart =
+              atoi (Tcl_GetVar (interp, "::visoptions.fieldlinesrandomstart", TCL_GLOBAL_ONLY));
+
+            vssolution.fieldlines_reltolerance =
+              atof (Tcl_GetVar (interp, "::visoptions.fieldlinestolerance", TCL_GLOBAL_ONLY));
+
+            if (strcmp (Tcl_GetVar (interp, "::visoptions.fieldlinesrktype", TCL_GLOBAL_ONLY), 
+                        "euler") == 0)
+              vssolution.fieldlines_rktype = 0;
+            else if (strcmp (Tcl_GetVar (interp, "::visoptions.fieldlinesrktype", TCL_GLOBAL_ONLY), 
+                             "eulercauchy") == 0)
+              vssolution.fieldlines_rktype = 1;
+            else if (strcmp (Tcl_GetVar (interp, "::visoptions.fieldlinesrktype", TCL_GLOBAL_ONLY), 
+                             "simpson") == 0)
+              vssolution.fieldlines_rktype = 2;
+            else if (strcmp (Tcl_GetVar (interp, "::visoptions.fieldlinesrktype", TCL_GLOBAL_ONLY), 
+                             "crungekutta") == 0)
+              vssolution.fieldlines_rktype = 3;
+
+
+            vssolution.fieldlines_rellength =
+              atof (Tcl_GetVar (interp, "::visoptions.fieldlineslength", TCL_GLOBAL_ONLY));
+
+            vssolution.fieldlines_maxpoints = 
+              atoi (Tcl_GetVar (interp, "::visoptions.fieldlinesmaxpoints", TCL_GLOBAL_ONLY));
+
+            vssolution.fieldlines_relthickness =
+              atof (Tcl_GetVar (interp, "::visoptions.fieldlinesthickness", TCL_GLOBAL_ONLY));
+
+
+            vssolution.fieldlines_fixedphase = 
+              (atoi (Tcl_GetVar (interp, "::visoptions.fieldlinesonlyonephase", TCL_GLOBAL_ONLY)) != 0);
+
+            if(vssolution.fieldlines_fixedphase)
+              vssolution.fieldlines_phase =
+                atof (Tcl_GetVar (interp, "::visoptions.fieldlinesphase", TCL_GLOBAL_ONLY));
+
+
+            if (strcmp (Tcl_GetVar (interp, "::visoptions.fieldlinesstartarea", TCL_GLOBAL_ONLY), 
+                        "box") == 0)
+              vssolution.fieldlines_startarea  = 0;
+            else if (strcmp (Tcl_GetVar (interp, "::visoptions.fieldlinesstartarea", TCL_GLOBAL_ONLY), 
+                             "file") == 0)
+              vssolution.fieldlines_startarea  = 1;
+            else if (strcmp (Tcl_GetVar (interp, "::visoptions.fieldlinesstartarea", TCL_GLOBAL_ONLY), 
+                             "face") == 0)
+              vssolution.fieldlines_startarea  = 2;
+
+                
+            if (vssolution.fieldlines_startarea == 0)
+              {
+                vssolution.fieldlines_startarea_parameter.SetSize(6);
+                vssolution.fieldlines_startarea_parameter[0] = atof (Tcl_GetVar (interp, "::visoptions.fieldlinesstartareap1x", TCL_GLOBAL_ONLY));
+                vssolution.fieldlines_startarea_parameter[1] = atof (Tcl_GetVar (interp, "::visoptions.fieldlinesstartareap1y", TCL_GLOBAL_ONLY));
+                vssolution.fieldlines_startarea_parameter[2] = atof (Tcl_GetVar (interp, "::visoptions.fieldlinesstartareap1z", TCL_GLOBAL_ONLY));
+                vssolution.fieldlines_startarea_parameter[3] = atof (Tcl_GetVar (interp, "::visoptions.fieldlinesstartareap2x", TCL_GLOBAL_ONLY));
+                vssolution.fieldlines_startarea_parameter[4] = atof (Tcl_GetVar (interp, "::visoptions.fieldlinesstartareap2y", TCL_GLOBAL_ONLY));
+                vssolution.fieldlines_startarea_parameter[5] = atof (Tcl_GetVar (interp, "::visoptions.fieldlinesstartareap2z", TCL_GLOBAL_ONLY));
+              }
+            else if (vssolution.fieldlines_startarea == 1)
+              {
+                vssolution.fieldlines_filename = Tcl_GetVar (interp, "::visoptions.fieldlinesfilename", TCL_GLOBAL_ONLY);
+              }
+            else if (vssolution.fieldlines_startarea == 2)
+              {
+                vssolution.fieldlines_startface = atoi (Tcl_GetVar (interp, "::visoptions.fieldlinesstartface", TCL_GLOBAL_ONLY));
+              }
+
+          
+            vssolution.deform =
+              atoi (Tcl_GetVar (interp, "::visoptions.deformation", TCL_GLOBAL_ONLY));
+            vssolution.scaledeform =
+              atof (Tcl_GetVar (interp, "::visoptions.scaledeform1", TCL_GLOBAL_ONLY)) *
+              atof (Tcl_GetVar (interp, "::visoptions.scaledeform2", TCL_GLOBAL_ONLY));
+
+
+            if (atoi (Tcl_GetVar (interp, "::visoptions.isolines", TCL_GLOBAL_ONLY)))
+              vssolution.numisolines = atoi (Tcl_GetVar (interp, "::visoptions.numiso", TCL_GLOBAL_ONLY));
+            else
+              vssolution.numisolines = 0;
+            vssolution.draw_isosurface = 
+              atoi (Tcl_GetVar (interp, "::visoptions.isosurf", TCL_GLOBAL_ONLY));
+
+            vssolution.SetSubdivision(atoi (Tcl_GetVar (interp, "::visoptions.subdivisions", TCL_GLOBAL_ONLY)));
+
+            vssolution.UpdateSolutionTimeStamp();
+          }
+      
+        if (strcmp (argv[1], "parametersrange") == 0)
+          {
+            vssolution.invcolor = 
+              atoi (Tcl_GetVar (interp, "::visoptions.invcolor", TCL_GLOBAL_ONLY));       
+            vssolution.mminval = 
+              atof (Tcl_GetVar (interp, "::visoptions.mminval", TCL_GLOBAL_ONLY));
+            vssolution.mmaxval = 
+              atof (Tcl_GetVar (interp, "::visoptions.mmaxval", TCL_GLOBAL_ONLY));
+            vssolution.lineartexture = 
+              atoi (Tcl_GetVar (interp, "::visoptions.lineartexture", TCL_GLOBAL_ONLY));
+            vssolution.numtexturecols = 
+              atoi (Tcl_GetVar (interp, "::visoptions.numtexturecols", TCL_GLOBAL_ONLY));
+
+            if (vssolution.usetexture == 0 || vssolution.logscale)
+              vssolution.UpdateSolutionTimeStamp();
+          }
+
+
+        if (argc >= 3 && strcmp (argv[1], "time") == 0)
+          {
+            vssolution.time = double (atoi (argv[2])) / 1000;
+         
+            vssolution.timetimestamp = NextTimeStamp();
+            cout << "\rtime = " << vssolution.time << "    " << flush;
+          }
+
+      }
+
+    
+    vsmesh.SetClippingPlane ();  // for computing parameters
+    vssolution.SetClippingPlane ();  // for computing parameters
+    glDisable(GL_CLIP_PLANE0);
+
+#ifdef PARALLELGL
+    vsmesh.Broadcast ();
+#endif    
+
+
+    return TCL_OK;
+  }
+
+  int Ng_Vis_Field (ClientData clientData,
+                    Tcl_Interp * interp,
+                    int argc, tcl_const char *argv[])
+  {
+    int i;
+    char buf[1000];
+    buf[0] = 0;
+
+    if (argc >= 2)
+      {
+        if (strcmp (argv[1], "setfield") == 0)
+          {
+            if (argc < 3)
+              return TCL_ERROR;
+
+            for (i = 0; i < vssolution.GetNSolData(); i++)
+              if (strcmp (vssolution.GetSolData(i)->name, argv[2]) == 0)
+                {
+                  cout << "found soldata " << i << endl;
+                }
+          }
+
+        if (strcmp (argv[1], "getnfieldnames") == 0)
+          {
+            sprintf (buf, "%d", vssolution.GetNSolData());
+          }
+      
+        if (strcmp (argv[1], "getfieldname") == 0)
+          {
+            sprintf (buf, "%s", vssolution.GetSolData(atoi(argv[2])-1)->name);
+          }
+
+        if (strcmp (argv[1], "iscomplex") == 0)
+          {
+            sprintf (buf, "%d", vssolution.GetSolData(atoi(argv[2])-1)->iscomplex);
+          }
+
+        if (strcmp (argv[1], "getfieldcomponents") == 0)
+          {
+            sprintf (buf, "%d", vssolution.GetSolData(atoi(argv[2])-1)->components);
+          }
+
+      
+        if (strcmp (argv[1], "getfieldnames") == 0)
+          {
+            for (i = 0; i < vssolution.GetNSolData(); i++)
+              {
+                strcat (buf, vssolution.GetSolData(i)->name);
+                strcat (buf, " ");
+              }
+            strcat (buf, "var1 var2 var3");
+            Tcl_SetResult (interp, buf, TCL_STATIC);
+          }
+
+        if (strcmp (argv[1], "setcomponent") == 0)
+          {
+            cout << "set component " << argv[2] << endl;
+          }
+
+        if (strcmp (argv[1], "getactivefield") == 0)
+          {
+            sprintf (buf, "1");
+          }
+
+        if (strcmp (argv[1], "getdimension") == 0)
+          {
+            sprintf (buf, "%d", mesh->GetDimension());
+          }
+      }
+
+    Tcl_SetResult (interp, buf, TCL_STATIC);
+    return TCL_OK;
+  }
+
+
+  extern "C" int Ng_Vis_Init (Tcl_Interp * interp);
+
+  int Ng_Vis_Init (Tcl_Interp * interp)
+  {
+    Tcl_CreateCommand (interp, "Ng_Vis_Set", Ng_Vis_Set,
+                       (ClientData)NULL,
+                       (Tcl_CmdDeleteProc*) NULL);
+
+    Tcl_CreateCommand (interp, "Ng_Vis_Field", Ng_Vis_Field,
+                       (ClientData)NULL,
+                       (Tcl_CmdDeleteProc*) NULL);
+
+
+    return TCL_OK;
+  }
+}
+
+#endif // NOTCL
diff --git a/contrib/Netgen/libsrc/visualization/vssolution.hpp b/contrib/Netgen/libsrc/visualization/vssolution.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9bf1b78de4ee99d8b8e9a7d9057600db0aab9003
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vssolution.hpp
@@ -0,0 +1,430 @@
+#ifndef FILE_VSSOLUTION
+#define FILE_VSSOLUTION
+
+
+
+#ifndef SMALLLIB
+#ifndef NOTCL
+
+extern 
+void ImportSolution (const char * filename);
+
+extern int Ng_Vis_Set (ClientData clientData,
+		       Tcl_Interp * interp,
+		       int argc, tcl_const char *argv[]);
+#endif
+#endif
+
+class FieldLineCalc;
+
+class VisualSceneSolution : public VisualScene
+{
+  friend class FieldLineCalc;
+  
+  class ClipPlaneTrig
+  {
+  public:
+    struct ps 
+    {
+      int pnr, locpnr;
+    };
+    ps points[3];
+    ElementIndex elnr;
+  };
+
+  class ClipPlanePoint
+  {
+  public:
+    ElementIndex elnr;
+    Point<3> lami;
+    Point<3> p;
+  };
+
+
+  int surfellist;
+  int linelist;
+  int clipplanelist_scal;
+  int clipplanelist_vec;
+  int isolinelist;
+  int clipplane_isolinelist;
+  int surface_vector_list;
+  // int cone_list;
+  int isosurface_list;
+
+  int pointcurvelist;
+
+  bool draw_fieldlines;
+  bool drawpointcurves;
+  bool draw_isosurface;
+  int num_fieldlines;
+  bool fieldlines_randomstart;
+  int fieldlineslist;
+  int num_fieldlineslists;
+  int fieldlines_startarea;
+  Array<double> fieldlines_startarea_parameter;
+  int fieldlines_startface;
+  string fieldlines_filename;
+  double fieldlines_reltolerance;
+  int fieldlines_rktype;
+  double fieldlines_rellength;
+  double fieldlines_relthickness;
+  int fieldlines_vecfunction;
+  bool fieldlines_fixedphase;
+  float fieldlines_phase;
+  int fieldlines_maxpoints;
+
+
+  int surfeltimestamp, clipplanetimestamp, solutiontimestamp;
+  int surfellinetimestamp;
+  int fieldlinestimestamp, surface_vector_timestamp;
+  int pointcurve_timestamp;
+  int isosurface_timestamp;
+  int subdivision_timestamp;
+  int timetimestamp;
+  double minval, maxval;
+
+  NgLock *lock;
+
+
+#ifdef PARALLELGL
+  Array<int> par_linelists;
+  Array<int> par_surfellists;
+#endif
+
+
+public:
+
+  enum EvalFunc { 
+    FUNC_ABS = 1, 
+    FUNC_ABS_TENSOR = 2,
+    FUNC_MISES = 3, 
+    FUNC_MAIN = 4
+  };
+  int evalfunc;
+
+  enum SolType
+    { 
+      SOL_NODAL = 1, 
+      SOL_ELEMENT = 2, 
+      SOL_SURFACE_ELEMENT = 3, 
+      SOL_NONCONTINUOUS = 4, 
+      SOL_SURFACE_NONCONTINUOUS = 5,
+      SOL_VIRTUALFUNCTION = 6,
+      SOL_MARKED_ELEMENTS = 10,
+      SOL_ELEMENT_ORDER = 11,
+    };
+
+  class SolData
+  {
+  public:
+    SolData ();
+    ~SolData ();
+    
+    char * name;
+    double * data;
+    int components;
+    int dist;
+    int order;
+    bool iscomplex;
+    bool draw_volume;
+    bool draw_surface;
+    SolType soltype;
+    SolutionData * solclass;
+
+    // internal variables:
+    int size;
+  };
+
+  
+
+
+  Array<SolData*> soldata;
+  
+
+
+
+  int usetexture;    // 0..no, 1..1D texture (standard), 2..2D-texture (complex)
+  int clipsolution;  // 0..no, 1..scal, 2..vec
+  int scalfunction, scalcomp, vecfunction;
+  int gridsize;
+  double xoffset, yoffset;
+
+  int autoscale, logscale;
+  double mminval, mmaxval;
+  int numisolines;
+  int subdivisions;
+
+  bool showclipsolution;
+  bool showsurfacesolution;
+  bool lineartexture;
+  int numtexturecols;
+
+  int multidimcomponent;
+
+  // bool fieldlineplot;
+  double time;
+
+  int deform;
+  double scaledeform;
+  bool imag_part;
+
+private:
+  void BuildFieldLinesFromFile(Array<Point3d> & startpoints);
+  void BuildFieldLinesFromFace(Array<Point3d> & startpoints);
+  void BuildFieldLinesFromBox(Array<Point3d> & startpoints);
+  void BuildFieldLinesFromLine(Array<Point3d> & startpoints);
+
+public:
+  VisualSceneSolution ();
+  virtual ~VisualSceneSolution ();
+
+  virtual void BuildScene (int zoomall = 0);
+  virtual void DrawScene ();
+  virtual void MouseDblClick (int px, int py);
+
+  void BuildFieldLinesPlot ();
+
+  void AddSolutionData (SolData * soldata);
+  void ClearSolutionData ();
+  void UpdateSolutionTimeStamp ();
+  SolData * GetSolData (int i);
+  int GetNSolData () { return soldata.Size(); }
+
+  void SaveSolutionData (const char * filename);
+
+
+  static void RealVec3d (const double * values, Vec3d & v, 
+			 bool iscomplex, bool imag);
+  static void RealVec3d (const double * values, Vec3d & v, 
+			 bool iscomplex, double phaser, double phasei);
+
+
+  void SetSubdivision (int sd)
+  {
+    subdivisions = sd;
+    subdivision_timestamp = solutiontimestamp = NextTimeStamp();
+  }
+
+  void GetMinMax (int funcnr, int comp, double & minv, double & maxv) const;
+
+private:
+  void GetClippingPlaneTrigs (Array<ClipPlaneTrig> & trigs, Array<ClipPlanePoint> & pts);
+  void GetClippingPlaneGrid (Array<ClipPlanePoint> & pts);
+  void DrawCone (const Point<3> & p1, const Point<3> & p2, double r);
+  void DrawCylinder (const Point<3> & p1, const Point<3> & p2, double r);
+
+
+  // Get Function Value, local coordinates lam1, lam2, lam3, 
+  bool GetValue (const SolData * data, ElementIndex elnr, 
+		   double lam1, double lam2, double lam3,
+		   int comp, double & val) const;
+
+  bool GetValue (const SolData * data, ElementIndex elnr,
+		 const double xref[], const double x[], const double dxdxref[], 
+		 int comp, double & val) const;
+
+  bool GetValueComplex (const SolData * data, ElementIndex elnr, 
+			double lam1, double lam2, double lam3,
+			int comp, complex<double> & val) const;
+
+  bool GetValues (const SolData * data, ElementIndex elnr, 
+		  double lam1, double lam2, double lam3,
+		  double * values) const;
+
+  bool GetValues (const SolData * data, ElementIndex elnr, 
+		  const double xref[], const double x[], const double dxdxref[], 
+		  double * values) const;
+
+  bool GetMultiValues (const SolData * data, ElementIndex elnr, int npt,
+		       const double * xref, int sxref,
+		       const double * x, int sx,
+		       const double * dxdxref, int sdxdxref,
+		       double * val, int sval) const;
+
+
+  bool GetSurfValue (const SolData * data, SurfaceElementIndex elnr,
+		     double lam1, double lam2, 
+		     int comp, double & val) const;
+
+  bool GetSurfValue (const SolData * data, SurfaceElementIndex elnr,
+		     const double xref[], const double x[], const double dxdxref[], 
+		     int comp, double & val) const;
+
+  
+  bool GetSurfValueComplex (const SolData * data, SurfaceElementIndex elnr,
+			    double lam1, double lam2, 
+			    int comp, complex<double> & val) const;
+
+  bool GetSurfValues (const SolData * data, SurfaceElementIndex elnr,
+		      double lam1, double lam2, 
+		      double * values) const;
+
+  bool GetSurfValues (const SolData * data, SurfaceElementIndex elnr,
+		      const double xref[], const double x[], const double dxdxref[], 
+		      double * values) const;
+
+  bool GetMultiSurfValues (const SolData * data, SurfaceElementIndex elnr, int npt,
+                           const double * xref, int sxref,
+                           const double * x, int sx,
+                           const double * dxdxref, int sdxdxref,
+                           double * val, int sval) const;
+  
+  double ExtractValue (const SolData * data, int comp, double * values) const;
+  complex<double> ExtractValueComplex (const SolData * data, int comp, double * values) const;
+
+
+  Vec<3> GetDeformation (ElementIndex elnr, const Point<3> & p) const;
+  Vec<3> GetSurfDeformation (SurfaceElementIndex selnr, double lam1, double lam2) const;
+
+  void GetPointDeformation (int pnum, Point<3> & p, SurfaceElementIndex elnr = -1) const;
+
+public:
+  /// draw elements (build lists)
+  void DrawSurfaceElements ();
+  void DrawSurfaceElementLines ();
+  void DrawSurfaceVectors ();
+  void DrawTrigSurfaceVectors(const Array< Point<3> > & lp, const Point<3> & pmin, const Point<3> & pmax,
+			      const int sei, const SolData * vsol);
+  void DrawIsoSurface(const SolData * sol, const SolData * grad, int comp);
+  
+  void DrawIsoLines (const Point<3> & p1, 
+		     const Point<3> & p2, 
+		     const Point<3> & p3,
+		     double val1, double val2, double val3);
+
+  // draw isolines between lines (p1,p2) and (p3,p4)
+  void DrawIsoLines2 (const Point<3> & p1, 
+		      const Point<3> & p2, 
+		      const Point<3> & p3,
+		      const Point<3> & p4,
+		      double val1, double val2, double val3, double val4);
+
+
+  void DrawClipPlaneTrigs (); // const SolData * sol, int comp);
+		  
+  void SetOpenGlColor(double val);
+
+  // 0 .. non, 1 .. scalar, 2 .. complex
+  void SetTextureMode (int texturemode) const;
+
+#ifndef SMALLLIB  
+#ifndef NOTCL
+
+  friend int Ng_Vis_Set (ClientData clientData,
+			 Tcl_Interp * interp,
+			 int argc, tcl_const char *argv[]);
+
+#endif
+#endif
+
+
+#ifdef PARALLELGL
+  void Broadcast ();
+#endif
+
+
+};
+
+
+
+
+class RKStepper
+{
+private:
+  Array<double> c,b;
+  TABLE<double> *a;
+  int steps;
+  int order;
+  
+  double tolerance;
+  
+  Array<Vec3d> K;
+  
+  int stepcount;
+  
+  double h;
+  double startt;
+  double startt_bak;
+  Point3d startval;
+  Point3d startval_bak;
+  
+  bool adaptive;
+  int adrun;
+  Point3d valh;
+  
+  int notrestarted;
+
+public:
+  
+  ~RKStepper();
+    
+  RKStepper(int type = 0);
+
+  void SetTolerance(const double tol){tolerance = tol;}
+        
+  void StartNextValCalc(const Point3d & astartval, const double astartt, const double ah, const bool aadaptive = false);
+
+  bool GetNextData(Point3d & val, double & t, double & ah);
+
+  bool FeedNextF(const Vec3d & f);
+};
+
+
+
+
+
+class FieldLineCalc
+{
+private:
+  const Mesh & mesh;
+  
+  VisualSceneSolution & vss;
+  
+  const VisualSceneSolution::SolData * vsol;
+
+  RKStepper stepper;
+
+  double maxlength;
+
+  int maxpoints;
+  
+  int direction;
+  
+  Point3d pmin, pmax;
+  double rad;
+  double phaser, phasei;
+  
+  double critical_value;
+
+  bool randomized;
+
+  double thickness;
+
+public:
+  FieldLineCalc(const Mesh & amesh, VisualSceneSolution & avss, const VisualSceneSolution::SolData * solution, 
+		const double rel_length, const int amaxpoints = -1, 
+		const double rel_thickness = -1, const double rel_tolerance = -1, const int rk_type = 0, const int adirection = 0);
+
+  void SetPhase(const double real, const double imag) { phaser = real; phasei = imag; }
+  
+  void SetCriticalValue(const double val) { critical_value = val; }
+
+  void Randomized(void) { randomized = true; }
+  void NotRandomized(void) { randomized = false; }
+
+  void Calc(const Point3d & startpoint, Array<Point3d> & points, Array<double> & vals, Array<bool> & drawelems, Array<int> & dirstart);
+  
+  void GenerateFieldLines(Array<Point3d> & potential_startpoints, const int numlines, const int gllist,
+			  const double minval, const double maxval, const int logscale, double phaser, double phasei);
+};
+
+
+
+
+extern VisualSceneSolution vssolution;
+
+
+
+
+#endif
+
diff --git a/contrib/Netgen/nglib/Makefile.am b/contrib/Netgen/nglib/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..8f8fb6e9cae3c440e7735a24a8409c6114313884
--- /dev/null
+++ b/contrib/Netgen/nglib/Makefile.am
@@ -0,0 +1,41 @@
+include_HEADERS = nglib.h
+
+dist_pkgdata_DATA = cube.surf
+
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc/include  $(MPI_INCLUDES) $(OCCFLAGS)
+
+lib_LTLIBRARIES = libnglib.la
+libnglib_la_SOURCES = nglib.cpp
+
+libnglib_la_LIBADD = \
+	$(top_builddir)/libsrc/interface/libinterface.la \
+	$(top_builddir)/libsrc/geom2d/libgeom2d.la \
+	$(top_builddir)/libsrc/csg/libcsg.la \
+	$(top_builddir)/libsrc/stlgeom/libstl.la \
+	$(top_builddir)/libsrc/occ/libocc.la \
+	$(top_builddir)/libsrc/meshing/libmesh.la \
+	$(top_builddir)/libsrc/gprim/libgprim.la \
+	$(top_builddir)/libsrc/linalg/libla.la \
+	$(top_builddir)/libsrc/general/libgen.la \
+	$(OCCLIBS) $(MPI_LIBS)
+
+libnglib_la_LDFLAGS = -avoid-version
+#  -rdynamic
+
+
+bin_PROGRAMS = ng_vol ng_stl
+#  ng_occ
+
+ng_vol_SOURCES = ng_vol.cpp
+ng_vol_LDADD = \
+	libnglib.la 
+ng_stl_SOURCES = ng_stl.cpp
+ng_stl_LDADD = \
+	libnglib.la 
+
+
+# ng_occ_SOURCES = ng_occ.cpp
+# ng_occ_LDADD = \
+#	libnglib.la 
+
+
diff --git a/contrib/Netgen/nglib/cube.surf b/contrib/Netgen/nglib/cube.surf
new file mode 100644
index 0000000000000000000000000000000000000000..1fee858b7a4016b00796fb23eb96efd1e09876ec
--- /dev/null
+++ b/contrib/Netgen/nglib/cube.surf
@@ -0,0 +1,22 @@
+8
+         0         0         0
+         1         0         0
+         1         1         1
+         1         0         1
+         0         1         1
+         0         0         1
+         0         1         0
+         1         1         0
+12
+       2       1       7
+       8       2       7
+       6       1       2
+       4       6       2
+       4       3       5
+       5       6       4
+       8       3       4
+       8       4       2
+       5       3       8
+       7       5       8
+       1       6       5
+       7       1       5
diff --git a/contrib/Netgen/nglib/hinge.stl b/contrib/Netgen/nglib/hinge.stl
new file mode 100644
index 0000000000000000000000000000000000000000..9f5105938ee3dd93714dbbc402ec67401bfc0f65
--- /dev/null
+++ b/contrib/Netgen/nglib/hinge.stl
@@ -0,0 +1,8486 @@
+solid
+  facet normal 5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 3.832020e+001 1.706140e+001 5.000000e+000
+      vertex 3.837337e+001 1.645363e+001 5.000000e+000
+      vertex 3.932020e+001 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 3.935818e+001 1.749552e+001 4.399139e+000
+      vertex 3.832020e+001 1.706140e+001 5.000000e+000
+      vertex 3.932020e+001 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 3.935818e+001 1.749552e+001 4.399139e+000
+      vertex 3.837337e+001 1.766917e+001 5.000000e+000
+      vertex 3.832020e+001 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 3.935818e+001 1.749552e+001 4.399139e+000
+      vertex 3.853128e+001 1.825847e+001 5.000000e+000
+      vertex 3.837337e+001 1.766917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 3.935818e+001 1.749552e+001 4.399139e+000
+      vertex 3.947097e+001 1.791645e+001 4.399139e+000
+      vertex 3.853128e+001 1.825847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 3.853128e+001 1.825847e+001 5.000000e+000
+      vertex 3.947097e+001 1.791645e+001 4.399139e+000
+      vertex 3.878911e+001 1.881140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 3.878911e+001 1.881140e+001 5.000000e+000
+      vertex 3.947097e+001 1.791645e+001 4.399139e+000
+      vertex 3.965514e+001 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 3.913904e+001 1.931116e+001 5.000000e+000
+      vertex 3.965514e+001 1.831140e+001 4.399139e+000
+      vertex 3.990509e+001 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 4.021323e+001 1.897651e+001 4.399139e+000
+      vertex 3.913904e+001 1.931116e+001 5.000000e+000
+      vertex 3.990509e+001 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 4.021323e+001 1.897651e+001 4.399139e+000
+      vertex 3.957044e+001 1.974256e+001 5.000000e+000
+      vertex 3.913904e+001 1.931116e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 4.021323e+001 1.897651e+001 4.399139e+000
+      vertex 4.057020e+001 1.922646e+001 4.399139e+000
+      vertex 3.957044e+001 1.974256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 3.957044e+001 1.974256e+001 5.000000e+000
+      vertex 4.057020e+001 1.922646e+001 4.399139e+000
+      vertex 4.007020e+001 2.009249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 4.007020e+001 2.009249e+001 5.000000e+000
+      vertex 4.057020e+001 1.922646e+001 4.399139e+000
+      vertex 4.062313e+001 2.035032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 4.062313e+001 2.035032e+001 5.000000e+000
+      vertex 4.057020e+001 1.922646e+001 4.399139e+000
+      vertex 4.096515e+001 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 4.121243e+001 2.050823e+001 5.000000e+000
+      vertex 4.096515e+001 1.941063e+001 4.399139e+000
+      vertex 4.138608e+001 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 4.182020e+001 1.956140e+001 4.399139e+000
+      vertex 4.121243e+001 2.050823e+001 5.000000e+000
+      vertex 4.138608e+001 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 4.182020e+001 1.956140e+001 4.399139e+000
+      vertex 4.182020e+001 2.056140e+001 5.000000e+000
+      vertex 4.121243e+001 2.050823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 4.182020e+001 1.956140e+001 4.399139e+000
+      vertex 4.225432e+001 1.952342e+001 4.399139e+000
+      vertex 4.182020e+001 2.056140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 4.182020e+001 2.056140e+001 5.000000e+000
+      vertex 4.225432e+001 1.952342e+001 4.399139e+000
+      vertex 4.242797e+001 2.050823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 4.242797e+001 2.050823e+001 5.000000e+000
+      vertex 4.225432e+001 1.952342e+001 4.399139e+000
+      vertex 4.267525e+001 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 4.301727e+001 2.035032e+001 5.000000e+000
+      vertex 4.267525e+001 1.941063e+001 4.399139e+000
+      vertex 4.307020e+001 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 4.357020e+001 2.009249e+001 5.000000e+000
+      vertex 4.307020e+001 1.922646e+001 4.399139e+000
+      vertex 4.342717e+001 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 4.406996e+001 1.974256e+001 5.000000e+000
+      vertex 4.342717e+001 1.897651e+001 4.399139e+000
+      vertex 4.373531e+001 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 4.450136e+001 1.931116e+001 5.000000e+000
+      vertex 4.373531e+001 1.866837e+001 4.399139e+000
+      vertex 4.398526e+001 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 4.485129e+001 1.881140e+001 5.000000e+000
+      vertex 4.398526e+001 1.831140e+001 4.399139e+000
+      vertex 4.510912e+001 1.825847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 4.485129e+001 1.881140e+001 5.000000e+000
+      vertex 4.450136e+001 1.931116e+001 5.000000e+000
+      vertex 4.398526e+001 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 3.837337e+001 1.645363e+001 5.000000e+000
+      vertex 3.853128e+001 1.586433e+001 5.000000e+000
+      vertex 3.947097e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 3.935818e+001 1.662728e+001 4.399139e+000
+      vertex 3.837337e+001 1.645363e+001 5.000000e+000
+      vertex 3.947097e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 3.935818e+001 1.662728e+001 4.399139e+000
+      vertex 3.932020e+001 1.706140e+001 4.399139e+000
+      vertex 3.837337e+001 1.645363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 3.853128e+001 1.586433e+001 5.000000e+000
+      vertex 3.878911e+001 1.531140e+001 5.000000e+000
+      vertex 3.965514e+001 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 3.947097e+001 1.620635e+001 4.399139e+000
+      vertex 3.853128e+001 1.586433e+001 5.000000e+000
+      vertex 3.965514e+001 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 3.878911e+001 1.531140e+001 5.000000e+000
+      vertex 3.913904e+001 1.481164e+001 5.000000e+000
+      vertex 3.990509e+001 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 3.965514e+001 1.581140e+001 4.399139e+000
+      vertex 3.878911e+001 1.531140e+001 5.000000e+000
+      vertex 3.990509e+001 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 3.913904e+001 1.481164e+001 5.000000e+000
+      vertex 3.957044e+001 1.438024e+001 5.000000e+000
+      vertex 3.990509e+001 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 3.990509e+001 1.545443e+001 4.399139e+000
+      vertex 3.957044e+001 1.438024e+001 5.000000e+000
+      vertex 4.021323e+001 1.514629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 4.021323e+001 1.514629e+001 4.399139e+000
+      vertex 3.957044e+001 1.438024e+001 5.000000e+000
+      vertex 4.057020e+001 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 4.057020e+001 1.489634e+001 4.399139e+000
+      vertex 3.957044e+001 1.438024e+001 5.000000e+000
+      vertex 4.007020e+001 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 4.062313e+001 1.377248e+001 5.000000e+000
+      vertex 4.057020e+001 1.489634e+001 4.399139e+000
+      vertex 4.007020e+001 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 4.062313e+001 1.377248e+001 5.000000e+000
+      vertex 4.096515e+001 1.471217e+001 4.399139e+000
+      vertex 4.057020e+001 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 4.062313e+001 1.377248e+001 5.000000e+000
+      vertex 4.121243e+001 1.361457e+001 5.000000e+000
+      vertex 4.096515e+001 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 4.096515e+001 1.471217e+001 4.399139e+000
+      vertex 4.121243e+001 1.361457e+001 5.000000e+000
+      vertex 4.138608e+001 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 4.138608e+001 1.459938e+001 4.399139e+000
+      vertex 4.121243e+001 1.361457e+001 5.000000e+000
+      vertex 4.182020e+001 1.456140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 4.182020e+001 1.456140e+001 4.399139e+000
+      vertex 4.121243e+001 1.361457e+001 5.000000e+000
+      vertex 4.182020e+001 1.356140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 4.225432e+001 1.459938e+001 4.399139e+000
+      vertex 4.182020e+001 1.356140e+001 5.000000e+000
+      vertex 4.242797e+001 1.361457e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 4.267525e+001 1.471217e+001 4.399139e+000
+      vertex 4.242797e+001 1.361457e+001 5.000000e+000
+      vertex 4.301727e+001 1.377248e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 4.357020e+001 1.403031e+001 5.000000e+000
+      vertex 4.267525e+001 1.471217e+001 4.399139e+000
+      vertex 4.301727e+001 1.377248e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 4.357020e+001 1.403031e+001 5.000000e+000
+      vertex 4.307020e+001 1.489634e+001 4.399139e+000
+      vertex 4.267525e+001 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 4.357020e+001 1.403031e+001 5.000000e+000
+      vertex 4.406996e+001 1.438024e+001 5.000000e+000
+      vertex 4.307020e+001 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 4.307020e+001 1.489634e+001 4.399139e+000
+      vertex 4.406996e+001 1.438024e+001 5.000000e+000
+      vertex 4.342717e+001 1.514629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 4.342717e+001 1.514629e+001 4.399139e+000
+      vertex 4.406996e+001 1.438024e+001 5.000000e+000
+      vertex 4.450136e+001 1.481164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 4.373531e+001 1.545443e+001 4.399139e+000
+      vertex 4.450136e+001 1.481164e+001 5.000000e+000
+      vertex 4.485129e+001 1.531140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 4.398526e+001 1.581140e+001 4.399139e+000
+      vertex 4.485129e+001 1.531140e+001 5.000000e+000
+      vertex 4.510912e+001 1.586433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 4.416943e+001 1.620635e+001 4.399139e+000
+      vertex 4.510912e+001 1.586433e+001 5.000000e+000
+      vertex 4.428222e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 4.416943e+001 1.620635e+001 4.399139e+000
+      vertex 4.398526e+001 1.581140e+001 4.399139e+000
+      vertex 4.510912e+001 1.586433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 4.510912e+001 1.586433e+001 5.000000e+000
+      vertex 4.526703e+001 1.645363e+001 5.000000e+000
+      vertex 4.428222e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 4.428222e+001 1.662728e+001 4.399139e+000
+      vertex 4.526703e+001 1.645363e+001 5.000000e+000
+      vertex 4.432020e+001 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 4.432020e+001 1.706140e+001 4.399139e+000
+      vertex 4.526703e+001 1.645363e+001 5.000000e+000
+      vertex 4.532020e+001 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 4.428222e+001 1.749552e+001 4.399139e+000
+      vertex 4.532020e+001 1.706140e+001 5.000000e+000
+      vertex 4.526703e+001 1.766917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 4.416943e+001 1.791645e+001 4.399139e+000
+      vertex 4.526703e+001 1.766917e+001 5.000000e+000
+      vertex 4.510912e+001 1.825847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 4.398526e+001 1.831140e+001 4.399139e+000
+      vertex 4.416943e+001 1.791645e+001 4.399139e+000
+      vertex 4.510912e+001 1.825847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 4.450136e+001 1.931116e+001 5.000000e+000
+      vertex 4.406996e+001 1.974256e+001 5.000000e+000
+      vertex 4.373531e+001 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 4.406996e+001 1.974256e+001 5.000000e+000
+      vertex 4.357020e+001 2.009249e+001 5.000000e+000
+      vertex 4.342717e+001 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 4.357020e+001 2.009249e+001 5.000000e+000
+      vertex 4.301727e+001 2.035032e+001 5.000000e+000
+      vertex 4.307020e+001 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 4.301727e+001 2.035032e+001 5.000000e+000
+      vertex 4.242797e+001 2.050823e+001 5.000000e+000
+      vertex 4.267525e+001 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 4.121243e+001 2.050823e+001 5.000000e+000
+      vertex 4.062313e+001 2.035032e+001 5.000000e+000
+      vertex 4.096515e+001 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 3.913904e+001 1.931116e+001 5.000000e+000
+      vertex 3.878911e+001 1.881140e+001 5.000000e+000
+      vertex 3.965514e+001 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 4.416943e+001 1.791645e+001 4.399139e+000
+      vertex 4.428222e+001 1.749552e+001 4.399139e+000
+      vertex 4.526703e+001 1.766917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 4.428222e+001 1.749552e+001 4.399139e+000
+      vertex 4.432020e+001 1.706140e+001 4.399139e+000
+      vertex 4.532020e+001 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 4.398526e+001 1.581140e+001 4.399139e+000
+      vertex 4.373531e+001 1.545443e+001 4.399139e+000
+      vertex 4.485129e+001 1.531140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 4.342717e+001 1.514629e+001 4.399139e+000
+      vertex 4.450136e+001 1.481164e+001 5.000000e+000
+      vertex 4.373531e+001 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 4.267525e+001 1.471217e+001 4.399139e+000
+      vertex 4.225432e+001 1.459938e+001 4.399139e+000
+      vertex 4.242797e+001 1.361457e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 4.225432e+001 1.459938e+001 4.399139e+000
+      vertex 4.182020e+001 1.456140e+001 4.399139e+000
+      vertex 4.182020e+001 1.356140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 8.715574e-002 0.000000e+000
+    outer loop
+      vertex 4.432020e+001 1.706140e+001 -8.881784e-016
+      vertex 4.428222e+001 1.662728e+001 -8.881784e-016
+      vertex 4.428222e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 8.715574e-002 0.000000e+000
+    outer loop
+      vertex 4.432020e+001 1.706140e+001 4.399139e+000
+      vertex 4.432020e+001 1.706140e+001 -8.881784e-016
+      vertex 4.428222e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 -8.715574e-002 0.000000e+000
+    outer loop
+      vertex 4.432020e+001 1.706140e+001 4.399139e+000
+      vertex 4.428222e+001 1.749552e+001 -8.881784e-016
+      vertex 4.432020e+001 1.706140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -9.961947e-001 -8.715574e-002 -9.048915e-016
+    outer loop
+      vertex 4.432020e+001 1.706140e+001 4.399139e+000
+      vertex 4.428222e+001 1.749552e+001 4.399139e+000
+      vertex 4.428222e+001 1.749552e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -9.659258e-001 -2.588190e-001 5.300545e-016
+    outer loop
+      vertex 4.428222e+001 1.749552e+001 -8.881784e-016
+      vertex 4.428222e+001 1.749552e+001 4.399139e+000
+      vertex 4.416943e+001 1.791645e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -9.659258e-001 -2.588190e-001 -1.030095e-015
+    outer loop
+      vertex 4.416943e+001 1.791645e+001 -8.881784e-016
+      vertex 4.428222e+001 1.749552e+001 4.399139e+000
+      vertex 4.416943e+001 1.791645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 -4.226183e-001 1.440206e-016
+    outer loop
+      vertex 4.398526e+001 1.831140e+001 -8.881784e-016
+      vertex 4.416943e+001 1.791645e+001 4.399139e+000
+      vertex 4.398526e+001 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 -5.735764e-001 -1.586601e-015
+    outer loop
+      vertex 4.373531e+001 1.866837e+001 -8.881784e-016
+      vertex 4.398526e+001 1.831140e+001 4.399139e+000
+      vertex 4.373531e+001 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 -7.071068e-001 -1.142109e-015
+    outer loop
+      vertex 4.342717e+001 1.897651e+001 -8.881784e-016
+      vertex 4.373531e+001 1.866837e+001 4.399139e+000
+      vertex 4.342717e+001 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 -8.191520e-001 -1.324456e-015
+    outer loop
+      vertex 4.307020e+001 1.922646e+001 -8.881784e-016
+      vertex 4.342717e+001 1.897651e+001 4.399139e+000
+      vertex 4.307020e+001 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 -9.063078e-001 -1.217252e-015
+    outer loop
+      vertex 4.267525e+001 1.941063e+001 -8.881784e-016
+      vertex 4.307020e+001 1.922646e+001 4.399139e+000
+      vertex 4.267525e+001 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 -9.659258e-001 -1.310129e-015
+    outer loop
+      vertex 4.225432e+001 1.952342e+001 -8.881784e-016
+      vertex 4.267525e+001 1.941063e+001 4.399139e+000
+      vertex 4.225432e+001 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 -9.961947e-001 -7.038636e-016
+    outer loop
+      vertex 4.182020e+001 1.956140e+001 -8.881784e-016
+      vertex 4.225432e+001 1.952342e+001 4.399139e+000
+      vertex 4.182020e+001 1.956140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 -9.961947e-001 -9.051759e-016
+    outer loop
+      vertex 4.138608e+001 1.952342e+001 -8.881784e-016
+      vertex 4.182020e+001 1.956140e+001 4.399139e+000
+      vertex 4.138608e+001 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 -9.659258e-001 -2.500203e-016
+    outer loop
+      vertex 4.096515e+001 1.941063e+001 -8.881784e-016
+      vertex 4.138608e+001 1.952342e+001 4.399139e+000
+      vertex 4.096515e+001 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 -9.063078e-001 -9.292106e-016
+    outer loop
+      vertex 4.057020e+001 1.922646e+001 -8.881784e-016
+      vertex 4.096515e+001 1.941063e+001 4.399139e+000
+      vertex 4.057020e+001 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 -8.191520e-001 -9.250596e-016
+    outer loop
+      vertex 4.021323e+001 1.897651e+001 -8.881784e-016
+      vertex 4.057020e+001 1.922646e+001 4.399139e+000
+      vertex 4.021323e+001 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 -7.071068e-001 -1.142109e-015
+    outer loop
+      vertex 3.990509e+001 1.866837e+001 -8.881784e-016
+      vertex 4.021323e+001 1.897651e+001 4.399139e+000
+      vertex 3.990509e+001 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 -5.735764e-001 -1.059565e-015
+    outer loop
+      vertex 3.965514e+001 1.831140e+001 -8.881784e-016
+      vertex 3.990509e+001 1.866837e+001 4.399139e+000
+      vertex 3.965514e+001 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 -4.226183e-001 -4.853241e-016
+    outer loop
+      vertex 3.947097e+001 1.791645e+001 -8.881784e-016
+      vertex 3.965514e+001 1.831140e+001 4.399139e+000
+      vertex 3.947097e+001 1.791645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 -2.588190e-001 -5.300545e-016
+    outer loop
+      vertex 3.935818e+001 1.749552e+001 -8.881784e-016
+      vertex 3.947097e+001 1.791645e+001 4.399139e+000
+      vertex 3.935818e+001 1.749552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 -8.715574e-002 -7.038636e-016
+    outer loop
+      vertex 3.932020e+001 1.706140e+001 -8.881784e-016
+      vertex 3.935818e+001 1.749552e+001 4.399139e+000
+      vertex 3.932020e+001 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 8.715574e-002 7.038636e-017
+    outer loop
+      vertex 3.935818e+001 1.662728e+001 -8.881784e-016
+      vertex 3.932020e+001 1.706140e+001 4.399139e+000
+      vertex 3.935818e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 2.588190e-001 0.000000e+000
+    outer loop
+      vertex 3.947097e+001 1.620635e+001 -8.881784e-016
+      vertex 3.935818e+001 1.662728e+001 4.399139e+000
+      vertex 3.947097e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 4.226183e-001 0.000000e+000
+    outer loop
+      vertex 3.965514e+001 1.581140e+001 -8.881784e-016
+      vertex 3.947097e+001 1.620635e+001 4.399139e+000
+      vertex 3.965514e+001 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 5.735764e-001 2.316081e-016
+    outer loop
+      vertex 3.990509e+001 1.545443e+001 -8.881784e-016
+      vertex 3.965514e+001 1.581140e+001 4.399139e+000
+      vertex 3.990509e+001 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 7.071068e-001 2.855272e-016
+    outer loop
+      vertex 4.021323e+001 1.514629e+001 -8.881784e-016
+      vertex 3.990509e+001 1.545443e+001 4.399139e+000
+      vertex 4.021323e+001 1.514629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 8.191520e-001 3.307707e-016
+    outer loop
+      vertex 4.057020e+001 1.489634e+001 -8.881784e-016
+      vertex 4.021323e+001 1.514629e+001 4.399139e+000
+      vertex 4.057020e+001 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 9.063078e-001 -6.826070e-016
+    outer loop
+      vertex 4.096515e+001 1.471217e+001 -8.881784e-016
+      vertex 4.057020e+001 1.489634e+001 4.399139e+000
+      vertex 4.096515e+001 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 9.659258e-001 -4.180408e-016
+    outer loop
+      vertex 4.138608e+001 1.459938e+001 -8.881784e-016
+      vertex 4.096515e+001 1.471217e+001 4.399139e+000
+      vertex 4.138608e+001 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 9.961947e-001 0.000000e+000
+    outer loop
+      vertex 4.182020e+001 1.456140e+001 -8.881784e-016
+      vertex 4.138608e+001 1.459938e+001 4.399139e+000
+      vertex 4.182020e+001 1.456140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 9.961947e-001 0.000000e+000
+    outer loop
+      vertex 4.225432e+001 1.459938e+001 -8.881784e-016
+      vertex 4.182020e+001 1.456140e+001 4.399139e+000
+      vertex 4.225432e+001 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 9.659258e-001 2.800343e-017
+    outer loop
+      vertex 4.267525e+001 1.471217e+001 -8.881784e-016
+      vertex 4.225432e+001 1.459938e+001 4.399139e+000
+      vertex 4.267525e+001 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 9.063078e-001 -3.659639e-016
+    outer loop
+      vertex 4.307020e+001 1.489634e+001 -8.881784e-016
+      vertex 4.267525e+001 1.471217e+001 4.399139e+000
+      vertex 4.307020e+001 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 8.191520e-001 5.956618e-016
+    outer loop
+      vertex 4.342717e+001 1.514629e+001 -8.881784e-016
+      vertex 4.307020e+001 1.489634e+001 4.399139e+000
+      vertex 4.342717e+001 1.514629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 7.071068e-001 0.000000e+000
+    outer loop
+      vertex 4.373531e+001 1.545443e+001 -8.881784e-016
+      vertex 4.342717e+001 1.514629e+001 4.399139e+000
+      vertex 4.373531e+001 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 5.735764e-001 0.000000e+000
+    outer loop
+      vertex 4.398526e+001 1.581140e+001 -8.881784e-016
+      vertex 4.373531e+001 1.545443e+001 4.399139e+000
+      vertex 4.398526e+001 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 4.226183e-001 0.000000e+000
+    outer loop
+      vertex 4.416943e+001 1.620635e+001 -8.881784e-016
+      vertex 4.398526e+001 1.581140e+001 4.399139e+000
+      vertex 4.416943e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.659258e-001 2.588190e-001 0.000000e+000
+    outer loop
+      vertex 4.428222e+001 1.662728e+001 -8.881784e-016
+      vertex 4.416943e+001 1.620635e+001 4.399139e+000
+      vertex 4.428222e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.659258e-001 2.588190e-001 0.000000e+000
+    outer loop
+      vertex 4.428222e+001 1.662728e+001 -8.881784e-016
+      vertex 4.416943e+001 1.620635e+001 -8.881784e-016
+      vertex 4.416943e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 4.226183e-001 0.000000e+000
+    outer loop
+      vertex 4.416943e+001 1.620635e+001 -8.881784e-016
+      vertex 4.398526e+001 1.581140e+001 -8.881784e-016
+      vertex 4.398526e+001 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 5.735764e-001 0.000000e+000
+    outer loop
+      vertex 4.398526e+001 1.581140e+001 -8.881784e-016
+      vertex 4.373531e+001 1.545443e+001 -8.881784e-016
+      vertex 4.373531e+001 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 7.071068e-001 8.614567e-016
+    outer loop
+      vertex 4.373531e+001 1.545443e+001 -8.881784e-016
+      vertex 4.342717e+001 1.514629e+001 -8.881784e-016
+      vertex 4.342717e+001 1.514629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 8.191520e-001 -3.257609e-016
+    outer loop
+      vertex 4.342717e+001 1.514629e+001 -8.881784e-016
+      vertex 4.307020e+001 1.489634e+001 -8.881784e-016
+      vertex 4.307020e+001 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 9.063078e-001 3.166431e-016
+    outer loop
+      vertex 4.267525e+001 1.471217e+001 -8.881784e-016
+      vertex 4.267525e+001 1.471217e+001 4.399139e+000
+      vertex 4.307020e+001 1.489634e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -2.588190e-001 9.659258e-001 0.000000e+000
+    outer loop
+      vertex 4.267525e+001 1.471217e+001 -8.881784e-016
+      vertex 4.225432e+001 1.459938e+001 -8.881784e-016
+      vertex 4.225432e+001 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 9.961947e-001 0.000000e+000
+    outer loop
+      vertex 4.225432e+001 1.459938e+001 -8.881784e-016
+      vertex 4.182020e+001 1.456140e+001 -8.881784e-016
+      vertex 4.182020e+001 1.456140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 9.961947e-001 -1.411631e-016
+    outer loop
+      vertex 4.182020e+001 1.456140e+001 -8.881784e-016
+      vertex 4.138608e+001 1.459938e+001 -8.881784e-016
+      vertex 4.138608e+001 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 9.659258e-001 -4.198697e-016
+    outer loop
+      vertex 4.138608e+001 1.459938e+001 -8.881784e-016
+      vertex 4.096515e+001 1.471217e+001 -8.881784e-016
+      vertex 4.096515e+001 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 9.063078e-001 3.691957e-016
+    outer loop
+      vertex 4.096515e+001 1.471217e+001 -8.881784e-016
+      vertex 4.057020e+001 1.489634e+001 -8.881784e-016
+      vertex 4.057020e+001 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 8.191520e-001 3.330001e-016
+    outer loop
+      vertex 4.057020e+001 1.489634e+001 -8.881784e-016
+      vertex 4.021323e+001 1.514629e+001 -8.881784e-016
+      vertex 4.021323e+001 1.514629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 7.071068e-001 2.855272e-016
+    outer loop
+      vertex 3.990509e+001 1.545443e+001 -8.881784e-016
+      vertex 3.990509e+001 1.545443e+001 4.399139e+000
+      vertex 4.021323e+001 1.514629e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 8.191520e-001 5.735764e-001 0.000000e+000
+    outer loop
+      vertex 3.990509e+001 1.545443e+001 -8.881784e-016
+      vertex 3.965514e+001 1.581140e+001 -8.881784e-016
+      vertex 3.965514e+001 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 4.226183e-001 0.000000e+000
+    outer loop
+      vertex 3.965514e+001 1.581140e+001 -8.881784e-016
+      vertex 3.947097e+001 1.620635e+001 -8.881784e-016
+      vertex 3.947097e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 2.588190e-001 2.090204e-016
+    outer loop
+      vertex 3.935818e+001 1.662728e+001 -8.881784e-016
+      vertex 3.935818e+001 1.662728e+001 4.399139e+000
+      vertex 3.947097e+001 1.620635e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 9.961947e-001 8.715574e-002 7.040056e-016
+    outer loop
+      vertex 3.935818e+001 1.662728e+001 -8.881784e-016
+      vertex 3.932020e+001 1.706140e+001 -8.881784e-016
+      vertex 3.932020e+001 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 -8.715574e-002 9.048915e-016
+    outer loop
+      vertex 3.932020e+001 1.706140e+001 -8.881784e-016
+      vertex 3.935818e+001 1.749552e+001 -8.881784e-016
+      vertex 3.935818e+001 1.749552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 -2.588190e-001 1.030095e-015
+    outer loop
+      vertex 3.947097e+001 1.791645e+001 -8.881784e-016
+      vertex 3.947097e+001 1.791645e+001 4.399139e+000
+      vertex 3.935818e+001 1.749552e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 9.063078e-001 -4.226183e-001 1.954566e-016
+    outer loop
+      vertex 3.947097e+001 1.791645e+001 -8.881784e-016
+      vertex 3.965514e+001 1.831140e+001 -8.881784e-016
+      vertex 3.965514e+001 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 -5.735764e-001 2.606088e-016
+    outer loop
+      vertex 3.965514e+001 1.831140e+001 -8.881784e-016
+      vertex 3.990509e+001 1.866837e+001 -8.881784e-016
+      vertex 3.990509e+001 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 -7.071068e-001 5.710544e-016
+    outer loop
+      vertex 4.021323e+001 1.897651e+001 -8.881784e-016
+      vertex 4.021323e+001 1.897651e+001 4.399139e+000
+      vertex 3.990509e+001 1.866837e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 5.735764e-001 -8.191520e-001 3.981523e-016
+    outer loop
+      vertex 4.021323e+001 1.897651e+001 -8.881784e-016
+      vertex 4.057020e+001 1.922646e+001 -8.881784e-016
+      vertex 4.057020e+001 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 -9.063078e-001 1.216174e-015
+    outer loop
+      vertex 4.057020e+001 1.922646e+001 -8.881784e-016
+      vertex 4.096515e+001 1.941063e+001 -8.881784e-016
+      vertex 4.096515e+001 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 -9.659258e-001 5.284567e-016
+    outer loop
+      vertex 4.096515e+001 1.941063e+001 -8.881784e-016
+      vertex 4.138608e+001 1.952342e+001 -8.881784e-016
+      vertex 4.138608e+001 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 -9.961947e-001 7.040056e-016
+    outer loop
+      vertex 4.138608e+001 1.952342e+001 -8.881784e-016
+      vertex 4.182020e+001 1.956140e+001 -8.881784e-016
+      vertex 4.182020e+001 1.956140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 -9.961947e-001 1.013479e-016
+    outer loop
+      vertex 4.182020e+001 1.956140e+001 -8.881784e-016
+      vertex 4.225432e+001 1.952342e+001 -8.881784e-016
+      vertex 4.225432e+001 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 -9.659258e-001 2.497501e-016
+    outer loop
+      vertex 4.225432e+001 1.952342e+001 -8.881784e-016
+      vertex 4.267525e+001 1.941063e+001 -8.881784e-016
+      vertex 4.267525e+001 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 -9.063078e-001 2.461305e-016
+    outer loop
+      vertex 4.267525e+001 1.941063e+001 -8.881784e-016
+      vertex 4.307020e+001 1.922646e+001 -8.881784e-016
+      vertex 4.307020e+001 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 -8.191520e-001 2.678479e-016
+    outer loop
+      vertex 4.307020e+001 1.922646e+001 -8.881784e-016
+      vertex 4.342717e+001 1.897651e+001 -8.881784e-016
+      vertex 4.342717e+001 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 -7.071068e-001 0.000000e+000
+    outer loop
+      vertex 4.342717e+001 1.897651e+001 -8.881784e-016
+      vertex 4.373531e+001 1.866837e+001 -8.881784e-016
+      vertex 4.373531e+001 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 -5.735764e-001 1.520218e-015
+    outer loop
+      vertex 4.373531e+001 1.866837e+001 -8.881784e-016
+      vertex 4.398526e+001 1.831140e+001 -8.881784e-016
+      vertex 4.398526e+001 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 -4.226183e-001 4.850219e-016
+    outer loop
+      vertex 4.398526e+001 1.831140e+001 -8.881784e-016
+      vertex 4.416943e+001 1.791645e+001 -8.881784e-016
+      vertex 4.416943e+001 1.791645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 2.137337e+001 1.245363e+001 5.000000e+000
+      vertex 2.232020e+001 1.306140e+001 4.399139e+000
+      vertex 2.132020e+001 1.306140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 2.137337e+001 1.245363e+001 5.000000e+000
+      vertex 2.235818e+001 1.262728e+001 4.399139e+000
+      vertex 2.232020e+001 1.306140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 2.137337e+001 1.245363e+001 5.000000e+000
+      vertex 2.153128e+001 1.186433e+001 5.000000e+000
+      vertex 2.235818e+001 1.262728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 2.235818e+001 1.262728e+001 4.399139e+000
+      vertex 2.153128e+001 1.186433e+001 5.000000e+000
+      vertex 2.247097e+001 1.220635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 2.247097e+001 1.220635e+001 4.399139e+000
+      vertex 2.153128e+001 1.186433e+001 5.000000e+000
+      vertex 2.178911e+001 1.131140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 2.265514e+001 1.181140e+001 4.399139e+000
+      vertex 2.178911e+001 1.131140e+001 5.000000e+000
+      vertex 2.213904e+001 1.081164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 2.290509e+001 1.145443e+001 4.399139e+000
+      vertex 2.213904e+001 1.081164e+001 5.000000e+000
+      vertex 2.257044e+001 1.038024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 2.321323e+001 1.114629e+001 4.399139e+000
+      vertex 2.257044e+001 1.038024e+001 5.000000e+000
+      vertex 2.307020e+001 1.003031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 2.357020e+001 1.089634e+001 4.399139e+000
+      vertex 2.307020e+001 1.003031e+001 5.000000e+000
+      vertex 2.362313e+001 9.772476e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 2.396515e+001 1.071217e+001 4.399139e+000
+      vertex 2.362313e+001 9.772476e+000 5.000000e+000
+      vertex 2.421243e+001 9.614573e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 2.438608e+001 1.059938e+001 4.399139e+000
+      vertex 2.421243e+001 9.614573e+000 5.000000e+000
+      vertex 2.482020e+001 1.056140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 2.438608e+001 1.059938e+001 4.399139e+000
+      vertex 2.396515e+001 1.071217e+001 4.399139e+000
+      vertex 2.421243e+001 9.614573e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 2.421243e+001 9.614573e+000 5.000000e+000
+      vertex 2.482020e+001 9.561400e+000 5.000000e+000
+      vertex 2.482020e+001 1.056140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 2.482020e+001 1.056140e+001 4.399139e+000
+      vertex 2.482020e+001 9.561400e+000 5.000000e+000
+      vertex 2.542797e+001 9.614573e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 2.525432e+001 1.059938e+001 4.399139e+000
+      vertex 2.542797e+001 9.614573e+000 5.000000e+000
+      vertex 2.601727e+001 9.772476e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 2.567525e+001 1.071217e+001 4.399139e+000
+      vertex 2.601727e+001 9.772476e+000 5.000000e+000
+      vertex 2.657020e+001 1.003031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 2.607020e+001 1.089634e+001 4.399139e+000
+      vertex 2.657020e+001 1.003031e+001 5.000000e+000
+      vertex 2.642717e+001 1.114629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 2.607020e+001 1.089634e+001 4.399139e+000
+      vertex 2.567525e+001 1.071217e+001 4.399139e+000
+      vertex 2.657020e+001 1.003031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 2.657020e+001 1.003031e+001 5.000000e+000
+      vertex 2.706996e+001 1.038024e+001 5.000000e+000
+      vertex 2.642717e+001 1.114629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 2.642717e+001 1.114629e+001 4.399139e+000
+      vertex 2.706996e+001 1.038024e+001 5.000000e+000
+      vertex 2.750136e+001 1.081164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 2.673531e+001 1.145443e+001 4.399139e+000
+      vertex 2.750136e+001 1.081164e+001 5.000000e+000
+      vertex 2.785129e+001 1.131140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 2.698526e+001 1.181140e+001 4.399139e+000
+      vertex 2.785129e+001 1.131140e+001 5.000000e+000
+      vertex 2.810912e+001 1.186433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 2.716943e+001 1.220635e+001 4.399139e+000
+      vertex 2.810912e+001 1.186433e+001 5.000000e+000
+      vertex 2.826703e+001 1.245363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 2.728222e+001 1.262728e+001 4.399139e+000
+      vertex 2.826703e+001 1.245363e+001 5.000000e+000
+      vertex 2.832020e+001 1.306140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 2.732020e+001 1.306140e+001 4.399139e+000
+      vertex 2.832020e+001 1.306140e+001 5.000000e+000
+      vertex 2.728222e+001 1.349552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 2.732020e+001 1.306140e+001 4.399139e+000
+      vertex 2.728222e+001 1.262728e+001 4.399139e+000
+      vertex 2.832020e+001 1.306140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 2.832020e+001 1.306140e+001 5.000000e+000
+      vertex 2.826703e+001 1.366917e+001 5.000000e+000
+      vertex 2.728222e+001 1.349552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 2.728222e+001 1.349552e+001 4.399139e+000
+      vertex 2.826703e+001 1.366917e+001 5.000000e+000
+      vertex 2.716943e+001 1.391645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 2.716943e+001 1.391645e+001 4.399139e+000
+      vertex 2.826703e+001 1.366917e+001 5.000000e+000
+      vertex 2.810912e+001 1.425847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 2.698526e+001 1.431140e+001 4.399139e+000
+      vertex 2.810912e+001 1.425847e+001 5.000000e+000
+      vertex 2.785129e+001 1.481140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 2.750136e+001 1.531116e+001 5.000000e+000
+      vertex 2.698526e+001 1.431140e+001 4.399139e+000
+      vertex 2.785129e+001 1.481140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 2.750136e+001 1.531116e+001 5.000000e+000
+      vertex 2.673531e+001 1.466837e+001 4.399139e+000
+      vertex 2.698526e+001 1.431140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 2.750136e+001 1.531116e+001 5.000000e+000
+      vertex 2.706996e+001 1.574256e+001 5.000000e+000
+      vertex 2.673531e+001 1.466837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 2.673531e+001 1.466837e+001 4.399139e+000
+      vertex 2.706996e+001 1.574256e+001 5.000000e+000
+      vertex 2.642717e+001 1.497651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 2.642717e+001 1.497651e+001 4.399139e+000
+      vertex 2.706996e+001 1.574256e+001 5.000000e+000
+      vertex 2.607020e+001 1.522646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 2.607020e+001 1.522646e+001 4.399139e+000
+      vertex 2.706996e+001 1.574256e+001 5.000000e+000
+      vertex 2.657020e+001 1.609249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 2.567525e+001 1.541063e+001 4.399139e+000
+      vertex 2.657020e+001 1.609249e+001 5.000000e+000
+      vertex 2.601727e+001 1.635032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 2.525432e+001 1.552342e+001 4.399139e+000
+      vertex 2.601727e+001 1.635032e+001 5.000000e+000
+      vertex 2.542797e+001 1.650823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 2.482020e+001 1.656140e+001 5.000000e+000
+      vertex 2.525432e+001 1.552342e+001 4.399139e+000
+      vertex 2.542797e+001 1.650823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 2.482020e+001 1.656140e+001 5.000000e+000
+      vertex 2.482020e+001 1.556140e+001 4.399139e+000
+      vertex 2.525432e+001 1.552342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 2.482020e+001 1.656140e+001 5.000000e+000
+      vertex 2.421243e+001 1.650823e+001 5.000000e+000
+      vertex 2.482020e+001 1.556140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 2.482020e+001 1.556140e+001 4.399139e+000
+      vertex 2.421243e+001 1.650823e+001 5.000000e+000
+      vertex 2.438608e+001 1.552342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 2.438608e+001 1.552342e+001 4.399139e+000
+      vertex 2.421243e+001 1.650823e+001 5.000000e+000
+      vertex 2.362313e+001 1.635032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 2.396515e+001 1.541063e+001 4.399139e+000
+      vertex 2.362313e+001 1.635032e+001 5.000000e+000
+      vertex 2.307020e+001 1.609249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 2.357020e+001 1.522646e+001 4.399139e+000
+      vertex 2.307020e+001 1.609249e+001 5.000000e+000
+      vertex 2.321323e+001 1.497651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 2.357020e+001 1.522646e+001 4.399139e+000
+      vertex 2.396515e+001 1.541063e+001 4.399139e+000
+      vertex 2.307020e+001 1.609249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 2.321323e+001 1.497651e+001 4.399139e+000
+      vertex 2.307020e+001 1.609249e+001 5.000000e+000
+      vertex 2.257044e+001 1.574256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 2.213904e+001 1.531116e+001 5.000000e+000
+      vertex 2.321323e+001 1.497651e+001 4.399139e+000
+      vertex 2.257044e+001 1.574256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 2.213904e+001 1.531116e+001 5.000000e+000
+      vertex 2.290509e+001 1.466837e+001 4.399139e+000
+      vertex 2.321323e+001 1.497651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 2.213904e+001 1.531116e+001 5.000000e+000
+      vertex 2.178911e+001 1.481140e+001 5.000000e+000
+      vertex 2.290509e+001 1.466837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 2.290509e+001 1.466837e+001 4.399139e+000
+      vertex 2.178911e+001 1.481140e+001 5.000000e+000
+      vertex 2.265514e+001 1.431140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 2.265514e+001 1.431140e+001 4.399139e+000
+      vertex 2.178911e+001 1.481140e+001 5.000000e+000
+      vertex 2.153128e+001 1.425847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 2.247097e+001 1.391645e+001 4.399139e+000
+      vertex 2.153128e+001 1.425847e+001 5.000000e+000
+      vertex 2.137337e+001 1.366917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 2.235818e+001 1.349552e+001 4.399139e+000
+      vertex 2.137337e+001 1.366917e+001 5.000000e+000
+      vertex 2.132020e+001 1.306140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 2.232020e+001 1.306140e+001 4.399139e+000
+      vertex 2.235818e+001 1.349552e+001 4.399139e+000
+      vertex 2.132020e+001 1.306140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 2.235818e+001 1.349552e+001 4.399139e+000
+      vertex 2.247097e+001 1.391645e+001 4.399139e+000
+      vertex 2.137337e+001 1.366917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 2.247097e+001 1.391645e+001 4.399139e+000
+      vertex 2.265514e+001 1.431140e+001 4.399139e+000
+      vertex 2.153128e+001 1.425847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 2.396515e+001 1.541063e+001 4.399139e+000
+      vertex 2.438608e+001 1.552342e+001 4.399139e+000
+      vertex 2.362313e+001 1.635032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 2.525432e+001 1.552342e+001 4.399139e+000
+      vertex 2.567525e+001 1.541063e+001 4.399139e+000
+      vertex 2.601727e+001 1.635032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 2.567525e+001 1.541063e+001 4.399139e+000
+      vertex 2.607020e+001 1.522646e+001 4.399139e+000
+      vertex 2.657020e+001 1.609249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 2.698526e+001 1.431140e+001 4.399139e+000
+      vertex 2.716943e+001 1.391645e+001 4.399139e+000
+      vertex 2.810912e+001 1.425847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 2.728222e+001 1.262728e+001 4.399139e+000
+      vertex 2.716943e+001 1.220635e+001 4.399139e+000
+      vertex 2.826703e+001 1.245363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 2.716943e+001 1.220635e+001 4.399139e+000
+      vertex 2.698526e+001 1.181140e+001 4.399139e+000
+      vertex 2.810912e+001 1.186433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 2.698526e+001 1.181140e+001 4.399139e+000
+      vertex 2.673531e+001 1.145443e+001 4.399139e+000
+      vertex 2.785129e+001 1.131140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 2.673531e+001 1.145443e+001 4.399139e+000
+      vertex 2.642717e+001 1.114629e+001 4.399139e+000
+      vertex 2.750136e+001 1.081164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 2.567525e+001 1.071217e+001 4.399139e+000
+      vertex 2.525432e+001 1.059938e+001 4.399139e+000
+      vertex 2.601727e+001 9.772476e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 2.525432e+001 1.059938e+001 4.399139e+000
+      vertex 2.482020e+001 1.056140e+001 4.399139e+000
+      vertex 2.542797e+001 9.614573e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 2.396515e+001 1.071217e+001 4.399139e+000
+      vertex 2.357020e+001 1.089634e+001 4.399139e+000
+      vertex 2.362313e+001 9.772476e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 2.357020e+001 1.089634e+001 4.399139e+000
+      vertex 2.321323e+001 1.114629e+001 4.399139e+000
+      vertex 2.307020e+001 1.003031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 2.321323e+001 1.114629e+001 4.399139e+000
+      vertex 2.290509e+001 1.145443e+001 4.399139e+000
+      vertex 2.257044e+001 1.038024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 2.290509e+001 1.145443e+001 4.399139e+000
+      vertex 2.265514e+001 1.181140e+001 4.399139e+000
+      vertex 2.213904e+001 1.081164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 2.265514e+001 1.181140e+001 4.399139e+000
+      vertex 2.247097e+001 1.220635e+001 4.399139e+000
+      vertex 2.178911e+001 1.131140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 8.715574e-002 -3.438588e-017
+    outer loop
+      vertex 2.732020e+001 1.306140e+001 0.000000e+000
+      vertex 2.728222e+001 1.262728e+001 0.000000e+000
+      vertex 2.728222e+001 1.262728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 8.715574e-002 0.000000e+000
+    outer loop
+      vertex 2.732020e+001 1.306140e+001 4.399139e+000
+      vertex 2.732020e+001 1.306140e+001 0.000000e+000
+      vertex 2.728222e+001 1.262728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 -8.715574e-002 0.000000e+000
+    outer loop
+      vertex 2.732020e+001 1.306140e+001 4.399139e+000
+      vertex 2.728222e+001 1.349552e+001 0.000000e+000
+      vertex 2.732020e+001 1.306140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 -8.715574e-002 -6.515219e-017
+    outer loop
+      vertex 2.732020e+001 1.306140e+001 4.399139e+000
+      vertex 2.728222e+001 1.349552e+001 4.399139e+000
+      vertex 2.728222e+001 1.349552e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -9.659258e-001 -2.588190e-001 1.414640e-015
+    outer loop
+      vertex 2.728222e+001 1.349552e+001 0.000000e+000
+      vertex 2.728222e+001 1.349552e+001 4.399139e+000
+      vertex 2.716943e+001 1.391645e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -9.659258e-001 -2.588190e-001 -3.545305e-016
+    outer loop
+      vertex 2.716943e+001 1.391645e+001 0.000000e+000
+      vertex 2.728222e+001 1.349552e+001 4.399139e+000
+      vertex 2.716943e+001 1.391645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 -4.226183e-001 -7.585588e-016
+    outer loop
+      vertex 2.698526e+001 1.431140e+001 0.000000e+000
+      vertex 2.716943e+001 1.391645e+001 4.399139e+000
+      vertex 2.698526e+001 1.431140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 -5.735764e-001 -2.635182e-016
+    outer loop
+      vertex 2.673531e+001 1.466837e+001 0.000000e+000
+      vertex 2.698526e+001 1.431140e+001 4.399139e+000
+      vertex 2.673531e+001 1.466837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 -7.071068e-001 -5.710544e-016
+    outer loop
+      vertex 2.642717e+001 1.497651e+001 0.000000e+000
+      vertex 2.673531e+001 1.466837e+001 4.399139e+000
+      vertex 2.642717e+001 1.497651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 -8.191520e-001 -8.612394e-016
+    outer loop
+      vertex 2.607020e+001 1.522646e+001 0.000000e+000
+      vertex 2.642717e+001 1.497651e+001 4.399139e+000
+      vertex 2.607020e+001 1.522646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 -9.063078e-001 -4.853241e-016
+    outer loop
+      vertex 2.567525e+001 1.541063e+001 0.000000e+000
+      vertex 2.607020e+001 1.522646e+001 4.399139e+000
+      vertex 2.567525e+001 1.541063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 -9.659258e-001 -5.300545e-016
+    outer loop
+      vertex 2.525432e+001 1.552342e+001 0.000000e+000
+      vertex 2.567525e+001 1.541063e+001 4.399139e+000
+      vertex 2.525432e+001 1.552342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 -9.961947e-001 -7.038636e-016
+    outer loop
+      vertex 2.482020e+001 1.556140e+001 0.000000e+000
+      vertex 2.525432e+001 1.552342e+001 4.399139e+000
+      vertex 2.482020e+001 1.556140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 -9.961947e-001 -5.029161e-016
+    outer loop
+      vertex 2.438608e+001 1.552342e+001 0.000000e+000
+      vertex 2.482020e+001 1.556140e+001 4.399139e+000
+      vertex 2.438608e+001 1.552342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 -9.659258e-001 -6.400577e-016
+    outer loop
+      vertex 2.396515e+001 1.541063e+001 0.000000e+000
+      vertex 2.438608e+001 1.552342e+001 4.399139e+000
+      vertex 2.396515e+001 1.541063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 -9.063078e-001 -5.879071e-016
+    outer loop
+      vertex 2.357020e+001 1.522646e+001 0.000000e+000
+      vertex 2.396515e+001 1.541063e+001 4.399139e+000
+      vertex 2.357020e+001 1.522646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 -8.191520e-001 -5.942889e-016
+    outer loop
+      vertex 2.321323e+001 1.497651e+001 0.000000e+000
+      vertex 2.357020e+001 1.522646e+001 4.399139e+000
+      vertex 2.321323e+001 1.497651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 -7.071068e-001 -5.710544e-016
+    outer loop
+      vertex 2.290509e+001 1.466837e+001 0.000000e+000
+      vertex 2.321323e+001 1.497651e+001 4.399139e+000
+      vertex 2.290509e+001 1.466837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 -5.735764e-001 -8.612394e-016
+    outer loop
+      vertex 2.265514e+001 1.431140e+001 0.000000e+000
+      vertex 2.290509e+001 1.466837e+001 4.399139e+000
+      vertex 2.265514e+001 1.431140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 -4.226183e-001 -3.146724e-016
+    outer loop
+      vertex 2.247097e+001 1.391645e+001 0.000000e+000
+      vertex 2.265514e+001 1.431140e+001 4.399139e+000
+      vertex 2.247097e+001 1.391645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 -2.588190e-001 -5.300545e-016
+    outer loop
+      vertex 2.235818e+001 1.349552e+001 0.000000e+000
+      vertex 2.247097e+001 1.391645e+001 4.399139e+000
+      vertex 2.235818e+001 1.349552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 -8.715574e-002 -7.038636e-016
+    outer loop
+      vertex 2.232020e+001 1.306140e+001 0.000000e+000
+      vertex 2.235818e+001 1.349552e+001 4.399139e+000
+      vertex 2.232020e+001 1.306140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 8.715574e-002 3.519318e-017
+    outer loop
+      vertex 2.235818e+001 1.262728e+001 0.000000e+000
+      vertex 2.232020e+001 1.306140e+001 4.399139e+000
+      vertex 2.235818e+001 1.262728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 2.588190e-001 -5.710544e-016
+    outer loop
+      vertex 2.247097e+001 1.220635e+001 0.000000e+000
+      vertex 2.235818e+001 1.262728e+001 4.399139e+000
+      vertex 2.247097e+001 1.220635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 4.226183e-001 1.706518e-016
+    outer loop
+      vertex 2.265514e+001 1.181140e+001 0.000000e+000
+      vertex 2.247097e+001 1.220635e+001 4.399139e+000
+      vertex 2.265514e+001 1.181140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 5.735764e-001 2.316081e-016
+    outer loop
+      vertex 2.290509e+001 1.145443e+001 0.000000e+000
+      vertex 2.265514e+001 1.181140e+001 4.399139e+000
+      vertex 2.290509e+001 1.145443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 7.071068e-001 -2.855272e-016
+    outer loop
+      vertex 2.321323e+001 1.114629e+001 0.000000e+000
+      vertex 2.290509e+001 1.145443e+001 4.399139e+000
+      vertex 2.321323e+001 1.114629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 8.191520e-001 0.000000e+000
+    outer loop
+      vertex 2.357020e+001 1.089634e+001 0.000000e+000
+      vertex 2.321323e+001 1.114629e+001 4.399139e+000
+      vertex 2.357020e+001 1.089634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 9.063078e-001 -3.413035e-016
+    outer loop
+      vertex 2.396515e+001 1.071217e+001 0.000000e+000
+      vertex 2.357020e+001 1.089634e+001 4.399139e+000
+      vertex 2.396515e+001 1.071217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 9.659258e-001 -2.090204e-016
+    outer loop
+      vertex 2.438608e+001 1.059938e+001 0.000000e+000
+      vertex 2.396515e+001 1.071217e+001 4.399139e+000
+      vertex 2.438608e+001 1.059938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 9.961947e-001 -7.038636e-017
+    outer loop
+      vertex 2.482020e+001 1.056140e+001 0.000000e+000
+      vertex 2.438608e+001 1.059938e+001 4.399139e+000
+      vertex 2.482020e+001 1.056140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 9.961947e-001 -3.318735e-016
+    outer loop
+      vertex 2.525432e+001 1.059938e+001 0.000000e+000
+      vertex 2.482020e+001 1.056140e+001 4.399139e+000
+      vertex 2.525432e+001 1.059938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 9.659258e-001 2.090204e-016
+    outer loop
+      vertex 2.567525e+001 1.071217e+001 0.000000e+000
+      vertex 2.525432e+001 1.059938e+001 4.399139e+000
+      vertex 2.567525e+001 1.071217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 9.063078e-001 0.000000e+000
+    outer loop
+      vertex 2.607020e+001 1.089634e+001 0.000000e+000
+      vertex 2.567525e+001 1.071217e+001 4.399139e+000
+      vertex 2.607020e+001 1.089634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 8.191520e-001 1.324456e-016
+    outer loop
+      vertex 2.642717e+001 1.114629e+001 0.000000e+000
+      vertex 2.607020e+001 1.089634e+001 4.399139e+000
+      vertex 2.642717e+001 1.114629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 7.071068e-001 5.710544e-016
+    outer loop
+      vertex 2.673531e+001 1.145443e+001 0.000000e+000
+      vertex 2.642717e+001 1.114629e+001 4.399139e+000
+      vertex 2.673531e+001 1.145443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 5.735764e-001 0.000000e+000
+    outer loop
+      vertex 2.698526e+001 1.181140e+001 0.000000e+000
+      vertex 2.673531e+001 1.145443e+001 4.399139e+000
+      vertex 2.698526e+001 1.181140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 4.226183e-001 -3.413035e-016
+    outer loop
+      vertex 2.716943e+001 1.220635e+001 0.000000e+000
+      vertex 2.698526e+001 1.181140e+001 4.399139e+000
+      vertex 2.716943e+001 1.220635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.659258e-001 2.588190e-001 -1.045102e-016
+    outer loop
+      vertex 2.728222e+001 1.262728e+001 0.000000e+000
+      vertex 2.716943e+001 1.220635e+001 4.399139e+000
+      vertex 2.728222e+001 1.262728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.659258e-001 2.588190e-001 -2.063153e-016
+    outer loop
+      vertex 2.728222e+001 1.262728e+001 0.000000e+000
+      vertex 2.716943e+001 1.220635e+001 0.000000e+000
+      vertex 2.716943e+001 1.220635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 4.226183e-001 0.000000e+000
+    outer loop
+      vertex 2.716943e+001 1.220635e+001 0.000000e+000
+      vertex 2.698526e+001 1.181140e+001 0.000000e+000
+      vertex 2.698526e+001 1.181140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 5.735764e-001 6.587610e-016
+    outer loop
+      vertex 2.698526e+001 1.181140e+001 0.000000e+000
+      vertex 2.673531e+001 1.145443e+001 0.000000e+000
+      vertex 2.673531e+001 1.145443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 7.071068e-001 2.895653e-016
+    outer loop
+      vertex 2.673531e+001 1.145443e+001 0.000000e+000
+      vertex 2.642717e+001 1.114629e+001 0.000000e+000
+      vertex 2.642717e+001 1.114629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 8.191520e-001 0.000000e+000
+    outer loop
+      vertex 2.642717e+001 1.114629e+001 0.000000e+000
+      vertex 2.607020e+001 1.089634e+001 0.000000e+000
+      vertex 2.607020e+001 1.089634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 9.063078e-001 3.402392e-016
+    outer loop
+      vertex 2.607020e+001 1.089634e+001 0.000000e+000
+      vertex 2.567525e+001 1.071217e+001 0.000000e+000
+      vertex 2.567525e+001 1.071217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 9.659258e-001 -1.845979e-016
+    outer loop
+      vertex 2.567525e+001 1.071217e+001 0.000000e+000
+      vertex 2.525432e+001 1.059938e+001 0.000000e+000
+      vertex 2.525432e+001 1.059938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 9.961947e-001 7.058154e-017
+    outer loop
+      vertex 2.525432e+001 1.059938e+001 0.000000e+000
+      vertex 2.482020e+001 1.056140e+001 0.000000e+000
+      vertex 2.482020e+001 1.056140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 9.961947e-001 -7.058154e-017
+    outer loop
+      vertex 2.482020e+001 1.056140e+001 0.000000e+000
+      vertex 2.438608e+001 1.059938e+001 0.000000e+000
+      vertex 2.438608e+001 1.059938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 9.659258e-001 -2.099348e-016
+    outer loop
+      vertex 2.438608e+001 1.059938e+001 0.000000e+000
+      vertex 2.396515e+001 1.071217e+001 0.000000e+000
+      vertex 2.396515e+001 1.071217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 9.063078e-001 0.000000e+000
+    outer loop
+      vertex 2.396515e+001 1.071217e+001 0.000000e+000
+      vertex 2.357020e+001 1.089634e+001 0.000000e+000
+      vertex 2.357020e+001 1.089634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 8.191520e-001 -1.303044e-016
+    outer loop
+      vertex 2.357020e+001 1.089634e+001 0.000000e+000
+      vertex 2.321323e+001 1.114629e+001 0.000000e+000
+      vertex 2.321323e+001 1.114629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 7.071068e-001 2.895653e-016
+    outer loop
+      vertex 2.321323e+001 1.114629e+001 0.000000e+000
+      vertex 2.290509e+001 1.145443e+001 0.000000e+000
+      vertex 2.290509e+001 1.145443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 5.735764e-001 2.316522e-016
+    outer loop
+      vertex 2.290509e+001 1.145443e+001 0.000000e+000
+      vertex 2.265514e+001 1.181140e+001 0.000000e+000
+      vertex 2.265514e+001 1.181140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 4.226183e-001 -3.909131e-016
+    outer loop
+      vertex 2.265514e+001 1.181140e+001 0.000000e+000
+      vertex 2.247097e+001 1.220635e+001 0.000000e+000
+      vertex 2.247097e+001 1.220635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 2.588190e-001 1.049674e-016
+    outer loop
+      vertex 2.247097e+001 1.220635e+001 0.000000e+000
+      vertex 2.235818e+001 1.262728e+001 0.000000e+000
+      vertex 2.235818e+001 1.262728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 8.715574e-002 7.040056e-016
+    outer loop
+      vertex 2.235818e+001 1.262728e+001 0.000000e+000
+      vertex 2.232020e+001 1.306140e+001 0.000000e+000
+      vertex 2.232020e+001 1.306140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 -8.715574e-002 9.048915e-016
+    outer loop
+      vertex 2.232020e+001 1.306140e+001 0.000000e+000
+      vertex 2.235818e+001 1.349552e+001 0.000000e+000
+      vertex 2.235818e+001 1.349552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 -2.588190e-001 1.134605e-015
+    outer loop
+      vertex 2.247097e+001 1.391645e+001 0.000000e+000
+      vertex 2.247097e+001 1.391645e+001 4.399139e+000
+      vertex 2.235818e+001 1.349552e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 -4.226183e-001 5.863697e-016
+    outer loop
+      vertex 2.247097e+001 1.391645e+001 0.000000e+000
+      vertex 2.265514e+001 1.431140e+001 0.000000e+000
+      vertex 2.265514e+001 1.431140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 -5.735764e-001 9.266089e-016
+    outer loop
+      vertex 2.265514e+001 1.431140e+001 0.000000e+000
+      vertex 2.290509e+001 1.466837e+001 0.000000e+000
+      vertex 2.290509e+001 1.466837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 -7.071068e-001 8.542176e-016
+    outer loop
+      vertex 2.290509e+001 1.466837e+001 0.000000e+000
+      vertex 2.321323e+001 1.497651e+001 0.000000e+000
+      vertex 2.321323e+001 1.497651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 -8.191520e-001 8.612394e-016
+    outer loop
+      vertex 2.357020e+001 1.522646e+001 0.000000e+000
+      vertex 2.357020e+001 1.522646e+001 4.399139e+000
+      vertex 2.321323e+001 1.497651e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 -9.063078e-001 8.542176e-016
+    outer loop
+      vertex 2.357020e+001 1.522646e+001 0.000000e+000
+      vertex 2.396515e+001 1.541063e+001 0.000000e+000
+      vertex 2.396515e+001 1.541063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 -9.659258e-001 9.193698e-016
+    outer loop
+      vertex 2.396515e+001 1.541063e+001 0.000000e+000
+      vertex 2.438608e+001 1.552342e+001 0.000000e+000
+      vertex 2.438608e+001 1.552342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 -9.961947e-001 7.040056e-016
+    outer loop
+      vertex 2.438608e+001 1.552342e+001 0.000000e+000
+      vertex 2.482020e+001 1.556140e+001 0.000000e+000
+      vertex 2.482020e+001 1.556140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 -9.961947e-001 9.051759e-016
+    outer loop
+      vertex 2.525432e+001 1.552342e+001 0.000000e+000
+      vertex 2.525432e+001 1.552342e+001 4.399139e+000
+      vertex 2.482020e+001 1.556140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 -9.659258e-001 1.031576e-015
+    outer loop
+      vertex 2.525432e+001 1.552342e+001 0.000000e+000
+      vertex 2.567525e+001 1.541063e+001 0.000000e+000
+      vertex 2.567525e+001 1.541063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 -9.063078e-001 5.863697e-016
+    outer loop
+      vertex 2.567525e+001 1.541063e+001 0.000000e+000
+      vertex 2.607020e+001 1.522646e+001 0.000000e+000
+      vertex 2.607020e+001 1.522646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 -8.191520e-001 9.266089e-016
+    outer loop
+      vertex 2.607020e+001 1.522646e+001 0.000000e+000
+      vertex 2.642717e+001 1.497651e+001 0.000000e+000
+      vertex 2.642717e+001 1.497651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 -7.071068e-001 1.143783e-015
+    outer loop
+      vertex 2.642717e+001 1.497651e+001 0.000000e+000
+      vertex 2.673531e+001 1.466837e+001 0.000000e+000
+      vertex 2.673531e+001 1.466837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 -5.735764e-001 6.298045e-016
+    outer loop
+      vertex 2.673531e+001 1.466837e+001 0.000000e+000
+      vertex 2.698526e+001 1.431140e+001 0.000000e+000
+      vertex 2.698526e+001 1.431140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 -4.226183e-001 1.049674e-015
+    outer loop
+      vertex 2.698526e+001 1.431140e+001 0.000000e+000
+      vertex 2.716943e+001 1.391645e+001 0.000000e+000
+      vertex 2.716943e+001 1.391645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 4.320200e+000 1.706140e+001 5.000000e+000
+      vertex 4.373373e+000 1.645363e+001 5.000000e+000
+      vertex 5.320200e+000 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 5.358181e+000 1.749552e+001 4.399139e+000
+      vertex 4.320200e+000 1.706140e+001 5.000000e+000
+      vertex 5.320200e+000 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 5.358181e+000 1.749552e+001 4.399139e+000
+      vertex 4.373373e+000 1.766917e+001 5.000000e+000
+      vertex 4.320200e+000 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 5.358181e+000 1.749552e+001 4.399139e+000
+      vertex 4.531276e+000 1.825847e+001 5.000000e+000
+      vertex 4.373373e+000 1.766917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 5.358181e+000 1.749552e+001 4.399139e+000
+      vertex 5.470968e+000 1.791645e+001 4.399139e+000
+      vertex 4.531276e+000 1.825847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 4.531276e+000 1.825847e+001 5.000000e+000
+      vertex 5.470968e+000 1.791645e+001 4.399139e+000
+      vertex 4.789111e+000 1.881140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 4.789111e+000 1.881140e+001 5.000000e+000
+      vertex 5.470968e+000 1.791645e+001 4.399139e+000
+      vertex 5.655136e+000 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 5.139044e+000 1.931116e+001 5.000000e+000
+      vertex 5.655136e+000 1.831140e+001 4.399139e+000
+      vertex 5.905089e+000 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 5.570443e+000 1.974256e+001 5.000000e+000
+      vertex 5.905089e+000 1.866837e+001 4.399139e+000
+      vertex 6.213231e+000 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 6.570200e+000 1.922646e+001 4.399139e+000
+      vertex 5.570443e+000 1.974256e+001 5.000000e+000
+      vertex 6.213231e+000 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 6.570200e+000 1.922646e+001 4.399139e+000
+      vertex 6.070200e+000 2.009249e+001 5.000000e+000
+      vertex 5.570443e+000 1.974256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 6.570200e+000 1.922646e+001 4.399139e+000
+      vertex 6.965150e+000 1.941063e+001 4.399139e+000
+      vertex 6.070200e+000 2.009249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 6.070200e+000 2.009249e+001 5.000000e+000
+      vertex 6.965150e+000 1.941063e+001 4.399139e+000
+      vertex 6.623129e+000 2.035032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 6.623129e+000 2.035032e+001 5.000000e+000
+      vertex 6.965150e+000 1.941063e+001 4.399139e+000
+      vertex 7.212431e+000 2.050823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 7.212431e+000 2.050823e+001 5.000000e+000
+      vertex 6.965150e+000 1.941063e+001 4.399139e+000
+      vertex 7.386080e+000 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 7.820200e+000 2.056140e+001 5.000000e+000
+      vertex 7.386080e+000 1.952342e+001 4.399139e+000
+      vertex 7.820200e+000 1.956140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 8.427969e+000 2.050823e+001 5.000000e+000
+      vertex 7.820200e+000 1.956140e+001 4.399139e+000
+      vertex 8.254320e+000 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 9.017271e+000 2.035032e+001 5.000000e+000
+      vertex 8.254320e+000 1.952342e+001 4.399139e+000
+      vertex 8.675250e+000 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 9.570200e+000 2.009249e+001 5.000000e+000
+      vertex 8.675250e+000 1.941063e+001 4.399139e+000
+      vertex 9.070200e+000 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 1.006996e+001 1.974256e+001 5.000000e+000
+      vertex 9.070200e+000 1.922646e+001 4.399139e+000
+      vertex 9.427169e+000 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 9.735311e+000 1.866837e+001 4.399139e+000
+      vertex 1.006996e+001 1.974256e+001 5.000000e+000
+      vertex 9.427169e+000 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 9.735311e+000 1.866837e+001 4.399139e+000
+      vertex 1.050136e+001 1.931116e+001 5.000000e+000
+      vertex 1.006996e+001 1.974256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 9.735311e+000 1.866837e+001 4.399139e+000
+      vertex 9.985264e+000 1.831140e+001 4.399139e+000
+      vertex 1.050136e+001 1.931116e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 1.050136e+001 1.931116e+001 5.000000e+000
+      vertex 9.985264e+000 1.831140e+001 4.399139e+000
+      vertex 1.085129e+001 1.881140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 1.085129e+001 1.881140e+001 5.000000e+000
+      vertex 9.985264e+000 1.831140e+001 4.399139e+000
+      vertex 1.016943e+001 1.791645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 1.110912e+001 1.825847e+001 5.000000e+000
+      vertex 1.016943e+001 1.791645e+001 4.399139e+000
+      vertex 1.028222e+001 1.749552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 1.126703e+001 1.766917e+001 5.000000e+000
+      vertex 1.028222e+001 1.749552e+001 4.399139e+000
+      vertex 1.132020e+001 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 -1.336752e-001 8.562984e-001
+    outer loop
+      vertex 1.126703e+001 1.766917e+001 5.000000e+000
+      vertex 1.110912e+001 1.825847e+001 5.000000e+000
+      vertex 1.028222e+001 1.749552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 4.531276e+000 1.586433e+001 5.000000e+000
+      vertex 5.470968e+000 1.620635e+001 4.399139e+000
+      vertex 4.373373e+000 1.645363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 4.531276e+000 1.586433e+001 5.000000e+000
+      vertex 5.655136e+000 1.581140e+001 4.399139e+000
+      vertex 5.470968e+000 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 4.531276e+000 1.586433e+001 5.000000e+000
+      vertex 4.789111e+000 1.531140e+001 5.000000e+000
+      vertex 5.655136e+000 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 5.655136e+000 1.581140e+001 4.399139e+000
+      vertex 4.789111e+000 1.531140e+001 5.000000e+000
+      vertex 5.905089e+000 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 5.905089e+000 1.545443e+001 4.399139e+000
+      vertex 4.789111e+000 1.531140e+001 5.000000e+000
+      vertex 5.139044e+000 1.481164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 6.213231e+000 1.514629e+001 4.399139e+000
+      vertex 5.139044e+000 1.481164e+001 5.000000e+000
+      vertex 5.570443e+000 1.438024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 6.570200e+000 1.489634e+001 4.399139e+000
+      vertex 5.570443e+000 1.438024e+001 5.000000e+000
+      vertex 6.070200e+000 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 6.623129e+000 1.377248e+001 5.000000e+000
+      vertex 6.570200e+000 1.489634e+001 4.399139e+000
+      vertex 6.070200e+000 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 6.623129e+000 1.377248e+001 5.000000e+000
+      vertex 6.965150e+000 1.471217e+001 4.399139e+000
+      vertex 6.570200e+000 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 6.623129e+000 1.377248e+001 5.000000e+000
+      vertex 7.386080e+000 1.459938e+001 4.399139e+000
+      vertex 6.965150e+000 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 6.623129e+000 1.377248e+001 5.000000e+000
+      vertex 7.212431e+000 1.361457e+001 5.000000e+000
+      vertex 7.386080e+000 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 7.386080e+000 1.459938e+001 4.399139e+000
+      vertex 7.212431e+000 1.361457e+001 5.000000e+000
+      vertex 7.820200e+000 1.356140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 7.820200e+000 1.456140e+001 4.399139e+000
+      vertex 7.820200e+000 1.356140e+001 5.000000e+000
+      vertex 8.427969e+000 1.361457e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 8.254320e+000 1.459938e+001 4.399139e+000
+      vertex 8.427969e+000 1.361457e+001 5.000000e+000
+      vertex 9.017271e+000 1.377248e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 8.675250e+000 1.471217e+001 4.399139e+000
+      vertex 9.017271e+000 1.377248e+001 5.000000e+000
+      vertex 9.570200e+000 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 9.070200e+000 1.489634e+001 4.399139e+000
+      vertex 9.570200e+000 1.403031e+001 5.000000e+000
+      vertex 1.006996e+001 1.438024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 9.427169e+000 1.514629e+001 4.399139e+000
+      vertex 1.006996e+001 1.438024e+001 5.000000e+000
+      vertex 1.050136e+001 1.481164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 9.735311e+000 1.545443e+001 4.399139e+000
+      vertex 1.050136e+001 1.481164e+001 5.000000e+000
+      vertex 9.985264e+000 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 9.735311e+000 1.545443e+001 4.399139e+000
+      vertex 9.427169e+000 1.514629e+001 4.399139e+000
+      vertex 1.050136e+001 1.481164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.230768e-001 2.962415e-001 8.562984e-001
+    outer loop
+      vertex 1.050136e+001 1.481164e+001 5.000000e+000
+      vertex 1.085129e+001 1.531140e+001 5.000000e+000
+      vertex 9.985264e+000 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 9.985264e+000 1.581140e+001 4.399139e+000
+      vertex 1.085129e+001 1.531140e+001 5.000000e+000
+      vertex 1.016943e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 2.182745e-001 8.562984e-001
+    outer loop
+      vertex 1.016943e+001 1.620635e+001 4.399139e+000
+      vertex 1.085129e+001 1.531140e+001 5.000000e+000
+      vertex 1.110912e+001 1.586433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 1.126703e+001 1.645363e+001 5.000000e+000
+      vertex 1.016943e+001 1.620635e+001 4.399139e+000
+      vertex 1.110912e+001 1.586433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 1.126703e+001 1.645363e+001 5.000000e+000
+      vertex 1.028222e+001 1.662728e+001 4.399139e+000
+      vertex 1.016943e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 1.126703e+001 1.645363e+001 5.000000e+000
+      vertex 1.132020e+001 1.706140e+001 5.000000e+000
+      vertex 1.028222e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 1.028222e+001 1.662728e+001 4.399139e+000
+      vertex 1.132020e+001 1.706140e+001 5.000000e+000
+      vertex 1.032020e+001 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.145160e-001 -4.501432e-002 8.562984e-001
+    outer loop
+      vertex 1.032020e+001 1.706140e+001 4.399139e+000
+      vertex 1.132020e+001 1.706140e+001 5.000000e+000
+      vertex 1.028222e+001 1.749552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.680911e-001 -2.182745e-001 8.562984e-001
+    outer loop
+      vertex 1.110912e+001 1.825847e+001 5.000000e+000
+      vertex 1.085129e+001 1.881140e+001 5.000000e+000
+      vertex 1.016943e+001 1.791645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 -4.230768e-001 8.562984e-001
+    outer loop
+      vertex 9.070200e+000 1.922646e+001 4.399139e+000
+      vertex 1.006996e+001 1.974256e+001 5.000000e+000
+      vertex 9.570200e+000 2.009249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 -4.680911e-001 8.562984e-001
+    outer loop
+      vertex 9.017271e+000 2.035032e+001 5.000000e+000
+      vertex 8.675250e+000 1.941063e+001 4.399139e+000
+      vertex 9.570200e+000 2.009249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 -4.988827e-001 8.562984e-001
+    outer loop
+      vertex 9.017271e+000 2.035032e+001 5.000000e+000
+      vertex 8.427969e+000 2.050823e+001 5.000000e+000
+      vertex 8.254320e+000 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 8.427969e+000 2.050823e+001 5.000000e+000
+      vertex 7.820200e+000 2.056140e+001 5.000000e+000
+      vertex 7.820200e+000 1.956140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 -5.145160e-001 8.562984e-001
+    outer loop
+      vertex 7.386080e+000 1.952342e+001 4.399139e+000
+      vertex 7.820200e+000 2.056140e+001 5.000000e+000
+      vertex 7.212431e+000 2.050823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 -3.652075e-001 8.562984e-001
+    outer loop
+      vertex 5.139044e+000 1.931116e+001 5.000000e+000
+      vertex 5.905089e+000 1.866837e+001 4.399139e+000
+      vertex 5.570443e+000 1.974256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.230768e-001 -2.962415e-001 8.562984e-001
+    outer loop
+      vertex 5.139044e+000 1.931116e+001 5.000000e+000
+      vertex 4.789111e+000 1.881140e+001 5.000000e+000
+      vertex 5.655136e+000 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 1.006996e+001 1.438024e+001 5.000000e+000
+      vertex 9.427169e+000 1.514629e+001 4.399139e+000
+      vertex 9.070200e+000 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.182745e-001 4.680911e-001 8.562984e-001
+    outer loop
+      vertex 9.070200e+000 1.489634e+001 4.399139e+000
+      vertex 8.675250e+000 1.471217e+001 4.399139e+000
+      vertex 9.570200e+000 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.336752e-001 4.988827e-001 8.562984e-001
+    outer loop
+      vertex 8.675250e+000 1.471217e+001 4.399139e+000
+      vertex 8.254320e+000 1.459938e+001 4.399139e+000
+      vertex 9.017271e+000 1.377248e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 8.254320e+000 1.459938e+001 4.399139e+000
+      vertex 7.820200e+000 1.456140e+001 4.399139e+000
+      vertex 8.427969e+000 1.361457e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.501432e-002 5.145160e-001 8.562984e-001
+    outer loop
+      vertex 7.820200e+000 1.456140e+001 4.399139e+000
+      vertex 7.386080e+000 1.459938e+001 4.399139e+000
+      vertex 7.820200e+000 1.356140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.962415e-001 4.230768e-001 8.562984e-001
+    outer loop
+      vertex 6.570200e+000 1.489634e+001 4.399139e+000
+      vertex 6.213231e+000 1.514629e+001 4.399139e+000
+      vertex 5.570443e+000 1.438024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 3.652075e-001 3.652075e-001 8.562984e-001
+    outer loop
+      vertex 5.139044e+000 1.481164e+001 5.000000e+000
+      vertex 6.213231e+000 1.514629e+001 4.399139e+000
+      vertex 5.905089e+000 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.988827e-001 1.336752e-001 8.562984e-001
+    outer loop
+      vertex 5.358181e+000 1.662728e+001 4.399139e+000
+      vertex 4.373373e+000 1.645363e+001 5.000000e+000
+      vertex 5.470968e+000 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.145160e-001 4.501432e-002 8.562984e-001
+    outer loop
+      vertex 5.358181e+000 1.662728e+001 4.399139e+000
+      vertex 5.320200e+000 1.706140e+001 4.399139e+000
+      vertex 4.373373e+000 1.645363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 8.715574e-002 0.000000e+000
+    outer loop
+      vertex 1.032020e+001 1.706140e+001 -8.881784e-016
+      vertex 1.028222e+001 1.662728e+001 -8.881784e-016
+      vertex 1.028222e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 8.715574e-002 0.000000e+000
+    outer loop
+      vertex 1.032020e+001 1.706140e+001 4.399139e+000
+      vertex 1.032020e+001 1.706140e+001 -8.881784e-016
+      vertex 1.028222e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.961947e-001 -8.715574e-002 0.000000e+000
+    outer loop
+      vertex 1.032020e+001 1.706140e+001 4.399139e+000
+      vertex 1.028222e+001 1.749552e+001 -8.881784e-016
+      vertex 1.032020e+001 1.706140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -9.961947e-001 -8.715574e-002 -9.067013e-016
+    outer loop
+      vertex 1.032020e+001 1.706140e+001 4.399139e+000
+      vertex 1.028222e+001 1.749552e+001 4.399139e+000
+      vertex 1.028222e+001 1.749552e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -9.659258e-001 -2.588190e-001 5.300545e-016
+    outer loop
+      vertex 1.028222e+001 1.749552e+001 -8.881784e-016
+      vertex 1.028222e+001 1.749552e+001 4.399139e+000
+      vertex 1.016943e+001 1.791645e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -9.659258e-001 -2.588190e-001 -6.400577e-016
+    outer loop
+      vertex 1.016943e+001 1.791645e+001 -8.881784e-016
+      vertex 1.028222e+001 1.749552e+001 4.399139e+000
+      vertex 1.016943e+001 1.791645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 -4.226183e-001 -5.879071e-016
+    outer loop
+      vertex 9.985264e+000 1.831140e+001 -8.881784e-016
+      vertex 1.016943e+001 1.791645e+001 4.399139e+000
+      vertex 9.985264e+000 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 -5.735764e-001 -5.942889e-016
+    outer loop
+      vertex 9.735311e+000 1.866837e+001 -8.881784e-016
+      vertex 9.985264e+000 1.831140e+001 4.399139e+000
+      vertex 9.735311e+000 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 -7.071068e-001 -1.142109e-015
+    outer loop
+      vertex 9.427169e+000 1.897651e+001 -8.881784e-016
+      vertex 9.735311e+000 1.866837e+001 4.399139e+000
+      vertex 9.427169e+000 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 -8.191520e-001 -6.296313e-016
+    outer loop
+      vertex 9.070200e+000 1.922646e+001 -8.881784e-016
+      vertex 9.427169e+000 1.897651e+001 4.399139e+000
+      vertex 9.070200e+000 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 -9.063078e-001 -1.046600e-015
+    outer loop
+      vertex 8.675250e+000 1.941063e+001 -8.881784e-016
+      vertex 9.070200e+000 1.922646e+001 4.399139e+000
+      vertex 8.675250e+000 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 -9.659258e-001 -1.310129e-015
+    outer loop
+      vertex 8.254320e+000 1.952342e+001 -8.881784e-016
+      vertex 8.675250e+000 1.941063e+001 4.399139e+000
+      vertex 8.254320e+000 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 -9.961947e-001 -7.038636e-016
+    outer loop
+      vertex 7.820200e+000 1.956140e+001 -8.881784e-016
+      vertex 8.254320e+000 1.952342e+001 4.399139e+000
+      vertex 7.820200e+000 1.956140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 -9.961947e-001 -9.051759e-016
+    outer loop
+      vertex 7.386080e+000 1.952342e+001 -8.881784e-016
+      vertex 7.820200e+000 1.956140e+001 4.399139e+000
+      vertex 7.386080e+000 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 -9.659258e-001 -3.545305e-016
+    outer loop
+      vertex 6.965150e+000 1.941063e+001 -8.881784e-016
+      vertex 7.386080e+000 1.952342e+001 4.399139e+000
+      vertex 6.965150e+000 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 -9.063078e-001 -7.585588e-016
+    outer loop
+      vertex 6.570200e+000 1.922646e+001 -8.881784e-016
+      vertex 6.965150e+000 1.941063e+001 4.399139e+000
+      vertex 6.570200e+000 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 -8.191520e-001 -1.040864e-015
+    outer loop
+      vertex 6.213231e+000 1.897651e+001 -8.881784e-016
+      vertex 6.570200e+000 1.922646e+001 4.399139e+000
+      vertex 6.213231e+000 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 -7.071068e-001 -8.565816e-016
+    outer loop
+      vertex 5.905089e+000 1.866837e+001 -8.881784e-016
+      vertex 6.213231e+000 1.897651e+001 4.399139e+000
+      vertex 5.905089e+000 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 -5.735764e-001 -3.980232e-016
+    outer loop
+      vertex 5.655136e+000 1.831140e+001 -8.881784e-016
+      vertex 5.905089e+000 1.866837e+001 4.399139e+000
+      vertex 5.655136e+000 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 -4.226183e-001 -8.512880e-016
+    outer loop
+      vertex 5.470968e+000 1.791645e+001 -8.881784e-016
+      vertex 5.655136e+000 1.831140e+001 4.399139e+000
+      vertex 5.470968e+000 1.791645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 -2.588190e-001 -7.250733e-016
+    outer loop
+      vertex 5.358181e+000 1.749552e+001 -8.881784e-016
+      vertex 5.470968e+000 1.791645e+001 4.399139e+000
+      vertex 5.358181e+000 1.749552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 -8.715574e-002 -7.038636e-016
+    outer loop
+      vertex 5.320200e+000 1.706140e+001 -8.881784e-016
+      vertex 5.358181e+000 1.749552e+001 4.399139e+000
+      vertex 5.320200e+000 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 8.715574e-002 7.038636e-017
+    outer loop
+      vertex 5.358181e+000 1.662728e+001 -8.881784e-016
+      vertex 5.320200e+000 1.706140e+001 4.399139e+000
+      vertex 5.358181e+000 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 2.588190e-001 -3.900374e-016
+    outer loop
+      vertex 5.470968e+000 1.620635e+001 -8.881784e-016
+      vertex 5.358181e+000 1.662728e+001 4.399139e+000
+      vertex 5.470968e+000 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 4.226183e-001 -1.829819e-016
+    outer loop
+      vertex 5.655136e+000 1.581140e+001 -8.881784e-016
+      vertex 5.470968e+000 1.620635e+001 4.399139e+000
+      vertex 5.655136e+000 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 5.735764e-001 6.622278e-017
+    outer loop
+      vertex 5.905089e+000 1.545443e+001 -8.881784e-016
+      vertex 5.655136e+000 1.581140e+001 4.399139e+000
+      vertex 5.905089e+000 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 7.071068e-001 7.071068e-001 -6.583952e-030
+    outer loop
+      vertex 6.213231e+000 1.514629e+001 -8.881784e-016
+      vertex 5.905089e+000 1.545443e+001 4.399139e+000
+      vertex 6.213231e+000 1.514629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 8.191520e-001 2.149666e-016
+    outer loop
+      vertex 6.570200e+000 1.489634e+001 -8.881784e-016
+      vertex 6.213231e+000 1.514629e+001 4.399139e+000
+      vertex 6.570200e+000 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 9.063078e-001 -2.559776e-016
+    outer loop
+      vertex 6.965150e+000 1.471217e+001 -8.881784e-016
+      vertex 6.570200e+000 1.489634e+001 4.399139e+000
+      vertex 6.965150e+000 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 9.659258e-001 -1.567653e-016
+    outer loop
+      vertex 7.386080e+000 1.459938e+001 -8.881784e-016
+      vertex 6.965150e+000 1.471217e+001 4.399139e+000
+      vertex 7.386080e+000 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 9.961947e-001 -1.759659e-017
+    outer loop
+      vertex 7.820200e+000 1.456140e+001 -8.881784e-016
+      vertex 7.386080e+000 1.459938e+001 4.399139e+000
+      vertex 7.820200e+000 1.456140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 9.961947e-001 7.038636e-017
+    outer loop
+      vertex 8.254320e+000 1.459938e+001 -8.881784e-016
+      vertex 7.820200e+000 1.456140e+001 4.399139e+000
+      vertex 8.254320e+000 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 9.659258e-001 -2.855272e-016
+    outer loop
+      vertex 8.675250e+000 1.471217e+001 -8.881784e-016
+      vertex 8.254320e+000 1.459938e+001 4.399139e+000
+      vertex 8.675250e+000 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 9.063078e-001 -1.953121e-016
+    outer loop
+      vertex 9.070200e+000 1.489634e+001 -8.881784e-016
+      vertex 8.675250e+000 1.471217e+001 4.399139e+000
+      vertex 9.070200e+000 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 8.191520e-001 -9.916256e-017
+    outer loop
+      vertex 9.427169e+000 1.514629e+001 -8.881784e-016
+      vertex 9.070200e+000 1.489634e+001 4.399139e+000
+      vertex 9.427169e+000 1.514629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 7.071068e-001 0.000000e+000
+    outer loop
+      vertex 9.735311e+000 1.545443e+001 -8.881784e-016
+      vertex 9.427169e+000 1.514629e+001 4.399139e+000
+      vertex 9.735311e+000 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 5.735764e-001 0.000000e+000
+    outer loop
+      vertex 9.985264e+000 1.581140e+001 -8.881784e-016
+      vertex 9.735311e+000 1.545443e+001 4.399139e+000
+      vertex 9.985264e+000 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 4.226183e-001 3.659639e-016
+    outer loop
+      vertex 1.016943e+001 1.620635e+001 -8.881784e-016
+      vertex 9.985264e+000 1.581140e+001 4.399139e+000
+      vertex 1.016943e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.659258e-001 2.588190e-001 0.000000e+000
+    outer loop
+      vertex 1.028222e+001 1.662728e+001 -8.881784e-016
+      vertex 1.016943e+001 1.620635e+001 4.399139e+000
+      vertex 1.028222e+001 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.659258e-001 2.588190e-001 3.909131e-016
+    outer loop
+      vertex 1.028222e+001 1.662728e+001 -8.881784e-016
+      vertex 1.016943e+001 1.620635e+001 -8.881784e-016
+      vertex 1.016943e+001 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 4.226183e-001 0.000000e+000
+    outer loop
+      vertex 1.016943e+001 1.620635e+001 -8.881784e-016
+      vertex 9.985264e+000 1.581140e+001 -8.881784e-016
+      vertex 9.985264e+000 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 5.735764e-001 0.000000e+000
+    outer loop
+      vertex 9.985264e+000 1.581140e+001 -8.881784e-016
+      vertex 9.735311e+000 1.545443e+001 -8.881784e-016
+      vertex 9.735311e+000 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 7.071068e-001 0.000000e+000
+    outer loop
+      vertex 9.735311e+000 1.545443e+001 -8.881784e-016
+      vertex 9.427169e+000 1.514629e+001 -8.881784e-016
+      vertex 9.427169e+000 1.514629e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 8.191520e-001 -9.410872e-017
+    outer loop
+      vertex 9.427169e+000 1.514629e+001 -8.881784e-016
+      vertex 9.070200e+000 1.489634e+001 -8.881784e-016
+      vertex 9.070200e+000 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 9.063078e-001 -1.954566e-016
+    outer loop
+      vertex 9.070200e+000 1.489634e+001 -8.881784e-016
+      vertex 8.675250e+000 1.471217e+001 -8.881784e-016
+      vertex 8.675250e+000 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 9.659258e-001 2.099348e-016
+    outer loop
+      vertex 8.675250e+000 1.471217e+001 -8.881784e-016
+      vertex 8.254320e+000 1.459938e+001 -8.881784e-016
+      vertex 8.254320e+000 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 9.961947e-001 1.809783e-017
+    outer loop
+      vertex 8.254320e+000 1.459938e+001 -8.881784e-016
+      vertex 7.820200e+000 1.456140e+001 -8.881784e-016
+      vertex 7.820200e+000 1.456140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.715574e-002 9.961947e-001 -5.429349e-017
+    outer loop
+      vertex 7.820200e+000 1.456140e+001 -8.881784e-016
+      vertex 7.386080e+000 1.459938e+001 -8.881784e-016
+      vertex 7.386080e+000 1.459938e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 9.659258e-001 -1.592609e-016
+    outer loop
+      vertex 7.386080e+000 1.459938e+001 -8.881784e-016
+      vertex 6.965150e+000 1.471217e+001 -8.881784e-016
+      vertex 6.965150e+000 1.471217e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 9.063078e-001 2.750870e-016
+    outer loop
+      vertex 6.965150e+000 1.471217e+001 -8.881784e-016
+      vertex 6.570200e+000 1.489634e+001 -8.881784e-016
+      vertex 6.570200e+000 1.489634e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 8.191520e-001 9.916256e-017
+    outer loop
+      vertex 6.213231e+000 1.514629e+001 -8.881784e-016
+      vertex 6.213231e+000 1.514629e+001 4.399139e+000
+      vertex 6.570200e+000 1.489634e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.071068e-001 7.071068e-001 1.447826e-016
+    outer loop
+      vertex 6.213231e+000 1.514629e+001 -8.881784e-016
+      vertex 5.905089e+000 1.545443e+001 -8.881784e-016
+      vertex 5.905089e+000 1.545443e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 5.735764e-001 -1.592609e-016
+    outer loop
+      vertex 5.905089e+000 1.545443e+001 -8.881784e-016
+      vertex 5.655136e+000 1.581140e+001 -8.881784e-016
+      vertex 5.655136e+000 1.581140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.063078e-001 4.226183e-001 -3.691957e-016
+    outer loop
+      vertex 5.655136e+000 1.581140e+001 -8.881784e-016
+      vertex 5.470968e+000 1.620635e+001 -8.881784e-016
+      vertex 5.470968e+000 1.620635e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 2.588190e-001 2.099348e-016
+    outer loop
+      vertex 5.470968e+000 1.620635e+001 -8.881784e-016
+      vertex 5.358181e+000 1.662728e+001 -8.881784e-016
+      vertex 5.358181e+000 1.662728e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 8.715574e-002 7.040056e-016
+    outer loop
+      vertex 5.358181e+000 1.662728e+001 -8.881784e-016
+      vertex 5.320200e+000 1.706140e+001 -8.881784e-016
+      vertex 5.320200e+000 1.706140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.961947e-001 -8.715574e-002 7.040056e-016
+    outer loop
+      vertex 5.320200e+000 1.706140e+001 -8.881784e-016
+      vertex 5.358181e+000 1.749552e+001 -8.881784e-016
+      vertex 5.358181e+000 1.749552e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 9.659258e-001 -2.588190e-001 6.400577e-016
+    outer loop
+      vertex 5.470968e+000 1.791645e+001 -8.881784e-016
+      vertex 5.470968e+000 1.791645e+001 4.399139e+000
+      vertex 5.358181e+000 1.749552e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 9.063078e-001 -4.226183e-001 9.338481e-016
+    outer loop
+      vertex 5.470968e+000 1.791645e+001 -8.881784e-016
+      vertex 5.655136e+000 1.831140e+001 -8.881784e-016
+      vertex 5.655136e+000 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 8.191520e-001 -5.735764e-001 5.942889e-016
+    outer loop
+      vertex 5.905089e+000 1.866837e+001 -8.881784e-016
+      vertex 5.905089e+000 1.866837e+001 4.399139e+000
+      vertex 5.655136e+000 1.831140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.071068e-001 -7.071068e-001 4.271088e-016
+    outer loop
+      vertex 5.905089e+000 1.866837e+001 -8.881784e-016
+      vertex 6.213231e+000 1.897651e+001 -8.881784e-016
+      vertex 6.213231e+000 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 5.735764e-001 -8.191520e-001 6.298045e-016
+    outer loop
+      vertex 6.213231e+000 1.897651e+001 -8.881784e-016
+      vertex 6.570200e+000 1.922646e+001 -8.881784e-016
+      vertex 6.570200e+000 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.226183e-001 -9.063078e-001 1.049674e-015
+    outer loop
+      vertex 6.570200e+000 1.922646e+001 -8.881784e-016
+      vertex 6.965150e+000 1.941063e+001 -8.881784e-016
+      vertex 6.965150e+000 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 2.588190e-001 -9.659258e-001 5.300545e-016
+    outer loop
+      vertex 7.386080e+000 1.952342e+001 -8.881784e-016
+      vertex 7.386080e+000 1.952342e+001 4.399139e+000
+      vertex 6.965150e+000 1.941063e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 8.715574e-002 -9.961947e-001 7.040056e-016
+    outer loop
+      vertex 7.386080e+000 1.952342e+001 -8.881784e-016
+      vertex 7.820200e+000 1.956140e+001 -8.881784e-016
+      vertex 7.820200e+000 1.956140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.715574e-002 -9.961947e-001 1.013479e-016
+    outer loop
+      vertex 7.820200e+000 1.956140e+001 -8.881784e-016
+      vertex 8.254320e+000 1.952342e+001 -8.881784e-016
+      vertex 8.254320e+000 1.952342e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -2.588190e-001 -9.659258e-001 3.547175e-016
+    outer loop
+      vertex 8.254320e+000 1.952342e+001 -8.881784e-016
+      vertex 8.675250e+000 1.941063e+001 -8.881784e-016
+      vertex 8.675250e+000 1.941063e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -4.226183e-001 -9.063078e-001 7.601089e-016
+    outer loop
+      vertex 8.675250e+000 1.941063e+001 -8.881784e-016
+      vertex 9.070200e+000 1.922646e+001 -8.881784e-016
+      vertex 9.070200e+000 1.922646e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -5.735764e-001 -8.191520e-001 2.678479e-016
+    outer loop
+      vertex 9.070200e+000 1.922646e+001 -8.881784e-016
+      vertex 9.427169e+000 1.897651e+001 -8.881784e-016
+      vertex 9.427169e+000 1.897651e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -7.071068e-001 -7.071068e-001 8.542176e-016
+    outer loop
+      vertex 9.427169e+000 1.897651e+001 -8.881784e-016
+      vertex 9.735311e+000 1.866837e+001 -8.881784e-016
+      vertex 9.735311e+000 1.866837e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -8.191520e-001 -5.735764e-001 8.614567e-016
+    outer loop
+      vertex 9.735311e+000 1.866837e+001 -8.881784e-016
+      vertex 9.985264e+000 1.831140e+001 -8.881784e-016
+      vertex 9.985264e+000 1.831140e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal -9.063078e-001 -4.226183e-001 8.542176e-016
+    outer loop
+      vertex 9.985264e+000 1.831140e+001 -8.881784e-016
+      vertex 1.016943e+001 1.791645e+001 -8.881784e-016
+      vertex 1.016943e+001 1.791645e+001 4.399139e+000
+    endloop
+  endfacet
+  facet normal 4.639778e-016 9.961947e-001 -8.715574e-002
+    outer loop
+      vertex -1.798000e-001 -2.938600e+000 5.000000e+000
+      vertex -1.798000e-001 -2.893023e+000 5.520945e+000
+      vertex 1.482020e+001 -2.893023e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal -2.425509e-016 9.961947e-001 -8.715574e-002
+    outer loop
+      vertex 1.482020e+001 -2.938600e+000 5.000000e+000
+      vertex -1.798000e-001 -2.938600e+000 5.000000e+000
+      vertex 1.482020e+001 -2.893023e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 2.425509e-016 9.961947e-001 8.715574e-002
+    outer loop
+      vertex 1.482020e+001 -2.938600e+000 5.000000e+000
+      vertex -1.798000e-001 -2.893023e+000 4.479055e+000
+      vertex -1.798000e-001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.635355e-016 9.961947e-001 8.715574e-002
+    outer loop
+      vertex 1.482020e+001 -2.938600e+000 5.000000e+000
+      vertex 1.482020e+001 -2.893023e+000 4.479055e+000
+      vertex -1.798000e-001 -2.893023e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal 5.101317e-016 9.659258e-001 2.588190e-001
+    outer loop
+      vertex -1.798000e-001 -2.893023e+000 4.479055e+000
+      vertex 1.482020e+001 -2.893023e+000 4.479055e+000
+      vertex -1.798000e-001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -5.234721e-016 9.659258e-001 2.588190e-001
+    outer loop
+      vertex -1.798000e-001 -2.757678e+000 3.973940e+000
+      vertex 1.482020e+001 -2.893023e+000 4.479055e+000
+      vertex 1.482020e+001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -4.809511e-016 9.063078e-001 4.226183e-001
+    outer loop
+      vertex -1.798000e-001 -2.536676e+000 3.500000e+000
+      vertex 1.482020e+001 -2.757678e+000 3.973940e+000
+      vertex 1.482020e+001 -2.536676e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal -5.134128e-016 8.191520e-001 5.735764e-001
+    outer loop
+      vertex -1.798000e-001 -2.236733e+000 3.071637e+000
+      vertex 1.482020e+001 -2.536676e+000 3.500000e+000
+      vertex 1.482020e+001 -2.236733e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal -5.024296e-016 7.071068e-001 7.071068e-001
+    outer loop
+      vertex -1.798000e-001 -1.866963e+000 2.701867e+000
+      vertex 1.482020e+001 -2.236733e+000 3.071637e+000
+      vertex 1.482020e+001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal -4.870609e-016 5.735764e-001 8.191520e-001
+    outer loop
+      vertex -1.798000e-001 -1.438600e+000 2.401924e+000
+      vertex 1.482020e+001 -1.866963e+000 2.701867e+000
+      vertex 1.482020e+001 -1.438600e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal -4.973720e-016 4.226183e-001 9.063078e-001
+    outer loop
+      vertex -1.798000e-001 -9.646604e-001 2.180922e+000
+      vertex 1.482020e+001 -1.438600e+000 2.401924e+000
+      vertex 1.482020e+001 -9.646604e-001 2.180922e+000
+    endloop
+  endfacet
+  facet normal -4.930284e-016 2.588190e-001 9.659258e-001
+    outer loop
+      vertex -1.798000e-001 -4.595445e-001 2.045577e+000
+      vertex 1.482020e+001 -9.646604e-001 2.180922e+000
+      vertex 1.482020e+001 -4.595445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal -4.799411e-016 8.715574e-002 9.961947e-001
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 2.000000e+000
+      vertex 1.482020e+001 -4.595445e-001 2.045577e+000
+      vertex 1.482020e+001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal -4.933374e-016 -8.715574e-002 9.961947e-001
+    outer loop
+      vertex -1.798000e-001 5.823445e-001 2.045577e+000
+      vertex 1.482020e+001 6.140000e-002 2.000000e+000
+      vertex 1.482020e+001 5.823445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal -4.872123e-016 -2.588190e-001 9.659258e-001
+    outer loop
+      vertex -1.798000e-001 1.087460e+000 2.180922e+000
+      vertex 1.482020e+001 5.823445e-001 2.045577e+000
+      vertex 1.482020e+001 1.087460e+000 2.180922e+000
+    endloop
+  endfacet
+  facet normal -4.809511e-016 -4.226183e-001 9.063078e-001
+    outer loop
+      vertex -1.798000e-001 1.561400e+000 2.401924e+000
+      vertex 1.482020e+001 1.087460e+000 2.180922e+000
+      vertex 1.482020e+001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal -5.049222e-016 -5.735764e-001 8.191520e-001
+    outer loop
+      vertex -1.798000e-001 1.989763e+000 2.701867e+000
+      vertex 1.482020e+001 1.561400e+000 2.401924e+000
+      vertex 1.482020e+001 1.989763e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal -4.605605e-016 -7.071068e-001 7.071068e-001
+    outer loop
+      vertex -1.798000e-001 2.359533e+000 3.071637e+000
+      vertex 1.482020e+001 1.989763e+000 2.701867e+000
+      vertex 1.482020e+001 2.359533e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal -4.955515e-016 -8.191520e-001 5.735764e-001
+    outer loop
+      vertex -1.798000e-001 2.659476e+000 3.500000e+000
+      vertex 1.482020e+001 2.359533e+000 3.071637e+000
+      vertex 1.482020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal -4.562199e-016 -9.063078e-001 4.226183e-001
+    outer loop
+      vertex -1.798000e-001 2.880478e+000 3.973940e+000
+      vertex 1.482020e+001 2.659476e+000 3.500000e+000
+      vertex 1.482020e+001 2.880478e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -4.815345e-016 -9.659258e-001 2.588190e-001
+    outer loop
+      vertex -1.798000e-001 3.015823e+000 4.479055e+000
+      vertex 1.482020e+001 2.880478e+000 3.973940e+000
+      vertex 1.482020e+001 3.015823e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal -4.851017e-016 -9.961947e-001 8.715574e-002
+    outer loop
+      vertex -1.798000e-001 3.061400e+000 5.000000e+000
+      vertex 1.482020e+001 3.015823e+000 4.479055e+000
+      vertex 1.482020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.933374e-016 -9.961947e-001 -8.715574e-002
+    outer loop
+      vertex -1.798000e-001 3.015823e+000 5.520945e+000
+      vertex 1.482020e+001 3.061400e+000 5.000000e+000
+      vertex 1.482020e+001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal -5.234721e-016 -9.659258e-001 -2.588190e-001
+    outer loop
+      vertex -1.798000e-001 2.880478e+000 6.026060e+000
+      vertex 1.482020e+001 3.015823e+000 5.520945e+000
+      vertex 1.482020e+001 2.880478e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -4.684391e-016 -9.063078e-001 -4.226183e-001
+    outer loop
+      vertex -1.798000e-001 2.659476e+000 6.500000e+000
+      vertex 1.482020e+001 2.880478e+000 6.026060e+000
+      vertex 1.482020e+001 2.659476e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal -5.134128e-016 -8.191520e-001 -5.735764e-001
+    outer loop
+      vertex -1.798000e-001 2.359533e+000 6.928363e+000
+      vertex 1.482020e+001 2.659476e+000 6.500000e+000
+      vertex 1.482020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal -5.128969e-016 -7.071068e-001 -7.071068e-001
+    outer loop
+      vertex -1.798000e-001 1.989763e+000 7.298133e+000
+      vertex 1.482020e+001 2.359533e+000 6.928363e+000
+      vertex 1.482020e+001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -4.543185e-016 -5.735764e-001 -8.191520e-001
+    outer loop
+      vertex -1.798000e-001 1.561400e+000 7.598076e+000
+      vertex 1.482020e+001 1.989763e+000 7.298133e+000
+      vertex 1.482020e+001 1.561400e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal -5.223961e-016 -4.226183e-001 -9.063078e-001
+    outer loop
+      vertex -1.798000e-001 1.087460e+000 7.819078e+000
+      vertex 1.482020e+001 1.561400e+000 7.598076e+000
+      vertex 1.482020e+001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal -4.529374e-016 -2.588190e-001 -9.659258e-001
+    outer loop
+      vertex -1.798000e-001 5.823445e-001 7.954423e+000
+      vertex 1.482020e+001 1.087460e+000 7.819078e+000
+      vertex 1.482020e+001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -4.799411e-016 -8.715574e-002 -9.961947e-001
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 8.000000e+000
+      vertex 1.482020e+001 5.823445e-001 7.954423e+000
+      vertex 1.482020e+001 6.140000e-002 8.000000e+000
+    endloop
+  endfacet
+  facet normal -5.189602e-016 8.715574e-002 -9.961947e-001
+    outer loop
+      vertex -1.798000e-001 -4.595445e-001 7.954423e+000
+      vertex 1.482020e+001 6.140000e-002 8.000000e+000
+      vertex 1.482020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -4.739404e-016 2.588190e-001 -9.659258e-001
+    outer loop
+      vertex -1.798000e-001 -9.646604e-001 7.819078e+000
+      vertex 1.482020e+001 -4.595445e-001 7.954423e+000
+      vertex 1.482020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal -5.015272e-016 4.226183e-001 -9.063078e-001
+    outer loop
+      vertex -1.798000e-001 -1.438600e+000 7.598076e+000
+      vertex 1.482020e+001 -9.646604e-001 7.819078e+000
+      vertex 1.482020e+001 -1.438600e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal -4.479280e-016 5.735764e-001 -8.191520e-001
+    outer loop
+      vertex -1.798000e-001 -1.866963e+000 7.298133e+000
+      vertex 1.482020e+001 -1.438600e+000 7.598076e+000
+      vertex 1.482020e+001 -1.866963e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -4.605605e-016 7.071068e-001 -7.071068e-001
+    outer loop
+      vertex -1.798000e-001 -2.236733e+000 6.928363e+000
+      vertex 1.482020e+001 -1.866963e+000 7.298133e+000
+      vertex 1.482020e+001 -2.236733e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal -4.955515e-016 8.191520e-001 -5.735764e-001
+    outer loop
+      vertex -1.798000e-001 -2.536676e+000 6.500000e+000
+      vertex 1.482020e+001 -2.236733e+000 6.928363e+000
+      vertex 1.482020e+001 -2.536676e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal -4.562199e-016 9.063078e-001 -4.226183e-001
+    outer loop
+      vertex -1.798000e-001 -2.757678e+000 6.026060e+000
+      vertex 1.482020e+001 -2.536676e+000 6.500000e+000
+      vertex 1.482020e+001 -2.757678e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -5.101317e-016 9.659258e-001 -2.588190e-001
+    outer loop
+      vertex -1.798000e-001 -2.893023e+000 5.520945e+000
+      vertex 1.482020e+001 -2.757678e+000 6.026060e+000
+      vertex 1.482020e+001 -2.893023e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 5.236890e-016 9.659258e-001 -2.588190e-001
+    outer loop
+      vertex -1.798000e-001 -2.893023e+000 5.520945e+000
+      vertex -1.798000e-001 -2.757678e+000 6.026060e+000
+      vertex 1.482020e+001 -2.757678e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal 4.688432e-016 9.063078e-001 -4.226183e-001
+    outer loop
+      vertex -1.798000e-001 -2.757678e+000 6.026060e+000
+      vertex -1.798000e-001 -2.536676e+000 6.500000e+000
+      vertex 1.482020e+001 -2.536676e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal 5.130737e-016 8.191520e-001 -5.735764e-001
+    outer loop
+      vertex -1.798000e-001 -2.536676e+000 6.500000e+000
+      vertex -1.798000e-001 -2.236733e+000 6.928363e+000
+      vertex 1.482020e+001 -2.236733e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 5.236890e-016 7.071068e-001 -7.071068e-001
+    outer loop
+      vertex -1.798000e-001 -2.236733e+000 6.928363e+000
+      vertex -1.798000e-001 -1.866963e+000 7.298133e+000
+      vertex 1.482020e+001 -1.866963e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal 4.635355e-016 5.735764e-001 -8.191520e-001
+    outer loop
+      vertex -1.798000e-001 -1.866963e+000 7.298133e+000
+      vertex -1.798000e-001 -1.438600e+000 7.598076e+000
+      vertex 1.482020e+001 -1.438600e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal 4.971507e-016 4.226183e-001 -9.063078e-001
+    outer loop
+      vertex -1.798000e-001 -1.438600e+000 7.598076e+000
+      vertex -1.798000e-001 -9.646604e-001 7.819078e+000
+      vertex 1.482020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal 4.635355e-016 2.588190e-001 -9.659258e-001
+    outer loop
+      vertex -1.798000e-001 -9.646604e-001 7.819078e+000
+      vertex -1.798000e-001 -4.595445e-001 7.954423e+000
+      vertex 1.482020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 4.799008e-016 8.715574e-002 -9.961947e-001
+    outer loop
+      vertex -1.798000e-001 -4.595445e-001 7.954423e+000
+      vertex -1.798000e-001 6.140000e-002 8.000000e+000
+      vertex 1.482020e+001 6.140000e-002 8.000000e+000
+    endloop
+  endfacet
+  facet normal 5.228044e-016 -8.715574e-002 -9.961947e-001
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 8.000000e+000
+      vertex -1.798000e-001 5.823445e-001 7.954423e+000
+      vertex 1.482020e+001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 4.582279e-016 -2.588190e-001 -9.659258e-001
+    outer loop
+      vertex -1.798000e-001 5.823445e-001 7.954423e+000
+      vertex -1.798000e-001 1.087460e+000 7.819078e+000
+      vertex 1.482020e+001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal 5.077660e-016 -4.226183e-001 -9.063078e-001
+    outer loop
+      vertex -1.798000e-001 1.087460e+000 7.819078e+000
+      vertex -1.798000e-001 1.561400e+000 7.598076e+000
+      vertex 1.482020e+001 1.561400e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal 4.564586e-016 -5.735764e-001 -8.191520e-001
+    outer loop
+      vertex -1.798000e-001 1.561400e+000 7.598076e+000
+      vertex -1.798000e-001 1.989763e+000 7.298133e+000
+      vertex 1.482020e+001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal 4.599971e-016 -7.071068e-001 -7.071068e-001
+    outer loop
+      vertex -1.798000e-001 1.989763e+000 7.298133e+000
+      vertex -1.798000e-001 2.359533e+000 6.928363e+000
+      vertex 1.482020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 4.953815e-016 -8.191520e-001 -5.735764e-001
+    outer loop
+      vertex -1.798000e-001 2.359533e+000 6.928363e+000
+      vertex -1.798000e-001 2.659476e+000 6.500000e+000
+      vertex 1.482020e+001 2.659476e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal 4.564586e-016 -9.063078e-001 -4.226183e-001
+    outer loop
+      vertex -1.798000e-001 2.659476e+000 6.500000e+000
+      vertex -1.798000e-001 2.880478e+000 6.026060e+000
+      vertex 1.482020e+001 2.880478e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal 4.829969e-016 -9.659258e-001 -2.588190e-001
+    outer loop
+      vertex -1.798000e-001 2.880478e+000 6.026060e+000
+      vertex -1.798000e-001 3.015823e+000 5.520945e+000
+      vertex 1.482020e+001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 4.852085e-016 -9.961947e-001 -8.715574e-002
+    outer loop
+      vertex -1.798000e-001 3.015823e+000 5.520945e+000
+      vertex -1.798000e-001 3.061400e+000 5.000000e+000
+      vertex 1.482020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.936122e-016 -9.961947e-001 8.715574e-002
+    outer loop
+      vertex -1.798000e-001 3.061400e+000 5.000000e+000
+      vertex -1.798000e-001 3.015823e+000 4.479055e+000
+      vertex 1.482020e+001 3.015823e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal 5.236890e-016 -9.659258e-001 2.588190e-001
+    outer loop
+      vertex -1.798000e-001 3.015823e+000 4.479055e+000
+      vertex -1.798000e-001 2.880478e+000 3.973940e+000
+      vertex 1.482020e+001 2.880478e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal 4.670740e-016 -9.063078e-001 4.226183e-001
+    outer loop
+      vertex -1.798000e-001 2.880478e+000 3.973940e+000
+      vertex -1.798000e-001 2.659476e+000 3.500000e+000
+      vertex 1.482020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 5.130737e-016 -8.191520e-001 5.735764e-001
+    outer loop
+      vertex -1.798000e-001 2.659476e+000 3.500000e+000
+      vertex -1.798000e-001 2.359533e+000 3.071637e+000
+      vertex 1.482020e+001 2.359533e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal 4.706124e-016 -7.071068e-001 7.071068e-001
+    outer loop
+      vertex -1.798000e-001 2.359533e+000 3.071637e+000
+      vertex -1.798000e-001 1.989763e+000 2.701867e+000
+      vertex 1.482020e+001 1.989763e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal 4.776893e-016 -5.735764e-001 8.191520e-001
+    outer loop
+      vertex -1.798000e-001 1.989763e+000 2.701867e+000
+      vertex -1.798000e-001 1.561400e+000 2.401924e+000
+      vertex 1.482020e+001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 4.953815e-016 -4.226183e-001 9.063078e-001
+    outer loop
+      vertex -1.798000e-001 1.561400e+000 2.401924e+000
+      vertex -1.798000e-001 1.087460e+000 2.180922e+000
+      vertex 1.482020e+001 1.087460e+000 2.180922e+000
+    endloop
+  endfacet
+  facet normal 4.812277e-016 -2.588190e-001 9.659258e-001
+    outer loop
+      vertex -1.798000e-001 1.087460e+000 2.180922e+000
+      vertex -1.798000e-001 5.823445e-001 2.045577e+000
+      vertex 1.482020e+001 5.823445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal 4.799008e-016 -8.715574e-002 9.961947e-001
+    outer loop
+      vertex -1.798000e-001 5.823445e-001 2.045577e+000
+      vertex -1.798000e-001 6.140000e-002 2.000000e+000
+      vertex 1.482020e+001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal 4.891892e-016 8.715574e-002 9.961947e-001
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 2.000000e+000
+      vertex -1.798000e-001 -4.595445e-001 2.045577e+000
+      vertex 1.482020e+001 -4.595445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal 4.741508e-016 2.588190e-001 9.659258e-001
+    outer loop
+      vertex -1.798000e-001 -4.595445e-001 2.045577e+000
+      vertex -1.798000e-001 -9.646604e-001 2.180922e+000
+      vertex 1.482020e+001 -9.646604e-001 2.180922e+000
+    endloop
+  endfacet
+  facet normal 4.759201e-016 4.226183e-001 9.063078e-001
+    outer loop
+      vertex -1.798000e-001 -9.646604e-001 2.180922e+000
+      vertex -1.798000e-001 -1.438600e+000 2.401924e+000
+      vertex 1.482020e+001 -1.438600e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 4.706124e-016 5.735764e-001 8.191520e-001
+    outer loop
+      vertex -1.798000e-001 -1.438600e+000 2.401924e+000
+      vertex -1.798000e-001 -1.866963e+000 2.701867e+000
+      vertex 1.482020e+001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal 4.599971e-016 7.071068e-001 7.071068e-001
+    outer loop
+      vertex -1.798000e-001 -1.866963e+000 2.701867e+000
+      vertex -1.798000e-001 -2.236733e+000 3.071637e+000
+      vertex 1.482020e+001 -2.236733e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal 4.812277e-016 8.191520e-001 5.735764e-001
+    outer loop
+      vertex -1.798000e-001 -2.236733e+000 3.071637e+000
+      vertex -1.798000e-001 -2.536676e+000 3.500000e+000
+      vertex 1.482020e+001 -2.536676e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 4.546894e-016 9.063078e-001 4.226183e-001
+    outer loop
+      vertex -1.798000e-001 -2.536676e+000 3.500000e+000
+      vertex -1.798000e-001 -2.757678e+000 3.973940e+000
+      vertex 1.482020e+001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal 4.639778e-016 9.961947e-001 -8.715574e-002
+    outer loop
+      vertex 3.482020e+001 -2.938600e+000 5.000000e+000
+      vertex 3.482020e+001 -2.893023e+000 5.520945e+000
+      vertex 4.982020e+001 -2.893023e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal -2.425509e-016 9.961947e-001 -8.715574e-002
+    outer loop
+      vertex 4.982020e+001 -2.938600e+000 5.000000e+000
+      vertex 3.482020e+001 -2.938600e+000 5.000000e+000
+      vertex 4.982020e+001 -2.893023e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 2.425509e-016 9.961947e-001 8.715574e-002
+    outer loop
+      vertex 4.982020e+001 -2.938600e+000 5.000000e+000
+      vertex 3.482020e+001 -2.893023e+000 4.479055e+000
+      vertex 3.482020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.635355e-016 9.961947e-001 8.715574e-002
+    outer loop
+      vertex 4.982020e+001 -2.938600e+000 5.000000e+000
+      vertex 4.982020e+001 -2.893023e+000 4.479055e+000
+      vertex 3.482020e+001 -2.893023e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal 5.101317e-016 9.659258e-001 2.588190e-001
+    outer loop
+      vertex 3.482020e+001 -2.893023e+000 4.479055e+000
+      vertex 4.982020e+001 -2.893023e+000 4.479055e+000
+      vertex 3.482020e+001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -5.234721e-016 9.659258e-001 2.588190e-001
+    outer loop
+      vertex 3.482020e+001 -2.757678e+000 3.973940e+000
+      vertex 4.982020e+001 -2.893023e+000 4.479055e+000
+      vertex 4.982020e+001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -4.809511e-016 9.063078e-001 4.226183e-001
+    outer loop
+      vertex 3.482020e+001 -2.536676e+000 3.500000e+000
+      vertex 4.982020e+001 -2.757678e+000 3.973940e+000
+      vertex 4.982020e+001 -2.536676e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal -5.134128e-016 8.191520e-001 5.735764e-001
+    outer loop
+      vertex 3.482020e+001 -2.236733e+000 3.071637e+000
+      vertex 4.982020e+001 -2.536676e+000 3.500000e+000
+      vertex 4.982020e+001 -2.236733e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal -5.024296e-016 7.071068e-001 7.071068e-001
+    outer loop
+      vertex 3.482020e+001 -1.866963e+000 2.701867e+000
+      vertex 4.982020e+001 -2.236733e+000 3.071637e+000
+      vertex 4.982020e+001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal -4.870609e-016 5.735764e-001 8.191520e-001
+    outer loop
+      vertex 3.482020e+001 -1.438600e+000 2.401924e+000
+      vertex 4.982020e+001 -1.866963e+000 2.701867e+000
+      vertex 4.982020e+001 -1.438600e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal -4.973720e-016 4.226183e-001 9.063078e-001
+    outer loop
+      vertex 3.482020e+001 -9.646604e-001 2.180922e+000
+      vertex 4.982020e+001 -1.438600e+000 2.401924e+000
+      vertex 4.982020e+001 -9.646604e-001 2.180922e+000
+    endloop
+  endfacet
+  facet normal -4.930284e-016 2.588190e-001 9.659258e-001
+    outer loop
+      vertex 3.482020e+001 -4.595445e-001 2.045577e+000
+      vertex 4.982020e+001 -9.646604e-001 2.180922e+000
+      vertex 4.982020e+001 -4.595445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal -4.799411e-016 8.715574e-002 9.961947e-001
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 2.000000e+000
+      vertex 4.982020e+001 -4.595445e-001 2.045577e+000
+      vertex 4.982020e+001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal -4.933374e-016 -8.715574e-002 9.961947e-001
+    outer loop
+      vertex 3.482020e+001 5.823445e-001 2.045577e+000
+      vertex 4.982020e+001 6.140000e-002 2.000000e+000
+      vertex 4.982020e+001 5.823445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal -4.872123e-016 -2.588190e-001 9.659258e-001
+    outer loop
+      vertex 3.482020e+001 1.087460e+000 2.180922e+000
+      vertex 4.982020e+001 5.823445e-001 2.045577e+000
+      vertex 4.982020e+001 1.087460e+000 2.180922e+000
+    endloop
+  endfacet
+  facet normal -4.809511e-016 -4.226183e-001 9.063078e-001
+    outer loop
+      vertex 3.482020e+001 1.561400e+000 2.401924e+000
+      vertex 4.982020e+001 1.087460e+000 2.180922e+000
+      vertex 4.982020e+001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal -5.049222e-016 -5.735764e-001 8.191520e-001
+    outer loop
+      vertex 3.482020e+001 1.989763e+000 2.701867e+000
+      vertex 4.982020e+001 1.561400e+000 2.401924e+000
+      vertex 4.982020e+001 1.989763e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal -4.605605e-016 -7.071068e-001 7.071068e-001
+    outer loop
+      vertex 3.482020e+001 2.359533e+000 3.071637e+000
+      vertex 4.982020e+001 1.989763e+000 2.701867e+000
+      vertex 4.982020e+001 2.359533e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal -4.955515e-016 -8.191520e-001 5.735764e-001
+    outer loop
+      vertex 3.482020e+001 2.659476e+000 3.500000e+000
+      vertex 4.982020e+001 2.359533e+000 3.071637e+000
+      vertex 4.982020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal -4.562199e-016 -9.063078e-001 4.226183e-001
+    outer loop
+      vertex 3.482020e+001 2.880478e+000 3.973940e+000
+      vertex 4.982020e+001 2.659476e+000 3.500000e+000
+      vertex 4.982020e+001 2.880478e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -4.815345e-016 -9.659258e-001 2.588190e-001
+    outer loop
+      vertex 3.482020e+001 3.015823e+000 4.479055e+000
+      vertex 4.982020e+001 2.880478e+000 3.973940e+000
+      vertex 4.982020e+001 3.015823e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal -4.851017e-016 -9.961947e-001 8.715574e-002
+    outer loop
+      vertex 3.482020e+001 3.061400e+000 5.000000e+000
+      vertex 4.982020e+001 3.015823e+000 4.479055e+000
+      vertex 4.982020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -4.933374e-016 -9.961947e-001 -8.715574e-002
+    outer loop
+      vertex 3.482020e+001 3.015823e+000 5.520945e+000
+      vertex 4.982020e+001 3.061400e+000 5.000000e+000
+      vertex 4.982020e+001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal -5.234721e-016 -9.659258e-001 -2.588190e-001
+    outer loop
+      vertex 3.482020e+001 2.880478e+000 6.026060e+000
+      vertex 4.982020e+001 3.015823e+000 5.520945e+000
+      vertex 4.982020e+001 2.880478e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -4.684391e-016 -9.063078e-001 -4.226183e-001
+    outer loop
+      vertex 3.482020e+001 2.659476e+000 6.500000e+000
+      vertex 4.982020e+001 2.880478e+000 6.026060e+000
+      vertex 4.982020e+001 2.659476e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal -5.134128e-016 -8.191520e-001 -5.735764e-001
+    outer loop
+      vertex 3.482020e+001 2.359533e+000 6.928363e+000
+      vertex 4.982020e+001 2.659476e+000 6.500000e+000
+      vertex 4.982020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal -5.128969e-016 -7.071068e-001 -7.071068e-001
+    outer loop
+      vertex 3.482020e+001 1.989763e+000 7.298133e+000
+      vertex 4.982020e+001 2.359533e+000 6.928363e+000
+      vertex 4.982020e+001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -4.543185e-016 -5.735764e-001 -8.191520e-001
+    outer loop
+      vertex 3.482020e+001 1.561400e+000 7.598076e+000
+      vertex 4.982020e+001 1.989763e+000 7.298133e+000
+      vertex 4.982020e+001 1.561400e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal -5.223961e-016 -4.226183e-001 -9.063078e-001
+    outer loop
+      vertex 3.482020e+001 1.087460e+000 7.819078e+000
+      vertex 4.982020e+001 1.561400e+000 7.598076e+000
+      vertex 4.982020e+001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal -4.529374e-016 -2.588190e-001 -9.659258e-001
+    outer loop
+      vertex 3.482020e+001 5.823445e-001 7.954423e+000
+      vertex 4.982020e+001 1.087460e+000 7.819078e+000
+      vertex 4.982020e+001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -4.799411e-016 -8.715574e-002 -9.961947e-001
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 8.000000e+000
+      vertex 4.982020e+001 5.823445e-001 7.954423e+000
+      vertex 4.982020e+001 6.140000e-002 8.000000e+000
+    endloop
+  endfacet
+  facet normal -5.189602e-016 8.715574e-002 -9.961947e-001
+    outer loop
+      vertex 3.482020e+001 -4.595445e-001 7.954423e+000
+      vertex 4.982020e+001 6.140000e-002 8.000000e+000
+      vertex 4.982020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -4.739404e-016 2.588190e-001 -9.659258e-001
+    outer loop
+      vertex 3.482020e+001 -9.646604e-001 7.819078e+000
+      vertex 4.982020e+001 -4.595445e-001 7.954423e+000
+      vertex 4.982020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal -5.015272e-016 4.226183e-001 -9.063078e-001
+    outer loop
+      vertex 3.482020e+001 -1.438600e+000 7.598076e+000
+      vertex 4.982020e+001 -9.646604e-001 7.819078e+000
+      vertex 4.982020e+001 -1.438600e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal -4.479280e-016 5.735764e-001 -8.191520e-001
+    outer loop
+      vertex 3.482020e+001 -1.866963e+000 7.298133e+000
+      vertex 4.982020e+001 -1.438600e+000 7.598076e+000
+      vertex 4.982020e+001 -1.866963e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -4.605605e-016 7.071068e-001 -7.071068e-001
+    outer loop
+      vertex 3.482020e+001 -2.236733e+000 6.928363e+000
+      vertex 4.982020e+001 -1.866963e+000 7.298133e+000
+      vertex 4.982020e+001 -2.236733e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal -4.955515e-016 8.191520e-001 -5.735764e-001
+    outer loop
+      vertex 3.482020e+001 -2.536676e+000 6.500000e+000
+      vertex 4.982020e+001 -2.236733e+000 6.928363e+000
+      vertex 4.982020e+001 -2.536676e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal -4.562199e-016 9.063078e-001 -4.226183e-001
+    outer loop
+      vertex 3.482020e+001 -2.757678e+000 6.026060e+000
+      vertex 4.982020e+001 -2.536676e+000 6.500000e+000
+      vertex 4.982020e+001 -2.757678e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -5.101317e-016 9.659258e-001 -2.588190e-001
+    outer loop
+      vertex 3.482020e+001 -2.893023e+000 5.520945e+000
+      vertex 4.982020e+001 -2.757678e+000 6.026060e+000
+      vertex 4.982020e+001 -2.893023e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 5.236890e-016 9.659258e-001 -2.588190e-001
+    outer loop
+      vertex 3.482020e+001 -2.893023e+000 5.520945e+000
+      vertex 3.482020e+001 -2.757678e+000 6.026060e+000
+      vertex 4.982020e+001 -2.757678e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal 4.688432e-016 9.063078e-001 -4.226183e-001
+    outer loop
+      vertex 3.482020e+001 -2.757678e+000 6.026060e+000
+      vertex 3.482020e+001 -2.536676e+000 6.500000e+000
+      vertex 4.982020e+001 -2.536676e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal 5.130737e-016 8.191520e-001 -5.735764e-001
+    outer loop
+      vertex 3.482020e+001 -2.536676e+000 6.500000e+000
+      vertex 3.482020e+001 -2.236733e+000 6.928363e+000
+      vertex 4.982020e+001 -2.236733e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 5.236890e-016 7.071068e-001 -7.071068e-001
+    outer loop
+      vertex 3.482020e+001 -2.236733e+000 6.928363e+000
+      vertex 3.482020e+001 -1.866963e+000 7.298133e+000
+      vertex 4.982020e+001 -1.866963e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal 4.635355e-016 5.735764e-001 -8.191520e-001
+    outer loop
+      vertex 3.482020e+001 -1.866963e+000 7.298133e+000
+      vertex 3.482020e+001 -1.438600e+000 7.598076e+000
+      vertex 4.982020e+001 -1.438600e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal 4.971507e-016 4.226183e-001 -9.063078e-001
+    outer loop
+      vertex 3.482020e+001 -1.438600e+000 7.598076e+000
+      vertex 3.482020e+001 -9.646604e-001 7.819078e+000
+      vertex 4.982020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal 4.635355e-016 2.588190e-001 -9.659258e-001
+    outer loop
+      vertex 3.482020e+001 -9.646604e-001 7.819078e+000
+      vertex 3.482020e+001 -4.595445e-001 7.954423e+000
+      vertex 4.982020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 4.799008e-016 8.715574e-002 -9.961947e-001
+    outer loop
+      vertex 3.482020e+001 -4.595445e-001 7.954423e+000
+      vertex 3.482020e+001 6.140000e-002 8.000000e+000
+      vertex 4.982020e+001 6.140000e-002 8.000000e+000
+    endloop
+  endfacet
+  facet normal 5.228044e-016 -8.715574e-002 -9.961947e-001
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 8.000000e+000
+      vertex 3.482020e+001 5.823445e-001 7.954423e+000
+      vertex 4.982020e+001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 4.582279e-016 -2.588190e-001 -9.659258e-001
+    outer loop
+      vertex 3.482020e+001 5.823445e-001 7.954423e+000
+      vertex 3.482020e+001 1.087460e+000 7.819078e+000
+      vertex 4.982020e+001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal 5.077660e-016 -4.226183e-001 -9.063078e-001
+    outer loop
+      vertex 3.482020e+001 1.087460e+000 7.819078e+000
+      vertex 3.482020e+001 1.561400e+000 7.598076e+000
+      vertex 4.982020e+001 1.561400e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal 4.564586e-016 -5.735764e-001 -8.191520e-001
+    outer loop
+      vertex 3.482020e+001 1.561400e+000 7.598076e+000
+      vertex 3.482020e+001 1.989763e+000 7.298133e+000
+      vertex 4.982020e+001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal 4.599971e-016 -7.071068e-001 -7.071068e-001
+    outer loop
+      vertex 3.482020e+001 1.989763e+000 7.298133e+000
+      vertex 3.482020e+001 2.359533e+000 6.928363e+000
+      vertex 4.982020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 4.953815e-016 -8.191520e-001 -5.735764e-001
+    outer loop
+      vertex 3.482020e+001 2.359533e+000 6.928363e+000
+      vertex 3.482020e+001 2.659476e+000 6.500000e+000
+      vertex 4.982020e+001 2.659476e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal 4.564586e-016 -9.063078e-001 -4.226183e-001
+    outer loop
+      vertex 3.482020e+001 2.659476e+000 6.500000e+000
+      vertex 3.482020e+001 2.880478e+000 6.026060e+000
+      vertex 4.982020e+001 2.880478e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal 4.829969e-016 -9.659258e-001 -2.588190e-001
+    outer loop
+      vertex 3.482020e+001 2.880478e+000 6.026060e+000
+      vertex 3.482020e+001 3.015823e+000 5.520945e+000
+      vertex 4.982020e+001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 4.852085e-016 -9.961947e-001 -8.715574e-002
+    outer loop
+      vertex 3.482020e+001 3.015823e+000 5.520945e+000
+      vertex 3.482020e+001 3.061400e+000 5.000000e+000
+      vertex 4.982020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 4.936122e-016 -9.961947e-001 8.715574e-002
+    outer loop
+      vertex 3.482020e+001 3.061400e+000 5.000000e+000
+      vertex 3.482020e+001 3.015823e+000 4.479055e+000
+      vertex 4.982020e+001 3.015823e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal 5.236890e-016 -9.659258e-001 2.588190e-001
+    outer loop
+      vertex 3.482020e+001 3.015823e+000 4.479055e+000
+      vertex 3.482020e+001 2.880478e+000 3.973940e+000
+      vertex 4.982020e+001 2.880478e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal 4.670740e-016 -9.063078e-001 4.226183e-001
+    outer loop
+      vertex 3.482020e+001 2.880478e+000 3.973940e+000
+      vertex 3.482020e+001 2.659476e+000 3.500000e+000
+      vertex 4.982020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 5.130737e-016 -8.191520e-001 5.735764e-001
+    outer loop
+      vertex 3.482020e+001 2.659476e+000 3.500000e+000
+      vertex 3.482020e+001 2.359533e+000 3.071637e+000
+      vertex 4.982020e+001 2.359533e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal 4.706124e-016 -7.071068e-001 7.071068e-001
+    outer loop
+      vertex 3.482020e+001 2.359533e+000 3.071637e+000
+      vertex 3.482020e+001 1.989763e+000 2.701867e+000
+      vertex 4.982020e+001 1.989763e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal 4.776893e-016 -5.735764e-001 8.191520e-001
+    outer loop
+      vertex 3.482020e+001 1.989763e+000 2.701867e+000
+      vertex 3.482020e+001 1.561400e+000 2.401924e+000
+      vertex 4.982020e+001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 4.953815e-016 -4.226183e-001 9.063078e-001
+    outer loop
+      vertex 3.482020e+001 1.561400e+000 2.401924e+000
+      vertex 3.482020e+001 1.087460e+000 2.180922e+000
+      vertex 4.982020e+001 1.087460e+000 2.180922e+000
+    endloop
+  endfacet
+  facet normal 4.812277e-016 -2.588190e-001 9.659258e-001
+    outer loop
+      vertex 3.482020e+001 1.087460e+000 2.180922e+000
+      vertex 3.482020e+001 5.823445e-001 2.045577e+000
+      vertex 4.982020e+001 5.823445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal 4.799008e-016 -8.715574e-002 9.961947e-001
+    outer loop
+      vertex 3.482020e+001 5.823445e-001 2.045577e+000
+      vertex 3.482020e+001 6.140000e-002 2.000000e+000
+      vertex 4.982020e+001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal 4.891892e-016 8.715574e-002 9.961947e-001
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 2.000000e+000
+      vertex 3.482020e+001 -4.595445e-001 2.045577e+000
+      vertex 4.982020e+001 -4.595445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal 4.741508e-016 2.588190e-001 9.659258e-001
+    outer loop
+      vertex 3.482020e+001 -4.595445e-001 2.045577e+000
+      vertex 3.482020e+001 -9.646604e-001 2.180922e+000
+      vertex 4.982020e+001 -9.646604e-001 2.180922e+000
+    endloop
+  endfacet
+  facet normal 4.759201e-016 4.226183e-001 9.063078e-001
+    outer loop
+      vertex 3.482020e+001 -9.646604e-001 2.180922e+000
+      vertex 3.482020e+001 -1.438600e+000 2.401924e+000
+      vertex 4.982020e+001 -1.438600e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 4.706124e-016 5.735764e-001 8.191520e-001
+    outer loop
+      vertex 3.482020e+001 -1.438600e+000 2.401924e+000
+      vertex 3.482020e+001 -1.866963e+000 2.701867e+000
+      vertex 4.982020e+001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal 4.599971e-016 7.071068e-001 7.071068e-001
+    outer loop
+      vertex 3.482020e+001 -1.866963e+000 2.701867e+000
+      vertex 3.482020e+001 -2.236733e+000 3.071637e+000
+      vertex 4.982020e+001 -2.236733e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal 4.812277e-016 8.191520e-001 5.735764e-001
+    outer loop
+      vertex 3.482020e+001 -2.236733e+000 3.071637e+000
+      vertex 3.482020e+001 -2.536676e+000 3.500000e+000
+      vertex 4.982020e+001 -2.536676e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 4.546894e-016 9.063078e-001 4.226183e-001
+    outer loop
+      vertex 3.482020e+001 -2.536676e+000 3.500000e+000
+      vertex 3.482020e+001 -2.757678e+000 3.973940e+000
+      vertex 4.982020e+001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -2.893023e+000 4.479055e+000
+      vertex 3.482020e+001 -4.938600e+000 5.000000e+000
+      vertex 3.482020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -2.893023e+000 4.479055e+000
+      vertex 3.482020e+001 -4.862639e+000 4.131759e+000
+      vertex 3.482020e+001 -4.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -2.893023e+000 4.479055e+000
+      vertex 3.482020e+001 -2.757678e+000 3.973940e+000
+      vertex 3.482020e+001 -4.862639e+000 4.131759e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.862639e+000 4.131759e+000
+      vertex 3.482020e+001 -2.757678e+000 3.973940e+000
+      vertex 3.482020e+001 -4.637063e+000 3.289899e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.637063e+000 3.289899e+000
+      vertex 3.482020e+001 -2.757678e+000 3.973940e+000
+      vertex 3.482020e+001 -2.536676e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.268727e+000 2.500000e+000
+      vertex 3.482020e+001 -2.536676e+000 3.500000e+000
+      vertex 3.482020e+001 -2.236733e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -3.768822e+000 1.786062e+000
+      vertex 3.482020e+001 -2.236733e+000 3.071637e+000
+      vertex 3.482020e+001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -3.152538e+000 1.169778e+000
+      vertex 3.482020e+001 -1.866963e+000 2.701867e+000
+      vertex 3.482020e+001 -2.438600e+000 6.698730e-001
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -3.152538e+000 1.169778e+000
+      vertex 3.482020e+001 -3.768822e+000 1.786062e+000
+      vertex 3.482020e+001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -1.866963e+000 2.701867e+000
+      vertex 3.482020e+001 -1.438600e+000 2.401924e+000
+      vertex 3.482020e+001 -2.438600e+000 6.698730e-001
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -2.438600e+000 6.698730e-001
+      vertex 3.482020e+001 -1.438600e+000 2.401924e+000
+      vertex 3.482020e+001 -1.648701e+000 3.015369e-001
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -1.648701e+000 3.015369e-001
+      vertex 3.482020e+001 -1.438600e+000 2.401924e+000
+      vertex 3.482020e+001 -9.646604e-001 2.180922e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -8.068409e-001 7.596123e-002
+      vertex 3.482020e+001 -9.646604e-001 2.180922e+000
+      vertex 3.482020e+001 -4.595445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 0.000000e+000
+      vertex 3.482020e+001 -4.595445e-001 2.045577e+000
+      vertex 3.482020e+001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.823445e-001 2.045577e+000
+      vertex 3.482020e+001 6.140000e-002 0.000000e+000
+      vertex 3.482020e+001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.823445e-001 2.045577e+000
+      vertex 3.482020e+001 1.087460e+000 2.180922e+000
+      vertex 3.482020e+001 6.140000e-002 0.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 0.000000e+000
+      vertex 3.482020e+001 1.087460e+000 2.180922e+000
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 3.482020e+001 1.087460e+000 2.180922e+000
+      vertex 3.482020e+001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 1.989763e+000 2.701867e+000
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 3.482020e+001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 1.989763e+000 2.701867e+000
+      vertex 3.482020e+001 4.150665e+000 6.093379e-001
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 1.989763e+000 2.701867e+000
+      vertex 3.482020e+001 2.359533e+000 3.071637e+000
+      vertex 3.482020e+001 4.150665e+000 6.093379e-001
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 4.150665e+000 6.093379e-001
+      vertex 3.482020e+001 2.359533e+000 3.071637e+000
+      vertex 3.482020e+001 4.813319e+000 1.336768e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 4.813319e+000 1.336768e+000
+      vertex 3.482020e+001 2.359533e+000 3.071637e+000
+      vertex 3.482020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.348164e+000 2.162726e+000
+      vertex 3.482020e+001 2.659476e+000 3.500000e+000
+      vertex 3.482020e+001 2.880478e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.740815e+000 3.064995e+000
+      vertex 3.482020e+001 2.880478e+000 3.973940e+000
+      vertex 3.482020e+001 3.015823e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.061400e+000 5.000000e+000
+      vertex 3.482020e+001 3.015823e+000 4.479055e+000
+      vertex 3.482020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.015823e+000 5.520945e+000
+      vertex 3.482020e+001 5.061400e+000 5.000000e+000
+      vertex 3.482020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.015823e+000 5.520945e+000
+      vertex 3.482020e+001 4.985439e+000 5.868241e+000
+      vertex 3.482020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.015823e+000 5.520945e+000
+      vertex 3.482020e+001 2.880478e+000 6.026060e+000
+      vertex 3.482020e+001 4.985439e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 4.985439e+000 5.868241e+000
+      vertex 3.482020e+001 2.880478e+000 6.026060e+000
+      vertex 3.482020e+001 4.759863e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 4.759863e+000 6.710101e+000
+      vertex 3.482020e+001 2.880478e+000 6.026060e+000
+      vertex 3.482020e+001 2.659476e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 4.391527e+000 7.500000e+000
+      vertex 3.482020e+001 2.659476e+000 6.500000e+000
+      vertex 3.482020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.891622e+000 8.213938e+000
+      vertex 3.482020e+001 2.359533e+000 6.928363e+000
+      vertex 3.482020e+001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.275338e+000 8.830222e+000
+      vertex 3.482020e+001 1.989763e+000 7.298133e+000
+      vertex 3.482020e+001 2.561400e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.275338e+000 8.830222e+000
+      vertex 3.482020e+001 3.891622e+000 8.213938e+000
+      vertex 3.482020e+001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 1.989763e+000 7.298133e+000
+      vertex 3.482020e+001 1.561400e+000 7.598076e+000
+      vertex 3.482020e+001 2.561400e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 2.561400e+000 9.330127e+000
+      vertex 3.482020e+001 1.561400e+000 7.598076e+000
+      vertex 3.482020e+001 1.771501e+000 9.698463e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 1.771501e+000 9.698463e+000
+      vertex 3.482020e+001 1.561400e+000 7.598076e+000
+      vertex 3.482020e+001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 9.296409e-001 9.924039e+000
+      vertex 3.482020e+001 1.087460e+000 7.819078e+000
+      vertex 3.482020e+001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 1.000000e+001
+      vertex 3.482020e+001 5.823445e-001 7.954423e+000
+      vertex 3.482020e+001 6.140000e-002 8.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -8.068409e-001 9.924039e+000
+      vertex 3.482020e+001 6.140000e-002 8.000000e+000
+      vertex 3.482020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -1.648701e+000 9.698463e+000
+      vertex 3.482020e+001 -4.595445e-001 7.954423e+000
+      vertex 3.482020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -2.438600e+000 9.330127e+000
+      vertex 3.482020e+001 -9.646604e-001 7.819078e+000
+      vertex 3.482020e+001 -1.438600e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -3.152538e+000 8.830222e+000
+      vertex 3.482020e+001 -1.438600e+000 7.598076e+000
+      vertex 3.482020e+001 -1.866963e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -2.236733e+000 6.928363e+000
+      vertex 3.482020e+001 -3.152538e+000 8.830222e+000
+      vertex 3.482020e+001 -1.866963e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -2.236733e+000 6.928363e+000
+      vertex 3.482020e+001 -3.768822e+000 8.213938e+000
+      vertex 3.482020e+001 -3.152538e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -2.236733e+000 6.928363e+000
+      vertex 3.482020e+001 -2.536676e+000 6.500000e+000
+      vertex 3.482020e+001 -3.768822e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -3.768822e+000 8.213938e+000
+      vertex 3.482020e+001 -2.536676e+000 6.500000e+000
+      vertex 3.482020e+001 -4.268727e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.268727e+000 7.500000e+000
+      vertex 3.482020e+001 -2.536676e+000 6.500000e+000
+      vertex 3.482020e+001 -2.757678e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.637063e+000 6.710101e+000
+      vertex 3.482020e+001 -2.757678e+000 6.026060e+000
+      vertex 3.482020e+001 -2.893023e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.862639e+000 5.868241e+000
+      vertex 3.482020e+001 -2.893023e+000 5.520945e+000
+      vertex 3.482020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.938600e+000 5.000000e+000
+      vertex 3.482020e+001 -4.862639e+000 5.868241e+000
+      vertex 3.482020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.061400e+000 5.000000e+000
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 3.482020e+001 5.980711e+000 4.019309e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.740815e+000 3.064995e+000
+      vertex 3.482020e+001 5.061400e+000 5.000000e+000
+      vertex 3.482020e+001 5.980711e+000 4.019309e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.740815e+000 3.064995e+000
+      vertex 3.482020e+001 3.015823e+000 4.479055e+000
+      vertex 3.482020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.740815e+000 3.064995e+000
+      vertex 3.482020e+001 5.348164e+000 2.162726e+000
+      vertex 3.482020e+001 2.880478e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 5.348164e+000 2.162726e+000
+      vertex 3.482020e+001 4.813319e+000 1.336768e+000
+      vertex 3.482020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 0.000000e+000
+      vertex 3.482020e+001 -8.068409e-001 7.596123e-002
+      vertex 3.482020e+001 -4.595445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -1.648701e+000 3.015369e-001
+      vertex 3.482020e+001 -9.646604e-001 2.180922e+000
+      vertex 3.482020e+001 -8.068409e-001 7.596123e-002
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -3.768822e+000 1.786062e+000
+      vertex 3.482020e+001 -4.268727e+000 2.500000e+000
+      vertex 3.482020e+001 -2.236733e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.268727e+000 2.500000e+000
+      vertex 3.482020e+001 -4.637063e+000 3.289899e+000
+      vertex 3.482020e+001 -2.536676e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.862639e+000 5.868241e+000
+      vertex 3.482020e+001 -4.637063e+000 6.710101e+000
+      vertex 3.482020e+001 -2.893023e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -4.637063e+000 6.710101e+000
+      vertex 3.482020e+001 -4.268727e+000 7.500000e+000
+      vertex 3.482020e+001 -2.757678e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -3.152538e+000 8.830222e+000
+      vertex 3.482020e+001 -2.438600e+000 9.330127e+000
+      vertex 3.482020e+001 -1.438600e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -2.438600e+000 9.330127e+000
+      vertex 3.482020e+001 -1.648701e+000 9.698463e+000
+      vertex 3.482020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 -1.648701e+000 9.698463e+000
+      vertex 3.482020e+001 -8.068409e-001 9.924039e+000
+      vertex 3.482020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 1.000000e+001
+      vertex 3.482020e+001 6.140000e-002 8.000000e+000
+      vertex 3.482020e+001 -8.068409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 9.296409e-001 9.924039e+000
+      vertex 3.482020e+001 5.823445e-001 7.954423e+000
+      vertex 3.482020e+001 6.140000e-002 1.000000e+001
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 1.771501e+000 9.698463e+000
+      vertex 3.482020e+001 1.087460e+000 7.819078e+000
+      vertex 3.482020e+001 9.296409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 2.359533e+000 6.928363e+000
+      vertex 3.482020e+001 3.891622e+000 8.213938e+000
+      vertex 3.482020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 3.482020e+001 4.759863e+000 6.710101e+000
+      vertex 3.482020e+001 2.659476e+000 6.500000e+000
+      vertex 3.482020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.966323e-001 8.200039e-002
+    outer loop
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+      vertex 1.482020e+001 5.980711e+000 4.019309e+000
+    endloop
+  endfacet
+  facet normal -4.306895e-017 -9.698266e-001 2.437957e-001
+    outer loop
+      vertex 3.482020e+001 5.980711e+000 4.019309e+000
+      vertex 1.482020e+001 5.980711e+000 4.019309e+000
+      vertex 3.482020e+001 5.740815e+000 3.064995e+000
+    endloop
+  endfacet
+  facet normal -4.425936e-017 -9.966323e-001 8.200039e-002
+    outer loop
+      vertex 3.482020e+001 5.980711e+000 4.019309e+000
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 1.482020e+001 5.980711e+000 4.019309e+000
+    endloop
+  endfacet
+  facet normal 1.692405e-017 -9.698266e-001 2.437957e-001
+    outer loop
+      vertex 1.482020e+001 5.980711e+000 4.019309e+000
+      vertex 1.482020e+001 5.740815e+000 3.064995e+000
+      vertex 3.482020e+001 5.740815e+000 3.064995e+000
+    endloop
+  endfacet
+  facet normal -3.581497e-018 -9.169362e-001 3.990338e-001
+    outer loop
+      vertex 3.482020e+001 5.740815e+000 3.064995e+000
+      vertex 1.482020e+001 5.740815e+000 3.064995e+000
+      vertex 3.482020e+001 5.348164e+000 2.162726e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.169362e-001 3.990338e-001
+    outer loop
+      vertex 3.482020e+001 5.348164e+000 2.162726e+000
+      vertex 1.482020e+001 5.740815e+000 3.064995e+000
+      vertex 1.482020e+001 5.348164e+000 2.162726e+000
+    endloop
+  endfacet
+  facet normal 1.313813e-017 -8.393837e-001 5.435393e-001
+    outer loop
+      vertex 3.482020e+001 4.813319e+000 1.336768e+000
+      vertex 1.482020e+001 5.348164e+000 2.162726e+000
+      vertex 1.482020e+001 4.813319e+000 1.336768e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -7.392549e-001 6.734257e-001
+    outer loop
+      vertex 3.482020e+001 4.150665e+000 6.093379e-001
+      vertex 1.482020e+001 4.813319e+000 1.336768e+000
+      vertex 1.482020e+001 4.150665e+000 6.093379e-001
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -6.192429e-001 7.851995e-001
+    outer loop
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 1.482020e+001 4.150665e+000 6.093379e-001
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -6.192429e-001 7.851995e-001
+    outer loop
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 3.482020e+001 4.150665e+000 6.093379e-001
+      vertex 1.482020e+001 4.150665e+000 6.093379e-001
+    endloop
+  endfacet
+  facet normal 2.820675e-018 -7.392549e-001 6.734257e-001
+    outer loop
+      vertex 3.482020e+001 4.150665e+000 6.093379e-001
+      vertex 3.482020e+001 4.813319e+000 1.336768e+000
+      vertex 1.482020e+001 4.813319e+000 1.336768e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.393837e-001 5.435393e-001
+    outer loop
+      vertex 3.482020e+001 4.813319e+000 1.336768e+000
+      vertex 3.482020e+001 5.348164e+000 2.162726e+000
+      vertex 1.482020e+001 5.348164e+000 2.162726e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -2.893023e+000 5.520945e+000
+      vertex 1.482020e+001 -4.862639e+000 5.868241e+000
+      vertex 1.482020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -2.893023e+000 5.520945e+000
+      vertex 1.482020e+001 -4.637063e+000 6.710101e+000
+      vertex 1.482020e+001 -4.862639e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -2.893023e+000 5.520945e+000
+      vertex 1.482020e+001 -2.757678e+000 6.026060e+000
+      vertex 1.482020e+001 -4.637063e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.637063e+000 6.710101e+000
+      vertex 1.482020e+001 -2.757678e+000 6.026060e+000
+      vertex 1.482020e+001 -4.268727e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.268727e+000 7.500000e+000
+      vertex 1.482020e+001 -2.757678e+000 6.026060e+000
+      vertex 1.482020e+001 -2.536676e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -3.768822e+000 8.213938e+000
+      vertex 1.482020e+001 -2.536676e+000 6.500000e+000
+      vertex 1.482020e+001 -2.236733e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -1.866963e+000 7.298133e+000
+      vertex 1.482020e+001 -3.768822e+000 8.213938e+000
+      vertex 1.482020e+001 -2.236733e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -1.866963e+000 7.298133e+000
+      vertex 1.482020e+001 -3.152538e+000 8.830222e+000
+      vertex 1.482020e+001 -3.768822e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -1.866963e+000 7.298133e+000
+      vertex 1.482020e+001 -1.438600e+000 7.598076e+000
+      vertex 1.482020e+001 -3.152538e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -3.152538e+000 8.830222e+000
+      vertex 1.482020e+001 -1.438600e+000 7.598076e+000
+      vertex 1.482020e+001 -2.438600e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -2.438600e+000 9.330127e+000
+      vertex 1.482020e+001 -1.438600e+000 7.598076e+000
+      vertex 1.482020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -1.648701e+000 9.698463e+000
+      vertex 1.482020e+001 -9.646604e-001 7.819078e+000
+      vertex 1.482020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -8.068409e-001 9.924039e+000
+      vertex 1.482020e+001 -4.595445e-001 7.954423e+000
+      vertex 1.482020e+001 6.140000e-002 8.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 6.140000e-002 1.000000e+001
+      vertex 1.482020e+001 6.140000e-002 8.000000e+000
+      vertex 1.482020e+001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 9.296409e-001 9.924039e+000
+      vertex 1.482020e+001 5.823445e-001 7.954423e+000
+      vertex 1.482020e+001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 1.771501e+000 9.698463e+000
+      vertex 1.482020e+001 1.087460e+000 7.819078e+000
+      vertex 1.482020e+001 1.561400e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 2.561400e+000 9.330127e+000
+      vertex 1.482020e+001 1.561400e+000 7.598076e+000
+      vertex 1.482020e+001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.275338e+000 8.830222e+000
+      vertex 1.482020e+001 1.989763e+000 7.298133e+000
+      vertex 1.482020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.891622e+000 8.213938e+000
+      vertex 1.482020e+001 2.359533e+000 6.928363e+000
+      vertex 1.482020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.891622e+000 8.213938e+000
+      vertex 1.482020e+001 3.275338e+000 8.830222e+000
+      vertex 1.482020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 2.659476e+000 6.500000e+000
+      vertex 1.482020e+001 4.391527e+000 7.500000e+000
+      vertex 1.482020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 2.659476e+000 6.500000e+000
+      vertex 1.482020e+001 4.759863e+000 6.710101e+000
+      vertex 1.482020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 2.659476e+000 6.500000e+000
+      vertex 1.482020e+001 2.880478e+000 6.026060e+000
+      vertex 1.482020e+001 4.759863e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 4.759863e+000 6.710101e+000
+      vertex 1.482020e+001 2.880478e+000 6.026060e+000
+      vertex 1.482020e+001 4.985439e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 4.985439e+000 5.868241e+000
+      vertex 1.482020e+001 2.880478e+000 6.026060e+000
+      vertex 1.482020e+001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 5.061400e+000 5.000000e+000
+      vertex 1.482020e+001 3.015823e+000 5.520945e+000
+      vertex 1.482020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.015823e+000 4.479055e+000
+      vertex 1.482020e+001 5.061400e+000 5.000000e+000
+      vertex 1.482020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.015823e+000 4.479055e+000
+      vertex 1.482020e+001 5.740815e+000 3.064995e+000
+      vertex 1.482020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.015823e+000 4.479055e+000
+      vertex 1.482020e+001 2.880478e+000 3.973940e+000
+      vertex 1.482020e+001 5.740815e+000 3.064995e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 5.740815e+000 3.064995e+000
+      vertex 1.482020e+001 2.880478e+000 3.973940e+000
+      vertex 1.482020e+001 5.348164e+000 2.162726e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 5.348164e+000 2.162726e+000
+      vertex 1.482020e+001 2.880478e+000 3.973940e+000
+      vertex 1.482020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 4.813319e+000 1.336768e+000
+      vertex 1.482020e+001 2.659476e+000 3.500000e+000
+      vertex 1.482020e+001 2.359533e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 4.150665e+000 6.093379e-001
+      vertex 1.482020e+001 2.359533e+000 3.071637e+000
+      vertex 1.482020e+001 1.989763e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 1.482020e+001 1.989763e+000 2.701867e+000
+      vertex 1.482020e+001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 1.087460e+000 2.180922e+000
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 1.482020e+001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 1.087460e+000 2.180922e+000
+      vertex 1.482020e+001 6.140000e-002 0.000000e+000
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 1.087460e+000 2.180922e+000
+      vertex 1.482020e+001 5.823445e-001 2.045577e+000
+      vertex 1.482020e+001 6.140000e-002 0.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 6.140000e-002 0.000000e+000
+      vertex 1.482020e+001 5.823445e-001 2.045577e+000
+      vertex 1.482020e+001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.595445e-001 2.045577e+000
+      vertex 1.482020e+001 6.140000e-002 0.000000e+000
+      vertex 1.482020e+001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.595445e-001 2.045577e+000
+      vertex 1.482020e+001 -8.068409e-001 7.596123e-002
+      vertex 1.482020e+001 6.140000e-002 0.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.595445e-001 2.045577e+000
+      vertex 1.482020e+001 -9.646604e-001 2.180922e+000
+      vertex 1.482020e+001 -8.068409e-001 7.596123e-002
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -8.068409e-001 7.596123e-002
+      vertex 1.482020e+001 -9.646604e-001 2.180922e+000
+      vertex 1.482020e+001 -1.648701e+000 3.015369e-001
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -1.648701e+000 3.015369e-001
+      vertex 1.482020e+001 -9.646604e-001 2.180922e+000
+      vertex 1.482020e+001 -1.438600e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -2.438600e+000 6.698730e-001
+      vertex 1.482020e+001 -1.438600e+000 2.401924e+000
+      vertex 1.482020e+001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -3.152538e+000 1.169778e+000
+      vertex 1.482020e+001 -1.866963e+000 2.701867e+000
+      vertex 1.482020e+001 -2.236733e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -3.768822e+000 1.786062e+000
+      vertex 1.482020e+001 -2.236733e+000 3.071637e+000
+      vertex 1.482020e+001 -4.268727e+000 2.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -3.768822e+000 1.786062e+000
+      vertex 1.482020e+001 -3.152538e+000 1.169778e+000
+      vertex 1.482020e+001 -2.236733e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -2.236733e+000 3.071637e+000
+      vertex 1.482020e+001 -2.536676e+000 3.500000e+000
+      vertex 1.482020e+001 -4.268727e+000 2.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.268727e+000 2.500000e+000
+      vertex 1.482020e+001 -2.536676e+000 3.500000e+000
+      vertex 1.482020e+001 -4.637063e+000 3.289899e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.637063e+000 3.289899e+000
+      vertex 1.482020e+001 -2.536676e+000 3.500000e+000
+      vertex 1.482020e+001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.862639e+000 4.131759e+000
+      vertex 1.482020e+001 -2.757678e+000 3.973940e+000
+      vertex 1.482020e+001 -2.893023e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.938600e+000 5.000000e+000
+      vertex 1.482020e+001 -2.893023e+000 4.479055e+000
+      vertex 1.482020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.862639e+000 5.868241e+000
+      vertex 1.482020e+001 -4.938600e+000 5.000000e+000
+      vertex 1.482020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+      vertex 1.482020e+001 5.061400e+000 5.000000e+000
+      vertex 1.482020e+001 5.980711e+000 4.019309e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 5.980711e+000 4.019309e+000
+      vertex 1.482020e+001 5.061400e+000 5.000000e+000
+      vertex 1.482020e+001 5.740815e+000 3.064995e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 5.061400e+000 5.000000e+000
+      vertex 1.482020e+001 4.985439e+000 5.868241e+000
+      vertex 1.482020e+001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 2.561400e+000 9.330127e+000
+      vertex 1.482020e+001 1.989763e+000 7.298133e+000
+      vertex 1.482020e+001 3.275338e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 2.561400e+000 9.330127e+000
+      vertex 1.482020e+001 1.771501e+000 9.698463e+000
+      vertex 1.482020e+001 1.561400e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 1.771501e+000 9.698463e+000
+      vertex 1.482020e+001 9.296409e-001 9.924039e+000
+      vertex 1.482020e+001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 6.140000e-002 1.000000e+001
+      vertex 1.482020e+001 5.823445e-001 7.954423e+000
+      vertex 1.482020e+001 9.296409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -8.068409e-001 9.924039e+000
+      vertex 1.482020e+001 6.140000e-002 8.000000e+000
+      vertex 1.482020e+001 6.140000e-002 1.000000e+001
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -8.068409e-001 9.924039e+000
+      vertex 1.482020e+001 -1.648701e+000 9.698463e+000
+      vertex 1.482020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -1.648701e+000 9.698463e+000
+      vertex 1.482020e+001 -2.438600e+000 9.330127e+000
+      vertex 1.482020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -3.768822e+000 8.213938e+000
+      vertex 1.482020e+001 -4.268727e+000 7.500000e+000
+      vertex 1.482020e+001 -2.536676e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.938600e+000 5.000000e+000
+      vertex 1.482020e+001 -4.862639e+000 4.131759e+000
+      vertex 1.482020e+001 -2.893023e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -4.862639e+000 4.131759e+000
+      vertex 1.482020e+001 -4.637063e+000 3.289899e+000
+      vertex 1.482020e+001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -2.438600e+000 6.698730e-001
+      vertex 1.482020e+001 -1.866963e+000 2.701867e+000
+      vertex 1.482020e+001 -3.152538e+000 1.169778e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 -2.438600e+000 6.698730e-001
+      vertex 1.482020e+001 -1.648701e+000 3.015369e-001
+      vertex 1.482020e+001 -1.438600e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 1.482020e+001 4.150665e+000 6.093379e-001
+      vertex 1.482020e+001 1.989763e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 4.150665e+000 6.093379e-001
+      vertex 1.482020e+001 4.813319e+000 1.336768e+000
+      vertex 1.482020e+001 2.359533e+000 3.071637e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 1.482020e+001 4.813319e+000 1.336768e+000
+      vertex 1.482020e+001 5.348164e+000 2.162726e+000
+      vertex 1.482020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.832020e+001 1.706140e+001 5.000000e+000
+      vertex 3.837337e+001 1.766917e+001 5.000000e+000
+      vertex 2.657020e+001 1.609249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.706996e+001 1.574256e+001 5.000000e+000
+      vertex 3.832020e+001 1.706140e+001 5.000000e+000
+      vertex 2.657020e+001 1.609249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.706996e+001 1.574256e+001 5.000000e+000
+      vertex 3.837337e+001 1.645363e+001 5.000000e+000
+      vertex 3.832020e+001 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.706996e+001 1.574256e+001 5.000000e+000
+      vertex 2.750136e+001 1.531116e+001 5.000000e+000
+      vertex 3.837337e+001 1.645363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.837337e+001 1.645363e+001 5.000000e+000
+      vertex 2.750136e+001 1.531116e+001 5.000000e+000
+      vertex 3.853128e+001 1.586433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.853128e+001 1.586433e+001 5.000000e+000
+      vertex 2.750136e+001 1.531116e+001 5.000000e+000
+      vertex 2.785129e+001 1.481140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.878911e+001 1.531140e+001 5.000000e+000
+      vertex 2.785129e+001 1.481140e+001 5.000000e+000
+      vertex 2.810912e+001 1.425847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.913904e+001 1.481164e+001 5.000000e+000
+      vertex 2.810912e+001 1.425847e+001 5.000000e+000
+      vertex 2.826703e+001 1.366917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.957044e+001 1.438024e+001 5.000000e+000
+      vertex 2.826703e+001 1.366917e+001 5.000000e+000
+      vertex 2.832020e+001 1.306140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.007020e+001 1.403031e+001 5.000000e+000
+      vertex 2.832020e+001 1.306140e+001 5.000000e+000
+      vertex 2.826703e+001 1.245363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.062313e+001 1.377248e+001 5.000000e+000
+      vertex 2.826703e+001 1.245363e+001 5.000000e+000
+      vertex 2.810912e+001 1.186433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.785129e+001 1.131140e+001 5.000000e+000
+      vertex 4.062313e+001 1.377248e+001 5.000000e+000
+      vertex 2.810912e+001 1.186433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.785129e+001 1.131140e+001 5.000000e+000
+      vertex 4.121243e+001 1.361457e+001 5.000000e+000
+      vertex 4.062313e+001 1.377248e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.785129e+001 1.131140e+001 5.000000e+000
+      vertex 2.750136e+001 1.081164e+001 5.000000e+000
+      vertex 4.121243e+001 1.361457e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.121243e+001 1.361457e+001 5.000000e+000
+      vertex 2.750136e+001 1.081164e+001 5.000000e+000
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.182020e+001 1.356140e+001 5.000000e+000
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.242797e+001 1.361457e+001 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.301727e+001 1.377248e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.242797e+001 1.361457e+001 5.000000e+000
+      vertex 4.182020e+001 1.356140e+001 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.837337e+001 1.766917e+001 5.000000e+000
+      vertex 3.853128e+001 1.825847e+001 5.000000e+000
+      vertex 2.601727e+001 1.635032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.657020e+001 1.609249e+001 5.000000e+000
+      vertex 3.837337e+001 1.766917e+001 5.000000e+000
+      vertex 2.601727e+001 1.635032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.878911e+001 1.881140e+001 5.000000e+000
+      vertex 2.601727e+001 1.635032e+001 5.000000e+000
+      vertex 3.853128e+001 1.825847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.878911e+001 1.881140e+001 5.000000e+000
+      vertex 2.542797e+001 1.650823e+001 5.000000e+000
+      vertex 2.601727e+001 1.635032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.878911e+001 1.881140e+001 5.000000e+000
+      vertex 3.913904e+001 1.931116e+001 5.000000e+000
+      vertex 2.542797e+001 1.650823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.542797e+001 1.650823e+001 5.000000e+000
+      vertex 3.913904e+001 1.931116e+001 5.000000e+000
+      vertex 3.957044e+001 1.974256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.482020e+001 1.656140e+001 5.000000e+000
+      vertex 3.957044e+001 1.974256e+001 5.000000e+000
+      vertex 4.007020e+001 2.009249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 9.570200e+000 2.009249e+001 5.000000e+000
+      vertex 4.007020e+001 2.009249e+001 5.000000e+000
+      vertex 9.017271e+000 2.035032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 9.570200e+000 2.009249e+001 5.000000e+000
+      vertex 2.482020e+001 1.656140e+001 5.000000e+000
+      vertex 4.007020e+001 2.009249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 9.570200e+000 2.009249e+001 5.000000e+000
+      vertex 1.006996e+001 1.974256e+001 5.000000e+000
+      vertex 2.482020e+001 1.656140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.482020e+001 1.656140e+001 5.000000e+000
+      vertex 1.006996e+001 1.974256e+001 5.000000e+000
+      vertex 2.421243e+001 1.650823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.421243e+001 1.650823e+001 5.000000e+000
+      vertex 1.006996e+001 1.974256e+001 5.000000e+000
+      vertex 1.050136e+001 1.931116e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 1.085129e+001 1.881140e+001 5.000000e+000
+      vertex 2.421243e+001 1.650823e+001 5.000000e+000
+      vertex 1.050136e+001 1.931116e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 1.085129e+001 1.881140e+001 5.000000e+000
+      vertex 2.362313e+001 1.635032e+001 5.000000e+000
+      vertex 2.421243e+001 1.650823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 1.085129e+001 1.881140e+001 5.000000e+000
+      vertex 1.110912e+001 1.825847e+001 5.000000e+000
+      vertex 2.362313e+001 1.635032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.362313e+001 1.635032e+001 5.000000e+000
+      vertex 1.110912e+001 1.825847e+001 5.000000e+000
+      vertex 1.126703e+001 1.766917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.307020e+001 1.609249e+001 5.000000e+000
+      vertex 1.126703e+001 1.766917e+001 5.000000e+000
+      vertex 1.132020e+001 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.257044e+001 1.574256e+001 5.000000e+000
+      vertex 1.132020e+001 1.706140e+001 5.000000e+000
+      vertex 1.126703e+001 1.645363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.213904e+001 1.531116e+001 5.000000e+000
+      vertex 1.126703e+001 1.645363e+001 5.000000e+000
+      vertex 1.110912e+001 1.586433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.178911e+001 1.481140e+001 5.000000e+000
+      vertex 1.110912e+001 1.586433e+001 5.000000e+000
+      vertex 1.085129e+001 1.531140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.153128e+001 1.425847e+001 5.000000e+000
+      vertex 1.085129e+001 1.531140e+001 5.000000e+000
+      vertex 1.050136e+001 1.481164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.137337e+001 1.366917e+001 5.000000e+000
+      vertex 1.050136e+001 1.481164e+001 5.000000e+000
+      vertex 1.006996e+001 1.438024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.132020e+001 1.306140e+001 5.000000e+000
+      vertex 1.006996e+001 1.438024e+001 5.000000e+000
+      vertex 9.570200e+000 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.137337e+001 1.245363e+001 5.000000e+000
+      vertex 9.570200e+000 1.403031e+001 5.000000e+000
+      vertex 9.017271e+000 1.377248e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.153128e+001 1.186433e+001 5.000000e+000
+      vertex 9.017271e+000 1.377248e+001 5.000000e+000
+      vertex 2.178911e+001 1.131140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.153128e+001 1.186433e+001 5.000000e+000
+      vertex 2.137337e+001 1.245363e+001 5.000000e+000
+      vertex 9.017271e+000 1.377248e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.007020e+001 2.009249e+001 5.000000e+000
+      vertex 4.062313e+001 2.035032e+001 5.000000e+000
+      vertex 9.017271e+000 2.035032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 9.017271e+000 2.035032e+001 5.000000e+000
+      vertex 4.062313e+001 2.035032e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 8.427969e+000 2.050823e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 7.820200e+000 2.056140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 8.427969e+000 2.050823e+001 5.000000e+000
+      vertex 9.017271e+000 2.035032e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.121243e+001 2.050823e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.062313e+001 2.035032e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.121243e+001 2.050823e+001 5.000000e+000
+      vertex 4.182020e+001 2.056140e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.182020e+001 2.056140e+001 5.000000e+000
+      vertex 4.242797e+001 2.050823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.301727e+001 2.035032e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.242797e+001 2.050823e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.301727e+001 2.035032e+001 5.000000e+000
+      vertex 4.357020e+001 2.009249e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.357020e+001 2.009249e+001 5.000000e+000
+      vertex 4.406996e+001 1.974256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.450136e+001 1.931116e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.406996e+001 1.974256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.450136e+001 1.931116e+001 5.000000e+000
+      vertex 4.485129e+001 1.881140e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.485129e+001 1.881140e+001 5.000000e+000
+      vertex 4.510912e+001 1.825847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.526703e+001 1.766917e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.510912e+001 1.825847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.526703e+001 1.766917e+001 5.000000e+000
+      vertex 4.532020e+001 1.706140e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.532020e+001 1.706140e+001 5.000000e+000
+      vertex 4.526703e+001 1.645363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.526703e+001 1.645363e+001 5.000000e+000
+      vertex 4.510912e+001 1.586433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.485129e+001 1.531140e+001 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.510912e+001 1.586433e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.485129e+001 1.531140e+001 5.000000e+000
+      vertex 4.450136e+001 1.481164e+001 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.450136e+001 1.481164e+001 5.000000e+000
+      vertex 4.406996e+001 1.438024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.357020e+001 1.403031e+001 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.406996e+001 1.438024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.357020e+001 1.403031e+001 5.000000e+000
+      vertex 4.301727e+001 1.377248e+001 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.121243e+001 1.361457e+001 5.000000e+000
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 4.182020e+001 1.356140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.062313e+001 1.377248e+001 5.000000e+000
+      vertex 4.007020e+001 1.403031e+001 5.000000e+000
+      vertex 2.826703e+001 1.245363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.957044e+001 1.438024e+001 5.000000e+000
+      vertex 2.832020e+001 1.306140e+001 5.000000e+000
+      vertex 4.007020e+001 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.957044e+001 1.438024e+001 5.000000e+000
+      vertex 3.913904e+001 1.481164e+001 5.000000e+000
+      vertex 2.826703e+001 1.366917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.878911e+001 1.531140e+001 5.000000e+000
+      vertex 2.810912e+001 1.425847e+001 5.000000e+000
+      vertex 3.913904e+001 1.481164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.878911e+001 1.531140e+001 5.000000e+000
+      vertex 3.853128e+001 1.586433e+001 5.000000e+000
+      vertex 2.785129e+001 1.481140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.137337e+001 1.366917e+001 5.000000e+000
+      vertex 1.006996e+001 1.438024e+001 5.000000e+000
+      vertex 2.132020e+001 1.306140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.137337e+001 1.366917e+001 5.000000e+000
+      vertex 2.153128e+001 1.425847e+001 5.000000e+000
+      vertex 1.050136e+001 1.481164e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.178911e+001 1.481140e+001 5.000000e+000
+      vertex 1.085129e+001 1.531140e+001 5.000000e+000
+      vertex 2.153128e+001 1.425847e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.213904e+001 1.531116e+001 5.000000e+000
+      vertex 1.110912e+001 1.586433e+001 5.000000e+000
+      vertex 2.178911e+001 1.481140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.213904e+001 1.531116e+001 5.000000e+000
+      vertex 2.257044e+001 1.574256e+001 5.000000e+000
+      vertex 1.126703e+001 1.645363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.257044e+001 1.574256e+001 5.000000e+000
+      vertex 2.307020e+001 1.609249e+001 5.000000e+000
+      vertex 1.132020e+001 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.307020e+001 1.609249e+001 5.000000e+000
+      vertex 2.362313e+001 1.635032e+001 5.000000e+000
+      vertex 1.126703e+001 1.766917e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.482020e+001 1.656140e+001 5.000000e+000
+      vertex 2.542797e+001 1.650823e+001 5.000000e+000
+      vertex 3.957044e+001 1.974256e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.750136e+001 1.081164e+001 5.000000e+000
+      vertex 2.706996e+001 1.038024e+001 5.000000e+000
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 2.706996e+001 1.038024e+001 5.000000e+000
+      vertex 2.657020e+001 1.003031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.601727e+001 9.772476e+000 5.000000e+000
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 2.657020e+001 1.003031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.601727e+001 9.772476e+000 5.000000e+000
+      vertex 2.542797e+001 9.614573e+000 5.000000e+000
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 2.542797e+001 9.614573e+000 5.000000e+000
+      vertex 2.482020e+001 9.561400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+      vertex 2.482020e+001 9.561400e+000 5.000000e+000
+      vertex 2.421243e+001 9.614573e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.362313e+001 9.772476e+000 5.000000e+000
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+      vertex 2.421243e+001 9.614573e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.362313e+001 9.772476e+000 5.000000e+000
+      vertex 2.307020e+001 1.003031e+001 5.000000e+000
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+      vertex 2.307020e+001 1.003031e+001 5.000000e+000
+      vertex 2.257044e+001 1.038024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.213904e+001 1.081164e+001 5.000000e+000
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+      vertex 2.257044e+001 1.038024e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.213904e+001 1.081164e+001 5.000000e+000
+      vertex 8.427969e+000 1.361457e+001 5.000000e+000
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.213904e+001 1.081164e+001 5.000000e+000
+      vertex 2.178911e+001 1.131140e+001 5.000000e+000
+      vertex 8.427969e+000 1.361457e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 8.427969e+000 1.361457e+001 5.000000e+000
+      vertex 2.178911e+001 1.131140e+001 5.000000e+000
+      vertex 9.017271e+000 1.377248e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 2.132020e+001 1.306140e+001 5.000000e+000
+      vertex 9.570200e+000 1.403031e+001 5.000000e+000
+      vertex 2.137337e+001 1.245363e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.373373e+000 1.766917e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 4.320200e+000 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.373373e+000 1.766917e+001 5.000000e+000
+      vertex 4.531276e+000 1.825847e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 4.531276e+000 1.825847e+001 5.000000e+000
+      vertex 4.789111e+000 1.881140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 5.139044e+000 1.931116e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 4.789111e+000 1.881140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 5.139044e+000 1.931116e+001 5.000000e+000
+      vertex 5.570443e+000 1.974256e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 5.570443e+000 1.974256e+001 5.000000e+000
+      vertex 6.070200e+000 2.009249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 6.623129e+000 2.035032e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 6.070200e+000 2.009249e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 6.623129e+000 2.035032e+001 5.000000e+000
+      vertex 7.212431e+000 2.050823e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 7.212431e+000 2.050823e+001 5.000000e+000
+      vertex 7.820200e+000 2.056140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 8.427969e+000 1.361457e+001 5.000000e+000
+      vertex 7.820200e+000 1.356140e+001 5.000000e+000
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+      vertex 7.820200e+000 1.356140e+001 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 1.482020e+001 5.061400e+000 5.000000e+000
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 7.212431e+000 1.361457e+001 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex 7.820200e+000 1.356140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 7.212431e+000 1.361457e+001 5.000000e+000
+      vertex 6.623129e+000 1.377248e+001 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex 6.623129e+000 1.377248e+001 5.000000e+000
+      vertex 6.070200e+000 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 5.570443e+000 1.438024e+001 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex 6.070200e+000 1.403031e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 5.570443e+000 1.438024e+001 5.000000e+000
+      vertex 5.139044e+000 1.481164e+001 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex 5.139044e+000 1.481164e+001 5.000000e+000
+      vertex 4.789111e+000 1.531140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.531276e+000 1.586433e+001 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex 4.789111e+000 1.531140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.531276e+000 1.586433e+001 5.000000e+000
+      vertex 4.373373e+000 1.645363e+001 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex 4.373373e+000 1.645363e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 4.373373e+000 1.645363e+001 5.000000e+000
+      vertex 4.320200e+000 1.706140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 1.482020e+001 6.061400e+000 5.000000e+000
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 2.482020e+001 9.561400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 3.482020e+001 6.061400e+000 5.000000e+000
+      vertex 3.482020e+001 5.061400e+000 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.526703e+001 1.645363e+001 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 4.062313e+001 2.035032e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 9.961947e-001 8.715574e-002
+    outer loop
+      vertex 3.482020e+001 5.061400e+000 5.000000e+000
+      vertex 3.482020e+001 4.985439e+000 5.868241e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 9.961947e-001 8.715574e-002
+    outer loop
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 3.482020e+001 4.985439e+000 5.868241e+000
+      vertex 4.982020e+001 4.985439e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 9.659258e-001 2.588190e-001
+    outer loop
+      vertex 4.982020e+001 4.985439e+000 5.868241e+000
+      vertex 3.482020e+001 4.985439e+000 5.868241e+000
+      vertex 3.482020e+001 4.759863e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 9.063078e-001 4.226183e-001
+    outer loop
+      vertex 4.982020e+001 4.759863e+000 6.710101e+000
+      vertex 3.482020e+001 4.759863e+000 6.710101e+000
+      vertex 3.482020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 8.191520e-001 5.735764e-001
+    outer loop
+      vertex 4.982020e+001 4.391527e+000 7.500000e+000
+      vertex 3.482020e+001 4.391527e+000 7.500000e+000
+      vertex 3.482020e+001 3.891622e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 7.071068e-001 7.071068e-001
+    outer loop
+      vertex 4.982020e+001 3.891622e+000 8.213938e+000
+      vertex 3.482020e+001 3.891622e+000 8.213938e+000
+      vertex 3.482020e+001 3.275338e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 5.735764e-001 8.191520e-001
+    outer loop
+      vertex 4.982020e+001 3.275338e+000 8.830222e+000
+      vertex 3.482020e+001 3.275338e+000 8.830222e+000
+      vertex 3.482020e+001 2.561400e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 4.226183e-001 9.063078e-001
+    outer loop
+      vertex 4.982020e+001 2.561400e+000 9.330127e+000
+      vertex 3.482020e+001 2.561400e+000 9.330127e+000
+      vertex 3.482020e+001 1.771501e+000 9.698463e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 2.588190e-001 9.659258e-001
+    outer loop
+      vertex 4.982020e+001 1.771501e+000 9.698463e+000
+      vertex 3.482020e+001 1.771501e+000 9.698463e+000
+      vertex 3.482020e+001 9.296409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 8.715574e-002 9.961947e-001
+    outer loop
+      vertex 4.982020e+001 9.296409e-001 9.924039e+000
+      vertex 3.482020e+001 9.296409e-001 9.924039e+000
+      vertex 3.482020e+001 6.140000e-002 1.000000e+001
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.715574e-002 9.961947e-001
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 1.000000e+001
+      vertex 3.482020e+001 6.140000e-002 1.000000e+001
+      vertex 3.482020e+001 -8.068409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -2.588190e-001 9.659258e-001
+    outer loop
+      vertex 4.982020e+001 -8.068409e-001 9.924039e+000
+      vertex 3.482020e+001 -8.068409e-001 9.924039e+000
+      vertex 3.482020e+001 -1.648701e+000 9.698463e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -4.226183e-001 9.063078e-001
+    outer loop
+      vertex 4.982020e+001 -1.648701e+000 9.698463e+000
+      vertex 3.482020e+001 -1.648701e+000 9.698463e+000
+      vertex 3.482020e+001 -2.438600e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -5.735764e-001 8.191520e-001
+    outer loop
+      vertex 4.982020e+001 -2.438600e+000 9.330127e+000
+      vertex 3.482020e+001 -2.438600e+000 9.330127e+000
+      vertex 3.482020e+001 -3.152538e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -7.071068e-001 7.071068e-001
+    outer loop
+      vertex 4.982020e+001 -3.152538e+000 8.830222e+000
+      vertex 3.482020e+001 -3.152538e+000 8.830222e+000
+      vertex 3.482020e+001 -3.768822e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.191520e-001 5.735764e-001
+    outer loop
+      vertex 4.982020e+001 -3.768822e+000 8.213938e+000
+      vertex 3.482020e+001 -3.768822e+000 8.213938e+000
+      vertex 3.482020e+001 -4.268727e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.063078e-001 4.226183e-001
+    outer loop
+      vertex 4.982020e+001 -4.268727e+000 7.500000e+000
+      vertex 3.482020e+001 -4.268727e+000 7.500000e+000
+      vertex 3.482020e+001 -4.637063e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.659258e-001 2.588190e-001
+    outer loop
+      vertex 4.982020e+001 -4.637063e+000 6.710101e+000
+      vertex 3.482020e+001 -4.637063e+000 6.710101e+000
+      vertex 3.482020e+001 -4.862639e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.961947e-001 8.715574e-002
+    outer loop
+      vertex 4.982020e+001 -4.862639e+000 5.868241e+000
+      vertex 3.482020e+001 -4.862639e+000 5.868241e+000
+      vertex 3.482020e+001 -4.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.961947e-001 -8.715574e-002
+    outer loop
+      vertex 4.982020e+001 -4.938600e+000 5.000000e+000
+      vertex 3.482020e+001 -4.938600e+000 5.000000e+000
+      vertex 3.482020e+001 -4.862639e+000 4.131759e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.659258e-001 -2.588190e-001
+    outer loop
+      vertex 4.982020e+001 -4.862639e+000 4.131759e+000
+      vertex 3.482020e+001 -4.862639e+000 4.131759e+000
+      vertex 3.482020e+001 -4.637063e+000 3.289899e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.063078e-001 -4.226183e-001
+    outer loop
+      vertex 4.982020e+001 -4.637063e+000 3.289899e+000
+      vertex 3.482020e+001 -4.637063e+000 3.289899e+000
+      vertex 3.482020e+001 -4.268727e+000 2.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.191520e-001 -5.735764e-001
+    outer loop
+      vertex 4.982020e+001 -4.268727e+000 2.500000e+000
+      vertex 3.482020e+001 -4.268727e+000 2.500000e+000
+      vertex 3.482020e+001 -3.768822e+000 1.786062e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -7.071068e-001 -7.071068e-001
+    outer loop
+      vertex 4.982020e+001 -3.768822e+000 1.786062e+000
+      vertex 3.482020e+001 -3.768822e+000 1.786062e+000
+      vertex 3.482020e+001 -3.152538e+000 1.169778e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -5.735764e-001 -8.191520e-001
+    outer loop
+      vertex 4.982020e+001 -3.152538e+000 1.169778e+000
+      vertex 3.482020e+001 -3.152538e+000 1.169778e+000
+      vertex 3.482020e+001 -2.438600e+000 6.698730e-001
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -4.226183e-001 -9.063078e-001
+    outer loop
+      vertex 4.982020e+001 -2.438600e+000 6.698730e-001
+      vertex 3.482020e+001 -2.438600e+000 6.698730e-001
+      vertex 3.482020e+001 -1.648701e+000 3.015369e-001
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -2.588190e-001 -9.659258e-001
+    outer loop
+      vertex 4.982020e+001 -1.648701e+000 3.015369e-001
+      vertex 3.482020e+001 -1.648701e+000 3.015369e-001
+      vertex 3.482020e+001 -8.068409e-001 7.596123e-002
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.715574e-002 -9.961947e-001
+    outer loop
+      vertex 4.982020e+001 -8.068409e-001 7.596123e-002
+      vertex 3.482020e+001 -8.068409e-001 7.596123e-002
+      vertex 3.482020e+001 6.140000e-002 0.000000e+000
+    endloop
+  endfacet
+  facet normal -5.124559e-017 -8.715574e-002 -9.961947e-001
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+      vertex 4.982020e+001 -8.068409e-001 7.596123e-002
+      vertex 3.482020e+001 6.140000e-002 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -2.588190e-001 -9.659258e-001
+    outer loop
+      vertex 4.982020e+001 -8.068409e-001 7.596123e-002
+      vertex 4.982020e+001 -1.648701e+000 3.015369e-001
+      vertex 3.482020e+001 -8.068409e-001 7.596123e-002
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -4.226183e-001 -9.063078e-001
+    outer loop
+      vertex 4.982020e+001 -1.648701e+000 3.015369e-001
+      vertex 4.982020e+001 -2.438600e+000 6.698730e-001
+      vertex 3.482020e+001 -1.648701e+000 3.015369e-001
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -5.735764e-001 -8.191520e-001
+    outer loop
+      vertex 4.982020e+001 -2.438600e+000 6.698730e-001
+      vertex 4.982020e+001 -3.152538e+000 1.169778e+000
+      vertex 3.482020e+001 -2.438600e+000 6.698730e-001
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -7.071068e-001 -7.071068e-001
+    outer loop
+      vertex 4.982020e+001 -3.152538e+000 1.169778e+000
+      vertex 4.982020e+001 -3.768822e+000 1.786062e+000
+      vertex 3.482020e+001 -3.152538e+000 1.169778e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.191520e-001 -5.735764e-001
+    outer loop
+      vertex 4.982020e+001 -3.768822e+000 1.786062e+000
+      vertex 4.982020e+001 -4.268727e+000 2.500000e+000
+      vertex 3.482020e+001 -3.768822e+000 1.786062e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.063078e-001 -4.226183e-001
+    outer loop
+      vertex 4.982020e+001 -4.268727e+000 2.500000e+000
+      vertex 4.982020e+001 -4.637063e+000 3.289899e+000
+      vertex 3.482020e+001 -4.268727e+000 2.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.659258e-001 -2.588190e-001
+    outer loop
+      vertex 4.982020e+001 -4.637063e+000 3.289899e+000
+      vertex 4.982020e+001 -4.862639e+000 4.131759e+000
+      vertex 3.482020e+001 -4.637063e+000 3.289899e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.961947e-001 -8.715574e-002
+    outer loop
+      vertex 4.982020e+001 -4.862639e+000 4.131759e+000
+      vertex 4.982020e+001 -4.938600e+000 5.000000e+000
+      vertex 3.482020e+001 -4.862639e+000 4.131759e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.961947e-001 8.715574e-002
+    outer loop
+      vertex 4.982020e+001 -4.938600e+000 5.000000e+000
+      vertex 4.982020e+001 -4.862639e+000 5.868241e+000
+      vertex 3.482020e+001 -4.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.659258e-001 2.588190e-001
+    outer loop
+      vertex 4.982020e+001 -4.862639e+000 5.868241e+000
+      vertex 4.982020e+001 -4.637063e+000 6.710101e+000
+      vertex 3.482020e+001 -4.862639e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.063078e-001 4.226183e-001
+    outer loop
+      vertex 4.982020e+001 -4.637063e+000 6.710101e+000
+      vertex 4.982020e+001 -4.268727e+000 7.500000e+000
+      vertex 3.482020e+001 -4.637063e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.191520e-001 5.735764e-001
+    outer loop
+      vertex 4.982020e+001 -4.268727e+000 7.500000e+000
+      vertex 4.982020e+001 -3.768822e+000 8.213938e+000
+      vertex 3.482020e+001 -4.268727e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -7.071068e-001 7.071068e-001
+    outer loop
+      vertex 4.982020e+001 -3.768822e+000 8.213938e+000
+      vertex 4.982020e+001 -3.152538e+000 8.830222e+000
+      vertex 3.482020e+001 -3.768822e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -5.735764e-001 8.191520e-001
+    outer loop
+      vertex 4.982020e+001 -3.152538e+000 8.830222e+000
+      vertex 4.982020e+001 -2.438600e+000 9.330127e+000
+      vertex 3.482020e+001 -3.152538e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -4.226183e-001 9.063078e-001
+    outer loop
+      vertex 4.982020e+001 -2.438600e+000 9.330127e+000
+      vertex 4.982020e+001 -1.648701e+000 9.698463e+000
+      vertex 3.482020e+001 -2.438600e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -2.588190e-001 9.659258e-001
+    outer loop
+      vertex 4.982020e+001 -1.648701e+000 9.698463e+000
+      vertex 4.982020e+001 -8.068409e-001 9.924039e+000
+      vertex 3.482020e+001 -1.648701e+000 9.698463e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.715574e-002 9.961947e-001
+    outer loop
+      vertex 4.982020e+001 -8.068409e-001 9.924039e+000
+      vertex 4.982020e+001 6.140000e-002 1.000000e+001
+      vertex 3.482020e+001 -8.068409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 8.715574e-002 9.961947e-001
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 1.000000e+001
+      vertex 4.982020e+001 9.296409e-001 9.924039e+000
+      vertex 3.482020e+001 6.140000e-002 1.000000e+001
+    endloop
+  endfacet
+  facet normal 0.000000e+000 2.588190e-001 9.659258e-001
+    outer loop
+      vertex 4.982020e+001 9.296409e-001 9.924039e+000
+      vertex 4.982020e+001 1.771501e+000 9.698463e+000
+      vertex 3.482020e+001 9.296409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 4.226183e-001 9.063078e-001
+    outer loop
+      vertex 4.982020e+001 1.771501e+000 9.698463e+000
+      vertex 4.982020e+001 2.561400e+000 9.330127e+000
+      vertex 3.482020e+001 1.771501e+000 9.698463e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 5.735764e-001 8.191520e-001
+    outer loop
+      vertex 4.982020e+001 2.561400e+000 9.330127e+000
+      vertex 4.982020e+001 3.275338e+000 8.830222e+000
+      vertex 3.482020e+001 2.561400e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 7.071068e-001 7.071068e-001
+    outer loop
+      vertex 4.982020e+001 3.275338e+000 8.830222e+000
+      vertex 4.982020e+001 3.891622e+000 8.213938e+000
+      vertex 3.482020e+001 3.275338e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 8.191520e-001 5.735764e-001
+    outer loop
+      vertex 4.982020e+001 3.891622e+000 8.213938e+000
+      vertex 4.982020e+001 4.391527e+000 7.500000e+000
+      vertex 3.482020e+001 3.891622e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 9.063078e-001 4.226183e-001
+    outer loop
+      vertex 4.982020e+001 4.391527e+000 7.500000e+000
+      vertex 4.982020e+001 4.759863e+000 6.710101e+000
+      vertex 3.482020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 9.659258e-001 2.588190e-001
+    outer loop
+      vertex 4.982020e+001 4.759863e+000 6.710101e+000
+      vertex 4.982020e+001 4.985439e+000 5.868241e+000
+      vertex 3.482020e+001 4.759863e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 1.432567e-016 1.253334e-017 -1.000000e+000
+    outer loop
+      vertex 4.428222e+001 1.749552e+001 -8.881784e-016
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.432020e+001 1.706140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 1.174024e-016 3.145787e-017 -1.000000e+000
+    outer loop
+      vertex 4.428222e+001 1.749552e+001 -8.881784e-016
+      vertex 4.416943e+001 1.791645e+001 -8.881784e-016
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 9.887861e-017 4.610785e-017 -1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.416943e+001 1.791645e+001 -8.881784e-016
+      vertex 4.398526e+001 1.831140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 8.409715e-017 5.888546e-017 -1.000000e+000
+    outer loop
+      vertex 4.373531e+001 1.866837e+001 -8.881784e-016
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.398526e+001 1.831140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.118001e-017 7.118001e-017 -1.000000e+000
+    outer loop
+      vertex 4.373531e+001 1.866837e+001 -8.881784e-016
+      vertex 4.342717e+001 1.897651e+001 -8.881784e-016
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 5.888546e-017 8.409715e-017 -1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.342717e+001 1.897651e+001 -8.881784e-016
+      vertex 4.307020e+001 1.922646e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 4.610785e-017 9.887861e-017 -1.000000e+000
+    outer loop
+      vertex 4.267525e+001 1.941063e+001 -8.881784e-016
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.307020e+001 1.922646e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 3.145787e-017 1.174024e-016 -1.000000e+000
+    outer loop
+      vertex 4.267525e+001 1.941063e+001 -8.881784e-016
+      vertex 4.225432e+001 1.952342e+001 -8.881784e-016
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 1.253334e-017 1.432567e-016 -1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.225432e+001 1.952342e+001 -8.881784e-016
+      vertex 4.182020e+001 1.956140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -1.618835e-017 1.850337e-016 -1.000000e+000
+    outer loop
+      vertex 4.138608e+001 1.952342e+001 -8.881784e-016
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.182020e+001 1.956140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -7.259975e-017 2.709460e-016 -1.000000e+000
+    outer loop
+      vertex 4.138608e+001 1.952342e+001 -8.881784e-016
+      vertex 4.096515e+001 1.941063e+001 -8.881784e-016
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 1.571783e-016 -1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.096515e+001 1.941063e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -1.383491e-031 1.571783e-016 -1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 4.096515e+001 1.941063e+001 -8.881784e-016
+      vertex 8.675250e+000 1.941063e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.259975e-017 2.709460e-016 -1.000000e+000
+    outer loop
+      vertex 8.254320e+000 1.952342e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 8.675250e+000 1.941063e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 1.618835e-017 1.850337e-016 -1.000000e+000
+    outer loop
+      vertex 8.254320e+000 1.952342e+001 -8.881784e-016
+      vertex 7.820200e+000 1.956140e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -1.253334e-017 1.432567e-016 -1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 7.820200e+000 1.956140e+001 -8.881784e-016
+      vertex 7.386080e+000 1.952342e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -3.145787e-017 1.174024e-016 -1.000000e+000
+    outer loop
+      vertex 6.965150e+000 1.941063e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 7.386080e+000 1.952342e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -4.610785e-017 9.887861e-017 -1.000000e+000
+    outer loop
+      vertex 6.965150e+000 1.941063e+001 -8.881784e-016
+      vertex 6.570200e+000 1.922646e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -5.888546e-017 8.409715e-017 -1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 6.570200e+000 1.922646e+001 -8.881784e-016
+      vertex 6.213231e+000 1.897651e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -7.118001e-017 7.118001e-017 -1.000000e+000
+    outer loop
+      vertex 5.905089e+000 1.866837e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 6.213231e+000 1.897651e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -8.409715e-017 5.888546e-017 -1.000000e+000
+    outer loop
+      vertex 5.905089e+000 1.866837e+001 -8.881784e-016
+      vertex 5.655136e+000 1.831140e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -9.887861e-017 4.610785e-017 -1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 5.655136e+000 1.831140e+001 -8.881784e-016
+      vertex 5.470968e+000 1.791645e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -1.174024e-016 3.145787e-017 -1.000000e+000
+    outer loop
+      vertex 5.358181e+000 1.749552e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 5.470968e+000 1.791645e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -1.432567e-016 1.253334e-017 -1.000000e+000
+    outer loop
+      vertex 5.358181e+000 1.749552e+001 -8.881784e-016
+      vertex 5.320200e+000 1.706140e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -1.850337e-016 -1.618835e-017 -1.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 5.320200e+000 1.706140e+001 -8.881784e-016
+      vertex 5.358181e+000 1.662728e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -2.709460e-016 -7.259975e-017 -1.000000e+000
+    outer loop
+      vertex 5.470968e+000 1.620635e+001 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex 5.358181e+000 1.662728e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -1.015055e-016 3.552714e-017 -1.000000e+000
+    outer loop
+      vertex 5.470968e+000 1.620635e+001 -8.881784e-016
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 5.470968e+000 1.620635e+001 -8.881784e-016
+      vertex 5.655136e+000 1.581140e+001 -8.881784e-016
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex 5.655136e+000 1.581140e+001 -8.881784e-016
+      vertex 5.905089e+000 1.545443e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 6.213231e+000 1.514629e+001 -8.881784e-016
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex 5.905089e+000 1.545443e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 6.213231e+000 1.514629e+001 -8.881784e-016
+      vertex 6.570200e+000 1.489634e+001 -8.881784e-016
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex 6.570200e+000 1.489634e+001 -8.881784e-016
+      vertex 6.965150e+000 1.471217e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 7.386080e+000 1.459938e+001 -8.881784e-016
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex 6.965150e+000 1.471217e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 6.691135e-017 -3.482212e-017 -1.000000e+000
+    outer loop
+      vertex 7.386080e+000 1.459938e+001 -8.881784e-016
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -7.350854e-018 -8.402064e-017 -1.000000e+000
+    outer loop
+      vertex 7.386080e+000 1.459938e+001 -8.881784e-016
+      vertex 7.820200e+000 1.456140e+001 -8.881784e-016
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal 6.587563e-018 -7.529618e-017 -1.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 7.820200e+000 1.456140e+001 -8.881784e-016
+      vertex 8.254320e+000 1.459938e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.134736e-017 4.995796e-017 -1.000000e+000
+    outer loop
+      vertex 2.290509e+001 1.145443e+001 0.000000e+000
+      vertex 8.254320e+000 1.459938e+001 -8.881784e-016
+      vertex 2.265514e+001 1.181140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 4.990041e-017 -4.995282e-017 -1.000000e+000
+    outer loop
+      vertex 2.290509e+001 1.145443e+001 0.000000e+000
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 8.254320e+000 1.459938e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 2.290509e+001 1.145443e+001 0.000000e+000
+      vertex 2.321323e+001 1.114629e+001 0.000000e+000
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 2.321323e+001 1.114629e+001 0.000000e+000
+      vertex 2.357020e+001 1.089634e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 2.396515e+001 1.071217e+001 0.000000e+000
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 2.357020e+001 1.089634e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 2.396515e+001 1.071217e+001 0.000000e+000
+      vertex 2.438608e+001 1.059938e+001 0.000000e+000
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 2.438608e+001 1.059938e+001 0.000000e+000
+      vertex 2.482020e+001 1.056140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 2.482020e+001 1.056140e+001 0.000000e+000
+      vertex 2.525432e+001 1.059938e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 2.567525e+001 1.071217e+001 0.000000e+000
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 2.525432e+001 1.059938e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 2.567525e+001 1.071217e+001 0.000000e+000
+      vertex 2.607020e+001 1.089634e+001 0.000000e+000
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 2.607020e+001 1.089634e+001 0.000000e+000
+      vertex 2.642717e+001 1.114629e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 2.673531e+001 1.145443e+001 0.000000e+000
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 2.642717e+001 1.114629e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -4.990041e-017 -4.995282e-017 -1.000000e+000
+    outer loop
+      vertex 2.673531e+001 1.145443e+001 0.000000e+000
+      vertex 4.138608e+001 1.459938e+001 -8.881784e-016
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal -7.134736e-017 4.995796e-017 -1.000000e+000
+    outer loop
+      vertex 2.673531e+001 1.145443e+001 0.000000e+000
+      vertex 2.698526e+001 1.181140e+001 0.000000e+000
+      vertex 4.138608e+001 1.459938e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -3.580541e-017 -1.336276e-016 -1.000000e+000
+    outer loop
+      vertex 4.138608e+001 1.459938e+001 -8.881784e-016
+      vertex 2.698526e+001 1.181140e+001 0.000000e+000
+      vertex 4.096515e+001 1.471217e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -7.033831e-017 3.279929e-017 -1.000000e+000
+    outer loop
+      vertex 4.096515e+001 1.471217e+001 -8.881784e-016
+      vertex 2.698526e+001 1.181140e+001 0.000000e+000
+      vertex 2.716943e+001 1.220635e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -7.004568e-017 1.876868e-017 -1.000000e+000
+    outer loop
+      vertex 4.057020e+001 1.489634e+001 -8.881784e-016
+      vertex 2.716943e+001 1.220635e+001 0.000000e+000
+      vertex 2.728222e+001 1.262728e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -6.785445e-017 5.936495e-018 -1.000000e+000
+    outer loop
+      vertex 2.732020e+001 1.306140e+001 0.000000e+000
+      vertex 4.057020e+001 1.489634e+001 -8.881784e-016
+      vertex 2.728222e+001 1.262728e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -5.596390e-017 -7.992473e-017 -1.000000e+000
+    outer loop
+      vertex 2.732020e+001 1.306140e+001 0.000000e+000
+      vertex 4.021323e+001 1.514629e+001 -8.881784e-016
+      vertex 4.057020e+001 1.489634e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -6.792726e-017 -5.942865e-018 -1.000000e+000
+    outer loop
+      vertex 2.732020e+001 1.306140e+001 0.000000e+000
+      vertex 2.728222e+001 1.349552e+001 0.000000e+000
+      vertex 4.021323e+001 1.514629e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -6.091015e-017 -6.091015e-017 -1.000000e+000
+    outer loop
+      vertex 4.021323e+001 1.514629e+001 -8.881784e-016
+      vertex 2.728222e+001 1.349552e+001 0.000000e+000
+      vertex 3.990509e+001 1.545443e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -6.755360e-017 -1.810093e-017 -1.000000e+000
+    outer loop
+      vertex 3.990509e+001 1.545443e+001 -8.881784e-016
+      vertex 2.728222e+001 1.349552e+001 0.000000e+000
+      vertex 2.716943e+001 1.391645e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -6.643400e-017 -3.097868e-017 -1.000000e+000
+    outer loop
+      vertex 3.965514e+001 1.581140e+001 -8.881784e-016
+      vertex 2.716943e+001 1.391645e+001 0.000000e+000
+      vertex 2.698526e+001 1.431140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -6.430222e-017 -4.502490e-017 -1.000000e+000
+    outer loop
+      vertex 3.947097e+001 1.620635e+001 -8.881784e-016
+      vertex 2.698526e+001 1.431140e+001 0.000000e+000
+      vertex 2.673531e+001 1.466837e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -6.091015e-017 -6.091015e-017 -1.000000e+000
+    outer loop
+      vertex 3.935818e+001 1.662728e+001 -8.881784e-016
+      vertex 2.673531e+001 1.466837e+001 0.000000e+000
+      vertex 2.642717e+001 1.497651e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -5.596390e-017 -7.992473e-017 -1.000000e+000
+    outer loop
+      vertex 3.932020e+001 1.706140e+001 -8.881784e-016
+      vertex 2.642717e+001 1.497651e+001 0.000000e+000
+      vertex 2.607020e+001 1.522646e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -7.004568e-017 1.876868e-017 -1.000000e+000
+    outer loop
+      vertex 3.935818e+001 1.749552e+001 -8.881784e-016
+      vertex 2.607020e+001 1.522646e+001 0.000000e+000
+      vertex 3.947097e+001 1.791645e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -6.785445e-017 5.936495e-018 -1.000000e+000
+    outer loop
+      vertex 3.935818e+001 1.749552e+001 -8.881784e-016
+      vertex 3.932020e+001 1.706140e+001 -8.881784e-016
+      vertex 2.607020e+001 1.522646e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 4.096515e+001 1.941063e+001 -8.881784e-016
+      vertex 4.057020e+001 1.922646e+001 -8.881784e-016
+      vertex 8.675250e+000 1.941063e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 8.675250e+000 1.941063e+001 -8.881784e-016
+      vertex 4.057020e+001 1.922646e+001 -8.881784e-016
+      vertex 9.070200e+000 1.922646e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 3.279817e-031 -2.423364e-016 -1.000000e+000
+    outer loop
+      vertex 9.070200e+000 1.922646e+001 -8.881784e-016
+      vertex 4.057020e+001 1.922646e+001 -8.881784e-016
+      vertex 2.482020e+001 1.556140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 1.631843e-017 -1.865205e-016 -1.000000e+000
+    outer loop
+      vertex 9.427169e+000 1.897651e+001 -8.881784e-016
+      vertex 2.482020e+001 1.556140e+001 0.000000e+000
+      vertex 2.438608e+001 1.552342e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 7.134736e-017 4.995796e-017 -1.000000e+000
+    outer loop
+      vertex 9.735311e+000 1.866837e+001 -8.881784e-016
+      vertex 2.438608e+001 1.552342e+001 0.000000e+000
+      vertex 9.985264e+000 1.831140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.719385e-017 7.719385e-017 -1.000000e+000
+    outer loop
+      vertex 9.735311e+000 1.866837e+001 -8.881784e-016
+      vertex 9.427169e+000 1.897651e+001 -8.881784e-016
+      vertex 2.438608e+001 1.552342e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -8.446175e-017 1.206239e-016 -1.000000e+000
+    outer loop
+      vertex 4.021323e+001 1.897651e+001 -8.881784e-016
+      vertex 2.482020e+001 1.556140e+001 0.000000e+000
+      vertex 4.057020e+001 1.922646e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -1.631843e-017 -1.865205e-016 -1.000000e+000
+    outer loop
+      vertex 4.021323e+001 1.897651e+001 -8.881784e-016
+      vertex 2.525432e+001 1.552342e+001 0.000000e+000
+      vertex 2.482020e+001 1.556140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -7.719385e-017 7.719385e-017 -1.000000e+000
+    outer loop
+      vertex 4.021323e+001 1.897651e+001 -8.881784e-016
+      vertex 3.990509e+001 1.866837e+001 -8.881784e-016
+      vertex 2.525432e+001 1.552342e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -7.134736e-017 4.995796e-017 -1.000000e+000
+    outer loop
+      vertex 2.525432e+001 1.552342e+001 0.000000e+000
+      vertex 3.990509e+001 1.866837e+001 -8.881784e-016
+      vertex 3.965514e+001 1.831140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -7.033831e-017 3.279929e-017 -1.000000e+000
+    outer loop
+      vertex 2.567525e+001 1.541063e+001 0.000000e+000
+      vertex 3.965514e+001 1.831140e+001 -8.881784e-016
+      vertex 3.947097e+001 1.791645e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -4.633298e-017 -9.936140e-017 -1.000000e+000
+    outer loop
+      vertex 2.607020e+001 1.522646e+001 0.000000e+000
+      vertex 2.567525e+001 1.541063e+001 0.000000e+000
+      vertex 3.947097e+001 1.791645e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -6.792726e-017 -5.942865e-018 -1.000000e+000
+    outer loop
+      vertex 3.932020e+001 1.706140e+001 -8.881784e-016
+      vertex 3.935818e+001 1.662728e+001 -8.881784e-016
+      vertex 2.642717e+001 1.497651e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -6.755360e-017 -1.810093e-017 -1.000000e+000
+    outer loop
+      vertex 3.947097e+001 1.620635e+001 -8.881784e-016
+      vertex 2.673531e+001 1.466837e+001 0.000000e+000
+      vertex 3.935818e+001 1.662728e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -6.643400e-017 -3.097868e-017 -1.000000e+000
+    outer loop
+      vertex 3.947097e+001 1.620635e+001 -8.881784e-016
+      vertex 3.965514e+001 1.581140e+001 -8.881784e-016
+      vertex 2.698526e+001 1.431140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -6.430222e-017 -4.502490e-017 -1.000000e+000
+    outer loop
+      vertex 3.965514e+001 1.581140e+001 -8.881784e-016
+      vertex 3.990509e+001 1.545443e+001 -8.881784e-016
+      vertex 2.716943e+001 1.391645e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -4.633298e-017 -9.936140e-017 -1.000000e+000
+    outer loop
+      vertex 4.096515e+001 1.471217e+001 -8.881784e-016
+      vertex 2.716943e+001 1.220635e+001 0.000000e+000
+      vertex 4.057020e+001 1.489634e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -6.587563e-018 -7.529618e-017 -1.000000e+000
+    outer loop
+      vertex 4.182020e+001 1.456140e+001 -8.881784e-016
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 4.138608e+001 1.459938e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.350854e-018 -8.402064e-017 -1.000000e+000
+    outer loop
+      vertex 4.182020e+001 1.456140e+001 -8.881784e-016
+      vertex 4.225432e+001 1.459938e+001 -8.881784e-016
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+    endloop
+  endfacet
+  facet normal -6.691135e-017 -3.482212e-017 -1.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 4.225432e+001 1.459938e+001 -8.881784e-016
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -5.921189e-017 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 3.482020e+001 6.140000e-002 0.000000e+000
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 4.267525e+001 1.471217e+001 -8.881784e-016
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+      vertex 4.225432e+001 1.459938e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 4.267525e+001 1.471217e+001 -8.881784e-016
+      vertex 4.307020e+001 1.489634e+001 -8.881784e-016
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+      vertex 4.307020e+001 1.489634e+001 -8.881784e-016
+      vertex 4.342717e+001 1.514629e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 4.373531e+001 1.545443e+001 -8.881784e-016
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+      vertex 4.342717e+001 1.514629e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 4.373531e+001 1.545443e+001 -8.881784e-016
+      vertex 4.398526e+001 1.581140e+001 -8.881784e-016
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+      vertex 4.398526e+001 1.581140e+001 -8.881784e-016
+      vertex 4.416943e+001 1.620635e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 2.709460e-016 -7.259975e-017 -1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.416943e+001 1.620635e+001 -8.881784e-016
+      vertex 4.428222e+001 1.662728e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 1.850337e-016 -1.618835e-017 -1.000000e+000
+    outer loop
+      vertex 4.432020e+001 1.706140e+001 -8.881784e-016
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.428222e+001 1.662728e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -3.580541e-017 -1.336276e-016 -1.000000e+000
+    outer loop
+      vertex 2.567525e+001 1.541063e+001 0.000000e+000
+      vertex 2.525432e+001 1.552342e+001 0.000000e+000
+      vertex 3.965514e+001 1.831140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 3.580541e-017 -1.336276e-016 -1.000000e+000
+    outer loop
+      vertex 2.438608e+001 1.552342e+001 0.000000e+000
+      vertex 2.396515e+001 1.541063e+001 0.000000e+000
+      vertex 9.985264e+000 1.831140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.033831e-017 3.279929e-017 -1.000000e+000
+    outer loop
+      vertex 9.985264e+000 1.831140e+001 -8.881784e-016
+      vertex 2.396515e+001 1.541063e+001 0.000000e+000
+      vertex 1.016943e+001 1.791645e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 4.633298e-017 -9.936140e-017 -1.000000e+000
+    outer loop
+      vertex 1.016943e+001 1.791645e+001 -8.881784e-016
+      vertex 2.396515e+001 1.541063e+001 0.000000e+000
+      vertex 2.357020e+001 1.522646e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 6.785445e-017 5.936495e-018 -1.000000e+000
+    outer loop
+      vertex 1.028222e+001 1.749552e+001 -8.881784e-016
+      vertex 2.357020e+001 1.522646e+001 0.000000e+000
+      vertex 1.032020e+001 1.706140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.004568e-017 1.876868e-017 -1.000000e+000
+    outer loop
+      vertex 1.028222e+001 1.749552e+001 -8.881784e-016
+      vertex 1.016943e+001 1.791645e+001 -8.881784e-016
+      vertex 2.357020e+001 1.522646e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 5.596390e-017 -7.992473e-017 -1.000000e+000
+    outer loop
+      vertex 2.357020e+001 1.522646e+001 0.000000e+000
+      vertex 2.321323e+001 1.497651e+001 0.000000e+000
+      vertex 1.032020e+001 1.706140e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 6.792726e-017 -5.942865e-018 -1.000000e+000
+    outer loop
+      vertex 1.032020e+001 1.706140e+001 -8.881784e-016
+      vertex 2.321323e+001 1.497651e+001 0.000000e+000
+      vertex 1.028222e+001 1.662728e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 6.091015e-017 -6.091015e-017 -1.000000e+000
+    outer loop
+      vertex 1.028222e+001 1.662728e+001 -8.881784e-016
+      vertex 2.321323e+001 1.497651e+001 0.000000e+000
+      vertex 2.290509e+001 1.466837e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 6.430222e-017 -4.502490e-017 -1.000000e+000
+    outer loop
+      vertex 1.016943e+001 1.620635e+001 -8.881784e-016
+      vertex 2.290509e+001 1.466837e+001 0.000000e+000
+      vertex 2.265514e+001 1.431140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 6.643400e-017 -3.097868e-017 -1.000000e+000
+    outer loop
+      vertex 9.985264e+000 1.581140e+001 -8.881784e-016
+      vertex 2.265514e+001 1.431140e+001 0.000000e+000
+      vertex 2.247097e+001 1.391645e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 6.755360e-017 -1.810093e-017 -1.000000e+000
+    outer loop
+      vertex 9.735311e+000 1.545443e+001 -8.881784e-016
+      vertex 2.247097e+001 1.391645e+001 0.000000e+000
+      vertex 2.235818e+001 1.349552e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 6.792726e-017 -5.942865e-018 -1.000000e+000
+    outer loop
+      vertex 9.427169e+000 1.514629e+001 -8.881784e-016
+      vertex 2.235818e+001 1.349552e+001 0.000000e+000
+      vertex 2.232020e+001 1.306140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 6.785445e-017 5.936495e-018 -1.000000e+000
+    outer loop
+      vertex 9.070200e+000 1.489634e+001 -8.881784e-016
+      vertex 2.232020e+001 1.306140e+001 0.000000e+000
+      vertex 2.235818e+001 1.262728e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 7.004568e-017 1.876868e-017 -1.000000e+000
+    outer loop
+      vertex 2.247097e+001 1.220635e+001 0.000000e+000
+      vertex 9.070200e+000 1.489634e+001 -8.881784e-016
+      vertex 2.235818e+001 1.262728e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 4.633298e-017 -9.936140e-017 -1.000000e+000
+    outer loop
+      vertex 2.247097e+001 1.220635e+001 0.000000e+000
+      vertex 8.675250e+000 1.471217e+001 -8.881784e-016
+      vertex 9.070200e+000 1.489634e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 7.033831e-017 3.279929e-017 -1.000000e+000
+    outer loop
+      vertex 2.247097e+001 1.220635e+001 0.000000e+000
+      vertex 2.265514e+001 1.181140e+001 0.000000e+000
+      vertex 8.675250e+000 1.471217e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 3.580541e-017 -1.336276e-016 -1.000000e+000
+    outer loop
+      vertex 8.675250e+000 1.471217e+001 -8.881784e-016
+      vertex 2.265514e+001 1.181140e+001 0.000000e+000
+      vertex 8.254320e+000 1.459938e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 8.446175e-017 1.206239e-016 -1.000000e+000
+    outer loop
+      vertex 9.427169e+000 1.897651e+001 -8.881784e-016
+      vertex 9.070200e+000 1.922646e+001 -8.881784e-016
+      vertex 2.482020e+001 1.556140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 5.596390e-017 -7.992473e-017 -1.000000e+000
+    outer loop
+      vertex 9.427169e+000 1.514629e+001 -8.881784e-016
+      vertex 2.232020e+001 1.306140e+001 0.000000e+000
+      vertex 9.070200e+000 1.489634e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 6.091015e-017 -6.091015e-017 -1.000000e+000
+    outer loop
+      vertex 9.427169e+000 1.514629e+001 -8.881784e-016
+      vertex 9.735311e+000 1.545443e+001 -8.881784e-016
+      vertex 2.235818e+001 1.349552e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 6.430222e-017 -4.502490e-017 -1.000000e+000
+    outer loop
+      vertex 9.985264e+000 1.581140e+001 -8.881784e-016
+      vertex 2.247097e+001 1.391645e+001 0.000000e+000
+      vertex 9.735311e+000 1.545443e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 6.643400e-017 -3.097868e-017 -1.000000e+000
+    outer loop
+      vertex 9.985264e+000 1.581140e+001 -8.881784e-016
+      vertex 1.016943e+001 1.620635e+001 -8.881784e-016
+      vertex 2.265514e+001 1.431140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 6.755360e-017 -1.810093e-017 -1.000000e+000
+    outer loop
+      vertex 1.028222e+001 1.662728e+001 -8.881784e-016
+      vertex 2.290509e+001 1.466837e+001 0.000000e+000
+      vertex 1.016943e+001 1.620635e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 5.921189e-017 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 1.482020e+001 6.140000e-002 0.000000e+000
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 1.015055e-016 3.552714e-017 -1.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+      vertex 4.416943e+001 1.620635e+001 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
+    outer loop
+      vertex 3.482020e+001 3.378025e+000 0.000000e+000
+      vertex 1.482020e+001 3.378025e+000 0.000000e+000
+      vertex 2.482020e+001 1.056140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 1.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 1.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.938600e+000 5.000000e+000
+      vertex -1.798000e-001 -2.893023e+000 4.479055e+000
+      vertex -1.798000e-001 -4.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -4.862639e+000 5.868241e+000
+      vertex -1.798000e-001 -2.938600e+000 5.000000e+000
+      vertex -1.798000e-001 -4.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -4.862639e+000 5.868241e+000
+      vertex -1.798000e-001 -2.893023e+000 5.520945e+000
+      vertex -1.798000e-001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -4.862639e+000 5.868241e+000
+      vertex -1.798000e-001 -4.637063e+000 6.710101e+000
+      vertex -1.798000e-001 -2.893023e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.893023e+000 5.520945e+000
+      vertex -1.798000e-001 -4.637063e+000 6.710101e+000
+      vertex -1.798000e-001 -2.757678e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.757678e+000 6.026060e+000
+      vertex -1.798000e-001 -4.637063e+000 6.710101e+000
+      vertex -1.798000e-001 -4.268727e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.536676e+000 6.500000e+000
+      vertex -1.798000e-001 -4.268727e+000 7.500000e+000
+      vertex -1.798000e-001 -3.768822e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.236733e+000 6.928363e+000
+      vertex -1.798000e-001 -3.768822e+000 8.213938e+000
+      vertex -1.798000e-001 -3.152538e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -1.866963e+000 7.298133e+000
+      vertex -1.798000e-001 -3.152538e+000 8.830222e+000
+      vertex -1.798000e-001 -1.438600e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -1.866963e+000 7.298133e+000
+      vertex -1.798000e-001 -2.236733e+000 6.928363e+000
+      vertex -1.798000e-001 -3.152538e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.893023e+000 4.479055e+000
+      vertex -1.798000e-001 -2.757678e+000 3.973940e+000
+      vertex -1.798000e-001 -4.862639e+000 4.131759e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -4.938600e+000 5.000000e+000
+      vertex -1.798000e-001 -2.893023e+000 4.479055e+000
+      vertex -1.798000e-001 -4.862639e+000 4.131759e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.536676e+000 3.500000e+000
+      vertex -1.798000e-001 -4.637063e+000 3.289899e+000
+      vertex -1.798000e-001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.536676e+000 3.500000e+000
+      vertex -1.798000e-001 -4.268727e+000 2.500000e+000
+      vertex -1.798000e-001 -4.637063e+000 3.289899e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.536676e+000 3.500000e+000
+      vertex -1.798000e-001 -2.236733e+000 3.071637e+000
+      vertex -1.798000e-001 -4.268727e+000 2.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -4.268727e+000 2.500000e+000
+      vertex -1.798000e-001 -2.236733e+000 3.071637e+000
+      vertex -1.798000e-001 -3.768822e+000 1.786062e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -3.768822e+000 1.786062e+000
+      vertex -1.798000e-001 -2.236733e+000 3.071637e+000
+      vertex -1.798000e-001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -3.152538e+000 1.169778e+000
+      vertex -1.798000e-001 -1.866963e+000 2.701867e+000
+      vertex -1.798000e-001 -2.438600e+000 6.698730e-001
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -3.152538e+000 1.169778e+000
+      vertex -1.798000e-001 -3.768822e+000 1.786062e+000
+      vertex -1.798000e-001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -1.438600e+000 2.401924e+000
+      vertex -1.798000e-001 -2.438600e+000 6.698730e-001
+      vertex -1.798000e-001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -1.438600e+000 2.401924e+000
+      vertex -1.798000e-001 -1.648701e+000 3.015369e-001
+      vertex -1.798000e-001 -2.438600e+000 6.698730e-001
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -1.438600e+000 2.401924e+000
+      vertex -1.798000e-001 -9.646604e-001 2.180922e+000
+      vertex -1.798000e-001 -1.648701e+000 3.015369e-001
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -1.648701e+000 3.015369e-001
+      vertex -1.798000e-001 -9.646604e-001 2.180922e+000
+      vertex -1.798000e-001 -8.068409e-001 7.596123e-002
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -8.068409e-001 7.596123e-002
+      vertex -1.798000e-001 -9.646604e-001 2.180922e+000
+      vertex -1.798000e-001 -4.595445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex -1.798000e-001 -4.595445e-001 2.045577e+000
+      vertex -1.798000e-001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 5.823445e-001 2.045577e+000
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex -1.798000e-001 6.140000e-002 2.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 5.823445e-001 2.045577e+000
+      vertex -1.798000e-001 1.087460e+000 2.180922e+000
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex -1.798000e-001 1.087460e+000 2.180922e+000
+      vertex -1.798000e-001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 1.989763e+000 2.701867e+000
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex -1.798000e-001 1.561400e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 1.989763e+000 2.701867e+000
+      vertex -1.798000e-001 2.359533e+000 3.071637e+000
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex -1.798000e-001 2.359533e+000 3.071637e+000
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 -2.095500e-016 -1.548743e-015
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex -1.798000e-001 2.359533e+000 3.071637e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 1.776357e-016 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.506140e+001 5.000000e+000
+      vertex -1.798000e-001 2.506140e+001 0.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 -2.628500e-015 1.840496e-015
+    outer loop
+      vertex -1.798000e-001 2.359533e+000 3.071637e+000
+      vertex -1.798000e-001 2.659476e+000 3.500000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 -2.086809e-015 9.730950e-016
+    outer loop
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex -1.798000e-001 2.659476e+000 3.500000e+000
+      vertex -1.798000e-001 2.880478e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 -1.863973e-015 4.994500e-016
+    outer loop
+      vertex -1.798000e-001 3.015823e+000 4.479055e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex -1.798000e-001 2.880478e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 -1.776357e-015 1.554111e-016
+    outer loop
+      vertex -1.798000e-001 3.015823e+000 4.479055e+000
+      vertex -1.798000e-001 3.061400e+000 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 -1.776357e-015 -1.554111e-016
+    outer loop
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex -1.798000e-001 3.061400e+000 5.000000e+000
+      vertex -1.798000e-001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 4.985439e+000 5.868241e+000
+      vertex -1.798000e-001 3.015823e+000 5.520945e+000
+      vertex -1.798000e-001 2.880478e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 4.759863e+000 6.710101e+000
+      vertex -1.798000e-001 2.880478e+000 6.026060e+000
+      vertex -1.798000e-001 2.659476e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 4.391527e+000 7.500000e+000
+      vertex -1.798000e-001 2.659476e+000 6.500000e+000
+      vertex -1.798000e-001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 3.891622e+000 8.213938e+000
+      vertex -1.798000e-001 2.359533e+000 6.928363e+000
+      vertex -1.798000e-001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 3.275338e+000 8.830222e+000
+      vertex -1.798000e-001 1.989763e+000 7.298133e+000
+      vertex -1.798000e-001 2.561400e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 3.275338e+000 8.830222e+000
+      vertex -1.798000e-001 3.891622e+000 8.213938e+000
+      vertex -1.798000e-001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 1.989763e+000 7.298133e+000
+      vertex -1.798000e-001 1.561400e+000 7.598076e+000
+      vertex -1.798000e-001 2.561400e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.561400e+000 9.330127e+000
+      vertex -1.798000e-001 1.561400e+000 7.598076e+000
+      vertex -1.798000e-001 1.771501e+000 9.698463e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 1.771501e+000 9.698463e+000
+      vertex -1.798000e-001 1.561400e+000 7.598076e+000
+      vertex -1.798000e-001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 9.296409e-001 9.924039e+000
+      vertex -1.798000e-001 1.087460e+000 7.819078e+000
+      vertex -1.798000e-001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 1.000000e+001
+      vertex -1.798000e-001 5.823445e-001 7.954423e+000
+      vertex -1.798000e-001 6.140000e-002 8.000000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -8.068409e-001 9.924039e+000
+      vertex -1.798000e-001 6.140000e-002 8.000000e+000
+      vertex -1.798000e-001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -1.648701e+000 9.698463e+000
+      vertex -1.798000e-001 -4.595445e-001 7.954423e+000
+      vertex -1.798000e-001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.438600e+000 9.330127e+000
+      vertex -1.798000e-001 -9.646604e-001 7.819078e+000
+      vertex -1.798000e-001 -1.438600e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -3.152538e+000 8.830222e+000
+      vertex -1.798000e-001 -2.438600e+000 9.330127e+000
+      vertex -1.798000e-001 -1.438600e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.536676e+000 6.500000e+000
+      vertex -1.798000e-001 -3.768822e+000 8.213938e+000
+      vertex -1.798000e-001 -2.236733e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -4.268727e+000 7.500000e+000
+      vertex -1.798000e-001 -2.536676e+000 6.500000e+000
+      vertex -1.798000e-001 -2.757678e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex -1.798000e-001 -8.068409e-001 7.596123e-002
+      vertex -1.798000e-001 -4.595445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -4.637063e+000 3.289899e+000
+      vertex -1.798000e-001 -4.862639e+000 4.131759e+000
+      vertex -1.798000e-001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -2.438600e+000 9.330127e+000
+      vertex -1.798000e-001 -1.648701e+000 9.698463e+000
+      vertex -1.798000e-001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 -1.648701e+000 9.698463e+000
+      vertex -1.798000e-001 -8.068409e-001 9.924039e+000
+      vertex -1.798000e-001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 1.000000e+001
+      vertex -1.798000e-001 6.140000e-002 8.000000e+000
+      vertex -1.798000e-001 -8.068409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 1.000000e+001
+      vertex -1.798000e-001 9.296409e-001 9.924039e+000
+      vertex -1.798000e-001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 9.296409e-001 9.924039e+000
+      vertex -1.798000e-001 1.771501e+000 9.698463e+000
+      vertex -1.798000e-001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 2.359533e+000 6.928363e+000
+      vertex -1.798000e-001 3.891622e+000 8.213938e+000
+      vertex -1.798000e-001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 4.391527e+000 7.500000e+000
+      vertex -1.798000e-001 4.759863e+000 6.710101e+000
+      vertex -1.798000e-001 2.659476e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex -1.798000e-001 4.759863e+000 6.710101e+000
+      vertex -1.798000e-001 4.985439e+000 5.868241e+000
+      vertex -1.798000e-001 2.880478e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal -1.000000e+000 -7.105427e-016 4.029688e-015
+    outer loop
+      vertex -1.798000e-001 4.985439e+000 5.868241e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex -1.798000e-001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal -1.061532e-017 -8.715574e-002 -9.961947e-001
+    outer loop
+      vertex 1.482020e+001 6.140000e-002 0.000000e+000
+      vertex 1.482020e+001 -8.068409e-001 7.596123e-002
+      vertex -1.798000e-001 -8.068409e-001 7.596123e-002
+    endloop
+  endfacet
+  facet normal 5.124559e-017 -8.715574e-002 -9.961947e-001
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 -8.881784e-016
+      vertex 1.482020e+001 6.140000e-002 0.000000e+000
+      vertex -1.798000e-001 -8.068409e-001 7.596123e-002
+    endloop
+  endfacet
+  facet normal 2.547676e-017 -2.588190e-001 -9.659258e-001
+    outer loop
+      vertex 1.482020e+001 -8.068409e-001 7.596123e-002
+      vertex 1.482020e+001 -1.648701e+000 3.015369e-001
+      vertex -1.798000e-001 -1.648701e+000 3.015369e-001
+    endloop
+  endfacet
+  facet normal -3.065033e-017 -2.588190e-001 -9.659258e-001
+    outer loop
+      vertex -1.798000e-001 -8.068409e-001 7.596123e-002
+      vertex 1.482020e+001 -8.068409e-001 7.596123e-002
+      vertex -1.798000e-001 -1.648701e+000 3.015369e-001
+    endloop
+  endfacet
+  facet normal 4.246127e-018 -4.226183e-001 -9.063078e-001
+    outer loop
+      vertex 1.482020e+001 -1.648701e+000 3.015369e-001
+      vertex 1.482020e+001 -2.438600e+000 6.698730e-001
+      vertex -1.798000e-001 -2.438600e+000 6.698730e-001
+    endloop
+  endfacet
+  facet normal 3.616145e-018 -4.226183e-001 -9.063078e-001
+    outer loop
+      vertex -1.798000e-001 -1.648701e+000 3.015369e-001
+      vertex 1.482020e+001 -1.648701e+000 3.015369e-001
+      vertex -1.798000e-001 -2.438600e+000 6.698730e-001
+    endloop
+  endfacet
+  facet normal 8.492254e-018 -5.735764e-001 -8.191520e-001
+    outer loop
+      vertex 1.482020e+001 -2.438600e+000 6.698730e-001
+      vertex 1.482020e+001 -3.152538e+000 1.169778e+000
+      vertex -1.798000e-001 -3.152538e+000 1.169778e+000
+    endloop
+  endfacet
+  facet normal -2.427694e-017 -5.735764e-001 -8.191520e-001
+    outer loop
+      vertex -1.798000e-001 -2.438600e+000 6.698730e-001
+      vertex 1.482020e+001 -2.438600e+000 6.698730e-001
+      vertex -1.798000e-001 -3.152538e+000 1.169778e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -7.071068e-001 -7.071068e-001
+    outer loop
+      vertex 1.482020e+001 -3.152538e+000 1.169778e+000
+      vertex 1.482020e+001 -3.768822e+000 1.786062e+000
+      vertex -1.798000e-001 -3.768822e+000 1.786062e+000
+    endloop
+  endfacet
+  facet normal -4.186913e-017 -7.071068e-001 -7.071068e-001
+    outer loop
+      vertex -1.798000e-001 -3.152538e+000 1.169778e+000
+      vertex 1.482020e+001 -3.152538e+000 1.169778e+000
+      vertex -1.798000e-001 -3.768822e+000 1.786062e+000
+    endloop
+  endfacet
+  facet normal 2.123063e-017 -8.191520e-001 -5.735764e-001
+    outer loop
+      vertex 1.482020e+001 -3.768822e+000 1.786062e+000
+      vertex 1.482020e+001 -4.268727e+000 2.500000e+000
+      vertex -1.798000e-001 -4.268727e+000 2.500000e+000
+    endloop
+  endfacet
+  facet normal -2.908199e-017 -8.191520e-001 -5.735764e-001
+    outer loop
+      vertex -1.798000e-001 -3.768822e+000 1.786062e+000
+      vertex 1.482020e+001 -3.768822e+000 1.786062e+000
+      vertex -1.798000e-001 -4.268727e+000 2.500000e+000
+    endloop
+  endfacet
+  facet normal -3.616145e-018 -9.063078e-001 -4.226183e-001
+    outer loop
+      vertex 1.482020e+001 -4.268727e+000 2.500000e+000
+      vertex 1.482020e+001 -4.637063e+000 3.289899e+000
+      vertex -1.798000e-001 -4.268727e+000 2.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.063078e-001 -4.226183e-001
+    outer loop
+      vertex -1.798000e-001 -4.268727e+000 2.500000e+000
+      vertex 1.482020e+001 -4.637063e+000 3.289899e+000
+      vertex -1.798000e-001 -4.637063e+000 3.289899e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.659258e-001 -2.588190e-001
+    outer loop
+      vertex -1.798000e-001 -4.637063e+000 3.289899e+000
+      vertex 1.482020e+001 -4.637063e+000 3.289899e+000
+      vertex -1.798000e-001 -4.862639e+000 4.131759e+000
+    endloop
+  endfacet
+  facet normal 6.130066e-017 -9.659258e-001 -2.588190e-001
+    outer loop
+      vertex -1.798000e-001 -4.862639e+000 4.131759e+000
+      vertex 1.482020e+001 -4.637063e+000 3.289899e+000
+      vertex 1.482020e+001 -4.862639e+000 4.131759e+000
+    endloop
+  endfacet
+  facet normal -2.802264e-017 -9.961947e-001 -8.715574e-002
+    outer loop
+      vertex -1.798000e-001 -4.938600e+000 5.000000e+000
+      vertex 1.482020e+001 -4.862639e+000 4.131759e+000
+      vertex 1.482020e+001 -4.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 3.318329e-017 -9.961947e-001 8.715574e-002
+    outer loop
+      vertex -1.798000e-001 -4.862639e+000 5.868241e+000
+      vertex 1.482020e+001 -4.938600e+000 5.000000e+000
+      vertex 1.482020e+001 -4.862639e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.659258e-001 2.588190e-001
+    outer loop
+      vertex -1.798000e-001 -4.637063e+000 6.710101e+000
+      vertex 1.482020e+001 -4.862639e+000 5.868241e+000
+      vertex 1.482020e+001 -4.637063e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 7.232290e-018 -9.063078e-001 4.226183e-001
+    outer loop
+      vertex -1.798000e-001 -4.268727e+000 7.500000e+000
+      vertex 1.482020e+001 -4.637063e+000 6.710101e+000
+      vertex 1.482020e+001 -4.268727e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.191520e-001 5.735764e-001
+    outer loop
+      vertex -1.798000e-001 -3.768822e+000 8.213938e+000
+      vertex 1.482020e+001 -4.268727e+000 7.500000e+000
+      vertex 1.482020e+001 -3.768822e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal -2.172279e-030 -7.071068e-001 7.071068e-001
+    outer loop
+      vertex -1.798000e-001 -3.152538e+000 8.830222e+000
+      vertex 1.482020e+001 -3.768822e+000 8.213938e+000
+      vertex 1.482020e+001 -3.152538e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 3.884310e-017 -5.735764e-001 8.191520e-001
+    outer loop
+      vertex -1.798000e-001 -2.438600e+000 9.330127e+000
+      vertex 1.482020e+001 -3.152538e+000 8.830222e+000
+      vertex 1.482020e+001 -2.438600e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -4.226183e-001 9.063078e-001
+    outer loop
+      vertex -1.798000e-001 -1.648701e+000 9.698463e+000
+      vertex 1.482020e+001 -2.438600e+000 9.330127e+000
+      vertex 1.482020e+001 -1.648701e+000 9.698463e+000
+    endloop
+  endfacet
+  facet normal 8.428841e-017 -2.588190e-001 9.659258e-001
+    outer loop
+      vertex -1.798000e-001 -8.068409e-001 9.924039e+000
+      vertex 1.482020e+001 -1.648701e+000 9.698463e+000
+      vertex 1.482020e+001 -8.068409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -8.715574e-002 9.961947e-001
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 1.000000e+001
+      vertex 1.482020e+001 -8.068409e-001 9.924039e+000
+      vertex 1.482020e+001 6.140000e-002 1.000000e+001
+    endloop
+  endfacet
+  facet normal -2.322295e-017 8.715574e-002 9.961947e-001
+    outer loop
+      vertex -1.798000e-001 9.296409e-001 9.924039e+000
+      vertex 1.482020e+001 6.140000e-002 1.000000e+001
+      vertex 1.482020e+001 9.296409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal 4.159406e-017 2.588190e-001 9.659258e-001
+    outer loop
+      vertex -1.798000e-001 1.771501e+000 9.698463e+000
+      vertex 1.482020e+001 9.296409e-001 9.924039e+000
+      vertex 1.482020e+001 1.771501e+000 9.698463e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 4.226183e-001 9.063078e-001
+    outer loop
+      vertex -1.798000e-001 2.561400e+000 9.330127e+000
+      vertex 1.482020e+001 1.771501e+000 9.698463e+000
+      vertex 1.482020e+001 2.561400e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal 5.816399e-017 5.735764e-001 8.191520e-001
+    outer loop
+      vertex -1.798000e-001 3.275338e+000 8.830222e+000
+      vertex 1.482020e+001 2.561400e+000 9.330127e+000
+      vertex 1.482020e+001 3.275338e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 7.071068e-001 7.071068e-001
+    outer loop
+      vertex -1.798000e-001 3.891622e+000 8.213938e+000
+      vertex 1.482020e+001 3.275338e+000 8.830222e+000
+      vertex 1.482020e+001 3.891622e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal 3.884310e-017 8.191520e-001 5.735764e-001
+    outer loop
+      vertex -1.798000e-001 4.391527e+000 7.500000e+000
+      vertex 1.482020e+001 3.891622e+000 8.213938e+000
+      vertex 1.482020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 7.145594e-017 9.063078e-001 4.226183e-001
+    outer loop
+      vertex -1.798000e-001 4.759863e+000 6.710101e+000
+      vertex 1.482020e+001 4.391527e+000 7.500000e+000
+      vertex 1.482020e+001 4.759863e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 9.659258e-001 2.588190e-001
+    outer loop
+      vertex -1.798000e-001 4.985439e+000 5.868241e+000
+      vertex 1.482020e+001 4.759863e+000 6.710101e+000
+      vertex 1.482020e+001 4.985439e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 9.961947e-001 8.715574e-002
+    outer loop
+      vertex 1.482020e+001 5.061400e+000 5.000000e+000
+      vertex -1.798000e-001 4.985439e+000 5.868241e+000
+      vertex 1.482020e+001 4.985439e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 9.961947e-001 8.715574e-002
+    outer loop
+      vertex 1.482020e+001 5.061400e+000 5.000000e+000
+      vertex -1.798000e-001 5.061400e+000 5.000000e+000
+      vertex -1.798000e-001 4.985439e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 1.910757e-017 9.659258e-001 2.588190e-001
+    outer loop
+      vertex -1.798000e-001 4.985439e+000 5.868241e+000
+      vertex -1.798000e-001 4.759863e+000 6.710101e+000
+      vertex 1.482020e+001 4.759863e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal -8.492254e-018 9.063078e-001 4.226183e-001
+    outer loop
+      vertex -1.798000e-001 4.759863e+000 6.710101e+000
+      vertex -1.798000e-001 4.391527e+000 7.500000e+000
+      vertex 1.482020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 8.191520e-001 5.735764e-001
+    outer loop
+      vertex -1.798000e-001 4.391527e+000 7.500000e+000
+      vertex -1.798000e-001 3.891622e+000 8.213938e+000
+      vertex 1.482020e+001 3.891622e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal -4.344559e-030 7.071068e-001 7.071068e-001
+    outer loop
+      vertex -1.798000e-001 3.275338e+000 8.830222e+000
+      vertex 1.482020e+001 3.275338e+000 8.830222e+000
+      vertex -1.798000e-001 3.891622e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 5.735764e-001 8.191520e-001
+    outer loop
+      vertex -1.798000e-001 3.275338e+000 8.830222e+000
+      vertex -1.798000e-001 2.561400e+000 9.330127e+000
+      vertex 1.482020e+001 2.561400e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal -1.153573e-017 4.226183e-001 9.063078e-001
+    outer loop
+      vertex -1.798000e-001 1.771501e+000 9.698463e+000
+      vertex 1.482020e+001 1.771501e+000 9.698463e+000
+      vertex -1.798000e-001 2.561400e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal -6.793803e-017 2.588190e-001 9.659258e-001
+    outer loop
+      vertex -1.798000e-001 1.771501e+000 9.698463e+000
+      vertex -1.798000e-001 9.296409e-001 9.924039e+000
+      vertex 1.482020e+001 9.296409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 8.715574e-002 9.961947e-001
+    outer loop
+      vertex -1.798000e-001 9.296409e-001 9.924039e+000
+      vertex -1.798000e-001 6.140000e-002 1.000000e+001
+      vertex 1.482020e+001 6.140000e-002 1.000000e+001
+    endloop
+  endfacet
+  facet normal 2.866136e-017 -8.715574e-002 9.961947e-001
+    outer loop
+      vertex -1.798000e-001 6.140000e-002 1.000000e+001
+      vertex -1.798000e-001 -8.068409e-001 9.924039e+000
+      vertex 1.482020e+001 -8.068409e-001 9.924039e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -2.588190e-001 9.659258e-001
+    outer loop
+      vertex -1.798000e-001 -8.068409e-001 9.924039e+000
+      vertex -1.798000e-001 -1.648701e+000 9.698463e+000
+      vertex 1.482020e+001 -1.648701e+000 9.698463e+000
+    endloop
+  endfacet
+  facet normal -8.492254e-018 -4.226183e-001 9.063078e-001
+    outer loop
+      vertex -1.798000e-001 -1.648701e+000 9.698463e+000
+      vertex -1.798000e-001 -2.438600e+000 9.330127e+000
+      vertex 1.482020e+001 -2.438600e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal -5.944578e-017 -5.735764e-001 8.191520e-001
+    outer loop
+      vertex -1.798000e-001 -2.438600e+000 9.330127e+000
+      vertex -1.798000e-001 -3.152538e+000 8.830222e+000
+      vertex 1.482020e+001 -3.152538e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -7.071068e-001 7.071068e-001
+    outer loop
+      vertex -1.798000e-001 -3.152538e+000 8.830222e+000
+      vertex -1.798000e-001 -3.768822e+000 8.213938e+000
+      vertex 1.482020e+001 -3.768822e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal -3.821514e-017 -8.191520e-001 5.735764e-001
+    outer loop
+      vertex -1.798000e-001 -3.768822e+000 8.213938e+000
+      vertex -1.798000e-001 -4.268727e+000 7.500000e+000
+      vertex 1.482020e+001 -4.268727e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 0.000000e+000 -9.063078e-001 4.226183e-001
+    outer loop
+      vertex -1.798000e-001 -4.268727e+000 7.500000e+000
+      vertex -1.798000e-001 -4.637063e+000 6.710101e+000
+      vertex 1.482020e+001 -4.637063e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal -1.910757e-017 -9.659258e-001 2.588190e-001
+    outer loop
+      vertex -1.798000e-001 -4.637063e+000 6.710101e+000
+      vertex -1.798000e-001 -4.862639e+000 5.868241e+000
+      vertex 1.482020e+001 -4.862639e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal -9.023020e-017 -9.961947e-001 8.715574e-002
+    outer loop
+      vertex -1.798000e-001 -4.862639e+000 5.868241e+000
+      vertex -1.798000e-001 -4.938600e+000 5.000000e+000
+      vertex 1.482020e+001 -4.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 2.016910e-017 -9.961947e-001 -8.715574e-002
+    outer loop
+      vertex -1.798000e-001 -4.938600e+000 5.000000e+000
+      vertex -1.798000e-001 -4.862639e+000 4.131759e+000
+      vertex 1.482020e+001 -4.862639e+000 4.131759e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -2.893023e+000 5.520945e+000
+      vertex 4.982020e+001 -4.862639e+000 5.868241e+000
+      vertex 4.982020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -2.893023e+000 5.520945e+000
+      vertex 4.982020e+001 -4.637063e+000 6.710101e+000
+      vertex 4.982020e+001 -4.862639e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -2.893023e+000 5.520945e+000
+      vertex 4.982020e+001 -2.757678e+000 6.026060e+000
+      vertex 4.982020e+001 -4.637063e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.637063e+000 6.710101e+000
+      vertex 4.982020e+001 -2.757678e+000 6.026060e+000
+      vertex 4.982020e+001 -4.268727e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.268727e+000 7.500000e+000
+      vertex 4.982020e+001 -2.757678e+000 6.026060e+000
+      vertex 4.982020e+001 -2.536676e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -3.768822e+000 8.213938e+000
+      vertex 4.982020e+001 -2.536676e+000 6.500000e+000
+      vertex 4.982020e+001 -2.236733e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -1.866963e+000 7.298133e+000
+      vertex 4.982020e+001 -3.768822e+000 8.213938e+000
+      vertex 4.982020e+001 -2.236733e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -1.866963e+000 7.298133e+000
+      vertex 4.982020e+001 -3.152538e+000 8.830222e+000
+      vertex 4.982020e+001 -3.768822e+000 8.213938e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -1.866963e+000 7.298133e+000
+      vertex 4.982020e+001 -1.438600e+000 7.598076e+000
+      vertex 4.982020e+001 -3.152538e+000 8.830222e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -3.152538e+000 8.830222e+000
+      vertex 4.982020e+001 -1.438600e+000 7.598076e+000
+      vertex 4.982020e+001 -2.438600e+000 9.330127e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -2.438600e+000 9.330127e+000
+      vertex 4.982020e+001 -1.438600e+000 7.598076e+000
+      vertex 4.982020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -1.648701e+000 9.698463e+000
+      vertex 4.982020e+001 -9.646604e-001 7.819078e+000
+      vertex 4.982020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -8.068409e-001 9.924039e+000
+      vertex 4.982020e+001 -4.595445e-001 7.954423e+000
+      vertex 4.982020e+001 6.140000e-002 8.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 1.000000e+001
+      vertex 4.982020e+001 6.140000e-002 8.000000e+000
+      vertex 4.982020e+001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 9.296409e-001 9.924039e+000
+      vertex 4.982020e+001 5.823445e-001 7.954423e+000
+      vertex 4.982020e+001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 1.771501e+000 9.698463e+000
+      vertex 4.982020e+001 1.087460e+000 7.819078e+000
+      vertex 4.982020e+001 1.561400e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.561400e+000 9.330127e+000
+      vertex 4.982020e+001 1.561400e+000 7.598076e+000
+      vertex 4.982020e+001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 3.275338e+000 8.830222e+000
+      vertex 4.982020e+001 1.989763e+000 7.298133e+000
+      vertex 4.982020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 3.891622e+000 8.213938e+000
+      vertex 4.982020e+001 2.359533e+000 6.928363e+000
+      vertex 4.982020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 3.891622e+000 8.213938e+000
+      vertex 4.982020e+001 3.275338e+000 8.830222e+000
+      vertex 4.982020e+001 2.359533e+000 6.928363e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.359533e+000 6.928363e+000
+      vertex 4.982020e+001 2.659476e+000 6.500000e+000
+      vertex 4.982020e+001 4.391527e+000 7.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 4.391527e+000 7.500000e+000
+      vertex 4.982020e+001 2.659476e+000 6.500000e+000
+      vertex 4.982020e+001 4.759863e+000 6.710101e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 4.759863e+000 6.710101e+000
+      vertex 4.982020e+001 2.659476e+000 6.500000e+000
+      vertex 4.982020e+001 2.880478e+000 6.026060e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 4.985439e+000 5.868241e+000
+      vertex 4.982020e+001 2.880478e+000 6.026060e+000
+      vertex 4.982020e+001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.982020e+001 3.015823e+000 5.520945e+000
+      vertex 4.982020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 3.015823e+000 4.479055e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.982020e+001 3.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 3.015823e+000 4.479055e+000
+      vertex 4.982020e+001 2.880478e+000 3.973940e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.982020e+001 2.880478e+000 3.973940e+000
+      vertex 4.982020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.359533e+000 3.071637e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.982020e+001 2.659476e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.359533e+000 3.071637e+000
+      vertex 4.982020e+001 1.989763e+000 2.701867e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.982020e+001 1.989763e+000 2.701867e+000
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 5.000000e+000
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 1.989763e+000 2.701867e+000
+      vertex 4.982020e+001 1.561400e+000 2.401924e+000
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.506140e+001 0.000000e+000
+      vertex 4.982020e+001 1.989763e+000 2.701867e+000
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 1.561400e+000 2.401924e+000
+      vertex 4.982020e+001 1.087460e+000 2.180922e+000
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+      vertex 4.982020e+001 1.087460e+000 2.180922e+000
+      vertex 4.982020e+001 5.823445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 2.000000e+000
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+      vertex 4.982020e+001 5.823445e-001 2.045577e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 2.000000e+000
+      vertex 4.982020e+001 -4.595445e-001 2.045577e+000
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 -8.881784e-016
+      vertex 4.982020e+001 -4.595445e-001 2.045577e+000
+      vertex 4.982020e+001 -8.068409e-001 7.596123e-002
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -8.068409e-001 7.596123e-002
+      vertex 4.982020e+001 -4.595445e-001 2.045577e+000
+      vertex 4.982020e+001 -9.646604e-001 2.180922e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -1.648701e+000 3.015369e-001
+      vertex 4.982020e+001 -9.646604e-001 2.180922e+000
+      vertex 4.982020e+001 -1.438600e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -2.438600e+000 6.698730e-001
+      vertex 4.982020e+001 -1.438600e+000 2.401924e+000
+      vertex 4.982020e+001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -3.152538e+000 1.169778e+000
+      vertex 4.982020e+001 -1.866963e+000 2.701867e+000
+      vertex 4.982020e+001 -3.768822e+000 1.786062e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -3.152538e+000 1.169778e+000
+      vertex 4.982020e+001 -2.438600e+000 6.698730e-001
+      vertex 4.982020e+001 -1.866963e+000 2.701867e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -1.866963e+000 2.701867e+000
+      vertex 4.982020e+001 -2.236733e+000 3.071637e+000
+      vertex 4.982020e+001 -3.768822e+000 1.786062e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -3.768822e+000 1.786062e+000
+      vertex 4.982020e+001 -2.236733e+000 3.071637e+000
+      vertex 4.982020e+001 -4.268727e+000 2.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.268727e+000 2.500000e+000
+      vertex 4.982020e+001 -2.236733e+000 3.071637e+000
+      vertex 4.982020e+001 -2.536676e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.637063e+000 3.289899e+000
+      vertex 4.982020e+001 -2.536676e+000 3.500000e+000
+      vertex 4.982020e+001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.862639e+000 4.131759e+000
+      vertex 4.982020e+001 -2.757678e+000 3.973940e+000
+      vertex 4.982020e+001 -2.893023e+000 4.479055e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.938600e+000 5.000000e+000
+      vertex 4.982020e+001 -2.893023e+000 4.479055e+000
+      vertex 4.982020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.862639e+000 5.868241e+000
+      vertex 4.982020e+001 -4.938600e+000 5.000000e+000
+      vertex 4.982020e+001 -2.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 5.061400e+000 5.000000e+000
+      vertex 4.982020e+001 4.985439e+000 5.868241e+000
+      vertex 4.982020e+001 3.015823e+000 5.520945e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 4.759863e+000 6.710101e+000
+      vertex 4.982020e+001 2.880478e+000 6.026060e+000
+      vertex 4.982020e+001 4.985439e+000 5.868241e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 3.275338e+000 8.830222e+000
+      vertex 4.982020e+001 2.561400e+000 9.330127e+000
+      vertex 4.982020e+001 1.989763e+000 7.298133e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 2.561400e+000 9.330127e+000
+      vertex 4.982020e+001 1.771501e+000 9.698463e+000
+      vertex 4.982020e+001 1.561400e+000 7.598076e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 1.771501e+000 9.698463e+000
+      vertex 4.982020e+001 9.296409e-001 9.924039e+000
+      vertex 4.982020e+001 1.087460e+000 7.819078e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 9.296409e-001 9.924039e+000
+      vertex 4.982020e+001 6.140000e-002 1.000000e+001
+      vertex 4.982020e+001 5.823445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 6.140000e-002 1.000000e+001
+      vertex 4.982020e+001 -8.068409e-001 9.924039e+000
+      vertex 4.982020e+001 6.140000e-002 8.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -8.068409e-001 9.924039e+000
+      vertex 4.982020e+001 -1.648701e+000 9.698463e+000
+      vertex 4.982020e+001 -4.595445e-001 7.954423e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -1.648701e+000 9.698463e+000
+      vertex 4.982020e+001 -2.438600e+000 9.330127e+000
+      vertex 4.982020e+001 -9.646604e-001 7.819078e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -3.768822e+000 8.213938e+000
+      vertex 4.982020e+001 -4.268727e+000 7.500000e+000
+      vertex 4.982020e+001 -2.536676e+000 6.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.862639e+000 4.131759e+000
+      vertex 4.982020e+001 -2.893023e+000 4.479055e+000
+      vertex 4.982020e+001 -4.938600e+000 5.000000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.862639e+000 4.131759e+000
+      vertex 4.982020e+001 -4.637063e+000 3.289899e+000
+      vertex 4.982020e+001 -2.757678e+000 3.973940e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -4.637063e+000 3.289899e+000
+      vertex 4.982020e+001 -4.268727e+000 2.500000e+000
+      vertex 4.982020e+001 -2.536676e+000 3.500000e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -2.438600e+000 6.698730e-001
+      vertex 4.982020e+001 -1.648701e+000 3.015369e-001
+      vertex 4.982020e+001 -1.438600e+000 2.401924e+000
+    endloop
+  endfacet
+  facet normal 1.000000e+000 0.000000e+000 0.000000e+000
+    outer loop
+      vertex 4.982020e+001 -8.068409e-001 7.596123e-002
+      vertex 4.982020e+001 -9.646604e-001 2.180922e+000
+      vertex 4.982020e+001 -1.648701e+000 3.015369e-001
+    endloop
+  endfacet
+endsolid
diff --git a/contrib/Netgen/nglib/ng_occ.cpp b/contrib/Netgen/nglib/ng_occ.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..78898baaf64c1f51ff9ab2cdc75db97cff2cfd05
--- /dev/null
+++ b/contrib/Netgen/nglib/ng_occ.cpp
@@ -0,0 +1,142 @@
+#include <iostream>
+#include <climits>
+
+#include "TopTools_IndexedMapOfShape.hxx"
+#include "TopoDS.hxx"
+#include "TopoDS_Face.hxx"
+#include "TopoDS_Shape.hxx"
+#include "GProp_GProps.hxx"
+#include "BRepGProp.hxx"
+
+using namespace std;
+
+namespace nglib {
+#include <nglib.h>
+}
+
+int main (int argc, char ** argv)
+{
+   using namespace nglib;
+   
+   cout << "Netgen NgLib - OpenCascade Test Case" << endl;
+   
+   if (argc != 2)
+   {
+      cerr << "use: ng_occ <src_step>" << endl;
+      return 1;
+   }
+   
+   // Define pointer to OCC Geometry
+   Ng_OCC_Geometry *occ_geom;
+   
+   Ng_Mesh *occ_mesh;
+   
+   Ng_Meshing_Parameters mp;
+   
+   TopTools_IndexedMapOfShape FMap;
+   
+   Ng_OCC_TopTools_IndexedMapOfShape *occ_fmap = (Ng_OCC_TopTools_IndexedMapOfShape*)&FMap;
+   
+   // Result of Netgen Operations
+   Ng_Result ng_res;
+
+   // Initialise the Netgen Core library
+   Ng_Init();
+
+   // Read in the OCC File
+   occ_geom = Ng_OCC_Load_STEP(argv[1]);
+   if(!occ_geom)
+   {
+      cout << "Error reading in STEP File: " << argv[1] << endl;
+	  return 1;
+   }
+   cout << "Successfully loaded STEP File: " << argv[1] << endl;
+
+   occ_mesh = Ng_NewMesh();
+   
+   ng_res = Ng_OCC_GetFMap(occ_geom,occ_fmap);
+
+   cout << "ng_res = " << ng_res << endl;
+	  
+   if(!FMap.Extent())
+   {
+      cout << "Error retrieving Face map...." << endl;
+	  return 1;
+   }
+
+   cout << "Successfully extracted the Face Map....:" << FMap.Extent() << endl;
+   
+   for(int i = 1; i <= FMap.Extent(); i++)
+   {
+      TopoDS_Face OCCface;
+	  OCCface = TopoDS::Face(FMap.FindKey(i));
+	  
+	  GProp_GProps faceProps;
+	  BRepGProp::SurfaceProperties(OCCface,faceProps);
+	  
+      cout << "Index: " << i 
+	       << " :: Area: " << faceProps.Mass() 
+		   << " :: Hash: " << OCCface.HashCode(1e+6) 
+		   << endl;
+   }	 
+   
+   mp.uselocalh = 1;
+   mp.elementsperedge = 2.0;
+   mp.elementspercurve = 2.0;
+   mp.maxh = 10.0;
+   mp.grading = 0.2;   
+   mp.closeedgeenable = 0;
+   mp.closeedgefact = 1.0;
+   mp.optsurfmeshenable = 1;
+   
+   
+   cout << "Setting Local Mesh size....." << endl;
+   cout << "OCC Mesh Pointer before call = " << occ_mesh << endl;
+   Ng_OCC_SetLocalMeshSize(occ_geom, occ_mesh, &mp);
+   cout << "Local Mesh size successfully set....." << endl;
+      cout << "OCC Mesh Pointer after call = " << occ_mesh << endl;
+   
+   cout << "Creating Edge Mesh....." << endl;
+   ng_res = Ng_OCC_GenerateEdgeMesh(occ_geom, occ_mesh, &mp);
+   if(ng_res != NG_OK)
+   {
+      Ng_DeleteMesh(occ_mesh);
+	  cout << "Error creating Edge Mesh.... Aborting!!" << endl;
+	  return 1;
+   }
+   else
+   {
+      cout << "Edge Mesh successfully created....." << endl;
+	  cout << "Number of points = " << Ng_GetNP(occ_mesh) << endl;
+   }
+
+   cout << "Creating Surface Mesh....." << endl;
+ 
+   ng_res = Ng_OCC_GenerateSurfaceMesh(occ_geom, occ_mesh, &mp);
+   if(ng_res != NG_OK)
+   {
+      Ng_DeleteMesh(occ_mesh);
+	  cout << "Error creating Surface Mesh..... Aborting!!" << endl;
+	  return 1;
+   }
+   else
+   {
+      cout << "Surface Mesh successfully created....." << endl;
+	  cout << "Number of points = " << Ng_GetNP(occ_mesh) << endl;
+	  cout << "Number of surface elements = " << Ng_GetNSE(occ_mesh) << endl;
+   }
+
+   cout << "Creating Volume Mesh....." << endl;
+   
+   ng_res = Ng_GenerateVolumeMesh(occ_mesh, &mp);
+   
+   cout << "Volume Mesh successfully created....." << endl;
+   cout << "Number of points = " << Ng_GetNP(occ_mesh) << endl;
+   cout << "Number of volume elements = " << Ng_GetNE(occ_mesh) << endl;
+   
+   cout << "Saving Mesh as VOL file....." << endl;
+   Ng_SaveMesh(occ_mesh,"test_occ.vol");
+      
+   return 0;
+}
+   
diff --git a/contrib/Netgen/nglib/ng_stl.cpp b/contrib/Netgen/nglib/ng_stl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..af8a2ed8092e83e12cec85951ac6646ab3767a17
--- /dev/null
+++ b/contrib/Netgen/nglib/ng_stl.cpp
@@ -0,0 +1,128 @@
+/*!
+   \file ng_stl.cpp
+   \author Philippose Rajan
+   \date 14 Feb 2009 (Created)
+
+   This sample utility demonstrates the use of the Netgen 
+   nglib library for reading, and meshing an STL geometry. 
+
+   The Program takes as input the name of an STL file 
+   saved in the STL ASCII Format, and generates a 3D Volume 
+   mesh which is saved into the file "test.vol".
+
+   test.vol can be viewed using the usual Netgen Mesher GUI
+*/
+
+
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+
+namespace nglib {
+#include <nglib.h>
+}
+
+int main (int argc, char ** argv)
+{
+   using namespace nglib;
+
+   cout << "Netgen (nglib) STL Testing" << endl;
+
+   if (argc < 2)
+   {
+      cerr << "use: ng_stl STL_filename" << endl;
+      return 1;
+   }
+
+   // Define pointer to a new Netgen Mesh
+   Ng_Mesh *mesh;
+   
+   // Define pointer to STL Geometry
+   Ng_STL_Geometry *stl_geom;
+
+   // Result of Netgen Operations
+   Ng_Result ng_res;
+   
+   // Initialise the Netgen Core library
+   Ng_Init();
+
+   // Actually create the mesh structure
+   mesh = Ng_NewMesh();
+
+   int np, ne;
+
+   // Read in the STL File
+   stl_geom = Ng_STL_LoadGeometry(argv[1]);
+   if(!stl_geom)
+   {
+      cout << "Error reading in STL File: " << argv[1] << endl;
+	  return 1;
+   }
+   cout << "Successfully loaded STL File: " << argv[1] << endl;
+
+
+   // Set the Meshing Parameters to be used
+   Ng_Meshing_Parameters mp;
+   mp.maxh = 1.0e+6;
+   mp.fineness = 0.4;
+   mp.second_order = 0;
+
+   cout << "Initialise the STL Geometry structure...." << endl;
+   ng_res = Ng_STL_InitSTLGeometry(stl_geom);
+   if(ng_res != NG_OK)
+   {
+      cout << "Error Initialising the STL Geometry....Aborting!!" << endl;
+	   return 1;
+   }
+
+   cout << "Start Edge Meshing...." << endl;
+   ng_res = Ng_STL_MakeEdges(stl_geom, mesh, &mp);
+   if(ng_res != NG_OK)
+   {
+      cout << "Error in Edge Meshing....Aborting!!" << endl;
+	   return 1;
+   }
+
+   cout << "Start Surface Meshing...." << endl;
+   ng_res = Ng_STL_GenerateSurfaceMesh(stl_geom, mesh, &mp);
+   if(ng_res != NG_OK)
+   {
+      cout << "Error in Surface Meshing....Aborting!!" << endl;
+	   return 1;
+   }
+   
+   cout << "Start Volume Meshing...." << endl;
+   ng_res = Ng_GenerateVolumeMesh (mesh, &mp);
+   if(ng_res != NG_OK)
+   {
+      cout << "Error in Volume Meshing....Aborting!!" << endl;
+	  return 1;
+   }
+   
+   cout << "Meshing successfully completed....!!" << endl;
+
+   // volume mesh output
+   np = Ng_GetNP(mesh);
+   cout << "Points: " << np << endl;
+
+   ne = Ng_GetNE(mesh);
+   cout << "Elements: " << ne << endl;
+
+   cout << "Saving Mesh in VOL Format...." << endl;
+   Ng_SaveMesh(mesh,"test.vol");
+
+
+   // refinement without geomety adaption:
+   // Ng_Uniform_Refinement (mesh);
+
+   // refinement with geomety adaption:   
+   Ng_STL_Uniform_Refinement (stl_geom, mesh);
+
+   cout << "elements after refinement: " << Ng_GetNE(mesh) << endl;
+   cout << "points   after refinement: " << Ng_GetNP(mesh) << endl;
+
+   Ng_SaveMesh(mesh,"test_ref.vol");
+
+   return 0;
+}
diff --git a/contrib/Netgen/nglib/ng_vol.cpp b/contrib/Netgen/nglib/ng_vol.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8ccb3f81bd9125ada690696ff0eb0a1b45e8ddd2
--- /dev/null
+++ b/contrib/Netgen/nglib/ng_vol.cpp
@@ -0,0 +1,95 @@
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+
+namespace nglib {
+#include <nglib.h>
+}
+
+int main (int argc, char ** argv)
+{
+  using namespace nglib;
+
+  cout << "Netgen Testing" << endl;
+
+  if (argc < 2)
+    {
+      cerr << "use: ng_vol filename" << endl;
+      return 1;
+    }
+
+
+
+  Ng_Mesh * mesh;
+
+  Ng_Init();
+
+  // creates mesh structure
+  mesh = Ng_NewMesh ();
+
+
+
+
+
+  int i, np, nse, ne;
+  double point[3];
+  int trig[3], tet[4];
+
+
+  // reads surface mesh from file
+  ifstream in(argv[1]);
+
+  in >> np;
+  cout << "Reading " << np  << " points..."; cout.flush();
+  for (i = 1; i <= np; i++)
+    {
+      in >> point[0] >> point[1] >> point[2];
+      Ng_AddPoint (mesh, point);
+    }
+  cout << "done" << endl;
+
+  in >> nse;
+  cout << "Reading " << nse  << " faces..."; cout.flush();
+  for (i = 1; i <= nse; i++)
+    {
+      in >> trig[0] >> trig[1] >> trig[2];
+      Ng_AddSurfaceElement (mesh, NG_TRIG, trig);
+    }
+  cout << "done" << endl;
+
+
+  // generate volume mesh
+  Ng_Meshing_Parameters mp;
+  mp.maxh = 1e6;
+  mp.fineness = 1;
+  mp.second_order = 0;
+
+  cout << "start meshing" << endl;
+  Ng_GenerateVolumeMesh (mesh, &mp);
+  cout << "meshing done" << endl;
+
+  // volume mesh output
+  np = Ng_GetNP(mesh);
+  cout << "Points: " << np << endl;
+
+  for (i = 1; i <= np; i++)
+    {
+      Ng_GetPoint (mesh, i, point);
+      cout << i << ": " << point[0] << " " << point[1] << " " << point[2] << endl;
+    }
+
+  ne = Ng_GetNE(mesh);
+  cout << "Elements: " << ne << endl;
+  for (i = 1; i <= ne; i++)
+    {
+      Ng_GetVolumeElement (mesh, i, tet);
+      cout << i << ": " << tet[0] << " " << tet[1] 
+	   << " " << tet[2] << " " << tet[3] << endl;
+    }
+
+  Ng_SaveMesh(mesh,"test.vol");
+
+
+  return 0;
+}
diff --git a/contrib/Netgen/nglib/nglib.cpp b/contrib/Netgen/nglib/nglib.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..57a5c172d74cc1abf4c45287bc5a49de57328756
--- /dev/null
+++ b/contrib/Netgen/nglib/nglib.cpp
@@ -0,0 +1,1225 @@
+/**************************************************************************/
+/* File:   nglib.cpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   7. May. 2000                                                   */
+/**************************************************************************/
+
+/*
+  
+  Interface to the netgen meshing kernel
+  
+*/
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <stlgeom.hpp>
+#include <geometry2d.hpp>
+#include <meshing.hpp>
+
+#ifdef OCCGEOMETRY
+#include <occgeom.hpp>
+#endif
+
+#include <nginterface.h>
+
+
+namespace netgen {
+   extern void MeshFromSpline2D (SplineGeometry2d & geometry,
+                                 Mesh *& mesh, 
+                                 MeshingParameters & mp);
+}
+
+namespace netgen
+{
+   int id, ntasks;
+}
+
+
+#ifdef PARALLEL
+#include <mpi.h>
+
+namespace netgen
+{
+  // MPI_Group MPI_HIGHORDER_WORLD;
+  // MPI_Comm MPI_HIGHORDER_COMM;
+  MPI_Comm mesh_comm;
+}
+#endif
+
+
+/*
+// should not be needed (occ currently requires it)
+namespace netgen {
+#include "../libsrc/visualization/vispar.hpp"
+  VisualizationParameters vispar;
+  VisualizationParameters :: VisualizationParameters() { ; }
+}
+*/
+
+
+namespace nglib {
+#include "nglib.h"
+}
+
+using namespace netgen;
+
+// constants and types:
+
+namespace nglib
+{
+   // initialize, deconstruct Netgen library:
+   DLL_HEADER void Ng_Init ()
+   {
+      mycout = &cout;
+      myerr = &cerr;
+      // netgen::testout->SetOutStream (new ofstream ("test.out"));
+      testout = new ofstream ("test.out");
+   }
+
+
+
+
+   // Clean-up functions before ending usage of nglib
+   DLL_HEADER void Ng_Exit ()
+   {
+      ;
+   }
+
+
+
+
+   // Create a new netgen mesh object
+   DLL_HEADER Ng_Mesh * Ng_NewMesh ()
+   {
+      Mesh * mesh = new Mesh;  
+      mesh->AddFaceDescriptor (FaceDescriptor (1, 1, 0, 1));
+      return (Ng_Mesh*)(void*)mesh;
+   }
+
+
+
+
+   // Delete an existing netgen mesh object
+   DLL_HEADER void Ng_DeleteMesh (Ng_Mesh * mesh)
+   {
+      if(mesh != NULL)
+      {
+         // Delete the Mesh structures
+         ((Mesh*)mesh)->DeleteMesh();
+
+         // Now delete the Mesh class itself
+         delete (Mesh*)mesh;
+
+         // Set the Ng_Mesh pointer to NULL
+         mesh = NULL;
+      }
+   }
+
+
+
+
+   // Save a netgen mesh in the native VOL format 
+   DLL_HEADER void Ng_SaveMesh(Ng_Mesh * mesh, const char* filename)
+   {
+      ((Mesh*)mesh)->Save(filename);
+   }
+
+
+
+
+   // Load a netgen native VOL mesh from a given file
+   DLL_HEADER Ng_Mesh * Ng_LoadMesh(const char* filename)
+   {
+      Mesh * mesh = new Mesh;
+      mesh->Load(filename);
+      return ( (Ng_Mesh*)mesh );
+   }
+
+
+
+
+   // Merge another mesh file into the currently loaded one
+   DLL_HEADER Ng_Result Ng_MergeMesh( Ng_Mesh* mesh, const char* filename)
+   {
+      Ng_Result status = NG_OK;
+
+      ifstream infile(filename);
+      Mesh * m = (Mesh*)mesh;
+
+      if(!infile.good())
+      {
+         status = NG_FILE_NOT_FOUND;
+      }
+
+      if(!m)
+      {
+         status = NG_ERROR;
+      }
+
+      if(status == NG_OK)
+      {
+         const int num_pts = m->GetNP();
+         const int face_offset = m->GetNFD();
+
+         m->Merge(infile, face_offset);
+
+         if(m->GetNP() > num_pts)
+         {
+            status = NG_OK;
+         }
+         else
+         {
+            status = NG_ERROR;
+         }
+      }
+
+      return status;
+   }
+
+
+
+
+   // Merge another mesh file into the currently loaded one
+   DLL_HEADER Ng_Result Ng_MergeMesh( Ng_Mesh* mesh1, Ng_Mesh* mesh2)
+   {
+      return NG_ERROR;
+   }
+
+
+
+
+   // Manually add a point to an existing mesh object
+   DLL_HEADER void Ng_AddPoint (Ng_Mesh * mesh, double * x)
+   {
+      Mesh * m = (Mesh*)mesh;
+      m->AddPoint (Point3d (x[0], x[1], x[2]));
+   }
+
+
+
+
+   // Manually add a surface element of a given type to an existing mesh object
+   DLL_HEADER void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et,
+                                         int * pi)
+   {
+      Mesh * m = (Mesh*)mesh;
+      Element2d el (3);
+      el.SetIndex (1);
+      el.PNum(1) = pi[0];
+      el.PNum(2) = pi[1];
+      el.PNum(3) = pi[2];
+      m->AddSurfaceElement (el);
+   }
+
+
+
+
+   // Manually add a volume element of a given type to an existing mesh object
+   DLL_HEADER void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et,
+                                        int * pi)
+   {
+      Mesh * m = (Mesh*)mesh;
+      Element el (4);
+      el.SetIndex (1);
+      el.PNum(1) = pi[0];
+      el.PNum(2) = pi[1];
+      el.PNum(3) = pi[2];
+      el.PNum(4) = pi[3];
+      m->AddVolumeElement (el);
+   }
+
+
+
+
+   // Obtain the number of points in the mesh
+   DLL_HEADER int Ng_GetNP (Ng_Mesh * mesh)
+   {
+      return ((Mesh*)mesh) -> GetNP();
+   }
+
+
+
+
+   // Obtain the number of surface elements in the mesh
+   DLL_HEADER int Ng_GetNSE (Ng_Mesh * mesh)
+   {
+      return ((Mesh*)mesh) -> GetNSE();
+   }
+
+
+
+
+   // Obtain the number of volume elements in the mesh
+   DLL_HEADER int Ng_GetNE (Ng_Mesh * mesh)
+   {
+      return ((Mesh*)mesh) -> GetNE();
+   }
+
+
+
+
+   //  Return point coordinates of a given point index in the mesh
+   DLL_HEADER void Ng_GetPoint (Ng_Mesh * mesh, int num, double * x)
+   {
+      const Point3d & p = ((Mesh*)mesh)->Point(num);
+      x[0] = p.X();
+      x[1] = p.Y();
+      x[2] = p.Z();
+   }
+
+
+
+
+   // Return the surface element at a given index "pi"
+   DLL_HEADER Ng_Surface_Element_Type 
+      Ng_GetSurfaceElement (Ng_Mesh * mesh, int num, int * pi)
+   {
+      const Element2d & el = ((Mesh*)mesh)->SurfaceElement(num);
+      for (int i = 1; i <= el.GetNP(); i++)
+         pi[i-1] = el.PNum(i);
+      Ng_Surface_Element_Type et;
+      switch (el.GetNP())
+      {
+      case 3: et = NG_TRIG; break;
+      case 4: et = NG_QUAD; break;
+      case 6: 
+         switch (el.GetNV())
+         {
+         case 3: et = NG_TRIG6; break;
+         case 4: et = NG_QUAD6; break;
+         default:
+            et = NG_TRIG6; break;
+         }
+         break;
+      case 8: et = NG_QUAD8; break;
+      default:
+         et = NG_TRIG; break; // for the compiler
+      }
+      return et;
+   }
+
+
+
+
+   // Return the volume element at a given index "pi"
+   DLL_HEADER Ng_Volume_Element_Type
+      Ng_GetVolumeElement (Ng_Mesh * mesh, int num, int * pi)
+   {
+      const Element & el = ((Mesh*)mesh)->VolumeElement(num);
+      for (int i = 1; i <= el.GetNP(); i++)
+         pi[i-1] = el.PNum(i);
+      Ng_Volume_Element_Type et;
+      switch (el.GetNP())
+      {
+      case 4: et = NG_TET; break;
+      case 5: et = NG_PYRAMID; break;
+      case 6: et = NG_PRISM; break;
+      case 10: et = NG_TET10; break;
+      default:
+         et = NG_TET; break; // for the compiler
+      }
+      return et;
+   }
+
+
+
+
+   // Set a global limit on the maximum mesh size allowed
+   DLL_HEADER void Ng_RestrictMeshSizeGlobal (Ng_Mesh * mesh, double h)
+   {
+      ((Mesh*)mesh) -> SetGlobalH (h);
+   }
+
+
+
+
+   // Set a local limit on the maximum mesh size allowed around the given point
+   DLL_HEADER void Ng_RestrictMeshSizePoint (Ng_Mesh * mesh, double * p, double h)
+   {
+      ((Mesh*)mesh) -> RestrictLocalH (Point3d (p[0], p[1], p[2]), h);
+   }
+
+
+
+
+   // Set a local limit on the maximum mesh size allowed within a given box region
+   DLL_HEADER void Ng_RestrictMeshSizeBox (Ng_Mesh * mesh, double * pmin, double * pmax, double h)
+   {
+      for (double x = pmin[0]; x < pmax[0]; x += h)
+         for (double y = pmin[1]; y < pmax[1]; y += h)
+            for (double z = pmin[2]; z < pmax[2]; z += h)
+               ((Mesh*)mesh) -> RestrictLocalH (Point3d (x, y, z), h);
+   }
+
+
+
+
+   // Generates volume mesh from an existing surface mesh
+   DLL_HEADER Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp)
+   {
+      Mesh * m = (Mesh*)mesh;
+
+      // Philippose - 30/08/2009
+      // Do not locally re-define "mparam" here... "mparam" is a global 
+      // object 
+      //MeshingParameters mparam;
+      mp->Transfer_Parameters();
+
+      m->CalcLocalH(mparam.grading);
+
+      MeshVolume (mparam, *m);
+      RemoveIllegalElements (*m);
+      OptimizeVolume (mparam, *m);
+
+      return NG_OK;
+   }
+
+
+
+
+   /* ------------------ 2D Meshing Functions ------------------------- */
+   DLL_HEADER void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x)
+   {
+      Mesh * m = (Mesh*)mesh;
+
+      m->AddPoint (Point3d (x[0], x[1], 0));
+   }
+
+
+
+
+   DLL_HEADER void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2)
+   {
+      Mesh * m = (Mesh*)mesh;
+
+      Segment seg;
+      seg[0] = pi1;
+      seg[1] = pi2;
+      m->AddSegment (seg);
+   }
+
+
+
+
+   DLL_HEADER int Ng_GetNP_2D (Ng_Mesh * mesh)
+   {
+      Mesh * m = (Mesh*)mesh;
+      return m->GetNP();
+   }
+
+
+
+
+   DLL_HEADER int Ng_GetNE_2D (Ng_Mesh * mesh)
+   {
+      Mesh * m = (Mesh*)mesh;
+      return m->GetNSE();
+   }
+
+
+
+
+   DLL_HEADER int Ng_GetNSeg_2D (Ng_Mesh * mesh)
+   {
+      Mesh * m = (Mesh*)mesh;
+      return m->GetNSeg();
+   }
+
+
+
+
+   DLL_HEADER void Ng_GetPoint_2D (Ng_Mesh * mesh, int num, double * x)
+   {
+      Mesh * m = (Mesh*)mesh;
+
+      Point<3> & p = m->Point(num);
+      x[0] = p(0);
+      x[1] = p(1);
+   }
+
+
+
+
+   DLL_HEADER Ng_Surface_Element_Type
+      Ng_GetElement_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum)
+   {
+      const Element2d & el = ((Mesh*)mesh)->SurfaceElement(num);
+      for (int i = 1; i <= el.GetNP(); i++)
+         pi[i-1] = el.PNum(i);
+
+      Ng_Surface_Element_Type et;
+      switch (el.GetNP())
+      {
+      case 3: et = NG_TRIG; break;
+      case 4: et = NG_QUAD; break;
+      case 6: 
+         switch (el.GetNV())
+         {
+         case 3: et = NG_TRIG6; break;
+         case 4: et = NG_QUAD6; break;
+         default:
+            et = NG_TRIG6; break;
+         }
+         break;
+      case 8: et = NG_QUAD8; break;
+      default:
+         et = NG_TRIG; break; // for the compiler
+      }
+
+      if (matnum)
+         *matnum = el.GetIndex();
+
+      return et;
+   }
+
+
+
+
+   DLL_HEADER void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum)
+   {
+      const Segment & seg = ((Mesh*)mesh)->LineSegment(num);
+      pi[0] = seg[0];
+      pi[1] = seg[1];
+
+      if (matnum)
+         *matnum = seg.edgenr;
+   }
+
+
+
+
+   DLL_HEADER Ng_Geometry_2D * Ng_LoadGeometry_2D (const char * filename)
+   {
+      SplineGeometry2d * geom = new SplineGeometry2d();
+      geom -> Load (filename);
+      return (Ng_Geometry_2D *)geom;
+   }
+
+
+
+
+   DLL_HEADER Ng_Result Ng_GenerateMesh_2D (Ng_Geometry_2D * geom,
+                                            Ng_Mesh ** mesh,
+                                            Ng_Meshing_Parameters * mp)
+   {
+      // use global variable mparam
+      //  MeshingParameters mparam;  
+      mp->Transfer_Parameters();
+
+      Mesh * m;
+      MeshFromSpline2D (*(SplineGeometry2d*)geom, m, mparam);
+
+      cout << m->GetNSE() << " elements, " << m->GetNP() << " points" << endl;
+
+      *mesh = (Ng_Mesh*)m;
+      return NG_OK;
+   }
+
+
+
+
+   DLL_HEADER void Ng_HP_Refinement (Ng_Geometry_2D * geom,
+      Ng_Mesh * mesh,
+      int levels)
+   {
+      Refinement2d ref(*(SplineGeometry2d*)geom);
+      HPRefinement (*(Mesh*)mesh, &ref, levels);
+   }
+
+
+
+
+   DLL_HEADER void Ng_HP_Refinement (Ng_Geometry_2D * geom,
+      Ng_Mesh * mesh,
+      int levels, double parameter)
+   {
+      Refinement2d ref(*(SplineGeometry2d*)geom);
+      HPRefinement (*(Mesh*)mesh, &ref, levels, parameter);
+   }
+
+
+
+
+   Array<STLReadTriangle> readtrias; //only before initstlgeometry
+   Array<Point<3> > readedges; //only before init stlgeometry
+
+   // loads geometry from STL file
+   DLL_HEADER Ng_STL_Geometry * Ng_STL_LoadGeometry (const char * filename, int binary)
+   {
+      int i;
+      STLGeometry geom;
+      STLGeometry* geo;
+      ifstream ist(filename);
+
+      if (binary)
+      {
+         geo = geom.LoadBinary(ist);
+      }
+      else
+      {
+         geo = geom.Load(ist);
+      }
+
+      readtrias.SetSize(0);
+      readedges.SetSize(0);
+
+      Point3d p;
+      Vec3d normal;
+      double p1[3];
+      double p2[3];
+      double p3[3];
+      double n[3];
+
+      Ng_STL_Geometry * geo2 = Ng_STL_NewGeometry();
+
+      for (i = 1; i <= geo->GetNT(); i++)
+      {
+         const STLTriangle& t = geo->GetTriangle(i);
+         p = geo->GetPoint(t.PNum(1));
+         p1[0] = p.X(); p1[1] = p.Y(); p1[2] = p.Z(); 
+         p = geo->GetPoint(t.PNum(2));
+         p2[0] = p.X(); p2[1] = p.Y(); p2[2] = p.Z(); 
+         p = geo->GetPoint(t.PNum(3));
+         p3[0] = p.X(); p3[1] = p.Y(); p3[2] = p.Z();
+         normal = t.Normal();
+         n[0] = normal.X(); n[1] = normal.Y(); n[2] = normal.Z();
+
+         Ng_STL_AddTriangle(geo2, p1, p2, p3, n);
+      }
+
+      return geo2;
+   }
+
+
+
+
+   // generate new STL Geometry
+   DLL_HEADER Ng_STL_Geometry * Ng_STL_NewGeometry ()
+   {
+      return (Ng_STL_Geometry*)(void*)new STLGeometry;
+   } 
+
+
+
+
+   // after adding triangles (and edges) initialize
+   DLL_HEADER Ng_Result Ng_STL_InitSTLGeometry (Ng_STL_Geometry * geom)
+   {
+      STLGeometry* geo = (STLGeometry*)geom;
+      geo->InitSTLGeometry(readtrias);
+      readtrias.SetSize(0);
+
+      if (readedges.Size() != 0)
+      {
+         /*
+         for (int i = 1; i <= readedges.Size(); i+=2)
+         {
+         cout << "e(" << readedges.Get(i) << "," << readedges.Get(i+1) << ")" << endl;
+         }
+         */
+         geo->AddEdges(readedges);
+      }
+
+      if (geo->GetStatus() == STLTopology::STL_GOOD || geo->GetStatus() == STLTopology::STL_WARNING) return NG_OK;
+      return NG_SURFACE_INPUT_ERROR;
+   }
+
+
+
+
+   // automatically generates edges:
+   DLL_HEADER Ng_Result Ng_STL_MakeEdges (Ng_STL_Geometry * geom,
+                                          Ng_Mesh* mesh,
+                                          Ng_Meshing_Parameters * mp)
+   {
+      STLGeometry* stlgeometry = (STLGeometry*)geom;
+      Mesh* me = (Mesh*)mesh;
+
+      // Philippose - 27/07/2009
+      // Do not locally re-define "mparam" here... "mparam" is a global 
+      // object 
+      //MeshingParameters mparam;
+      mp->Transfer_Parameters();
+
+      me -> SetGlobalH (mparam.maxh);
+      me -> SetLocalH (stlgeometry->GetBoundingBox().PMin() - Vec3d(10, 10, 10),
+                       stlgeometry->GetBoundingBox().PMax() + Vec3d(10, 10, 10),
+                       0.3);
+
+      me -> LoadLocalMeshSize (mp->meshsize_filename);
+      /*
+      if (mp->meshsize_filename)
+      {
+      ifstream infile (mp->meshsize_filename);
+      if (!infile.good()) return NG_FILE_NOT_FOUND;
+      me -> LoadLocalMeshSize (infile);
+      }
+      */
+
+      STLMeshing (*stlgeometry, *me);
+
+      stlgeometry->edgesfound = 1;
+      stlgeometry->surfacemeshed = 0;
+      stlgeometry->surfaceoptimized = 0;
+      stlgeometry->volumemeshed = 0;
+
+      return NG_OK;
+   }
+
+
+
+
+   // generates mesh, empty mesh be already created.
+   DLL_HEADER Ng_Result Ng_STL_GenerateSurfaceMesh (Ng_STL_Geometry * geom,
+                                                    Ng_Mesh* mesh,
+                                                    Ng_Meshing_Parameters * mp)
+   {
+      STLGeometry* stlgeometry = (STLGeometry*)geom;
+      Mesh* me = (Mesh*)mesh;
+
+      // Philippose - 27/07/2009
+      // Do not locally re-define "mparam" here... "mparam" is a global 
+      // object
+      //MeshingParameters mparam;
+      mp->Transfer_Parameters();
+
+
+      /*
+      me -> SetGlobalH (mparam.maxh);
+      me -> SetLocalH (stlgeometry->GetBoundingBox().PMin() - Vec3d(10, 10, 10),
+      stlgeometry->GetBoundingBox().PMax() + Vec3d(10, 10, 10),
+      0.3);
+      */
+      /*
+      STLMeshing (*stlgeometry, *me);
+
+      stlgeometry->edgesfound = 1;
+      stlgeometry->surfacemeshed = 0;
+      stlgeometry->surfaceoptimized = 0;
+      stlgeometry->volumemeshed = 0;
+      */  
+      int retval = STLSurfaceMeshing (*stlgeometry, *me);
+      if (retval == MESHING3_OK)
+      {
+         (*mycout) << "Success !!!!" << endl;
+         stlgeometry->surfacemeshed = 1;
+         stlgeometry->surfaceoptimized = 0;
+         stlgeometry->volumemeshed = 0;
+      } 
+      else if (retval == MESHING3_OUTERSTEPSEXCEEDED)
+      {
+         (*mycout) << "ERROR: Give up because of too many trials. Meshing aborted!" << endl;
+      }
+      else if (retval == MESHING3_TERMINATE)
+      {
+         (*mycout) << "Meshing Stopped!" << endl;
+      }
+      else
+      {
+         (*mycout) << "ERROR: Surface meshing not successful. Meshing aborted!" << endl;
+      }
+
+
+      STLSurfaceOptimization (*stlgeometry, *me, mparam);
+
+      return NG_OK;
+   }
+
+
+
+
+   // fills STL Geometry
+   // positive orientation
+   // normal vector may be null-pointer
+   DLL_HEADER void Ng_STL_AddTriangle (Ng_STL_Geometry * geom, 
+                                       double * p1, double * p2, double * p3, 
+                                       double * nv)
+   {
+      Point<3> apts[3];
+      apts[0] = Point<3>(p1[0],p1[1],p1[2]);
+      apts[1] = Point<3>(p2[0],p2[1],p2[2]);
+      apts[2] = Point<3>(p3[0],p3[1],p3[2]);
+
+      Vec<3> n;
+      if (!nv)
+         n = Cross (apts[0]-apts[1], apts[0]-apts[2]);
+      else
+         n = Vec<3>(nv[0],nv[1],nv[2]);
+
+      readtrias.Append(STLReadTriangle(apts,n));
+   }
+
+   // add (optional) edges:
+   DLL_HEADER void Ng_STL_AddEdge (Ng_STL_Geometry * geom, 
+      double * p1, double * p2)
+   {
+      readedges.Append(Point3d(p1[0],p1[1],p1[2]));
+      readedges.Append(Point3d(p2[0],p2[1],p2[2]));
+   }
+
+
+
+
+#ifdef OCCGEOMETRY
+   // --------------------- OCC Geometry / Meshing Utility Functions -------------------
+   // Create new OCC Geometry Object
+   DLL_HEADER Ng_OCC_Geometry * Ng_OCC_NewGeometry ()
+   {
+      return (Ng_OCC_Geometry*)(void*)new OCCGeometry;
+   } 
+
+
+
+
+   // Delete the OCC Geometry Object
+   DLL_HEADER Ng_Result Ng_OCC_DeleteGeometry(Ng_OCC_Geometry * geom)
+   {
+      if (geom != NULL)
+      {
+         delete (OCCGeometry*)geom;
+         geom = NULL;
+         return NG_OK;
+      }
+      
+      return NG_ERROR;
+   }
+
+
+
+   
+   // Loads geometry from STEP File
+   DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_STEP (const char * filename)
+   {
+      // Call the STEP File Load function. Note.. the geometry class 
+      // is created and instantiated within the load function
+      OCCGeometry * occgeo = LoadOCC_STEP(filename);
+
+      return ((Ng_OCC_Geometry *)occgeo);
+   }
+
+
+
+   
+   // Loads geometry from IGES File
+   DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_IGES (const char * filename)
+   {
+      // Call the IGES File Load function. Note.. the geometry class 
+      // is created and instantiated within the load function
+      OCCGeometry * occgeo = LoadOCC_IGES(filename);
+
+      return ((Ng_OCC_Geometry *)occgeo);
+   }
+
+
+
+   
+   // Loads geometry from BREP File
+   DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_BREP (const char * filename)
+   {
+      // Call the BREP File Load function. Note.. the geometry class 
+      // is created and instantiated within the load function
+      OCCGeometry * occgeo = LoadOCC_BREP(filename);
+
+      return ((Ng_OCC_Geometry *)occgeo);
+   }
+
+
+
+
+   // Locally limit the size of the mesh to be generated at various points 
+   // based on the topology of the geometry
+   DLL_HEADER Ng_Result Ng_OCC_SetLocalMeshSize (Ng_OCC_Geometry * geom,
+                                                 Ng_Mesh * mesh,
+                                                 Ng_Meshing_Parameters * mp)
+   {
+      OCCGeometry * occgeom = (OCCGeometry*)geom;
+      Mesh * me = (Mesh*)mesh;
+
+      me->geomtype = Mesh::GEOM_OCC;
+
+      mp->Transfer_Parameters();
+      
+      occparam.resthcloseedgeenable = mp->closeedgeenable;
+      occparam.resthcloseedgefac = mp->closeedgefact;
+
+      // Delete the mesh structures in order to start with a clean 
+      // slate
+      me->DeleteMesh();
+
+      OCCSetLocalMeshSize(*occgeom, *me);
+
+      return(NG_OK);
+   }
+
+
+
+   
+   // Mesh the edges and add Face descriptors to prepare for surface meshing
+   DLL_HEADER Ng_Result Ng_OCC_GenerateEdgeMesh (Ng_OCC_Geometry * geom,
+                                                 Ng_Mesh * mesh,
+                                                 Ng_Meshing_Parameters * mp)
+   {
+      OCCGeometry * occgeom = (OCCGeometry*)geom;
+      Mesh * me = (Mesh*)mesh;
+
+      mp->Transfer_Parameters();
+
+      OCCFindEdges(*occgeom, *me);
+
+      if((me->GetNP()) && (me->GetNFD()))
+      {
+         return NG_OK;
+      }
+      else
+      {
+         return NG_ERROR;
+      }
+   }
+
+
+
+   
+   // Mesh the edges and add Face descriptors to prepare for surface meshing
+   DLL_HEADER Ng_Result Ng_OCC_GenerateSurfaceMesh (Ng_OCC_Geometry * geom,
+                                                    Ng_Mesh * mesh,
+                                                    Ng_Meshing_Parameters * mp)
+   {
+      int numpoints = 0;
+
+      OCCGeometry * occgeom = (OCCGeometry*)geom;
+      Mesh * me = (Mesh*)mesh;
+
+      // Set the internal meshing parameters structure from the nglib meshing 
+      // parameters structure
+      mp->Transfer_Parameters();
+
+
+      // Only go into surface meshing if the face descriptors have already been added
+      if(!me->GetNFD())
+         return NG_ERROR;
+
+      numpoints = me->GetNP();
+
+      // Initially set up only for surface meshing without any optimisation
+      int perfstepsend = MESHCONST_MESHSURFACE;
+
+      // Check and if required, enable surface mesh optimisation step
+      if(mp->optsurfmeshenable)
+      {
+         perfstepsend = MESHCONST_OPTSURFACE;
+      }
+
+      OCCMeshSurface(*occgeom, *me, perfstepsend);
+
+      me->CalcSurfacesOfNode();
+      
+      if(me->GetNP() <= numpoints)
+         return NG_ERROR;
+
+      if(me->GetNSE() <= 0)
+         return NG_ERROR;
+
+      return NG_OK;
+   }
+
+
+
+
+   // Extract the face map from the OCC geometry
+   // The face map basically gives an index to each face in the geometry, 
+   // which can be used to access a specific face
+   DLL_HEADER Ng_Result Ng_OCC_GetFMap(Ng_OCC_Geometry * geom, 
+                                       Ng_OCC_TopTools_IndexedMapOfShape * FMap)
+   {
+      OCCGeometry* occgeom = (OCCGeometry*)geom;
+      TopTools_IndexedMapOfShape *occfmap = (TopTools_IndexedMapOfShape *)FMap;
+
+      // Copy the face map from the geometry to the given variable
+      occfmap->Assign(occgeom->fmap);
+
+      if(occfmap->Extent())
+      {
+         return NG_OK;
+      }
+      else
+      {
+         return NG_ERROR;
+      }
+   }
+
+   // ------------------ End - OCC Geometry / Meshing Utility Functions ----------------
+#endif
+
+
+
+
+   // ------------------ Begin - Meshing Parameters related functions ------------------
+   // Constructor for the local nglib meshing parameters class
+   DLL_HEADER Ng_Meshing_Parameters :: Ng_Meshing_Parameters()
+   {
+      uselocalh = 1;
+
+      maxh = 1000;
+      minh = 0.0;
+
+      fineness = 0.5;
+      grading = 0.3;
+
+      elementsperedge = 2.0;
+      elementspercurve = 2.0;
+
+      closeedgeenable = 0;
+      closeedgefact = 2.0;
+
+      second_order = 0;
+      quad_dominated = 0;
+
+      meshsize_filename = 0;
+
+      optsurfmeshenable = 1;
+      optvolmeshenable = 1;
+
+      optsteps_2d = 3;
+      optsteps_3d = 3;
+
+      invert_tets = 0;
+      invert_trigs = 0;
+
+      check_overlap = 1;
+      check_overlapping_boundary = 1;
+   }
+
+
+
+
+   // Reset the local meshing parameters to the default values
+   DLL_HEADER void Ng_Meshing_Parameters :: Reset_Parameters()
+   {
+      uselocalh = 1;
+
+      maxh = 1000;
+      minh = 0;
+
+      fineness = 0.5;
+      grading = 0.3;
+
+      elementsperedge = 2.0;
+      elementspercurve = 2.0;
+
+      closeedgeenable = 0;
+      closeedgefact = 2.0;
+
+      second_order = 0;
+      quad_dominated = 0;
+
+      meshsize_filename = 0;
+
+      optsurfmeshenable = 1;
+      optvolmeshenable = 1;
+
+      optsteps_2d = 3;
+      optsteps_3d = 3;
+
+      invert_tets = 0;
+      invert_trigs = 0;
+
+      check_overlap = 1;
+      check_overlapping_boundary = 1;
+   }
+
+
+
+
+   // 
+   DLL_HEADER void Ng_Meshing_Parameters :: Transfer_Parameters()
+   {
+      mparam.uselocalh = uselocalh;
+      
+      mparam.maxh = maxh;
+      mparam.minh = minh;
+
+      mparam.grading = grading;
+      mparam.curvaturesafety = elementspercurve;
+      mparam.segmentsperedge = elementsperedge;
+
+      mparam.secondorder = second_order;
+      mparam.quad = quad_dominated;
+
+      mparam.meshsizefilename = meshsize_filename;
+
+      mparam.optsteps2d = optsteps_2d;
+      mparam.optsteps3d = optsteps_3d;
+
+      mparam.inverttets = invert_tets;
+      mparam.inverttrigs = invert_trigs;
+
+      mparam.checkoverlap = check_overlap;
+      mparam.checkoverlappingboundary = check_overlapping_boundary;
+   }
+   // ------------------ End - Meshing Parameters related functions --------------------
+
+
+
+
+   // ------------------ Begin - Second Order Mesh generation functions ----------------
+   DLL_HEADER void Ng_Generate_SecondOrder(Ng_Mesh * mesh)
+   {
+      Refinement ref;
+      ref.MakeSecondOrder(*(Mesh*) mesh);
+   }
+
+
+
+
+   DLL_HEADER void Ng_2D_Generate_SecondOrder(Ng_Geometry_2D * geom,
+					  Ng_Mesh * mesh)
+   {
+      ( (SplineGeometry2d*)geom ) -> GetRefinement().MakeSecondOrder( * (Mesh*) mesh );
+   }
+
+
+
+
+   DLL_HEADER void Ng_STL_Generate_SecondOrder(Ng_STL_Geometry * geom,
+					   Ng_Mesh * mesh)
+   {
+      ((STLGeometry*)geom)->GetRefinement().MakeSecondOrder(*(Mesh*) mesh);
+   }
+
+
+
+
+   DLL_HEADER void Ng_CSG_Generate_SecondOrder (Ng_CSG_Geometry * geom,
+					   Ng_Mesh * mesh)
+   {
+      ((CSGeometry*)geom)->GetRefinement().MakeSecondOrder(*(Mesh*) mesh);
+   }
+
+
+
+
+#ifdef OCCGEOMETRY
+   DLL_HEADER void Ng_OCC_Generate_SecondOrder (Ng_OCC_Geometry * geom,
+                  Ng_Mesh * mesh)
+   {
+      ((OCCGeometry*)geom )->GetRefinement().MakeSecondOrder(*(Mesh*) mesh);
+   }
+#endif
+   // ------------------ End - Second Order Mesh generation functions ------------------
+
+
+
+
+   // ------------------ Begin - Uniform Mesh Refinement functions ---------------------
+   DLL_HEADER void Ng_Uniform_Refinement (Ng_Mesh * mesh)
+   {
+      Refinement ref;
+      ref.Refine ( * (Mesh*) mesh );
+   }
+
+
+
+
+   DLL_HEADER void Ng_2D_Uniform_Refinement (Ng_Geometry_2D * geom,
+      Ng_Mesh * mesh)
+   {
+      ( (SplineGeometry2d*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh );
+   }
+
+
+
+
+   DLL_HEADER void Ng_STL_Uniform_Refinement (Ng_STL_Geometry * geom,
+      Ng_Mesh * mesh)
+   {
+      ( (STLGeometry*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh );
+   }
+
+
+
+
+   DLL_HEADER void Ng_CSG_Uniform_Refinement (Ng_CSG_Geometry * geom,
+      Ng_Mesh * mesh)
+   {
+      ( (CSGeometry*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh );
+   }
+
+
+
+
+#ifdef OCCGEOMETRY
+   DLL_HEADER void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom,
+      Ng_Mesh * mesh)
+   {
+      ( (OCCGeometry*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh );
+   }
+#endif
+   // ------------------ End - Uniform Mesh Refinement functions -----------------------
+} // End of namespace nglib
+
+
+
+
+// compatibility functions:
+namespace netgen 
+{
+   char geomfilename[255];
+
+   DLL_HEADER void MyError (const char * ch)
+   {
+      cerr << ch;
+   }
+
+
+
+
+   //Destination for messages, errors, ...
+   DLL_HEADER void Ng_PrintDest(const char * s)
+   {
+      (*mycout) << s << flush;
+   }
+
+
+
+
+   DLL_HEADER double GetTime ()
+   {
+      return 0;
+   }
+
+
+
+
+   void ResetTime ()
+   {
+      ;
+   }
+
+
+
+
+   void MyBeep (int i)
+   {
+      ;
+   }
+
+
+
+
+   void Render()
+   {
+      ; 
+   }
+} // End of namespace netgen
+
+
+
+
+void Ng_Redraw () { ; }
+void Ng_ClearSolutionData () { ; }
+void Ng_SetSolutionData (Ng_SolutionData * soldata) { ; }
+void Ng_InitSolutionData (Ng_SolutionData * soldata) { ; }
+
+
diff --git a/contrib/Netgen/nglib/nglib.h b/contrib/Netgen/nglib/nglib.h
new file mode 100644
index 0000000000000000000000000000000000000000..fc4d174ad938b9491ee6c6d88c7cd62211cf4f9f
--- /dev/null
+++ b/contrib/Netgen/nglib/nglib.h
@@ -0,0 +1,744 @@
+#ifndef NGLIB
+#define NGLIB
+
+/**************************************************************************/
+/* File:   nglib.h                                                        */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   7. May. 2000                                                   */
+/**************************************************************************/
+
+/*!
+   \file nglib.h
+   \brief Library interface to the netgen meshing kernel
+   \author Joachim Schoeberl
+   \date 7. May 2000
+
+   This header file provides access to the core functionality of the Netgen 
+   Mesher via a library interface, without an interactive User Interface.
+
+   The intention of providing these set of functions is to allow system 
+   developers to integrate Netgen into top-level code, to act as the low 
+   level mesh generation / optimisation kernel.  
+*/
+
+// Philippose - 14.02.2009
+// Modifications for creating a DLL in Windows
+#if 0 // GMSH #ifdef WIN32
+   #ifdef NGLIB_EXPORTS || nglib_EXPORTS
+      #define DLL_HEADER   __declspec(dllexport)
+   #else
+      #define DLL_HEADER   __declspec(dllimport)
+   #endif
+#else
+   #define DLL_HEADER 
+#endif
+
+
+
+// ** Constants used within Netgen *********************
+/// Maximum allowed number of nodes per volume element
+#define NG_VOLUME_ELEMENT_MAXPOINTS 10
+
+/// Maximum allowed number of nodes per surface element
+#define NG_SURFACE_ELEMENT_MAXPOINTS 8
+
+
+
+// *** Data-types for accessing Netgen functionality ***
+/// Data type for NETGEN mesh
+typedef void * Ng_Mesh;
+
+/// Data type for NETGEN CSG geometry
+typedef void * Ng_CSG_Geometry;
+
+/// Data type for NETGEN 2D geometry
+typedef void * Ng_Geometry_2D;
+
+/// Data type for NETGEN STL geometry
+typedef void * Ng_STL_Geometry;
+
+#ifdef OCCGEOMETRY
+/// Data type for NETGEN OpenCascade geometry
+typedef void * Ng_OCC_Geometry;
+typedef void * Ng_OCC_TopTools_IndexedMapOfShape;
+#endif
+
+
+// *** Special Enum types used within Netgen ***********
+/// Currently implemented surface element types
+enum Ng_Surface_Element_Type 
+   { NG_TRIG = 1, NG_QUAD = 2, NG_TRIG6 = 3, NG_QUAD6 = 4, NG_QUAD8 = 5 };
+
+/// Currently implemented volume element types
+enum Ng_Volume_Element_Type 
+   { NG_TET = 1, NG_PYRAMID = 2, NG_PRISM = 3, NG_TET10 = 4 };
+
+/// Values returned by Netgen functions
+enum Ng_Result 
+   { 
+     NG_ERROR               = -1,   
+     NG_OK                  = 0, 
+     NG_SURFACE_INPUT_ERROR = 1,
+     NG_VOLUME_FAILURE      = 2, 
+     NG_STL_INPUT_ERROR     = 3,
+     NG_SURFACE_FAILURE     = 4,
+     NG_FILE_NOT_FOUND      = 5 
+   };
+
+
+
+// *** Classes required for use within Netgen **********
+/// Netgen Meshing Parameters class
+class Ng_Meshing_Parameters 
+{
+public:
+   int uselocalh;                      //!< Switch to enable / disable usage of local mesh size modifiers
+
+   double maxh;                        //!< Maximum global mesh size allowed
+   double minh;                        //!< Minimum global mesh size allowed
+
+   double fineness;                    //!< Mesh density: 0...1 (0 => coarse; 1 => fine)
+   double grading;                     //!< Mesh grading: 0...1 (0 => uniform mesh; 1 => aggressive local grading)
+
+   double elementsperedge;             //!< Number of elements to generate per edge of the geometry
+   double elementspercurve;            //!< Elements to generate per curvature radius
+
+   int closeedgeenable;                //!< Enable / Disable mesh refinement at close edges
+   double closeedgefact;               //!< Factor to use for refinement at close edges (larger => finer)
+
+   int second_order;                   //!< Generate second-order surface and volume elements
+   int quad_dominated;                 //!< Creates a Quad-dominated mesh 
+
+   char * meshsize_filename;           //!< Optional external mesh size file 
+
+   int optsurfmeshenable;              //!< Enable / Disable automatic surface mesh optimization
+   int optvolmeshenable;               //!< Enable / Disable automatic volume mesh optimization
+
+   int optsteps_3d;                     //!< Number of optimize steps to use for 3-D mesh optimization
+   int optsteps_2d;                     //!< Number of optimize steps to use for 2-D mesh optimization
+
+   // Philippose - 13/09/2010
+   // Added a couple more parameters into the meshing parameters list 
+   // from Netgen into Nglib
+   int invert_tets;                    //!< Invert all the volume elements
+   int invert_trigs;                   //!< Invert all the surface triangle elements
+
+   int check_overlap;                  //!< Check for overlapping surfaces during Surface meshing
+   int check_overlapping_boundary;     //!< Check for overlapping surface elements before volume meshing
+
+
+   /*!
+      Default constructor for the Mesh Parameters class
+
+      Note: This constructor initialises the variables in the 
+      class with the following default values
+      - #uselocalh: 1
+      - #maxh: 1000.0
+      - #fineness: 0.5
+      - #grading: 0.3
+      - #elementsperedge: 2.0
+      - #elementspercurve: 2.0
+      - #closeedgeenable: 0
+      - #closeedgefact: 2.0
+      - #secondorder: 0
+      - #meshsize_filename: null
+      - #quad_dominated: 0
+      - #optsurfmeshenable: 1
+      - #optvolmeshenable: 1
+      - #optsteps_2d: 3
+      - #optsteps_3d: 3
+      - #invert_tets: 0
+      - #invert_trigs:0 
+      - #check_overlap: 1
+      - #check_overlapping_boundary: 1
+   */
+   DLL_HEADER Ng_Meshing_Parameters();
+
+
+
+   /*!
+       Reset the meshing parameters to their defaults
+
+       This member function resets all the meshing parameters 
+       of the object to the default values
+   */
+   DLL_HEADER void Reset_Parameters();
+
+
+
+   /*!
+       Transfer local meshing parameters to internal meshing parameters
+
+       This member function transfers all the meshing parameters 
+       defined in the local meshing parameters structure of nglib into 
+       the internal meshing parameters structure used by the Netgen core
+   */
+   DLL_HEADER void Transfer_Parameters();
+};
+
+
+
+
+// *** Functions Exported by this Library *************
+
+// ------------------------------------------------------------------
+// Netgen library initialisation / destruction functions
+
+/*! \brief Initialise the Netgen library and prepare for use
+
+    This function needs to be called by the third-party 
+    program before beginning to use the other Netgen 
+    specific functions.
+*/
+DLL_HEADER void Ng_Init ();
+
+
+/*! \brief Exit the Netgen meshing kernel in a clean manner
+
+    Use this function to exit the meshing sub-system in 
+    a clean and orderly manner.
+*/
+DLL_HEADER void Ng_Exit ();
+  
+
+/*! \brief Create a new (and empty) Netgen Mesh Structure
+
+    This function creates a new Netgen Mesh, initialises 
+    it, and returns a pointer to the created mesh structure. 
+
+    Use the returned pointer for subsequent operations 
+    which involve mesh operations.
+
+    \return Ng_Mesh Pointer to a Netgen Mesh type #Ng_Mesh
+*/
+DLL_HEADER  Ng_Mesh * Ng_NewMesh ();
+
+
+/*! \brief Delete an existing Netgen Mesh Structure
+
+    Use this function to delete an existing Netgen mesh 
+    structure and release the used memory. 
+
+    \param mesh Pointer to an existing Netgen Mesh structure 
+                of type #Ng_Mesh
+*/
+DLL_HEADER void Ng_DeleteMesh (Ng_Mesh * mesh);
+
+
+/*! \brief Save a Netgen Mesh to disk
+
+    This function allows a generated mesh structure to be saved 
+    to disk.
+
+    A Mesh saved using this function, will be written to disk 
+    in the Netgen VOL file format.
+
+    \param mesh    Pointer to an existing Netgen Mesh structure 
+                   of type #Ng_Mesh
+    \param filename Pointer to a character array containing the 
+                    name of the file to which the mesh should 
+                    be saved
+*/
+DLL_HEADER void Ng_SaveMesh(Ng_Mesh * mesh, const char* filename);
+
+
+/*! \brief Load a Netgen VOL Mesh from disk into memory
+
+    A Netgen mesh saved in the internal VOL format can be loaded 
+    into a Netgen Mesh structure using this function. 
+
+    \param filename Pointer to a character array containing the 
+                    name of the file to load
+    \return Ng_Mesh Pointer to a Netgen Mesh type #Ng_Mesh containing 
+                    the mesh loaded from disk
+*/
+DLL_HEADER Ng_Mesh * Ng_LoadMesh(const char* filename);
+
+
+/*! \brief Merge a Netgen VOL Mesh from disk into an existing mesh in memory
+
+    A Netgen mesh saved in the internal VOL format can be merged 
+    into an existing Netgen Mesh structure using this function. 
+
+    \param mesh       Name of the Mesh structure already existent in memory
+    \param filename   Pointer to a character array containing the 
+                      name of the file to load
+    \return Ng_Result Status of the merge operation
+*/
+DLL_HEADER Ng_Result Ng_MergeMesh(Ng_Mesh * mesh, const char* filename);
+
+
+/*! \brief Merge one Netgen Mesh into another Netgen Mesh in the case 
+    when both are already in memory
+
+    (NOTE: FUNCTION STILL WORK IN PROGRESS!!!)
+
+    This function can be used to merge two Netgen meshes already present 
+    in memory.
+
+    \param mesh1      Parent Mesh structure into which the second mesh 
+                      will be merged
+    \param mesh2      Child mesh structure which will get merged into 
+                      the parent mesh
+    \return Ng_Result Status of the merge operation
+*/
+DLL_HEADER Ng_Result Ng_MergeMesh(Ng_Mesh * mesh1, Ng_Mesh * mesh2);
+// ------------------------------------------------------------------
+
+
+
+// ------------------------------------------------------------------
+// Basic Meshing functions for manually adding points, surface elements 
+// and volume elements to a Netgen Mesh structure
+
+/*! \brief Add a point to a given Netgen Mesh Structure
+
+    This function allows points to be directly added to a Netgen 
+    mesh structure by providing the co-ordinates.
+
+    Each call to the function allows only one point to be added.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \param x    Pointer to an array of type double containing the co-ordinates 
+                of the point to be added in the form: \n
+                - x[0] = X co-ordinate
+                - x[1] = Y co-ordinate
+                - x[2] = Z co-ordinate
+*/
+DLL_HEADER void Ng_AddPoint (Ng_Mesh * mesh, double * x);
+
+
+/*! \brief Add a surface element to a given Netgen Mesh Structure
+
+    This function allows the top-level code to directly add individual 
+    Surface Elements to a Netgen Mesh Structure by providing the type of 
+    element to be added and the indices of the points which constitute the 
+    element.
+
+    <i>Note:</i> 
+    - The points referred to by the surface elements must have been
+      added prior to calling this function. 
+    - Currently only triangular elements are supported, and the Surface Element 
+      Type argument is not used.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \param et   Surface Element type provided via the enumerated type 
+                #Ng_Surface_Element_Type 
+    \param pi   Pointer to an array of integers containing the indices of the 
+                points which constitute the surface element being added
+*/
+DLL_HEADER void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et, int * pi);
+
+
+/*! \brief Add a volume element to a given Netgen Mesh Structure
+
+    This function allows the top-level code to directly add individual 
+    Volume Elements to a Netgen Mesh Structure by providing the type of 
+    element to be added and the indices of the points which constitute the 
+    element.
+
+    <i>Note:</i> 
+    - The points referred to by the volume elements must have been
+      added prior to calling this function. 
+    - Currently only tetrahedral elements are supported, and the Volume Element 
+      Type argument is not used.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \param et   Volume Element type provided via the enumerated type 
+                #Ng_Volume_Element_Type 
+    \param pi   Pointer to an array of integers containing the indices of the 
+                points which constitute the volume element being added
+
+*/
+DLL_HEADER void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et, int * pi);
+  
+// ------------------------------------------------------------------
+
+
+
+// ------------------------------------------------------------------
+// Local Mesh Size restriction / limiting utilities
+
+/*! \brief Apply a global restriction on mesh element size
+
+    This utility allows the user to apply a global mesh element 
+    size limitation. 
+
+    During mesh creation, in the absence of an explicit local 
+    size restriction around the neighbourhood of a point within 
+    the meshing domain, this global size restriction will be 
+    utilised.
+
+    <b>Note</b>: This function only limits the <b>Maximum</b> 
+    size of an element within the mesh.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \param h    Variable of type <i>double</i>, specifying the maximum
+                allowable mesh size
+*/
+DLL_HEADER void Ng_RestrictMeshSizeGlobal (Ng_Mesh * mesh, double h);
+
+
+/*! \brief Locally restrict the mesh element size at the given point
+
+    Unlike the function #Ng_RestrictMeshSizeGlobal, this function 
+    allows the user to locally restrict the maximum allowable mesh 
+    size at a given point.
+
+    The point is specified via its three cartesian co-ordinates.
+
+    <b>Note</b>: This function only limits the <b>Maximum</b> size 
+    of the elements around the specified point.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \param p    Pointer to an Array of type <i>double</i>, containing 
+                the three co-ordinates of the point in the form: \n
+                - p[0] = X co-ordinate
+                - p[1] = Y co-ordinate
+                - p[2] = Z co-ordinate
+    \param h    Variable of type <i>double</i>, specifying the maximum
+                allowable mesh size at that point
+*/
+DLL_HEADER void Ng_RestrictMeshSizePoint (Ng_Mesh * mesh, double * p, double h);
+
+
+/*! \brief Locally restrict the mesh element size within a specified box
+
+    Similar to the function #Ng_RestrictMeshSizePoint, this function 
+    allows the size of elements within a mesh to be locally limited.
+
+    However, rather than limit the mesh size at a single point, this 
+    utility restricts the local mesh size within a 3D Box region, specified 
+    via the co-ordinates of the two diagonally opposite points of a cuboid.
+
+    <b>Note</b>: This function only limits the <b>Maximum</b> size 
+    of the elements within the specified region.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \param pmin Pointer to an Array of type <i>double</i>, containing 
+                the three co-ordinates of the first point of the cuboid: \n
+                - pmin[0] = X co-ordinate
+                - pmin[1] = Y co-ordinate
+                - pmin[2] = Z co-ordinate
+    \param pmax Pointer to an Array of type <i>double</i>, containing 
+                the three co-ordinates of the opposite point of the 
+                cuboid: \n
+                - pmax[0] = X co-ordinate
+                - pmax[1] = Y co-ordinate
+                - pmax[2] = Z co-ordinate
+    \param h    Variable of type <i>double</i>, specifying the maximum
+                allowable mesh size at that point
+*/
+DLL_HEADER void Ng_RestrictMeshSizeBox (Ng_Mesh * mesh, double * pmin, double * pmax, double h);
+
+// ------------------------------------------------------------------
+
+
+
+// ------------------------------------------------------------------
+// 3D Mesh Generation functions
+
+/*! \brief Create a 3D Volume Mesh given a Surface Mesh
+
+    After creating a surface mesh, this function can be utilised 
+    to automatically generate the corresponding 3D Volume Mesh.
+
+    Mesh generation parameters (such as grading, maximum element size, 
+    etc.) are specified via the meshing parameters class which also 
+    needs to be passed to this function.
+
+    <b>Note</b>: Currently, Netgen generates pure tetrahedral volume 
+    meshes.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \param mp   Pointer to a copy of the Meshing Parameters class
+                (#Ng_Meshing_Parameters), filled up with the 
+                required values
+
+    \return Ng_Result Status of the Mesh Generation routine. More 
+                      details regarding the return value can be 
+                      found in the description of #Ng_Result
+*/
+DLL_HEADER Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp);
+
+// ------------------------------------------------------------------
+
+
+
+// ------------------------------------------------------------------
+// Basic Mesh information functions
+
+/*! \brief Returns the Number of Points present in the specified Mesh
+
+    Given an already existent Netgen Mesh Structure, this function 
+    returns the number of points currently present within the Mesh.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \return 
+                Integer Data-type with the number of points in the Mesh
+*/
+DLL_HEADER int Ng_GetNP (Ng_Mesh * mesh);
+
+
+/*! \brief Returns the Number of Surface Elements present in the specified Mesh
+
+    Given an already existent Netgen Mesh Structure, this function 
+    returns the number of surface elements currently present within 
+    the Mesh.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \return 
+                Integer Data-type with the number of surface elements in the Mesh
+*/
+DLL_HEADER int Ng_GetNSE (Ng_Mesh * mesh);
+
+
+/*! \brief Returns the Number of Volume Elements present in the specified Mesh
+
+    Given an already existent Netgen Mesh Structure, this function 
+    returns the number of volume elements currently present within 
+    the Mesh.
+
+    \param mesh Pointer to an existing Netgen Mesh structure of 
+                type #Ng_Mesh
+    \return 
+                Integer Data-type with the number of volume elements in the Mesh
+*/
+DLL_HEADER int Ng_GetNE (Ng_Mesh * mesh);
+
+// ------------------------------------------------------------------
+
+
+
+// ------------------------------------------------------------------
+// Mesh Topology functions
+// Use these functions to extract points, surface / volume elements, 
+// perform topological searches, etc..etc...
+  
+//  Return the Point Coordinates of a specified Point
+// The x, y and z co-ordinates are returned in the array pointer as 
+// x[0] = x ; x[1] = y ; x[2] = z
+DLL_HEADER void Ng_GetPoint (Ng_Mesh * mesh, int num, double * x);
+
+
+
+// return surface and volume element in pi
+DLL_HEADER Ng_Surface_Element_Type 
+Ng_GetSurfaceElement (Ng_Mesh * mesh, int num, int * pi);
+
+DLL_HEADER Ng_Volume_Element_Type
+Ng_GetVolumeElement (Ng_Mesh * mesh, int num, int * pi);
+
+// ------------------------------------------------------------------
+
+
+
+
+// **********************************************************
+// **   2D Meshing                                         **
+// **********************************************************
+
+
+// feeds points and boundary to mesh
+
+DLL_HEADER void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x);
+DLL_HEADER void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2);
+  
+// ask for number of points, elements and boundary segments
+DLL_HEADER int Ng_GetNP_2D (Ng_Mesh * mesh);
+DLL_HEADER int Ng_GetNE_2D (Ng_Mesh * mesh);
+DLL_HEADER int Ng_GetNSeg_2D (Ng_Mesh * mesh);
+  
+//  return point coordinates
+DLL_HEADER void Ng_GetPoint_2D (Ng_Mesh * mesh, int num, double * x);
+
+// return 2d elements
+DLL_HEADER Ng_Surface_Element_Type 
+Ng_GetElement_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = NULL);
+
+// return 2d boundary segment
+DLL_HEADER void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = NULL);
+
+
+// load 2d netgen spline geometry
+DLL_HEADER Ng_Geometry_2D * Ng_LoadGeometry_2D (const char * filename);
+
+// generate 2d mesh, mesh is allocated by function
+DLL_HEADER Ng_Result Ng_GenerateMesh_2D (Ng_Geometry_2D * geom,
+                                         Ng_Mesh ** mesh,
+                                         Ng_Meshing_Parameters * mp);
+  
+DLL_HEADER void Ng_HP_Refinement (Ng_Geometry_2D * geom,
+                                  Ng_Mesh * mesh,
+                                  int levels);
+  
+
+
+
+
+// **********************************************************
+// **   STL Meshing                                        **
+// **********************************************************
+
+
+// loads geometry from STL file
+DLL_HEADER Ng_STL_Geometry * Ng_STL_LoadGeometry (const char * filename, int binary = 0);
+
+
+// generate new STL Geometry
+DLL_HEADER Ng_STL_Geometry * Ng_STL_NewGeometry ();
+  
+
+// fills STL Geometry
+// positive orientation
+// normal vector may be null-pointer
+DLL_HEADER void Ng_STL_AddTriangle (Ng_STL_Geometry * geom, 
+                         double * p1, double * p2, double * p3, 
+                         double * nv = NULL);
+
+// add (optional) edges :
+DLL_HEADER void Ng_STL_AddEdge (Ng_STL_Geometry * geom, 
+                     double * p1, double * p2);
+
+// after adding triangles (and edges) initialize
+DLL_HEADER Ng_Result Ng_STL_InitSTLGeometry (Ng_STL_Geometry * geom);
+
+// automatically generates edges:
+DLL_HEADER Ng_Result Ng_STL_MakeEdges (Ng_STL_Geometry * geom,
+                            Ng_Mesh* mesh,
+                            Ng_Meshing_Parameters * mp);
+
+
+// generates mesh, empty mesh must be already created.
+DLL_HEADER Ng_Result Ng_STL_GenerateSurfaceMesh (Ng_STL_Geometry * geom,
+                                                 Ng_Mesh * mesh,
+                                                 Ng_Meshing_Parameters * mp);
+
+
+#ifdef ACIS
+
+// **********************************************************
+// **   ACIS Meshing                                       **
+// **********************************************************
+
+/// Data type for NETGEN STL geomty
+typedef void * Ng_ACIS_Geometry;
+
+// loads geometry from STL file
+DLL_HEADER Ng_ACIS_Geometry * Ng_ACIS_LoadGeometry (const char * filename);
+  
+// generates mesh, empty mesh must be already created.
+DLL_HEADER Ng_Result Ng_ACIS_GenerateSurfaceMesh (Ng_ACIS_Geometry * geom,
+                                                  Ng_Mesh * mesh,
+                                                  Ng_Meshing_Parameters * mp);
+
+
+#endif
+
+
+
+#ifdef OCCGEOMETRY
+
+// **********************************************************
+// **   OpenCascade Geometry / Meshing Utilities           **
+// **********************************************************
+
+// Create new OCC Geometry Object
+DLL_HEADER Ng_OCC_Geometry * Ng_OCC_NewGeometry ();
+
+// Delete an OCC Geometry Object
+DLL_HEADER Ng_Result Ng_OCC_DeleteGeometry (Ng_OCC_Geometry * geom);
+
+// Loads geometry from STEP file
+DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_STEP (const char * filename);
+
+// Loads geometry from IGES file
+DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_IGES (const char * filename);
+
+// Loads geometry from BREP file
+DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_BREP (const char * filename);
+
+// Set the local mesh size based on geometry / topology
+DLL_HEADER Ng_Result Ng_OCC_SetLocalMeshSize (Ng_OCC_Geometry * geom,
+                                              Ng_Mesh * mesh,
+                                              Ng_Meshing_Parameters * mp);
+
+// Mesh the edges and add Face descriptors to prepare for surface meshing
+DLL_HEADER Ng_Result Ng_OCC_GenerateEdgeMesh (Ng_OCC_Geometry * geom,
+                                              Ng_Mesh * mesh,
+                                              Ng_Meshing_Parameters * mp);
+
+// Mesh the surfaces of an OCC geometry
+DLL_HEADER Ng_Result Ng_OCC_GenerateSurfaceMesh (Ng_OCC_Geometry * geom,
+                                                 Ng_Mesh * mesh,
+                                                 Ng_Meshing_Parameters * mp); 
+
+// Get the face map of an already loaded OCC geometry
+DLL_HEADER Ng_Result Ng_OCC_GetFMap(Ng_OCC_Geometry * geom, 
+                                    Ng_OCC_TopTools_IndexedMapOfShape * FMap);
+
+#endif // OCCGEOMETRY
+
+
+
+// **********************************************************
+// **   Mesh refinement algorithms                         **
+// **********************************************************
+
+// uniform mesh refinement
+DLL_HEADER void Ng_Uniform_Refinement (Ng_Mesh * mesh);
+
+
+// uniform mesh refinement with geometry adaption:
+
+DLL_HEADER void Ng_2D_Uniform_Refinement (Ng_Geometry_2D * geom,
+					  Ng_Mesh * mesh);
+
+DLL_HEADER void Ng_STL_Uniform_Refinement (Ng_STL_Geometry * geom,
+					   Ng_Mesh * mesh);
+
+DLL_HEADER void Ng_CSG_Uniform_Refinement (Ng_CSG_Geometry * geom,
+					   Ng_Mesh * mesh);
+
+#ifdef OCCGEOMETRY
+DLL_HEADER void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom,
+					   Ng_Mesh * mesh);
+#endif
+
+
+
+// **********************************************************
+// **   Second Order mesh algorithms                       **
+// **********************************************************
+
+// convert mesh to second order
+DLL_HEADER void Ng_Generate_SecondOrder (Ng_Mesh * mesh);
+
+
+// convert mesh to second order with geometry adaption:
+
+DLL_HEADER void Ng_2D_Generate_SecondOrder (Ng_Geometry_2D * geom,
+					  Ng_Mesh * mesh);
+
+DLL_HEADER void Ng_STL_Generate_SecondOrder (Ng_STL_Geometry * geom,
+					   Ng_Mesh * mesh);
+
+DLL_HEADER void Ng_CSG_Generate_SecondOrder (Ng_CSG_Geometry * geom,
+					   Ng_Mesh * mesh);
+
+#ifdef OCCGEOMETRY
+DLL_HEADER void Ng_OCC_Generate_SecondOrder (Ng_OCC_Geometry * geom,
+					   Ng_Mesh * mesh);
+#endif
+
+
+#endif // NGLIB
diff --git a/contrib/Netgen/nglib_gmsh.cpp b/contrib/Netgen/nglib_gmsh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e9d94a84a9a77d39e878f87d1b3382ef586c0c1f
--- /dev/null
+++ b/contrib/Netgen/nglib_gmsh.cpp
@@ -0,0 +1,402 @@
+// Interface to the Netgen meshing kernel for Gmsh. This file replaces
+// the original nglib.cpp file from the Netgen distribution.
+
+#include <GmshMessage.h>
+#include <linalg.hpp>
+#include <meshing.hpp>
+#include <nginterface.h>
+
+namespace netgen
+{
+   int id, ntasks;
+   MeshingParameters mparam;
+}
+
+namespace nglib {
+#include "nglib.h"
+}
+
+using namespace netgen;
+
+namespace nglib
+{
+  class mystreambuf : public streambuf {
+    int index;
+    char txt[1024];
+  public:
+    mystreambuf() : index(0) {}
+    int sync()
+    {
+      txt[index] = '\0';
+      if(!index || (index == 1 && (txt[0] == '.' || txt[0] == '+' ||
+                                   txt[0] == ' ' || txt[0] == '*'))){
+        // ignore these messages
+      }
+      else{
+        if(!strncmp(txt, "ERROR", 5)) { Msg::Error(txt); }
+        else if(!strncmp(txt, "WARNING", 7)) { Msg::Warning(txt); }
+        else { Msg::Info(txt); }
+      }
+      index = 0;
+      return 0;
+    }
+    int overflow(int ch)
+    {
+      if(index < 1023){
+        txt[index] = ch;
+        if(txt[index] == '\n' || txt[index] == '\r') txt[index] = ' ';
+        if(!index && txt[0] == ' '){
+          // skip initial spaces
+        }
+        else{
+          index++;
+        }
+      }
+      return 0;
+    }
+  };
+
+   // initialize, deconstruct Netgen library:
+   void Ng_Init ()
+   {
+     // mycout = &cout;
+     // myerr = &cerr;
+     // netgen::testout->SetOutStream (new ofstream ("test.out"));
+     // testout = new ofstream ("test.out");
+     static bool first = true;
+     if(first){
+       first = false;
+       //mycout = &cout;
+       //myerr = &cout;
+       //testout = &cout;
+       mycout = new ostream(new mystreambuf());
+       myerr = new ostream(new mystreambuf());
+       testout = new ofstream("/dev/null");
+       //testout = new ostream(new mystreambuf());
+     }
+   }
+
+   // Clean-up functions before ending usage of nglib
+  void Ng_Exit ()
+   {
+      ;
+   }
+
+   // Create a new netgen mesh object
+  Ng_Mesh * Ng_NewMesh ()
+  {
+    Mesh * mesh = new Mesh;
+    mesh->AddFaceDescriptor (FaceDescriptor (1, 1, 0, 1));
+    return (Ng_Mesh*)(void*)mesh;
+  }
+
+  // Delete an existing netgen mesh object
+  void Ng_DeleteMesh (Ng_Mesh * mesh)
+  {
+    if(mesh != NULL)
+      {
+        // Delete the Mesh structures
+        ((Mesh*)mesh)->DeleteMesh();
+
+        // Now delete the Mesh class itself
+        delete (Mesh*)mesh;
+
+        // Set the Ng_Mesh pointer to NULL
+        mesh = NULL;
+      }
+  }
+
+  // Manually add a point to an existing mesh object
+  void Ng_AddPoint (Ng_Mesh * mesh, double * x)
+  {
+    Mesh * m = (Mesh*)mesh;
+    m->AddPoint (Point3d (x[0], x[1], x[2]));
+  }
+
+  // Manually add a surface element of a given type to an existing mesh object
+  void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et,
+                             int * pi)
+  {
+    Mesh * m = (Mesh*)mesh;
+    Element2d el (3);
+    el.SetIndex (1);
+    el.PNum(1) = pi[0];
+    el.PNum(2) = pi[1];
+    el.PNum(3) = pi[2];
+    m->AddSurfaceElement (el);
+  }
+
+  // Manually add a volume element of a given type to an existing mesh object
+  void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et,
+                            int * pi)
+  {
+    Mesh * m = (Mesh*)mesh;
+    Element el (4);
+    el.SetIndex (1);
+    el.PNum(1) = pi[0];
+    el.PNum(2) = pi[1];
+    el.PNum(3) = pi[2];
+    el.PNum(4) = pi[3];
+    m->AddVolumeElement (el);
+  }
+
+  // Obtain the number of points in the mesh
+  int Ng_GetNP (Ng_Mesh * mesh)
+  {
+    return ((Mesh*)mesh) -> GetNP();
+  }
+
+  // Obtain the number of volume elements in the mesh
+  int Ng_GetNE (Ng_Mesh * mesh)
+  {
+    return ((Mesh*)mesh) -> GetNE();
+  }
+
+  //  Return point coordinates of a given point index in the mesh
+  void Ng_GetPoint (Ng_Mesh * mesh, int num, double * x)
+  {
+    const Point3d & p = ((Mesh*)mesh)->Point(num);
+    x[0] = p.X();
+    x[1] = p.Y();
+    x[2] = p.Z();
+  }
+
+  // Return the volume element at a given index "pi"
+  Ng_Volume_Element_Type
+  Ng_GetVolumeElement (Ng_Mesh * mesh, int num, int * pi)
+  {
+    const Element & el = ((Mesh*)mesh)->VolumeElement(num);
+    for (int i = 1; i <= el.GetNP(); i++)
+      pi[i-1] = el.PNum(i);
+    Ng_Volume_Element_Type et;
+    switch (el.GetNP())
+      {
+      case 4: et = NG_TET; break;
+      case 5: et = NG_PYRAMID; break;
+      case 6: et = NG_PRISM; break;
+      case 10: et = NG_TET10; break;
+      default:
+        et = NG_TET; break; // for the compiler
+      }
+    return et;
+  }
+
+  // Generates volume mesh from an existing surface mesh
+  Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp)
+  {
+    Mesh * m = (Mesh*)mesh;
+
+    // Philippose - 30/08/2009
+    // Do not locally re-define "mparam" here... "mparam" is a global
+    // object
+    //MeshingParameters mparam;
+    mp->Transfer_Parameters();
+
+    m->CalcLocalH(mparam.grading);
+
+    MeshVolume (mparam, *m);
+    RemoveIllegalElements (*m);
+    OptimizeVolume (mparam, *m);
+
+    return NG_OK;
+  }
+
+  // Generates volume mesh from an existing surface mesh
+  Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, double maxh)
+  {
+    Mesh *m = (Mesh*)mesh;
+
+    MeshingParameters mparam;
+    mparam.uselocalh = 1;
+    mparam.maxh = maxh;
+
+    try{
+      m->CalcLocalH(mparam.grading);
+      MeshVolume(mparam, *m);
+      //RemoveIllegalElements(*m);
+      //OptimizeVolume(mparam, *m);
+    }
+    catch(netgen::NgException error){
+      return NG_VOLUME_FAILURE;
+    }
+    return NG_OK;
+  }
+
+// optimizes an existing 3D mesh
+  Ng_Result Ng_OptimizeVolumeMesh(Ng_Mesh *mesh, double maxh)
+  {
+    Mesh *m = (Mesh*)mesh;
+
+    MeshingParameters mparam;
+    mparam.uselocalh = 1;
+    mparam.maxh = maxh;
+
+    try{
+      m->CalcLocalH(mparam.grading);
+      //MeshVolume(mparam, *m);
+      RemoveIllegalElements(*m);
+      OptimizeVolume(mparam, *m);
+    }
+    catch(netgen::NgException error){
+      return NG_VOLUME_FAILURE;
+    }
+    return NG_OK;
+  }
+
+  // ------------------ Begin - Meshing Parameters related functions ------------------
+  // Constructor for the local nglib meshing parameters class
+  Ng_Meshing_Parameters :: Ng_Meshing_Parameters()
+  {
+    uselocalh = 1;
+
+    maxh = 1000;
+    minh = 0.0;
+
+    fineness = 0.5;
+    grading = 0.3;
+
+    elementsperedge = 2.0;
+    elementspercurve = 2.0;
+
+    closeedgeenable = 0;
+    closeedgefact = 2.0;
+
+    second_order = 0;
+    quad_dominated = 0;
+
+    meshsize_filename = 0;
+
+    optsurfmeshenable = 1;
+    optvolmeshenable = 1;
+
+    optsteps_2d = 3;
+    optsteps_3d = 3;
+
+    invert_tets = 0;
+    invert_trigs = 0;
+
+    check_overlap = 1;
+    check_overlapping_boundary = 1;
+  }
+
+  // Reset the local meshing parameters to the default values
+  void Ng_Meshing_Parameters :: Reset_Parameters()
+  {
+    uselocalh = 1;
+
+    maxh = 1000;
+    minh = 0;
+
+    fineness = 0.5;
+    grading = 0.3;
+
+    elementsperedge = 2.0;
+    elementspercurve = 2.0;
+
+    closeedgeenable = 0;
+    closeedgefact = 2.0;
+
+    second_order = 0;
+    quad_dominated = 0;
+
+    meshsize_filename = 0;
+
+    optsurfmeshenable = 1;
+    optvolmeshenable = 1;
+
+    optsteps_2d = 3;
+    optsteps_3d = 3;
+
+    invert_tets = 0;
+    invert_trigs = 0;
+
+    check_overlap = 1;
+    check_overlapping_boundary = 1;
+  }
+
+  //
+  void Ng_Meshing_Parameters :: Transfer_Parameters()
+  {
+    mparam.uselocalh = uselocalh;
+
+    mparam.maxh = maxh;
+    mparam.minh = minh;
+
+    mparam.grading = grading;
+    mparam.curvaturesafety = elementspercurve;
+    mparam.segmentsperedge = elementsperedge;
+
+    mparam.secondorder = second_order;
+    mparam.quad = quad_dominated;
+
+    mparam.meshsizefilename = meshsize_filename;
+
+    mparam.optsteps2d = optsteps_2d;
+    mparam.optsteps3d = optsteps_3d;
+
+    mparam.inverttets = invert_tets;
+    mparam.inverttrigs = invert_trigs;
+
+    mparam.checkoverlap = check_overlap;
+    mparam.checkoverlappingboundary = check_overlapping_boundary;
+  }
+
+} // End of namespace nglib
+
+// compatibility functions:
+namespace netgen
+{
+   char geomfilename[255];
+
+   void MyError (const char * ch)
+   {
+     (*myerr) << ch;
+   }
+
+
+
+
+   //Destination for messages, errors, ...
+   void Ng_PrintDest(const char * s)
+   {
+      (*mycout) << s << flush;
+   }
+
+
+
+
+   double GetTime ()
+   {
+      return 0;
+   }
+
+
+
+
+   void ResetTime ()
+   {
+      ;
+   }
+
+
+
+
+   void MyBeep (int i)
+   {
+      ;
+   }
+
+
+
+
+   void Render()
+   {
+      ;
+   }
+} // End of namespace netgen
+
+
+void Ng_Redraw () { ; }
+void Ng_ClearSolutionData () { ; }
+void Ng_SetSolutionData (Ng_SolutionData * soldata) { ; }
+void Ng_InitSolutionData (Ng_SolutionData * soldata) { ; }
diff --git a/contrib/Netgen/nglib_gmsh.h b/contrib/Netgen/nglib_gmsh.h
new file mode 100644
index 0000000000000000000000000000000000000000..d7e940ddeac2e582348aabfbf337dc27ae209f91
--- /dev/null
+++ b/contrib/Netgen/nglib_gmsh.h
@@ -0,0 +1,9 @@
+#ifndef _NGLIB_GMSH_H_
+#define _NGLIB_GMSH_H_
+
+#include "nglib.h"
+
+Ng_Result Ng_GenerateVolumeMesh(Ng_Mesh *mesh, double maxh);
+Ng_Result Ng_OptimizeVolumeMesh(Ng_Mesh *mesh, double maxh);
+
+#endif
diff --git a/contrib/hxt/hxt_boundary_recovery.cxx b/contrib/hxt/hxt_boundary_recovery.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a6a59021d2684a6e11e914a2059b711052750b9a
--- /dev/null
+++ b/contrib/hxt/hxt_boundary_recovery.cxx
@@ -0,0 +1,605 @@
+#define __STDC_LIMIT_MACROS // FIXME Gmsh (for UINT_MAX & co in C++ code)
+#include <limits.h>
+extern "C" {
+#include "hxt_mesh.h"
+#include "predicates.h"
+#include "hxt_omp.h"
+}
+
+#include <assert.h>
+#include <math.h>
+#include <set>
+#include <vector>
+#include <time.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;
+      }
+  }
+#ifndef NDEBUG
+  if(i==3)
+    HXT_WARNING("should never happen (file:%s line:%d)\n", __FILE__, __LINE__);
+#endif
+
+  // version%4 : corresponding face in adjacent tet
+  // version/4 : which of the 3 rotation of the facet the tetrahedra has...
+  return 4*i + iface2;
+}
+
+
+int 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 HXT_STATUS_OK;
+    }
+
+    // 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);
+
+
+  {
+    hullsize = 0;
+
+    // Create the tetrahedra and connect those that share a common face.
+    //    printf("Connect %d tetrahedra\n", mesh->tetrahedra.num);
+    std::vector<triface> ts( mesh->tetrahedra.num );
+    for (uint64_t i = 0; i < mesh->tetrahedra.num; i++) {
+      maketetrahedron(&ts[i]); // ts[i].ver = 11.
+
+      uint32_t* nodes = mesh->tetrahedra.node + 4*i;
+      point p[4];
+
+      p[0] = idx2verlist[nodes[0]];
+      p[1] = idx2verlist[nodes[1]];
+      p[2] = idx2verlist[nodes[2]];
+      if(nodes[3]==HXT_GHOST_VERTEX) {
+        hullsize++;
+        p[3] = dummypoint;
+      }
+      else {
+        p[3] = idx2verlist[nodes[3]];
+      }
+      setvertices(ts[i], p[0], p[1], p[2], p[3]);
+
+      #ifndef NDEBUG
+      for (int j=0; j<4; j++) {
+        if(mesh->tetrahedra.neigh[4*i+j]>=4*mesh->tetrahedra.num)
+          return HXT_ERROR_MSG(HXT_STATUS_ERROR, "A tetrahedron is missing a neighbor");
+      }
+      #endif
+    }
+
+    // we can make this in parallel, iterations are totally independent
+    #pragma omp parallel for
+    for (uint64_t i = 0; i < mesh->tetrahedra.num; i++) {
+      triface tf1 = ts[i];
+
+      for (tf1.ver=0; tf1.ver<4; tf1.ver++){
+        uint64_t neigh = mesh->tetrahedra.neigh[4*i + tf1.ver];
+        uint64_t n = neigh/4;
+        int iface2 = neigh%4;
+
+        triface tf2 = ts[n];
+
+        // the face of the neighbor tetrahedra that is the same
+        uint32_t face2[3] = {mesh->tetrahedra.node[4*n+((iface2+1)&3)],
+                             mesh->tetrahedra.node[4*n+((iface2&2)^3)],
+                             mesh->tetrahedra.node[4*n+((iface2+3)&2)]};
+
+        tf2.ver = computeTetGenVersion2(mesh->tetrahedra.node[4*i+((tf1.ver+1)&3)], face2, iface2);
+        bond(tf1,tf2);
+      }
+    }
+  }
+
+  {
+    // Create the point-to-tet map, and clean up the temporary spaces used in each tet.
+    triface tetloop;
+    tetrahedrons->traversalinit();
+    tetloop.tet = tetrahedrontraverse();
+    while (tetloop.tet != (tetrahedron *) NULL) {
+      tetrahedron tptr = encode(tetloop);
+      for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
+        // 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();
+    }
+  }
+
+  {
+    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();
+  }
+
+// TODO: is this usefull ?
+#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);
+            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);
+          }
+        }
+      }
+    }
+
+    // TODO: maybe fill a vector with triface and use that to convert in parallel ?
+    int elementnumber = 0; // firstindex; // in->firstnumber;
+    {
+      // number tets
+      triface tetloop;
+      tetrahedrons->traversalinit();
+      tetloop.tet = alltetrahedrontraverse();
+      while (tetloop.tet != (tetrahedron *) NULL) {
+        setelemindex(tetloop.tet, elementnumber);
+        tetloop.tet = alltetrahedrontraverse();
+        elementnumber++;
+      }
+    }
+
+    if(elementnumber!=tetrahedrons->items)
+      return HXT_ERROR_MSG(HXT_STATUS_ERROR, "This can not happen...");
+
+    {
+      // move data to HXT
+      triface tetloop;
+      tetrahedrons->traversalinit();
+      tetloop.tet = alltetrahedrontraverse();
+
+      // TODO: maybe free during recovery to save size...
+      mesh->tetrahedra.num  = tetrahedrons->items;
+      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);
+
+        mesh->tetrahedra.colors[counter] = 0;
+        mesh->tetrahedra.flag[counter] = 0;
+
+        for (tetloop.ver=0;tetloop.ver<4;tetloop.ver++){
+          int k = tetloop.ver;
+          triface N;
+          fsym(tetloop, N);
+
+          if(p[k]==dummypoint) {
+            if(k!=3)
+              return HXT_ERROR_MSG(HXT_STATUS_ERROR, "Error: the ghost vertex is not the third vertex");
+            mesh->tetrahedra.node[4*counter+k] = HXT_GHOST_VERTEX;
+          }
+          else {
+            mesh->tetrahedra.node[4*counter+k] = pointmark(p[k]);
+            if (mesh->tetrahedra.node[4*counter+k] >= mesh->vertices.num)
+              return HXT_ERROR_MSG(HXT_STATUS_ERROR, "ERROR : index %u out of range (%u)\n",
+                                   mesh->tetrahedra.node[4*counter+k], mesh->vertices.num);
+          }
+
+          // set the neighbor
+          uint64_t ngh =  elemindex(N.tet);
+          int face = N.ver%4;
+
+          mesh->tetrahedra.neigh[4*counter+k] = 4*ngh+face;
+        }
+
+        counter++;
+        tetloop.tet = alltetrahedrontraverse();
+      }
+    } // mesh output
+  }
+
+  delete in;
+  delete b;
+  return HXT_STATUS_OK;
+}
+
+extern "C" {
+  HXTStatus hxt_boundary_recovery(HXTMesh *mesh)
+  {
+    HXTStatus status;
+    try{
+      tetgenmesh *m = new tetgenmesh();
+      status = (HXTStatus) m->reconstructmesh((void*)mesh);
+      if(status!=HXT_STATUS_OK)
+        HXT_TRACE(status);
+      delete m;
+    }
+    catch (...){
+      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "failed to recover constrained lines/triangles") ;
+    }
+
+    return status;
+  }
+}
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_mesh3d.c b/contrib/hxt/hxt_mesh3d.c
new file mode 100644
index 0000000000000000000000000000000000000000..dc446f92a205375dc3e21ac2f3dfdf0662cad140
--- /dev/null
+++ b/contrib/hxt/hxt_mesh3d.c
@@ -0,0 +1,420 @@
+// #include "hxt_mesh_size.h"
+#include "hxt_tetDelaunay.h"
+// #include "hxt_vertices.h"
+#include "hxt_mesh3d.h"
+#include "predicates.h"
+#include "hxt_tetFlag.h"
+
+// #if defined(_MSC_VER)
+// #define _CRT_RAND_S 
+// #include <stdlib.h> 
+// double drand48() {
+//   double a;
+//   rand_s(&a);
+//   return a;
+// }
+// #endif
+
+HXTStatus hxtCreateNodalSizeFromFunction(HXTMesh* mesh, HXTDelaunayOptions* delOptions,
+                                         double (*mesh_size)(double x, double y, double z, void* userData),
+                                         void* userData)
+{
+  HXT_CHECK(hxtAlignedMalloc(&delOptions->nodalSizes,mesh->vertices.num*sizeof(double)));
+
+  #pragma omp parallel for
+  for (uint32_t i=0; i<mesh->vertices.num; i++) {
+    double* coord = &mesh->vertices.coord[4*i];
+    delOptions->nodalSizes[i] = mesh_size(coord[0], coord[1], coord[2], userData);
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+HXTStatus hxtCreateNodalsizeFromTrianglesAndLines(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->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;
+        }
+      }
+    }
+  }
+
+  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 + (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;
+    }
+  }
+  return HXT_STATUS_OK;    
+}
+
+HXTStatus hxtCreateNodalsizeFromMesh(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;
+        }
+      }
+    }
+  }
+  return HXT_STATUS_OK;
+}
+
+HXTStatus hxtDestroyNodalsize(HXTDelaunayOptions* delOptions)
+{
+  HXT_CHECK( hxtAlignedFree(&delOptions->nodalSizes) );
+  return HXT_STATUS_OK;
+}
+
+
+
+
+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");
+
+  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;
+  }
+
+  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;
+}
+
+
+
+// refine 
+
+
+double hxtTetCircumcenter(double a[3], double b[3], double c[3], double d[3],
+                            double circumcenter[3], double *xi, double *eta, double *zeta)
+{
+  double xba, yba, zba, xca, yca, zca, xda, yda, zda;
+  double balength, calength, dalength;
+  double xcrosscd, ycrosscd, zcrosscd;
+  double xcrossdb, ycrossdb, zcrossdb;
+  double xcrossbc, ycrossbc, zcrossbc;
+  double denominator;
+  double xcirca, ycirca, zcirca;
+
+  /* Use coordinates relative to point `a' of the tetrahedron. */
+  xba = b[0] - a[0];
+  yba = b[1] - a[1];
+  zba = b[2] - a[2];
+  xca = c[0] - a[0];
+  yca = c[1] - a[1];
+  zca = c[2] - a[2];
+  xda = d[0] - a[0];
+  yda = d[1] - a[1];
+  zda = d[2] - a[2];
+  /* Squares of lengths of the edges incident to `a'. */
+  balength = xba * xba + yba * yba + zba * zba;
+  calength = xca * xca + yca * yca + zca * zca;
+  dalength = xda * xda + yda * yda + zda * zda;
+  /* Cross products of these edges. */
+  xcrosscd = yca * zda - yda * zca;
+  ycrosscd = zca * xda - zda * xca;
+  zcrosscd = xca * yda - xda * yca;
+  xcrossdb = yda * zba - yba * zda;
+  ycrossdb = zda * xba - zba * xda;
+  zcrossdb = xda * yba - xba * yda;
+  xcrossbc = yba * zca - yca * zba;
+  ycrossbc = zba * xca - zca * xba;
+  zcrossbc = xba * yca - xca * yba;
+
+  /* Calculate the denominator of the formulae. */
+  /* Use orient3d() from http://www.cs.cmu.edu/~quake/robust.html     */
+  /*   to ensure a correctly signed (and reasonably accurate) result, */
+  /*   avoiding any possibility of division by zero.                  */
+  const double xxx =  orient3d(b, c, d, a);
+  denominator = 0.5 / xxx;
+
+  /* Calculate offset (from `a') of circumcenter. */
+  xcirca = (balength * xcrosscd + calength * xcrossdb + dalength * xcrossbc) *
+    denominator;
+  ycirca = (balength * ycrosscd + calength * ycrossdb + dalength * ycrossbc) *
+    denominator;
+  zcirca = (balength * zcrosscd + calength * zcrossdb + dalength * zcrossbc) *
+    denominator;
+  circumcenter[0] =  xcirca + a[0];
+  circumcenter[1] =  ycirca + a[1];
+  circumcenter[2] =  zcirca + a[2];
+
+  /*
+ printf(" %g %g %g %g\n",
+         sqrt((a[0]-xcirca)*(a[0]-xcirca)+(a[1]-ycirca)*(a[1]-ycirca)+(a[2]-zcirca)*(a[2]-zcirca)),
+         sqrt((b[0]-xcirca)*(b[0]-xcirca)+(b[1]-ycirca)*(b[1]-ycirca)+(b[2]-zcirca)*(b[2]-zcirca)),
+         sqrt((c[0]-xcirca)*(c[0]-xcirca)+(c[1]-ycirca)*(c[1]-ycirca)+(c[2]-zcirca)*(c[2]-zcirca)),
+         sqrt((d[0]-xcirca)*(d[0]-xcirca)+(d[1]-ycirca)*(d[1]-ycirca)+(d[2]-zcirca)*(d[2]-zcirca)) );
+  */
+
+  if (xi != (double *) NULL) {
+    /* To interpolate a linear function at the circumcenter, define a    */
+    /*   coordinate system with a xi-axis directed from `a' to `b',      */
+    /*   an eta-axis directed from `a' to `c', and a zeta-axis directed  */
+    /*   from `a' to `d'.  The values for xi, eta, and zeta are computed */
+     /*   by Cramer's Rule for solving systems of linear equations.       */
+    *xi = (xcirca * xcrosscd + ycirca * ycrosscd + zcirca * zcrosscd) *
+      (2.0 * denominator);
+    *eta = (xcirca * xcrossdb + ycirca * ycrossdb + zcirca * zcrossdb) *
+      (2.0 * denominator);
+    *zeta = (xcirca * xcrossbc + ycirca * ycrossbc + zcirca * zcrossbc) *
+      (2.0 * denominator);
+  }
+  return xxx;
+}
+
+
+static HXTStatus hxtRefineTetrahedraOneStep(HXTMesh* mesh, HXTDelaunayOptions* delOptions,
+                                            double (*mesh_size)(double x, double y, double z, void* userData),
+                                            void* userData , 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)) );
+
+  
+  // 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;
+
+    
+    #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 && getProcessedFlag(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]);
+
+        setProcessedFlag(mesh, i); // we do not need to refine that tetrahedra anymore
+
+        // all new edges will have a length equal to circumradius2
+        double meshSize;
+        //        HXTStatus status = hxtMeshSizeEvaluate ( sizeField, circumcenter, &meshSize);
+
+        if(u <= 0 || v <= 0 || w <= 0 || 1.-u-v-w <= 0)
+          continue;
+        
+        if(mesh_size!=NULL) {
+          meshSize = mesh_size(circumcenter[0], circumcenter[1], circumcenter[2], userData);
+        }
+        else { // we suppose delOptions->nodalSize!=NULL
+          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 (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++;
+        }
+      }
+    }
+
+    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;
+      }
+
+      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;
+        }
+      }
+    }
+
+    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++;
+        }
+      }
+    }
+  }
+
+  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, delOptions));
+
+  *nbAdd = mesh->vertices.num - delOptions->numVerticesInMesh;
+  delOptions->numVerticesInMesh = mesh->vertices.num;
+  return HXT_STATUS_OK;
+}
+
+HXTStatus hxtRefineTetrahedra(HXTMesh* mesh,
+                              HXTDelaunayOptions* delOptions,
+                              double (*mesh_size)(double x, double y, double z, void* userData),
+                              void* userData) {
+  int iter = 0;
+  while(iter++ < 40){
+    int nbAdd=0;
+    HXT_CHECK(hxtRefineTetrahedraOneStep(mesh, delOptions, mesh_size, userData, &nbAdd, iter));
+    //    uint32_t nb;
+    //    HXT_CHECK(hxtColorMesh(mesh,&nb));
+    if (nbAdd == 0) break;
+  }
+  return HXT_STATUS_OK;
+}
+
+
diff --git a/contrib/hxt/hxt_mesh3d.h b/contrib/hxt/hxt_mesh3d.h
new file mode 100644
index 0000000000000000000000000000000000000000..23afb959df1c8e7ca1c92a5c29a3fead6d3ef329
--- /dev/null
+++ b/contrib/hxt/hxt_mesh3d.h
@@ -0,0 +1,28 @@
+#ifndef _HXT_MESH_3D_
+#define _HXT_MESH_3D_
+
+#include "hxt_tetDelaunay.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, HXTDelaunayOptions* delOptions);
+
+/// Compute sizes at vertices of the mesh from a mesh_size function
+HXTStatus hxtCreateNodalSizeFromFunction(HXTMesh* mesh, HXTDelaunayOptions* delOptions,
+                                         double (*mesh_size)(double x, double y, double z, void* userData),
+                                         void* userData);
+
+/// Compute sizes at vertices of the mesh from existing edges
+HXTStatus hxtCreateNodalsizeFromTrianglesAndLines(HXTMesh* mesh, HXTDelaunayOptions* delOptions);
+HXTStatus hxtCreateNodalsizeFromMesh(HXTMesh* mesh, HXTDelaunayOptions* delOptions);
+HXTStatus hxtDestroyNodalsize(HXTDelaunayOptions* delOptions);
+
+/// Add points at tets circumcenter in order to fullfill a mesh size constraint 
+HXTStatus hxtRefineTetrahedra(HXTMesh* mesh,
+                              HXTDelaunayOptions* delOptions,
+                              double (*mesh_size)(double x, double y, double z, void* userData),
+                              void* userData);
+
+#endif
diff --git a/contrib/hxt/hxt_mesh3d_main.c b/contrib/hxt/hxt_mesh3d_main.c
new file mode 100644
index 0000000000000000000000000000000000000000..a5f3df74ffc78b77c10bc7d9e6cb529db4fc74e9
--- /dev/null
+++ b/contrib/hxt/hxt_mesh3d_main.c
@@ -0,0 +1,166 @@
+#include "hxt_mesh3d.h"
+#include "hxt_tetDelaunay.h"
+#include "hxt_tetRepair.h"
+#include "hxt_tetUtils.h"
+#include "hxt_tetFlag.h"
+#include "hxt_tetColor.h"
+#include "hxt_tetOpti.h"
+
+
+HXTStatus hxtTetMesh3d(HXTMesh* mesh,
+                      int defaulThreads,
+                      int DelaunayThreads,
+                      int optimizationThreads,
+                      int reproducible,
+                      int verbosity,
+                      int displayStat,
+                      int refine,
+                      int optimize,
+                      double qualityThreshold,
+                      HXTStatus (*bnd_recovery)(HXTMesh* mesh),
+                      double (*mesh_size)(double x, double y, double z, void* userData),
+                      void* userData) {
+
+  if(defaulThreads>0) {
+    omp_set_num_threads(defaulThreads);
+  }
+
+  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, DelaunayThreads};
+  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 nbMissingTriangles, nbLinesNotInTriangles, nbMissingLines=0;
+  uint16_t nbColors;
+  uint64_t* tri2TetMap = NULL;
+  uint64_t* lines2TriMap = NULL;
+  uint64_t* lines2TetMap = NULL;
+
+  HXT_CHECK( hxtAlignedMalloc(&tri2TetMap, mesh->triangles.num*sizeof(uint64_t)) );
+  HXT_CHECK( hxtAlignedMalloc(&lines2TriMap, mesh->lines.num*sizeof(uint64_t)) );
+  
+  HXT_CHECK( hxtGetTri2TetMap(mesh, tri2TetMap, &nbMissingTriangles) );
+  HXT_CHECK( hxtGetLines2TriMap(mesh, lines2TriMap, &nbLinesNotInTriangles) );
+
+  if(nbLinesNotInTriangles!=0) {
+    HXT_CHECK( hxtAlignedMalloc(&lines2TetMap, mesh->lines.num*sizeof(uint64_t)) );
+    if(nbMissingTriangles==0) {
+      HXT_CHECK( hxtGetLines2TetMap(mesh, lines2TetMap, &nbMissingLines) );
+    }
+  }
+
+
+  t[2] = omp_get_wtime();
+
+  if (nbMissingTriangles!=0 || nbMissingLines!=0){
+    if(bnd_recovery==NULL)
+      return HXT_ERROR_MSG(HXT_STATUS_ERROR,
+        "there are missing features but no boundary recovery function is given");
+
+    if(nbMissingTriangles)
+      HXT_INFO("Recovering %lu missing facet(s)", nbMissingTriangles);
+    else if(nbMissingLines)
+      HXT_INFO("Recovering %lu missing edge(s)", nbMissingLines);
+
+    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();
+
+    HXT_CHECK( hxtGetTri2TetMap(mesh, tri2TetMap, &nbMissingTriangles) );
+    if(nbMissingTriangles!=0)
+      return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+        "%d boundary face%s still missing (after recovery step).",
+        nbMissingTriangles, (nbMissingTriangles>1)?"s are":" is" );
+
+    if(nbLinesNotInTriangles!=0)
+      HXT_CHECK( hxtGetLines2TetMap(mesh, lines2TetMap, &nbMissingLines) );
+
+    if(nbMissingLines!=0)
+      return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+        "%d constrained edge%s still missing (after recovery step).",
+        nbMissingLines, (nbMissingLines>1)?"s are":" is" );
+  }
+
+  HXT_CHECK( hxtConstrainTriangles(mesh, tri2TetMap) );
+  
+  if(nbLinesNotInTriangles!=0)
+    HXT_CHECK( hxtConstrainLinesNotInTriangles(mesh, lines2TetMap, lines2TriMap) );
+
+  HXT_CHECK( hxtColorMesh(mesh, &nbColors) );
+
+  HXT_CHECK( hxtMapColorsToBrep(mesh, nbColors, tri2TetMap) );
+
+  HXT_CHECK( hxtAlignedFree(&tri2TetMap) );
+  HXT_CHECK( hxtAlignedFree(&lines2TetMap) );
+  HXT_CHECK( hxtAlignedFree(&lines2TriMap) );
+
+  t[4] = omp_get_wtime();
+
+  if(refine){
+    // HXT_CHECK(hxtComputeMeshSizeFromMesh(mesh, &delOptions));
+    if(mesh_size==NULL)
+    	HXT_CHECK(hxtCreateNodalsizeFromTrianglesAndLines(mesh, &delOptions));
+    else
+    	HXT_CHECK(hxtCreateNodalSizeFromFunction(mesh, &delOptions, mesh_size, userData) );
+
+    if(nbColors!=mesh->brep.numVolumes) {
+      HXT_CHECK( setFlagsToProcessOnlyVolumesInBrep(mesh) );
+    }
+
+    HXT_CHECK(hxtRefineTetrahedra(mesh, &delOptions, mesh_size, userData));
+
+    HXT_CHECK( hxtDestroyNodalsize(&delOptions) );
+  }
+
+  t[5] = omp_get_wtime();
+
+
+  if(optimize)
+    HXT_CHECK( hxtOptimizeTetrahedra(mesh, &bbox, optimizationThreads, delOptions.minSizeEnd, qualityThreshold, numVerticesConstrained) );
+
+  t[6] = omp_get_wtime();
+
+  
+  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..f54e9001c6eeab8f2dfb9b1914510bbe2a2ce98f
--- /dev/null
+++ b/contrib/hxt/hxt_mesh3d_main.h
@@ -0,0 +1,20 @@
+#ifndef _HXT_MESH_3D_MAIN_
+#define _HXT_MESH_3D_MAIN_
+
+#include "hxt_mesh.h"
+
+HXTStatus hxtTetMesh3d(HXTMesh* mesh,
+                      int defaulThreads,
+                      int DelaunayThreads,
+                      int optimizationThreads,
+                      int reproducible,
+                      int verbosity,
+                      int displayStat,
+                      int refine,
+                      int optimize,
+                      double qualityThreshold,
+                      HXTStatus (*bnd_recovery)(HXTMesh* mesh),
+                      double (*mesh_size)(double x, double y, double z, void* userData),
+                      void* userData);
+
+#endif
\ No newline at end of file
diff --git a/contrib/hxt/hxt_tetColor.c b/contrib/hxt/hxt_tetColor.c
new file mode 100644
index 0000000000000000000000000000000000000000..ec9570377070543a05e3a2389e8a0327d5038f7c
--- /dev/null
+++ b/contrib/hxt/hxt_tetColor.c
@@ -0,0 +1,470 @@
+#include "hxt_tetColor.h"
+#include "hxt_tetFlag.h"
+#include "hxt_sort.h"
+
+
+/***************************************************
+ *      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 && getFacetConstraint(mesh, 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-2; // -2 because we began at one AND because colorout is not counted...
+
+  HXT_CHECK( hxtFree(&stack) );
+
+  #pragma omp parallel for
+  for (uint64_t 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]-=2;
+    }
+    else {
+      mesh->tetrahedra.colors[i]--;
+    }
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+
+HXTStatus hxtFillVolumeBrep(HXTMesh* mesh, uint64_t* tri2TetMap,
+                            uint16_t numVolumes, uint16_t** numSurfacesPerVolume_ptr, uint16_t** surfacesPerVolume_ptr)
+{
+  int nbTriangleColor = 0;
+  uint16_t colorTriMax = 0;
+  char triangleColor[65536] = {0};
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->triangles.num; i++) {
+    #pragma omp atomic write
+    triangleColor[mesh->triangles.colors[i]] = 1;
+  }
+
+  #pragma omp parallel for reduction(+: nbTriangleColor) reduction(max: colorTriMax)
+  for (unsigned i=0; i<65536; i++) {
+    if(triangleColor[i]==1) {
+      nbTriangleColor++;
+      triangleColor[i] = 0;
+      if(i>colorTriMax)
+        colorTriMax = i;
+    }
+  }
+
+  uint16_t* numSurfacesPerVolume;
+  uint16_t* surfacesPerVolume;
+  HXT_CHECK( hxtAlignedMalloc(&numSurfacesPerVolume, sizeof(uint16_t)*numVolumes) );
+
+  // a surface can only appear in two volumes. (this allocate to much, but usually not by a lot)
+  HXT_CHECK( hxtAlignedMalloc(&surfacesPerVolume, sizeof(uint16_t)*nbTriangleColor*2) );
+
+  const int maxThreads = omp_get_max_threads();
+  int *numColors;
+  HXT_CHECK( hxtAlignedMalloc(&numColors, maxThreads*sizeof(int)) );
+
+  int currentSurfaceIndex = 0;
+  int totalCount = 0;
+  
+  #pragma omp parallel
+  {
+    // for every tet-color
+    for (uint16_t color=0; color<numVolumes; color++) {
+      #pragma omp for
+      for (uint64_t tri=0; tri<mesh->triangles.num; tri++) {
+        // the tetrahedra on both sides of the triangle
+        uint64_t tet1 = tri2TetMap[tri];
+        uint64_t tet2 = mesh->tetrahedra.neigh[tet1];
+        tet1/=4;
+        tet2/=4;
+
+        // we do a xor because the surface is not a bounding if the same volume is on both its sides
+        if((mesh->tetrahedra.colors[tet1]==color)^(mesh->tetrahedra.colors[tet2]==color)) {
+          #pragma omp atomic write // this atomic should do nothing (it usually is already atomic)
+          triangleColor[mesh->triangles.colors[tri]] = 1;
+        }
+      }
+
+      int threadID = omp_get_thread_num();
+      int localCount = 0;
+
+      #pragma omp for schedule(static)
+      for (uint16_t i=0; i<=colorTriMax; i++) {
+        if(triangleColor[i]==1) {
+          localCount++;
+        }
+      }
+
+      numColors[threadID] = localCount;
+
+      #pragma omp barrier
+      #pragma omp single
+      {
+        int nthreads = omp_get_num_threads();
+        currentSurfaceIndex+=totalCount;
+        totalCount = 0;
+        for (int thread=0; thread<nthreads; thread++) {
+          int tsum = totalCount + numColors[thread];
+          numColors[thread] = totalCount;
+          totalCount = tsum;
+        }
+
+        numSurfacesPerVolume[color] = totalCount;
+      }
+
+      #pragma omp for schedule(static)
+      for (uint16_t i=0; i<=colorTriMax; i++) {
+        if(triangleColor[i]==1) {
+          surfacesPerVolume[currentSurfaceIndex + numColors[threadID]] = i;
+          numColors[threadID]++;
+          triangleColor[i] = 0;
+        }
+      }
+    }
+  }
+
+  *numSurfacesPerVolume_ptr = numSurfacesPerVolume;
+  *surfacesPerVolume_ptr = surfacesPerVolume;
+  
+  HXT_CHECK( hxtAlignedFree(&numColors) );
+
+  return HXT_STATUS_OK;
+}
+
+
+static int compareVolumes(uint16_t numSurfaces1,
+                          uint16_t numSurfaces2,
+                          uint16_t* surfaces1,
+                          uint16_t* surfaces2)
+{
+  if(numSurfaces1<numSurfaces2)
+    return -1;
+  else if(numSurfaces1>numSurfaces2)
+    return 1;
+
+  int diff = 0;
+  uint64_t* surface1Sorted;
+  uint64_t* surface2Sorted;
+  HXT_CHECK( hxtAlignedMalloc(&surface1Sorted, numSurfaces1*sizeof(uint64_t)) );
+  HXT_CHECK( hxtAlignedMalloc(&surface2Sorted, numSurfaces1*sizeof(uint64_t)) );
+
+  uint64_t max1 = 0;
+  uint64_t max2 = 0;
+  for (uint16_t i=0; i<numSurfaces1; i++) {
+    surface1Sorted[i] = surfaces1[i];
+    if(surfaces1[i]>max1)
+      max1 = surfaces1[i];
+    surface2Sorted[i] = surfaces2[i];
+    if(surfaces2[i]>max2)
+      max2 = surfaces2[i];
+  }
+
+  if(max1!=max2) {
+    diff = max1 - max2;
+    goto endGoto;
+  }
+
+  HXT_CHECK( group1_sort(surface1Sorted, numSurfaces1, max1) );
+  HXT_CHECK( group1_sort(surface2Sorted, numSurfaces1, max1) );
+
+
+  for (uint16_t i=0; i<numSurfaces1; i++) {
+    if(surface1Sorted[i]!=surface2Sorted[i]) {
+      diff = surface1Sorted[i]-surface2Sorted[i];
+      goto endGoto;
+    }
+  }
+
+endGoto:
+  HXT_CHECK( hxtAlignedFree(&surface1Sorted) );
+  HXT_CHECK( hxtAlignedFree(&surface2Sorted) );
+
+  return diff;
+}
+
+
+static inline HXTStatus swapPairsIfNeeded(uint16_t numVolumes,
+                                         uint16_t* numSurfacesPerVolume,
+                                         uint16_t* surfacesPerVolume,
+                                         HXTGroup2* pairs)
+{
+  int alreadySwapped = 0;
+  for (int i=1; i<numVolumes; i++) {
+    if(pairs[i-1].v[1]==pairs[i].v[1]){
+      int vol1 = pairs[i-1].v[0];
+      int vol2 = pairs[i].v[0];
+      uint16_t start1 = numSurfacesPerVolume[vol1-1];
+      uint16_t end1 = numSurfacesPerVolume[vol1];
+      uint16_t start2 = numSurfacesPerVolume[vol2-1];
+      uint16_t end2 = numSurfacesPerVolume[vol2];
+      int comp = compareVolumes(end1-start1, end2-start2,
+                                &surfacesPerVolume[start1],
+                                &surfacesPerVolume[start2]);
+      if(comp > 0) {
+        if(alreadySwapped)
+          return HXT_ERROR_MSG(HXT_STATUS_ERROR, "The minimum surface of volume %d and %d appears more than twice...", vol1, vol2);
+        HXTGroup2 tmp = pairs[i-1];
+        pairs[i-1] = pairs[i];
+        pairs[i] = tmp;
+        alreadySwapped = 1;
+      }
+      else if (comp==0) {
+        return HXT_ERROR_MSG(HXT_STATUS_ERROR, "duplicated volume definition in the BREP");
+      }
+      else {
+        alreadySwapped = 0;
+      }
+    }
+  }
+  return HXT_STATUS_OK;
+}
+
+
+static HXTStatus getVolumesHashes(uint16_t numVolumes,
+                                  uint16_t* numSurfacesPerVolume,
+                                  uint16_t* surfacesPerVolume,
+                                  HXTGroup2* pairs)
+{
+  uint16_t maxMin = 0;
+
+  #pragma omp parallel for reduction(max:maxMin)
+  for (int vol=0; vol<numVolumes; vol++) {
+    pairs[vol].v[0] = vol;
+    uint16_t start = vol==0?0:numSurfacesPerVolume[vol-1];
+    uint16_t end = numSurfacesPerVolume[vol];
+
+    uint64_t hash = 0;
+    uint16_t minimum = UINT16_MAX;
+    while(start<end) {
+      uint16_t s = surfacesPerVolume[start++];
+
+      hash ^= (UINT64_C(1)<<(s & 31));
+      if(s < minimum)
+        minimum = s;
+    }
+
+    if(minimum > maxMin)
+      maxMin = minimum;
+
+    hash |= (uint64_t) minimum << 32;
+    pairs[vol].v[1] = hash;
+  }
+
+  HXT_CHECK( group2_sort_v1(pairs, numVolumes, (uint64_t) maxMin << 32) );
+
+  // it can happen that two volumes have the same hash. We must give them a unique order
+  HXT_CHECK( swapPairsIfNeeded(numVolumes, numSurfacesPerVolume, surfacesPerVolume, pairs) );
+
+  return HXT_STATUS_OK;
+}
+
+
+static HXTStatus matchVolumes(HXTMesh* mesh, uint16_t* numSurfacesPerVolume, uint16_t* surfacesPerVolume, uint16_t nbColors)
+{
+  HXTGroup2* ourPairs;
+  const uint16_t ourNumVolumes = nbColors;
+  const uint16_t theirNumVolumes = mesh->brep.numVolumes;
+  HXT_CHECK( hxtAlignedMalloc(&ourPairs, 2*ourNumVolumes*sizeof(HXTGroup2) ) );
+  HXTGroup2* theirPairs = ourPairs + ourNumVolumes;
+
+  // we make a scan so that we can do things in parallel...
+  int theirSum = 0;
+  for (uint16_t vol=0; vol<theirNumVolumes; vol++) {
+    theirSum+=mesh->brep.numSurfacesPerVolume[vol];
+    mesh->brep.numSurfacesPerVolume[vol] = theirSum;
+  }
+
+  int ourSum = 0;
+  for (uint16_t vol=0; vol<ourNumVolumes; vol++) {
+    ourSum+=numSurfacesPerVolume[vol];
+    numSurfacesPerVolume[vol] = ourSum;
+  }
+
+  HXT_CHECK( getVolumesHashes(ourNumVolumes,
+                              numSurfacesPerVolume,
+                              surfacesPerVolume, ourPairs) );
+  HXT_CHECK( getVolumesHashes(theirNumVolumes,
+                              mesh->brep.numSurfacesPerVolume,
+                              mesh->brep.surfacesPerVolume, theirPairs) );
+
+  // now that we sorted every volumes, see if they match
+  // ourPair contains all volumes, while ourPairs can skip some volumes...
+  int ourIndex = 0;
+  int volNotCorresponding = theirNumVolumes;
+  for (int theirIndex=0; theirIndex<theirNumVolumes; theirIndex++) {
+    while (1) {
+
+      if(ourIndex>=ourNumVolumes)
+        return HXT_ERROR_MSG(HXT_STATUS_ERROR, "Volumes do not match the BREP");
+      
+      if(ourPairs[ourIndex].v[1]==theirPairs[theirIndex].v[1]){
+        if(ourIndex<ourNumVolumes-1 && ourPairs[ourIndex+1].v[1]==ourPairs[ourIndex].v[1]) {
+          // we have to check further because there was a collision in the hashes
+          uint16_t ourVol = ourPairs[ourIndex].v[0];
+          uint16_t theirVol = theirPairs[theirIndex].v[0];
+
+          uint16_t ourStart = ourVol==0? 0:numSurfacesPerVolume[ourVol-1];
+          uint16_t ourEnd = numSurfacesPerVolume[ourVol];
+          uint16_t theirStart = theirVol==0? 0:mesh->brep.numSurfacesPerVolume[theirVol-1];
+          uint16_t theirEnd = mesh->brep.numSurfacesPerVolume[theirVol];
+
+          int cmp = compareVolumes(ourEnd - ourStart, theirEnd - theirStart, 
+                                   &surfacesPerVolume[ourStart], &mesh->brep.surfacesPerVolume[theirStart]);
+
+          if(cmp==0) {
+            ourPairs[ourIndex++].v[1] = theirPairs[theirIndex].v[0];
+            break;
+          }
+        }
+        else {
+          ourPairs[ourIndex++].v[1] = theirPairs[theirIndex].v[0];
+          break;
+        }
+      }
+
+      ourPairs[ourIndex++].v[1] = volNotCorresponding++;
+    }
+  }
+
+  while(ourIndex<ourNumVolumes) {
+    ourPairs[ourIndex++].v[1] = volNotCorresponding++;
+  }
+
+  #pragma omp parallel for
+  for (int i=0; i<ourNumVolumes; i++) {
+    uint16_t ourVol = ourPairs[i].v[0];
+    uint16_t theirVol = ourPairs[i].v[1];
+
+    theirPairs[ourVol].v[1] = theirVol;
+  }
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    uint16_t color = mesh->tetrahedra.colors[i];
+    if(color!=UINT16_MAX) {
+      mesh->tetrahedra.colors[i] = theirPairs[color].v[1];
+    }
+  }
+
+  #pragma omp parallel for
+  for (int vol=0; vol<theirNumVolumes; vol++) {
+    uint16_t start = vol==0?0:mesh->brep.numSurfacesPerVolume[vol-1];
+    uint16_t end = mesh->brep.numSurfacesPerVolume[vol];
+    mesh->brep.numSurfacesPerVolume[vol] = end-start;
+  }
+
+  HXT_CHECK( hxtAlignedFree(&ourPairs) );
+
+  return HXT_STATUS_OK;
+}
+
+
+
+HXTStatus hxtMapColorsToBrep(HXTMesh* mesh, uint16_t nbColors, uint64_t* tri2TetMap)
+{
+  if(mesh->brep.numVolumes==0) {
+  #ifndef NDEBUG
+    if(mesh->brep.numSurfacesPerVolume!=NULL) {
+      HXT_WARNING("mesh->brep.numSurfacesPerVolume is not null but numVolumes=0\nAttempting to free it");
+      HXT_CHECK( hxtFree(&mesh->brep.numSurfacesPerVolume) );
+    }
+    if(mesh->brep.surfacesPerVolume!=NULL) {
+      HXT_WARNING("mesh->brep.surfacesPerVolume is not null but numVolumes=0\nAttempting to free it");
+      HXT_CHECK( hxtFree(&mesh->brep.surfacesPerVolume) );
+    }
+  #endif
+    mesh->brep.numVolumes = nbColors;
+    
+    HXT_CHECK( hxtFillVolumeBrep(mesh, tri2TetMap,
+                              nbColors,
+                              &mesh->brep.numSurfacesPerVolume,
+                              &mesh->brep.surfacesPerVolume) );
+  }
+  else {
+    if(mesh->brep.numVolumes>nbColors)
+      return HXT_ERROR_MSG(HXT_STATUS_ERROR, "brep contains more volumes than there really are !");
+
+    if(mesh->brep.numVolumes<nbColors)
+      HXT_INFO("%u out of %u volumes will be refined", mesh->brep.numVolumes, nbColors);
+    
+
+    // match our brep with the brep given...
+    uint16_t* numSurfacesPerVolume;
+    uint16_t* surfacesPerVolume;
+    HXT_CHECK( hxtFillVolumeBrep(mesh, tri2TetMap,
+                              nbColors,
+                              &numSurfacesPerVolume,
+                              &surfacesPerVolume) );
+
+    HXT_CHECK( matchVolumes(mesh, numSurfacesPerVolume, surfacesPerVolume, nbColors) );
+
+
+    HXT_CHECK( hxtAlignedFree( &numSurfacesPerVolume ) );
+    HXT_CHECK( hxtAlignedFree( &surfacesPerVolume ) );
+  }
+
+
+  return HXT_STATUS_OK;
+}
+
+
+HXTStatus setFlagsToProcessOnlyVolumesInBrep(HXTMesh* mesh)
+{
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    if(mesh->tetrahedra.colors[i]>=mesh->brep.numVolumes) {
+      setProcessedFlag(mesh, i);
+    }
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+
+// TODO: compute a boundinng box following some surface mesh colors !
+// we should get this bbox from the triangles (and not the tetrahedra) to be quick
+// we should thus have a function that receive a volume color and receive a list of surface colors !
\ No newline at end of file
diff --git a/contrib/hxt/hxt_tetColor.h b/contrib/hxt/hxt_tetColor.h
new file mode 100644
index 0000000000000000000000000000000000000000..8a841956d8c5a0c0285745d0ada3dd46df90f42b
--- /dev/null
+++ b/contrib/hxt/hxt_tetColor.h
@@ -0,0 +1,32 @@
+#ifndef _HXT_TETCOLOR_
+#define _HXT_TETCOLOR_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hxt_mesh.h"
+
+/// Gives a unique color to each enclosed volume
+HXTStatus hxtColorMesh(HXTMesh* mesh, uint16_t *nbVolumes);
+
+/* call hxtFillVolumeBrep if mesh->brep.numVolumes = 0
+ * or change the colors so that they match the volumes described by
+ * mesh->brep.numSurfacesPerVolume
+ * mesh->brep.surfacesPerVolume
+ */
+HXTStatus hxtMapColorsToBrep(HXTMesh* mesh, uint16_t nbColors, uint64_t* tri2TetMap);
+
+
+// fill mesh->brep.numVolumes, mesh->brep.numSurfacesPerVolume and mesh->brep.surfacesPerVolume
+HXTStatus hxtFillVolumeBrep(HXTMesh* mesh, uint64_t* tri2TetMap,
+                            uint16_t numVolumes, uint16_t** numSurfacesPerVolume, uint16_t** surfacesPerVolume);
+
+/* set the processed flag (see hxt_tetFlag.h) for colors that are not in colorsToMesh */
+HXTStatus setFlagsToProcessOnlyVolumesInBrep(HXTMesh* mesh);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/hxt/hxt_tetDelaunay.c b/contrib/hxt/hxt_tetDelaunay.c
new file mode 100644
index 0000000000000000000000000000000000000000..c8cb4a459fc3df3302174d3edf459fa75bfafb57
--- /dev/null
+++ b/contrib/hxt/hxt_tetDelaunay.c
@@ -0,0 +1,1907 @@
+#include "hxt_tetDelaunay.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_tetDelaunay.h.
+* \author Célestin Marot
+*/
+
+/* compile-time parameters */
+#define SMALLEST_ROUND 2048
+#define DELETED_BUFFER_SIZE 8182
+// #define HXT_DELAUNAY_LOW_MEMORY /* doesn't use any buffer (a lot slower, except if you are at the limit of filling the RAM) */
+
+/* usefull macros */
+#define ABS(x) ((x) >= 0 ? (x) : -(x))
+#define MAX(x,y) ((x)>(y) ? (x) : (y))
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+
+#define HXT_OMP_CHECK(status) do{ HXTStatus _tmp_ = (status); \
+    if(_tmp_<0){ \
+      if(_tmp_>HXT_STATUS_INTERNAL) \
+        HXT_TRACE_MSG(_tmp_, "cannot break OpenMP region -> exiting"); \
+      fflush(stdout); fflush(stderr); \
+      exit(_tmp_); \
+    } \
+  }while(0)
+
+
+typedef struct{
+  uint32_t hxtDeclareAligned node[3];
+  uint16_t flag;
+  uint64_t neigh; // the tet on the other side of the boundar
+} cavityBnd_t;
+
+typedef struct {
+#ifndef HXT_DELAUNAY_LOW_MEMORY
+  uint64_t hxtDeclareAligned Map[1024];
+#endif
+
+  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;
+
+
+/***********************************
+ * create the initial tetrahedron 
+ * surrounded by 4 ghost tetrahedra
+ ***********************************/
+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){
+    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;
+
+  uint32_t i=0, j=1, k=2, l=3;
+  for (i=0; orientation==0.0 && i<nToInsert-3; i++)
+  {
+    for (j=i+1; orientation==0.0 && j<nToInsert-2; j++)
+    {
+      for (k=j+1; orientation==0.0 && k<nToInsert-1; k++)
+      {
+        for (l=k+1; orientation==0.0 && l<nToInsert; l++)
+        {
+          orientation = orient3d(vertices[nodeInfo[i].node].coord,
+                                 vertices[nodeInfo[j].node].coord,
+                                 vertices[nodeInfo[k].node].coord,
+                                 vertices[nodeInfo[l].node].coord);
+        }
+      }
+    }
+  }
+  l--; k--; j--; i--;
+
+
+  if(orientation==0.0){
+    return HXT_ERROR_MSG(HXT_STATUS_FAILED, "all vertices are coplanar");
+  }
+
+  // swap 0<->i  1<->j 2<->k 3<->l
+  {
+    hxtNodeInfo tmp = nodeInfo[i];
+    nodeInfo[i] = nodeInfo[0];
+    nodeInfo[0] = tmp;
+    nodeInfo[0].status = HXT_STATUS_TRUE;
+    i = 0;
+
+    tmp = nodeInfo[j];
+    nodeInfo[j] = nodeInfo[1];
+    nodeInfo[1] = tmp;
+    nodeInfo[1].status = HXT_STATUS_TRUE;
+    j = 1;
+
+    tmp = nodeInfo[k];
+    nodeInfo[k] = nodeInfo[2];
+    nodeInfo[2] = tmp;
+    nodeInfo[2].status = HXT_STATUS_TRUE;
+    k = 2;
+
+    tmp = nodeInfo[l];
+    nodeInfo[l] = nodeInfo[3];
+    nodeInfo[3] = tmp;
+    nodeInfo[3].status = HXT_STATUS_TRUE;
+    l = 3;
+  }
+
+
+  if(orientation > 0.0){
+    uint32_t tmp = i;
+    i = j;
+    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[ 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[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.num = 5;
+
+  for (uint64_t tet=0; tet<5; tet++){
+    mesh->tetrahedra.colors[tet] = UINT16_MAX;
+    mesh->tetrahedra.flag[tet] = 0;
+  }
+
+  return HXT_STATUS_OK;
+}
+
+/***********************************
+ * 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=0;
+  passes[0] = nToInsert;
+
+  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;
+  }
+
+  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->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 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 = hxtTetrahedraDoubleSize(mesh);
+    }
+  } // implicit barrier here
+
+  if(status!=HXT_STATUS_OK)
+    HXT_TRACE(status);
+
+  return status;
+}
+
+
+// pragma atomic capture to get tetrahedra.num and update it at the same time before caling this function !
+static inline HXTStatus reserveNewTet(HXTMesh* mesh){
+  if(mesh->tetrahedra.num > mesh->tetrahedra.size){
+    HXT_CHECK( synchronizeReallocation(mesh, NULL, NULL) );
+  }
+
+  return HXT_STATUS_OK;
+}
+
+static inline HXTStatus reserveNewDeleted(TetLocal* 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 reserveNewBnd(TetLocal* local, uint64_t 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;
+}
+/***********************************************/
+
+/************************************
+ * 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
+  uint64_t h0 = vertices[nodes[0]].padding.hilbertDist;
+  uint64_t h1 = vertices[nodes[1]].padding.hilbertDist;
+  uint64_t h2 = vertices[nodes[2]].padding.hilbertDist;
+  uint64_t h3 = nodes[3]==HXT_GHOST_VERTEX ? h2 : vertices[nodes[3]].padding.hilbertDist;
+
+  /* if a vertex has a hilbert index UINT64_MAX, it means that only certain volume are meshed
+   * and this vertex was outside of the bounding box. we should never go in any tetrahedra that is outside
+   * the meshed volume, so we return false... */
+  if(h0==UINT64_MAX || h1==UINT64_MAX || h2==UINT64_MAX || h3==UINT64_MAX)
+    return HXT_STATUS_INTERNAL;
+  
+  if((h0- local->partition.startDist>=rel) || 
+     (h1- local->partition.startDist>=rel) ||
+     (h2- local->partition.startDist>=rel) ||
+     (h3- local->partition.startDist>=rel))
+    return HXT_STATUS_INTERNAL;
+
+  return HXT_STATUS_OK;
+
+}
+
+
+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;
+}
+
+/* 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)
+{
+  double *vtaCoord = mesh->vertices.coord + 4*vta;
+  double vtaNodalSize = nodalSizes[vta];
+
+  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 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 filterTet(HXTMesh* mesh, const double *nodalSizes, const uint64_t curTet, const uint32_t vta){
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+
+  double *vtaCoord = vertices[vta].coord;
+  double vtaNodalSize = nodalSizes[vta];
+
+  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;
+}
+
+
+/* restore the structure as it was before the failed insertion attempt */
+static inline void restoreDeleted(HXTMesh* mesh, TetLocal* local, const uint64_t prevDeleted){
+  for (uint64_t i=prevDeleted; i<local->deleted.num; i++)
+    unsetDeletedFlag(mesh, local->deleted.tetID[i]);
+
+  local->deleted.num = prevDeleted;
+}
+
+
+/***********************************
+ * 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};
+
+  // 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]) {
+
+        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;
+}
+
+
+/* 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;
+
+  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;
+
+  if(Node[3]==HXT_GHOST_VERTEX){ 
+    double det = orient3d(a,b,c,e);
+
+    if(det!=0.0){
+      return det;
+    }
+
+    // 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;
+  }
+
+  double* const __restrict__ d = vertices[Node[3]].coord;
+
+  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;
+
+  double* const vtaCoord = vertices[vta].coord;
+  unsigned enteringFace = 4;
+
+#ifndef NDEBUG
+  uint64_t TotalCount = 0;
+#endif
+  
+
+  while(1){
+    const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + 4*nextTet;
+    const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + 4*nextTet;
+
+  #ifndef NDEBUG
+    if(curNode[3]==HXT_GHOST_VERTEX){
+      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "walked outside of the domain");
+    }
+  #endif
+
+    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[getNode0FromFacet(index)]].coord;
+        const double* __restrict__ b = vertices[curNode[getNode1FromFacet(index)]].coord;
+        const double* __restrict__ c = vertices[curNode[getNode2FromFacet(index)]].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]);
+          }
+
+          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;
+        }
+      }
+    }
+
+    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;
+    }
+
+    //    printf("nextTet %u %g %u %u\n",nextTet,Min, count, neigh);
+    nextTet = curNeigh[neigh]/4;
+    enteringFace = curNeigh[neigh]&3;
+
+  #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
+  }
+}
+
+
+/***********************************
+ * digging cavity
+ ***********************************/
+
+/* 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++;
+}
+
+/* delete a tetrahedron 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;
+  setDeletedFlag(mesh, neigh);
+
+  return HXT_STATUS_OK;
+}
+
+/* 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 (uint64_t i=0; i<local->ball.num; i++) {
+    if(local->ball.bnd[i].node[2]==HXT_GHOST_VERTEX){
+
+    }
+    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;
+}
+
+
+static HXTStatus undeleteTetrahedron(TetLocal* local, HXTMesh* mesh, 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
+  }
+  unsetDeletedFlag(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;
+
+#ifdef DEBUG
+  int nbndFace2 = (getDeletedFlag(mesh, curNeigh[0]/4)==0) + (getDeletedFlag(mesh, curNeigh[1]/4)==0) + (getDeletedFlag(mesh, curNeigh[2]/4)==0) + (getDeletedFlag(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
+
+  HXT_CHECK( reserveNewBnd(local, 3) );
+
+  if(curNeigh[0]!=bndFaces[0] && curNeigh[0]!=bndFaces[1] && curNeigh[0]!=bndFaces[2] && curNeigh[0]!=bndFaces[3])
+    bndPush(local, (getFacetConstraint(mesh, tetToUndelete, 0)   ) |
+                   (getEdgeConstraint(mesh, tetToUndelete, 1)>>1) | // constraint on edge 1 (facet 0 2) goes on edge 0
+                   (getEdgeConstraint(mesh, tetToUndelete, 0)<<1) | // constraint on edge 0 (facet 0 1) goes on edge 1
+                   (getEdgeConstraint(mesh, tetToUndelete, 2)   ),  // constraint on edge 2 (facet 0 3) goes on edge 2
+                   curNode[2], curNode[1], curNode[3], 4*tetToUndelete+0);
+
+  if(curNeigh[1]!=bndFaces[0] && curNeigh[1]!=bndFaces[1] && curNeigh[1]!=bndFaces[2] && curNeigh[1]!=bndFaces[3])
+    bndPush(local,  (getFacetConstraint(mesh, tetToUndelete, 1)>>1) |// constraint on facet 1 goes on facet 0
+                    (getEdgeConstraint(mesh, tetToUndelete, 0)   ) | // constraint on edge 0 (facet 1 0) goes on edge 0
+                    (getEdgeConstraint(mesh, tetToUndelete, 3)>>2) | // constraint on edge 3 (facet 1 2) goes on edge 1
+                    (getEdgeConstraint(mesh, tetToUndelete, 4)>>2),  // constraint on edge 4 (facet 1 3) goes on edge 2
+                    curNode[0], curNode[2], curNode[3], 4*tetToUndelete+1);
+
+  if(curNeigh[2]!=bndFaces[0] && curNeigh[2]!=bndFaces[1] && curNeigh[2]!=bndFaces[2] && curNeigh[2]!=bndFaces[3])
+    bndPush(local,  (getFacetConstraint(mesh, tetToUndelete, 2)>>2) |// constraint on facet 2 goes on facet 0
+                    (getEdgeConstraint(mesh, tetToUndelete, 3)>>3) | // constraint on edge 3 (facet 2 1) goes on edge 0
+                    (getEdgeConstraint(mesh, tetToUndelete, 1)   ) | // constraint on edge 1 (facet 2 0) goes on edge 1
+                    (getEdgeConstraint(mesh, tetToUndelete, 5)>>3),  // constraint on edge 5 (facet 2 3) goes on edge 2
+                     curNode[1], curNode[0], curNode[3], 4*tetToUndelete+2);
+
+  if(curNeigh[3]!=bndFaces[0] && curNeigh[3]!=bndFaces[1] && curNeigh[3]!=bndFaces[2] && curNeigh[3]!=bndFaces[3])
+    bndPush(local, (getFacetConstraint(mesh, tetToUndelete, 3)>>3) |// constraint on facet 3 goes on facet 0
+                   (getEdgeConstraint(mesh, tetToUndelete, 2)>>2) | // constraint on edge 2 (facet 3 0) goes on edge 0
+                   (getEdgeConstraint(mesh, tetToUndelete, 4)>>3) | // constraint on edge 4 (facet 3 1) goes on edge 1
+                   (getEdgeConstraint(mesh, tetToUndelete, 5)>>3),  // constraint on edge 5 (facet 3 2) goes on edge 2
+                   curNode[0], curNode[1], curNode[2], 4*tetToUndelete+3);
+
+  return HXT_STATUS_OK;
+}
+
+
+static HXTStatus reshapeCavityIfNeeded(TetLocal* local, HXTMesh* mesh, const uint32_t vta) {
+  // 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, mesh->tetrahedra.neigh[local->ball.bnd[blindFace].neigh]/4) );
+  }
+  return HXT_STATUS_OK;
+}
+
+
+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");
+
+  // 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;
+  }
+
+  for (uint64_t i=prevDeleted; i<local->deleted.num; i++) {
+    uint64_t delTet = local->deleted.tetID[i];
+    int exist = 1;
+    for (int edge=0; exist && edge<6; edge++) {
+      if(getEdgeConstraint(mesh, delTet, edge) && (mesh->tetrahedra.colors[delTet] & (1U<<edge))==0) {
+        unsigned in_facet;
+        unsigned out_facet;
+
+        getFacetsFromEdge(edge, &in_facet, &out_facet);
+
+        int edgeIsSafe = 0;
+        uint64_t curTet = delTet;
+
+        // first turn
+        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;
+
+          uint32_t* nodes = mesh->tetrahedra.node + 4*curTet;
+          for (out_facet=0; out_facet<3; out_facet++)
+            if(nodes[out_facet]==newV)
+              break;
+
+          if(getDeletedFlag(mesh, curTet)!=0) {
+            // mark that the edge as been treate
+            #ifdef DEBUG
+              if((mesh->tetrahedra.colors[curTet] & (1U<<getEdgeFromFacets(in_facet, out_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<<getEdgeFromFacets(in_facet, out_facet));
+          }
+          else {
+            edgeIsSafe=1;
+          }
+
+        } while (curTet!=delTet);
+
+        if(!edgeIsSafe) { // we must find a tetrahedron on the opposite side of vta and delete it.
+          getFacetsFromEdge(edge, &in_facet, &out_facet);
+          curTet = delTet;
+
+          uint64_t tetContainingVta = local->deleted.tetID[prevDeleted];
+          uint64_t tetToUndelete = HXT_NO_ADJACENT;
+          double distMax = 0.0;
+          double* vtaCoord = mesh->vertices.coord + 4*vta;
+
+        #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];
+
+          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");
+          }
+        #endif
+
+          // second turn
+          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;
+
+            uint32_t* nodes = mesh->tetrahedra.node + 4*curTet;
+            for (out_facet=0; out_facet<3; out_facet++)
+              if(nodes[out_facet]==newV)
+                break;
+
+            double* coord1 = mesh->vertices.coord + newV;
+            double* coord2 = mesh->vertices.coord + nodes[in_facet];
+
+            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;
+              }
+
+              if(dist>distMax) {
+                dist = distMax;
+                tetToUndelete = curTet;
+              }
+            }
+          } while (curTet!=delTet);
+
+          if(tetToUndelete==delTet)
+            exist = 0;
+
+          // printf("undeleting tetrahedron %lu\n", tetToUndelete);
+          mesh->tetrahedra.colors[tetToUndelete] = color;
+          HXT_CHECK( undeleteTetrahedron(local, mesh, tetToUndelete) );
+        }
+      }
+    }
+  }
+
+  for (uint64_t i=prevDeleted; i<local->deleted.num; i++) {
+    uint64_t delTet = local->deleted.tetID[i];
+    mesh->tetrahedra.colors[delTet] = color;
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+/* 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 firstTet, const uint32_t vta, int* edgeConstraint){
+  // add tetrahedra to cavity
+  local->deleted.tetID[local->deleted.num++] = firstTet;
+  setDeletedFlag(mesh, firstTet);
+  local->ball.num = 0;
+
+  
+
+  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;
+
+    *edgeConstraint += isAnyEdgeConstrained(mesh, curTet)!=0;
+
+    /* 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) );
+
+    // we unrolled the loop for speed (also because indices are not trivial, we would need a 4X4 array)
+
+    /* 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 && getDeletedFlag(mesh, neigh)==0){
+      if(getFacetConstraint(mesh, curTet, 0) || 
+        tetInsphere(mesh, neigh*4, vta)>=0.0){
+        bndPush(local, mesh->tetrahedra.flag[curTet] & UINT16_C(0x107),
+                       /* corresponds to :
+                       getFacetConstraint(mesh, curTet, 0) | 
+                       getEdgeConstraint(mesh, curTet, 0) |
+                       getEdgeConstraint(mesh, curTet, 1) |
+                       getEdgeConstraint(mesh, curTet, 2) */
+                       curNode[1], curNode[2], curNode[3], curNeigh[0]);
+      }
+      else{
+        HXT_CHECK( deletedPush(mesh, local, neigh) );
+      }
+    }
+
+    neigh = curNeigh[1]/4;
+    if(curNeigh[1]!=HXT_NO_ADJACENT && getDeletedFlag(mesh, neigh)==0){
+      if(getFacetConstraint(mesh, curTet, 1) || 
+        tetInsphere(mesh, neigh*4, vta)>=0.0){
+        bndPush(local, (getFacetConstraint(mesh, curTet, 1)>>1) |// constraint on facet 1 goes on facet 0
+                       (getEdgeConstraint(mesh, curTet, 3)>>3) | // constraint on edge 3 (facet 1 2) goes on edge 0
+                       (getEdgeConstraint(mesh, curTet, 0)<<1) | // constraint on edge 0 (facet 1 0) goes on edge 1
+                       (getEdgeConstraint(mesh, curTet, 4)>>2),  // constraint on edge 4 (facet 1 3) goes on edge 2
+                       curNode[2], curNode[0], curNode[3], curNeigh[1]);
+      }
+      else{
+        HXT_CHECK( deletedPush(mesh, local, neigh) );
+      }
+    }
+
+    neigh = curNeigh[2]/4;
+    if(curNeigh[2]!=HXT_NO_ADJACENT && getDeletedFlag(mesh, neigh)==0){
+      if(getFacetConstraint(mesh, curTet, 2)|| 
+        tetInsphere(mesh, neigh*4, vta)>=0.0){
+        bndPush(local, (getFacetConstraint(mesh, curTet, 2)>>2) |// constraint on facet 2 goes on facet 0
+                       (getEdgeConstraint(mesh, curTet, 1)>>1) | // constraint on edge 1 (facet 2 0) goes on edge 0
+                       (getEdgeConstraint(mesh, curTet, 3)>>2) | // constraint on edge 3 (facet 2 1) goes on edge 1
+                       (getEdgeConstraint(mesh, curTet, 5)>>3),  // constraint on edge 5 (facet 2 3) goes on edge 2
+                       curNode[0], curNode[1], curNode[3], curNeigh[2]);
+      }
+      else{
+        HXT_CHECK( deletedPush(mesh, local, neigh) );
+      }
+    }
+
+    neigh = curNeigh[3]/4;
+    if(curNeigh[3]!=HXT_NO_ADJACENT && getDeletedFlag(mesh, neigh)==0){
+      if(getFacetConstraint(mesh, curTet, 3) || 
+        tetInsphere(mesh, neigh*4, vta)>=0.0){
+        
+        bndPush(local, (getFacetConstraint(mesh, curTet, 3)>>3) |// constraint on facet 3 goes on facet 0
+                       (getEdgeConstraint(mesh, curTet, 4)>>4) | // constraint on edge 4 (facet 3 1) goes on edge 0
+                       (getEdgeConstraint(mesh, curTet, 2)>>1) | // constraint on edge 2 (facet 3 0) goes on edge 1
+                       (getEdgeConstraint(mesh, curTet, 5)>>3),  // constraint on edge 5 (facet 3 2) goes on edge 2
+                       // 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;
+
+#ifndef NDEBUG
+  int ghost_is_there = 0;
+#endif
+
+HXT_ASSERT(((size_t) bnd)%SIMD_ALIGN==0);
+HXT_ASSERT(((size_t) verticesID)%SIMD_ALIGN==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;
+    }
+  }
+
+  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]];
+
+    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]];
+    }
+
+  }
+
+  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
+
+  #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;
+  }
+
+  #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){
+
+  uint64_t tlength = 0;
+  const uint64_t middle = blength*3/2; // 3N
+
+  // 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};
+
+  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;
+
+    // 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]];
+
+      // 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;
+
+  uint64_t start = clength - blength;
+
+  // #pragma vector aligned
+  #pragma omp simd
+  for (uint64_t i=0; i<blength; i++)
+  {
+
+    __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;
+
+  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;
+
+  HXT_CHECK( walking2Cavity(mesh, local, curTet, vta) );
+
+  if(nodalSizes!=NULL && filterTet(mesh, nodalSizes, *curTet, vta)){
+    return HXT_STATUS_FALSE;
+  }
+
+  const uint16_t color = mesh->tetrahedra.colors[*curTet];
+  int edgeConstraint = 0;
+  HXTStatus status = diggingACavity(mesh, local, *curTet, vta, &edgeConstraint);
+
+  if(status==HXT_STATUS_INTERNAL){
+    restoreDeleted(mesh, local, prevDeleted);
+    return HXT_STATUS_TRYAGAIN;
+  }
+  else{
+    HXT_CHECK(status);
+  }
+
+  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);
+  //   return HXT_STATUS_FALSE;
+  // }
+
+  // reshape the cavity if it is not star shaped
+  if(!perfectlyDelaunay)
+    HXT_CHECK( reshapeCavityIfNeeded(local, mesh, vta) );
+
+  if(nodalSizes!=NULL && filterCavity(local, mesh, nodalSizes, vta)) {
+    restoreDeleted(mesh, local, prevDeleted);
+    return HXT_STATUS_FALSE;
+  }
+
+
+  if(local->ball.num > local->deleted.num){
+    uint64_t needed = MAX(DELETED_BUFFER_SIZE,local->ball.num)-local->deleted.num;
+
+    uint64_t ntet;
+
+    #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;
+      setDeletedFlag(mesh, ntet+i);
+    }
+
+    local->deleted.num+=needed;
+  }
+
+  HXT_CHECK( fillingACavity(mesh, local, verticesID, curTet, vta, color) );
+
+  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;
+  
+
+  /******************************************************
+          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;
+
+    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) );
+    }
+
+    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) );
+
+    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 (uint32_t i=0; i<nToInsert; i++) {
+        nodeInfo[i].hilbertDist = verticesToInsert[i].padding.hilbertDist;
+      }
+
+      for (unsigned i=options->numVerticesInMesh < SMALLEST_ROUND; i<npasses; i++) {
+        HXT_CHECK( hxtNodeInfoSort(nodeInfo+passes[i], passes[i+1]-passes[i], nbits) );
+      }
+
+      const uint32_t nodalMin = mesh->vertices.num - nToInsert;
+      double* sizesToInsert = options->nodalSizes + nodalMin;
+
+      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) );
+    }
+  }
+
+  /******************************************************
+        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;
+      }
+    }
+    nthreads = MIN(nthreads, 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=(nthreads+1)/2;
+      }
+      else if(passLength < (uint32_t) 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;
+
+      if(p==0 && maxThreads<=1) {
+        nbits = hxtAdvancedHilbertBits(options->bbox, options->minSizeStart, options->minSizeEnd,
+                                       options->numVerticesInMesh,
+                                       options->numVerticesInMesh + nToInsert,
+                                       options->numVerticesInMesh,
+                                       nToInsert,
+                                       1);
+
+        HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num, &nbits, bboxShift) );
+      }
+      else {
+        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{
+          HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num - nToInsert + passEnd, &nbits, bboxShift) );
+        }
+      }
+
+      
+
+      #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(getDeletedFlag(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(getDeletedFlag(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;
+                  }
+                  /* fall through */
+                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);
+      }
+
+      /******************************************************
+      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;
+    }
+  }
+
+  /******************************************************
+                  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;
+      }
+    }
+  }
+  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) );
+
+  /***************************************************************
+    if reordering allowed, remove vertices we could not insert
+  ***************************************************************/
+  if(!noReordering && totalNumSkipped!=0){
+    /* remove deleted vertices and change tetrahedra.node accordingly */
+
+    uint32_t* numInserted;
+    HXT_CHECK( hxtAlignedMalloc(&numInserted, omp_get_max_threads()*sizeof(uint32_t)) );
+
+    uint32_t firstShifted = mesh->vertices.num - nToInsert;
+    uint32_t n = nToInsert;
+
+    // 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
+
+      #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
+
+      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 (int i=0; i<threadID; i++) {
+        start+=numInserted[i];
+      }
+      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++;
+
+        // 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;
+      }
+
+      // 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;
+      }
+    }
+
+    HXT_CHECK( hxtAlignedFree(&numInserted) );
+
+    // 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];
+    }
+
+    if(options->verbosity>1)
+      HXT_INFO("%u vertices removed (vertices not inserted in the mesh are removed when using hxtDelaunay)\n", totalNumSkipped);
+
+    mesh->vertices.num = mesh->vertices.num - totalNumSkipped;
+  }
+
+  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);
+
+  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;
+}
+
+
+/*****************************************
+ * complete the HXTDelaunayOptions struct
+ * when there are missing fields.
+ ****************************************/
+static HXTStatus DelaunayOptionsInit(HXTMesh* mesh,
+                                HXTDelaunayOptions* userOptions,
+                                HXTDelaunayOptions* options,
+                                HXTBbox* bbox){
+HXT_ASSERT(mesh!=NULL);
+
+  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;
+
+    // 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;
+
+    // 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;
+    }
+
+    #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;
+    }
+
+    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(options->numVerticesInMesh <= mesh->vertices.num);
+
+  if(options->bbox==NULL){
+    options->bbox = bbox;
+    hxtBboxInit(bbox);
+    HXT_CHECK( hxtBboxAdd(bbox, mesh->vertices.coord, mesh->vertices.num) );
+  }
+
+  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();
+
+  // 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]);
+
+  return HXT_STATUS_OK;
+}
+
+
+/*****************************************
+ * 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 uint32_t nToInsert = mesh->vertices.num - options.numVerticesInMesh;
+
+  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;
+  }
+
+  HXT_CHECK( parallelDelaunay3D(mesh, &options, nodeInfo, nToInsert, 0) );
+
+  HXT_CHECK( hxtAlignedFree(&nodeInfo) );
+
+  return HXT_STATUS_OK;
+}
+
+
+/************************************************
+ * 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);
+
+  HXTDelaunayOptions options;
+  HXTBbox bbox;
+  HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) );
+
+  if(options.reproducible && nToInsert<2048) // not worth launching threads and having to reorder tets after...
+    options.delaunayThreads = 1;
+
+HXT_ASSERT(options.numVerticesInMesh+nToInsert <= mesh->vertices.num);
+
+  HXT_CHECK( parallelDelaunay3D(mesh, &options, nodeInfo, nToInsert, 1) );
+
+  return HXT_STATUS_OK;
+}
+
diff --git a/contrib/hxt/hxt_tetDelaunay.h b/contrib/hxt/hxt_tetDelaunay.h
new file mode 100644
index 0000000000000000000000000000000000000000..34020fdaeb25913787d9240f4ea3b99f296eec5b
--- /dev/null
+++ b/contrib/hxt/hxt_tetDelaunay.h
@@ -0,0 +1,113 @@
+#ifndef _HXT_TETDELAUNAY_
+#define _HXT_TETDELAUNAY_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hxt_mesh.h"
+#include "hxt_vertices.h"
+
+/**
+* \file hxt_tetDelaunay.h Delaunay tetrahedrization
+* \author Célestin Marot
+*/
+
+/**
+ * \struct HXTDelaunayOptions
+ * 
+ * Options for the Delaunay functions hxtDelaunay() and hxtDelaunaySteadyVertices()
+ * 
+ *
+ */
+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;
+
+
+/**
+ * \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
+ *
+ * \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);
+
+
+/**
+ * \brief Delaunay of a set of vertices
+ * \details This perform the insertion of the vertices
+ * from numVerticesInMesh to mesh->vertices.num\n
+ *
+ * \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 !
+ *
+ * \param mesh: a valid Delaunay mesh
+ * \param options: options to give to the Delaunay algorithm \ref HXTDelaunayOptions
+ */
+HXTStatus hxtDelaunay(HXTMesh* mesh, HXTDelaunayOptions* options);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/contrib/hxt/hxt_tetFlag.c b/contrib/hxt/hxt_tetFlag.c
new file mode 100644
index 0000000000000000000000000000000000000000..60a34249dbfe3b7c3667f6dd318e921fc1740440
--- /dev/null
+++ b/contrib/hxt/hxt_tetFlag.c
@@ -0,0 +1,711 @@
+#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 tmp1 = i[1]; i[1] = i[2]; i[2] = tmp1;
+
+    if(i[0]>i[1]){
+      uint32_t tmp2 = i[0]; i[0] = i[1]; i[1] = tmp2;
+    }
+  }
+}
+// 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);
+}
+
+/**************************************************************************************
+ *   Lines --> Triangles   MAPPING  (for each line, get 3*tri+e)
+ *************************************************************************************/
+HXTStatus hxtGetLines2TriMap(HXTMesh* mesh, uint64_t* lines2TriMap, uint64_t* missing)
+{
+  HXTGroup2* edgeKey = NULL;
+  uint64_t* numEdges = NULL;
+
+  const int maxThreads = omp_get_max_threads();
+  const uint64_t n = mesh->vertices.num;
+  const uint64_t numEdgesTotal = mesh->triangles.num*3+mesh->lines.num;
+
+  HXT_CHECK( hxtMalloc(&numEdges, maxThreads*sizeof(uint64_t)) );
+  HXT_CHECK( hxtAlignedMalloc(&edgeKey, numEdgesTotal*sizeof(HXTGroup2)) );
+
+  #pragma omp parallel
+  {
+    #pragma omp for nowait
+    for (uint64_t i=0; i<mesh->lines.num; i++) {
+      uint32_t v0 = mesh->lines.node[2*i];
+      uint32_t v1 = mesh->lines.node[2*i+1];
+
+      if(v0<v1) {
+        edgeKey[i].v[0] = v0*n + v1;
+        edgeKey[i].v[1] = 2*i;
+      }
+      else if(v0<v1){
+        edgeKey[i].v[0] = v1*n + v0;
+        edgeKey[i].v[1] = 2*i;
+      }
+      else {
+        edgeKey[i].v[0] = v0*n + v0; // the line begins and ends at the same point...
+        edgeKey[i].v[1] = 1;
+        lines2TriMap[i] = HXT_NO_ADJACENT;
+      }
+    }
+
+    #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);
+
+      edgeKey[mesh->lines.num+3*i].v[0] = v[0]*n + v[1];
+      edgeKey[mesh->lines.num+3*i].v[1] = 2*(3*i)+1;
+      edgeKey[mesh->lines.num+3*i+1].v[0] = v[0]*n + v[2];
+      edgeKey[mesh->lines.num+3*i+1].v[1] = 2*(3*i+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] = 2*(3*i+2)+1;
+    }
+  }
+
+  HXT_CHECK( group2_sort_v0(edgeKey, numEdgesTotal, n*(n-1)-1) );
+
+  #pragma omp parallel
+  {
+    int threadID = omp_get_thread_num();
+    uint64_t localNum = 0;
+
+    #pragma omp for
+    for (uint64_t i=0; i<numEdgesTotal; i++) {
+      if(edgeKey[i].v[1]%2==0) {
+        if(i!=numEdgesTotal-1 && edgeKey[i].v[0]==edgeKey[i+1].v[0]) {
+        #ifndef NDEBUG
+          if(edgeKey[i+1].v[1]%2==0) {
+            HXT_ERROR_MSG(HXT_STATUS_ERROR, "Duplicated line in mesh->lines (%lu & %lu)\n"
+                                           "\tThis case is not handled in Release mode, FIX IT !!",
+                                           edgeKey[i].v[1]/2, edgeKey[i+1].v[1]/2);
+            exit(EXIT_FAILURE);
+          }
+          else
+        #endif
+          {
+            lines2TriMap[edgeKey[i].v[1]/2] = edgeKey[i+1].v[1]/2;
+          }
+        }
+        else /* the edge is not in a triangle */ {
+          localNum++;
+          lines2TriMap[edgeKey[i].v[1]/2] = HXT_NO_ADJACENT;
+        }
+      }
+    }
+
+    numEdges[threadID] = localNum;
+
+    #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) );
+  HXT_CHECK( hxtAlignedFree(&edgeKey) );
+
+  return HXT_STATUS_OK;
+}
+
+
+/**************************************************************************************
+ *   Lines --> Tetrahedra   MAPPING  (for each line, get 6*tet+e)
+ *************************************************************************************/
+HXTStatus hxtGetLines2TetMap(HXTMesh* mesh, uint64_t* lines2TetMap, uint64_t* missing)
+{
+HXT_ASSERT( lines2TetMap!=NULL );
+HXT_ASSERT( mesh!=NULL );
+
+  const int maxThreads = omp_get_max_threads();
+  const uint64_t n = mesh->vertices.num;
+  HXTStatus status = HXT_STATUS_OK;
+  uint64_t numEdgesTotal;
+
+  HXTGroup2* edgeKey = NULL;
+  uint64_t* numEdges;
+  unsigned char* edgeFlag;
+  HXT_CHECK( hxtMalloc(&numEdges, maxThreads*sizeof(uint64_t)) );
+  HXT_CHECK( hxtAlignedMalloc(&edgeFlag, mesh->tetrahedra.num*sizeof(char)) );
+  memset(edgeFlag, 0, mesh->tetrahedra.num*sizeof(char));
+
+
+  #pragma omp parallel
+  {
+    const int threadID = omp_get_thread_num();
+    uint64_t localNum = 0;
+
+    #pragma omp for schedule(static)
+    for (uint64_t tet=0; tet<mesh->tetrahedra.num; tet++) { // for each tetrahedra
+      for (int edge=0; edge<6; edge++) {
+
+        unsigned in_facet, out_facet;
+        getFacetsFromEdge(edge, &in_facet, &out_facet);
+
+        uint32_t p0, p1;
+        {
+          unsigned n0, n1;
+          getNodesFromEdge(edge, &n0, &n1);
+          p0 = mesh->tetrahedra.node[4*tet + n0];
+          p1 = mesh->tetrahedra.node[4*tet + n1];
+        }
+
+        if(p0==HXT_GHOST_VERTEX || p1==HXT_GHOST_VERTEX)
+          continue;
+
+        int truth = 1;
+
+        uint64_t curTet = tet;
+        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, tet)) {
+            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!=tet);
+
+        if(truth){
+          edgeFlag[tet] |= 1U<<edge;
+          localNum++;
+        }
+      }
+    }
+
+    numEdges[threadID] = localNum;
+
+    #pragma omp barrier
+    #pragma omp single
+    {
+      int nthreads = omp_get_num_threads();
+      numEdgesTotal = mesh->lines.num;
+      for (int i=0; i<nthreads; i++) {
+        uint32_t tsum = numEdgesTotal + numEdges[i];
+        numEdges[i] = numEdgesTotal;
+        numEdgesTotal = tsum;
+      }
+
+#ifndef NDEBUG
+      if(numEdgesTotal>2*mesh->tetrahedra.num+mesh->lines.num){
+        HXT_ERROR_MSG(HXT_STATUS_ERROR,
+                      "There is less than 2 tetrahedra per edge in average,"
+                      "which means the mesh is totally broken !");
+        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 l=0; l<mesh->lines.num; l++) {
+        uint32_t p0 = mesh->lines.node[2*l+0];
+        uint32_t p1 = mesh->lines.node[2*l+1];
+
+        if(p0<p1) {
+          edgeKey[l].v[0] = p0*n + p1;
+          edgeKey[l].v[1] = 2*l;
+        }
+        else if(p0>p1){
+          edgeKey[l].v[0] = p1*n + p0;
+          edgeKey[l].v[1] = 2*l;
+        }
+        else {
+          edgeKey[l].v[0] = p0*n + p0; // the line begins and ends at the same point...
+          edgeKey[l].v[1] = 1;
+          lines2TetMap[l] = HXT_NO_ADJACENT;
+        }
+
+        
+      }
+
+      localNum = numEdges[threadID];
+      #pragma omp for schedule(static)
+      for (uint64_t tet=0; tet<mesh->tetrahedra.num; tet++) {
+        for (unsigned edge=0; edge<6; edge++) {
+          if(edgeFlag[tet] & (1U<<edge)){
+            uint32_t p0, p1;
+            {
+              unsigned n0, n1;
+              getNodesFromEdge(edge, &n0, &n1);
+              p0 = mesh->tetrahedra.node[4*tet + n0];
+              p1 = mesh->tetrahedra.node[4*tet + n1];
+            }
+
+            if(p0<p1){
+              edgeKey[localNum].v[0] = p0*n + p1;
+            }
+            else {
+              edgeKey[localNum].v[0] = p1*n + p0;
+            }
+            edgeKey[localNum].v[1] = 2*(6*tet+edge)+1;
+
+            localNum++;
+          }
+        }
+      }
+    }
+  }
+
+  HXT_CHECK( hxtAlignedFree(&edgeFlag) );
+  HXT_CHECK( status );
+
+  HXT_CHECK( group2_sort_v0(edgeKey, numEdgesTotal, n*(n-1)-1) );
+
+  #pragma omp parallel
+  {
+    const int threadID = omp_get_thread_num();
+    uint64_t localNum = 0;
+
+    #pragma omp for
+    for (uint64_t i=0; i<numEdgesTotal; i++) {
+      if(edgeKey[i].v[1]%2==0) {
+        if(i!=numEdgesTotal-1 && edgeKey[i].v[0]==edgeKey[i+1].v[0]) {
+        #ifndef NDEBUG
+          if(edgeKey[i+1].v[1]%2==0) {
+            HXT_ERROR_MSG(HXT_STATUS_ERROR, "Duplicated line in mesh->lines (%lu & %lu)\n"
+                                           "\tThis case is not handled in Release mode, FIX IT !!",
+                                           edgeKey[i].v[1]/2, edgeKey[i+1].v[1]/2);
+            exit(EXIT_FAILURE);
+          }
+          else
+        #endif
+          {
+            lines2TetMap[edgeKey[i].v[1]/2] = edgeKey[i+1].v[1]/2;
+          }
+        }
+        else {
+          lines2TetMap[edgeKey[i].v[1]/2] = HXT_NO_ADJACENT;
+          localNum++;
+        }
+      }   
+    }
+
+    numEdges[threadID] = localNum;
+
+    #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) );
+  HXT_CHECK( hxtAlignedFree(&edgeKey) );
+  return HXT_STATUS_OK;
+}
+
+
+/**************************************************************************************
+ *   Triangles --> Tetrahedra   MAPPING
+ *************************************************************************************/
+HXTStatus hxtGetTri2TetMap(HXTMesh* mesh, uint64_t* tri2TetMap, uint64_t* missing)
+{
+  HXT_ASSERT(tri2TetMap!=NULL);
+
+  if(mesh->triangles.num==0)
+    return HXT_STATUS_OK;
+
+  const uint64_t n = mesh->vertices.num;
+  const int maxThreads = omp_get_max_threads();
+  HXTStatus status = HXT_STATUS_OK;
+  uint64_t numTrianglesTotal;
+
+
+  HXTGroup2 *triKey = NULL;
+  HXTGroup3* pairKey = NULL;
+  uint64_t *numTriangles;
+  HXT_CHECK( hxtMalloc(&numTriangles, maxThreads*sizeof(uint64_t)) );
+  
+
+#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 <= 2642246){
+    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 <= 2642246){
+        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 <= 2642246){
+        #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] = 2*i;
+          triKey[i].v[0] = v[0]*(n-1)*n + v[1]*n + v[2];
+        }
+      }
+      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] = 2*i;
+          pairKey[i].v[1] = v[0]*(n-1) + v[1];
+          pairKey[i].v[0] = v[2];
+        }
+      }
+
+      // add the triangle from the tetrahedral mesh to the triKey struct array
+      localNum = numTriangles[threadID];
+      if(n <= 2642246){
+        #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] = 2*(4*i+j)+1;
+                triKey[localNum].v[0] = v[0]*(n-1)*n + v[1]*n + v[2]; // max: (n-3)(n-1)n + (n-2)n + (n-1)
+                                                                      //    = (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] = 2*(4*i+j)+1;
+                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];              // max: n-1
+
+                localNum++;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  HXT_CHECK(status);
+
+  if(n <= 2642246){
+    // sort triKey
+    HXT_CHECK( group2_sort_v0(triKey, numTrianglesTotal, (n-2)*(n-1)*n-1) );
+  }
+  else{
+    HXT_CHECK( group3_sort_v0(pairKey, numTrianglesTotal, n-1) );
+    HXT_CHECK( group3_sort_v1(pairKey, numTrianglesTotal, (n-2)*(n-1)-1) );
+  }
+
+
+  #pragma omp parallel
+  {
+    const int threadID = omp_get_thread_num();
+    uint64_t localNum = 0;
+
+    if(n <= 2642246){
+      #pragma omp for
+      for (uint64_t i=0; i<numTrianglesTotal; i++) {
+        if(triKey[i].v[1]%2==0) {
+          if(i!=numTrianglesTotal-1 && triKey[i].v[0]==triKey[i+1].v[0]) {
+          #ifndef NDEBUG
+            if(triKey[i+1].v[1]%2==0) {
+              HXT_ERROR_MSG(HXT_STATUS_ERROR, "Duplicated triangle in mesh->triangles (%lu & %lu)\n"
+                                             "\tThis case is not handled in Release mode, FIX IT !!",
+                                             triKey[i].v[1]/2, triKey[i+1].v[1]/2);
+              exit(EXIT_FAILURE);
+            }
+            else
+          #endif
+            {
+              tri2TetMap[triKey[i].v[1]/2] = triKey[i+1].v[1]/2;
+            }
+          }
+          else /* the triangle is missing */ {
+            localNum++;
+            tri2TetMap[triKey[i].v[1]/2] = HXT_NO_ADJACENT;
+          }
+        }
+      }
+    }
+    else{
+      #pragma omp for
+      for (uint64_t i=0; i<numTrianglesTotal; i++) {
+        if(pairKey[i].v[2]%2==0) {
+          if(i!=numTrianglesTotal-1 && pairKey[i].v[0]==pairKey[i+1].v[0] && pairKey[i].v[1]==pairKey[i+1].v[1]) {
+          #ifndef NDEBUG
+            if(pairKey[i+1].v[2]%2==0) {
+              HXT_ERROR_MSG(HXT_STATUS_ERROR, "Duplicated triangle in mesh->triangles (%lu & %lu)\n"
+                                             "\tThis case is not handled in Release mode, FIX IT !!",
+                                             pairKey[i].v[2]/2, pairKey[i+1].v[2]/2);
+              exit(EXIT_FAILURE);
+            }
+            else
+          #endif
+            {
+              tri2TetMap[pairKey[i].v[2]/2] = pairKey[i+1].v[2]/2;
+            }
+          }
+          else /* the triangle is missing */ {
+            localNum++;
+            tri2TetMap[pairKey[i].v[2]/2] = HXT_NO_ADJACENT;
+          }
+        }
+      }
+    }
+
+    numTriangles[threadID] = localNum;
+
+    #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) );
+  HXT_CHECK( hxtAlignedFree(&triKey) );
+  HXT_CHECK( hxtAlignedFree(&pairKey) );
+
+  return HXT_STATUS_OK;
+}
+
+
+/**************************************************************************************
+ *   Constrain facets of tetrahedron if it is in tri2TetMap
+ *************************************************************************************/
+HXTStatus hxtConstrainTriangles(HXTMesh* mesh, uint64_t* tri2TetMap)
+{
+  HXT_ASSERT(tri2TetMap!=NULL);
+  HXT_ASSERT(mesh!=NULL);
+#ifdef DEBUG
+  for (uint64_t i=0; i<mesh->triangles.num; i++) {
+    if(tri2TetMap[i]==HXT_NO_ADJACENT)
+      return HXT_ERROR_MSG(HXT_STATUS_ERROR, "There are missing mappings in tri2TetMap");
+  }
+#endif
+
+  char* faceFlag;
+  HXT_CHECK( hxtAlignedMalloc(&faceFlag, 4*mesh->tetrahedra.num*sizeof(char)) );
+  memset(faceFlag, 0, 4*mesh->tetrahedra.num*sizeof(char));
+
+  // fill faceFlag
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->triangles.num; i++) {
+    faceFlag[tri2TetMap[i]] = 1;
+    faceFlag[mesh->tetrahedra.neigh[tri2TetMap[i]]] = 1;
+  }
+
+  // constrain corresponding flag, teetrahedron by tetrahedron to avoid race conditions
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    for (uint64_t j=0; j<4; j++) {
+      if(faceFlag[4*i+j]) {
+        setFacetConstraint(mesh, i, j);
+      }
+    }
+  }
+
+  HXT_CHECK( hxtAlignedFree(&faceFlag) );
+
+  return HXT_STATUS_OK;
+}
+
+
+
+/**************************************************************************************
+ *   Constrain edge of tetrahedron if it is in lines2TetMap but not in lines2TriMap
+ *************************************************************************************/
+HXTStatus hxtConstrainLinesNotInTriangles(HXTMesh* mesh, uint64_t* lines2TetMap, uint64_t* lines2TriMap)
+{
+  HXT_ASSERT(lines2TetMap!=NULL);
+  HXT_ASSERT(lines2TriMap!=NULL);
+  HXT_ASSERT(mesh!=NULL);
+
+#ifdef DEBUG
+  for (uint64_t i=0; i<mesh->lines.num; i++) {
+    if(lines2TetMap[i]==HXT_NO_ADJACENT && mesh->lines.node[2*i]!=mesh->lines.node[2*i+1])
+      return HXT_ERROR_MSG(HXT_STATUS_ERROR, "There are missing mappings in lines2TetMap");
+  }
+#endif
+
+  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=0; i<mesh->lines.num; i++) {
+    if(lines2TriMap[i]==HXT_NO_ADJACENT && lines2TetMap[i]!=HXT_NO_ADJACENT) {
+      // turn around the edge to set edgeFlag of all tetrahedra to 1...
+      uint64_t firstTet = lines2TetMap[i]/6;
+      uint64_t curTet = firstTet;
+      int edge = lines2TetMap[i]%6;
+
+      unsigned in_facet, out_facet;
+      getFacetsFromEdge(edge, &in_facet, &out_facet);
+
+      do
+      {
+        edgeFlag[6*curTet + getEdgeFromFacets(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);
+    }
+  }
+
+  #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])
+        setEdgeConstraint(mesh, i, j);
+    }
+  }
+
+
+  HXT_CHECK( hxtAlignedFree(&edgeFlag) );
+
+  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..a8aaa7ba823f53dfc177d59d7fdcc8dd9a66d40c
--- /dev/null
+++ b/contrib/hxt/hxt_tetFlag.h
@@ -0,0 +1,228 @@
+#ifndef _HXT_TETFLAG_
+#define _HXT_TETFLAG_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hxt_mesh.h"
+
+/* Get a mapping between triangles and tetrahedra
+ * tri2TetMap must be an array of size: `mesh->triangles.num`
+ *
+ * if *nbMissing = 0 at the end of function:
+ *    `tri2TetMap[i] = 4*tet+face | HXT_NO_ADJACENT` , where `4*tet+face`
+ *    is a facet of tetrahedron `tet` that correspond
+ *    to the triangle `i` in mesh->triangles.
+ *    If there is no such tetrahedron (happens only if nbMissing!=0),
+ *    it is set to HXT_NO_ADJACENT.
+ *   
+ *    The tetrahedron on the other side is easily obtainable by doing
+ *    `mesh->tetrahedra.neigh[tri2TetMap[i]]`
+ */
+HXTStatus hxtGetTri2TetMap(HXTMesh* mesh, uint64_t* tri2TetMap, uint64_t* nbMissing);
+
+/* Same as above with lines and triangles: `lines2TriMap = 3*tri+edge | HXT_NO_ADJACENT`
+  (edge is 0,1 or 2, and it correspond to the nodes 0-1, 1-2 and 2-0 of the triangles)
+  Lines that begin and ends at the same point are not reported in nbMissing, but the mapping is
+  set to HXT_NO_ADJACENT nevertheless */
+HXTStatus hxtGetLines2TriMap(HXTMesh* mesh, uint64_t* lines2TriMap, uint64_t* nbMissing);
+
+/* Same as above with lines and tets: `lines2TetMap = 6*tet+edge | HXT_NO_ADJACENT`
+ (edge is a number between 0 & 5, explained below in "ANATOMY OF A TET.")
+   Lines that begin and ends at the same point are not reported in nbMissing, but the mapping is
+  set to HXT_NO_ADJACENT nevertheless */
+HXTStatus hxtGetLines2TetMap(HXTMesh* mesh, uint64_t* lines2TetMap, uint64_t* nbMissing);
+
+
+/* Constrain facets of tetrahedra (set the right tetrahedra.flag)
+ * that corresponding to a triangle.
+ * tri2TetMap is the array that comes from hxtGetTri2TetMap() */
+HXTStatus hxtConstrainTriangles(HXTMesh* mesh, uint64_t* tri2TetMap);
+
+/* Constrain edges of tetrahedra (set the right tetrahedra.flag)
+ * that corresponding to a line that isn't in any triangle.
+ * lines2TetMap is the array that comes from hxtGetLines2TetMap()
+ * lines2TriMap is the array that comes from hxtGetLines2TriMap() */
+HXTStatus hxtConstrainLinesNotInTriangles(HXTMesh* mesh, uint64_t* lines2TetMap, uint64_t* lines2TriMap);
+
+
+/*****************************
+ *  ANATOMY OF A TERAHEDRON  *
+ *****************************
+
+      node
+       0
+       |\`-_
+       | \  `-_                   facet
+       |  \    `-_                 1
+       |   \      `2_              |
+       |    0        `-_          /
+       |     \   facet  `-_   <--'
+       |      \    2       `-_
+       1       \              `-_
+       | facet  node_____4_______=> node
+       |   3    /1              _-'  3
+       |       /             _-'
+       |      /  facet    _-'
+       |     /    0    _-'
+       |    3       _5'
+       |   /     _-'
+       |  /   _-'
+       | / _-'
+       |/-'
+      node
+       2
+
+  facets `i` is the facet that does not contain node `i`
+  edge 0 contain node 0 & 1
+  edge 1 contain node 0 & 2
+  edge 2 contain node 0 & 3
+  edge 3 contain node 1 & 2
+  edge 4 contain node 1 & 3
+  edge 5 contain node 2 & 3
+
+  - - - - - - - - - - - - - - - - - - - - - - - - - - - 
+
+ ********************************
+ *  mesh->tetrahedra.flag[tet]  *
+ ********************************
+ *  flag is a 16-bit number
+ *
+ *  0  edge between facet 0 and facet 1 is constrained
+ *  1  edge betwwen facet 0 and facet 2 is constrained
+ *  2  edge betwwen facet 0 and facet 3 is constrained
+ *  3  edge betwwen facet 1 and facet 2 is constrained
+ *  4  edge betwwen facet 1 and facet 3 is constrained
+ *  5  edge between facet 2 and facet 3 is constrained
+ *  6  the tetrahedron is deleted
+ *  7  the tetrahedron has already been processed (a vertex was already inserted inside it and it failed)
+ *  8  facet 0 is constrained
+ *  9  facet 1 is constrained
+ *  10 facet 2 is constrained
+ *  11 facet 3 is constrained
+ *  12 -unused-
+ *  13 -unused-
+ *  14 -unused-
+ *  15 -unused-
+ */
+
+
+/***************************
+ * combined operations
+ ***************************/
+
+static inline uint16_t isAnyEdgeConstrained(HXTMesh* mesh, uint64_t tet) {
+  return mesh->tetrahedra.flag[tet] & UINT16_C(0x3F);
+}
+
+static inline uint16_t isAnyFacetConstrained(HXTMesh* mesh, uint64_t tet) {
+  return mesh->tetrahedra.flag[tet] & UINT16_C(0xF00);
+}
+
+static inline uint16_t isAnyThingConstrained(HXTMesh* mesh, uint64_t tet) {
+  return mesh->tetrahedra.flag[tet] & UINT16_C(0xF3F);
+}
+
+
+/***************************
+ * edges operations
+ ***************************/
+static inline int getEdgeFromFacets(unsigned facet1, unsigned facet2) {
+  static const int facets2EdgeNum[4][4] = {{-1, 0, 1, 2},
+                                           { 0,-1, 3, 4},
+                                           { 1, 3,-1, 5},
+                                           { 2, 4, 5,-1}};
+  return facets2EdgeNum[facet1][facet2];
+}
+
+static inline void getFacetsFromEdge(int edgeNum, unsigned* facetMin, unsigned* facetMax) {
+  static const unsigned edgeNum2FacetMin[6] = { 0, 0, 0, 1, 1, 2};
+  static const unsigned edgeNum2FacetMax[6] = { 1, 2, 3, 2, 3, 3};
+  *facetMin = edgeNum2FacetMin[edgeNum];
+  *facetMax = edgeNum2FacetMax[edgeNum];
+}
+
+static inline void getNodesFromEdge(int edgeNum, unsigned* nodeMin, unsigned* nodeMax) {
+  getFacetsFromEdge(5-edgeNum, nodeMin, nodeMax);
+}
+
+static inline uint16_t getEdgeConstraint(HXTMesh* mesh, uint64_t tet, int edgeNum) {
+  return mesh->tetrahedra.flag[tet] & (1U<<edgeNum);
+}
+
+static inline void setEdgeConstraint(HXTMesh* mesh, uint64_t tet, int edgeNum) {
+  mesh->tetrahedra.flag[tet] |= (1U<<edgeNum);
+}
+
+static inline void unsetEdgeConstraint(HXTMesh* mesh, uint64_t tet, int edgeNum) {
+  mesh->tetrahedra.flag[tet] &= ~(1U<<edgeNum);
+}
+
+
+/***************************
+ * facets operations
+ ***************************/
+static inline unsigned getNode0FromFacet(unsigned facet) {
+  return (facet+1)&3;
+}
+
+static inline unsigned getNode1FromFacet(unsigned facet) {
+  return (facet+3)&2;
+}
+
+static inline unsigned getNode2FromFacet(unsigned facet) {
+  return (facet&2)^3;
+}
+
+static inline uint16_t getFacetConstraint(HXTMesh* mesh, uint64_t tet, unsigned facet) {
+  return mesh->tetrahedra.flag[tet] & (1U<<(facet+8));
+}
+
+static inline void setFacetConstraint(HXTMesh* mesh, uint64_t tet, unsigned facet) {
+  mesh->tetrahedra.flag[tet] |= (1U<<(facet+8));
+}
+
+static inline void unsetFacetConstraint(HXTMesh* mesh, uint64_t tet, unsigned facet) {
+  mesh->tetrahedra.flag[tet] &= ~(1U<<(facet+8));
+}
+
+
+/***************************
+ * deleted flag operations
+ ***************************/
+static inline uint16_t getDeletedFlag(HXTMesh* mesh, uint64_t tet) {
+  return mesh->tetrahedra.flag[tet] & UINT16_C(0x40);
+}
+
+static inline void setDeletedFlag(HXTMesh* mesh, uint64_t tet) {
+  mesh->tetrahedra.flag[tet] |= UINT16_C(0x40);
+}
+
+static inline void unsetDeletedFlag(HXTMesh* mesh, uint64_t tet) {
+  mesh->tetrahedra.flag[tet] &= ~UINT16_C(0x40);
+}
+
+
+/***************************
+ * processed flag operations
+ ***************************/
+static inline uint16_t getProcessedFlag(HXTMesh* mesh, uint64_t tet) {
+  return mesh->tetrahedra.flag[tet] & UINT16_C(0x80);
+}
+
+static inline void setProcessedFlag(HXTMesh* mesh, uint64_t tet) {
+  mesh->tetrahedra.flag[tet] |= UINT16_C(0x80);
+}
+
+
+static inline void unsetProcessedFlag(HXTMesh* mesh, uint64_t tet) {
+  mesh->tetrahedra.flag[tet] &= ~UINT16_C(0x80);
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/hxt/hxt_tetOpti.c b/contrib/hxt/hxt_tetOpti.c
new file mode 100644
index 0000000000000000000000000000000000000000..c2f85fc5d2cfb98cfae3b34d541bdc7499785f49
--- /dev/null
+++ b/contrib/hxt/hxt_tetOpti.c
@@ -0,0 +1,1617 @@
+#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
+       |\`-_
+       | \  `-_ 
+       |  \    `-_                in_facet
+       |   \      `-_              |
+       |    \   up   `-_          /
+       |     \  facet   `-_   <--'
+our    |      \            `-_
+edge   | out   \              `-_
+-----> | facet  \v_out__________`>v_in
+       |        /               _-'
+       |       /             _-'
+       |      /  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_0_______`>v_1           |     \v_0_______`>v_2           |     \v_0_______`>v_3
+       |     /         _-'              |     /         _-'              |     /         _-'   
+       |    /       _-'                 |    /       _-'                 |    /       _-'      
+       |   /     _-'                    |   /     _-'                    |   /     _-'         
+       |  /   _-'                       |  /   _-'                       |  /   _-'            
+       | / _-'                          | / _-'                          | / _-'               
+       |/-'                             |/-'                             |/-'                  
+      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_1_______`>v_2           |     \v_1_______`>v_0           |     \v_1_______`>v_3
+       |     /         _-'              |     /         _-'              |     /         _-'   
+       |    /       _-'                 |    /       _-'                 |    /       _-'      
+       |   /     _-'                    |   /     _-'                    |   /     _-'         
+       |  /   _-'                       |  /   _-'                       |  /   _-'            
+       | / _-'                          | / _-'                          | / _-'               
+       |/-'                             |/-'                             |/-'                  
+      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_2_______`>v_0           |     \v_2_______`>v_1
+       |     /         _-'              |     /         _-'              |     /         _-'   
+       |    /       _-'                 |    /       _-'                 |    /       _-'      
+       |   /     _-'                    |   /     _-'                    |   /     _-'         
+       |  /   _-'                       |  /   _-'                       |  /   _-'            
+       | / _-'                          | / _-'                          | / _-'               
+       |/-'                             |/-'                             |/-'                  
+      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_2_______`>v_1           |     \v_3_______`>v_0           |     \v_3_______`>v_2
+       |     /         _-'              |     /         _-'              |     /         _-'   
+       |    /       _-'                 |    /       _-'                 |    /       _-'      
+       |   /     _-'                    |   /     _-'                    |   /     _-'         
+       |  /   _-'                       |  /   _-'                       |  /   _-'            
+       | / _-'                          | / _-'                          | / _-'               
+       |/-'                             |/-'                             |/-'                  
+      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 unsigned num_triangles;             /* number of different triangles                       */
+  const unsigned 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] = {
+  {0},{0},{0},
+  {
+    // 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;
+    // setDeletedFlag(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 (uint64_t 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(getFacetConstraint(mesh, 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((getFacetConstraint(mesh, tet_0, out_facet_0)!=0)==(getFacetConstraint(mesh, 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] = ((getFacetConstraint(mesh, tet_0, in_facet_0)!=0)<<12) +
+//                           ((getFacetConstraint(mesh, 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] = ((getFacetConstraint(mesh, tet_0, DOWN_FACET(in_facet_0, out_facet_0))!=0)<<12) +
+//                           ((getFacetConstraint(mesh, 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] = ((getFacetConstraint(mesh, tet_0, UP_FACET(in_facet_0, out_facet_0))!=0)<<12) +
+//                           ((getFacetConstraint(mesh, 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,
+                                        unsigned in_facet, unsigned 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(getEdgeConstraint(mesh, badTet, getEdgeFromFacets(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;
+
+    {
+      unsigned up_facet = UP_FACET(in_facet, out_facet);
+      unsigned down_facet = DOWN_FACET(in_facet, out_facet);
+
+      // add the neighbor up and down
+      local->cavity.neigh_up[local->cavity.num] = mesh->tetrahedra.neigh[4*curTet + up_facet];
+      local->cavity.neigh_down[local->cavity.num] = mesh->tetrahedra.neigh[4*curTet + down_facet];
+
+      int upDownEdge = getEdgeFromFacets(up_facet, down_facet);
+      int upOutEdge = getEdgeFromFacets(up_facet, out_facet);
+      int upInEdge = getEdgeFromFacets(up_facet, in_facet);
+      int downOutEdge = getEdgeFromFacets(down_facet, out_facet);
+      int downInEdge = getEdgeFromFacets(down_facet, in_facet);
+
+
+      // TODO: just store one flag for up and down. the one of the default tetrahedron
+      local->cavity.flag[local->cavity.num] = (getFacetConstraint(mesh, curTet, up_facet)!=0) +
+                                              ((getEdgeConstraint(mesh, curTet, upOutEdge)!=0)<<1) +
+                                              ((getEdgeConstraint(mesh, curTet, upDownEdge)!=0)<<2) +
+                                              ((getEdgeConstraint(mesh, curTet, upInEdge)!=0)<<3) +
+                                              ((getFacetConstraint(mesh, curTet, down_facet)!=0)<<4) +
+                                              ((getEdgeConstraint(mesh, curTet, downOutEdge)!=0)<<5) +
+                                              ((getEdgeConstraint(mesh, curTet, downInEdge)!=0)<<6) +
+                                              ((getEdgeConstraint(mesh, curTet, upDownEdge)!=0)<<7);
+    }
+    // add the annulus vertex
+    uint32_t oldV = mesh->tetrahedra.node[4*curTet + out_facet];
+    uint32_t newV = mesh->tetrahedra.node[4*curTet + in_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
+      || (getFacetConstraint(mesh, neigh/4, neigh%4)!=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,
+                             unsigned in_facet, unsigned out_facet)
+{
+  HXT_CHECK( buildEdgeCavity(mesh, local, badTet, 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 unsigned 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 (unsigned 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 (unsigned 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 (unsigned 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
+    // setDeletedFlag(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 (unsigned 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]&1)<<8 |// face (bit 0) is the up_facet => 0  (bit 8)
+                                            (local->cavity.flag[-n0-1]&2)>>1 |// first edge (bit 1) was between up_facet and out_facet => 0-1  (bit 0)
+                                            (local->cavity.flag[-n0-1]&4)>>1 |// second edge (bit 2) was between up_facet and down_facet => 0-2 (bit 1)
+                                            (local->cavity.flag[-n0-1]&8)>>1; // third edge (bit 3) was between up_facet and in_facet => 0-3    (bit 2)
+
+        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]&1)<<9 |// face (bit 0) is the up_facet => 1  (bit 9)
+                                            (local->cavity.flag[-n1-1]&2)<<3 |// first edge (bit 1) was between up_facet and out_facet => 1-3   (bit 4)
+                                            (local->cavity.flag[-n1-1]&4)<<1 |// second edge (bit 2) was between up_facet and down_facet => 1-2 (bit 3)
+                                            (local->cavity.flag[-n1-1]&8)>>3; // third edge (bit 3) was between up_facet and in_facet => 0-1    (bit 0)
+
+        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)<<11 |// face (bit 0) is the up_facet => 3  (bit 11)
+                                            (local->cavity.flag[-n2-1]&2)<<1  |// first edge (bit 1) was between up_facet and out_facet => 0-3   (bit 2)
+                                            (local->cavity.flag[-n2-1]&4)<<3  |// second edge (bit 2) was between up_facet and down_facet => 2-3 (bit 5)
+                                            (local->cavity.flag[-n2-1]&8)<<1;  // third edge (bit 3) was between up_facet and in_facet => 1-3    (bit 4)
+
+        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]&16)<<4 |// face (bit 4) is the down_facet => 0  (bit 8)
+                                              (local->cavity.flag[-n0-1]&32)>>5 |// first edge (bit 5) was between down_facet and out_facet => 0-1   (bit 0)
+                                              (local->cavity.flag[-n0-1]&64)>>5 |// second edge (bit 6) was between down_facet and in_facet => 0-2   (bit 1)
+                                              (local->cavity.flag[-n0-1]&128)>>5;// third edge (bit 7) was between down_facet and up_facet => 0-3    (bit 2)
+
+        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]&16)<<5 |// face (bit 4) is the down_facet => 1  (bit 9)
+                                              (local->cavity.flag[-n1-1]&32)>>2 |// first edge (bit 5) was between down_facet and out_facet => 1-2   (bit 3)
+                                              (local->cavity.flag[-n1-1]&64)>>6 |// second edge (bit 6) was between down_facet and in_facet => 0-1   (bit 0)
+                                              (local->cavity.flag[-n1-1]&128)>>3;// third edge (bit 7) was between down_facet and up_facet => 1-3    (bit 4)
+
+        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]&16)<<6 |// face (bit 4) is the down_facet => 2*4  (bit 10)
+                                              (local->cavity.flag[-n2-1]&32)>>4 |// first edge (bit 5) was between down_facet and out_facet => 0-2   (bit 1)
+                                              (local->cavity.flag[-n2-1]&64)>>3 |// second edge (bit 6) was between down_facet and in_facet => 1-2   (bit 3)
+                                              (local->cavity.flag[-n2-1]&128)>>2;// third edge (bit 7) was between down_facet and up_facet => 2-3    (bit 5)
+
+        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 startFace,
+                                          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[startFace];
+  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++] = startFace;
+  setDeletedFlag(mesh, startFace/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(getDeletedFlag(mesh, neighTet))
+        continue;
+
+      if(getFacetConstraint(mesh, curTet, f))
+        return HXT_STATUS_INTERNAL;
+
+      for(unsigned k=0; k<4; k++){
+        if(k!=f && getEdgeConstraint(mesh, curTet, getEdgeFromFacets(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;
+
+      setDeletedFlag(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++) {
+    unsetDeletedFlag(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,
+                                int maxThreads,
+                                double minSize,
+                                double qualityThreshold,
+                                uint32_t numVerticesConstrained){
+  ThreadLocal* locals = NULL;
+  ThreadShared* shared = NULL;
+  volatile HXTStatus globalStatus = HXT_STATUS_OK;
+  uint32_t seed = 1;
+  uint32_t nbits = 0;
+  int changePartitions = 1;
+
+  if(maxThreads<0)
+    maxThreads = omp_get_num_procs();
+  else if(maxThreads==0)
+    maxThreads = omp_get_max_threads();
+
+  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 j=0; j<4; j++) {
+          if(neighs[j]==HXT_NO_ADJACENT ||
+            mesh->tetrahedra.colors[neighs[j]/4]!=color) {
+            qual[j]=DBL_MAX;
+          }
+          else
+            qual[j] = shared->quality2.values[neighs[j]/4];
+        }
+
+        unsigned 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 (uint64_t i=0; i<locals[threadID].deleted.num; i++) {
+      uint64_t delTet = locals[threadID].deleted.tetID[i];
+      setDeletedFlag(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..32350cda57ab91e57f2cf365267f285223c15dfb
--- /dev/null
+++ b/contrib/hxt/hxt_tetOpti.h
@@ -0,0 +1,7 @@
+#ifndef HXT_MESH_H_
+#define HXT_MESH_H_
+#include "hxt_api.h"
+#include "hxt_mesh.h"
+#include "hxt_bbox.h"
+HXTStatus hxtOptimizeTetrahedra(HXTMesh *mesh, HXTBbox* bbox, int maxThreads, 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..427c4f683d826a29c2f619bc805df84882e85abe
--- /dev/null
+++ b/contrib/hxt/hxt_tetPostpro.c
@@ -0,0 +1,182 @@
+#include "predicates.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) {
+      setDeletedFlag(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)
+        setDeletedFlag(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) {
+      setDeletedFlag(mesh, i);
+    }
+    else {
+      for (int j=0; j<4; j++) {
+        if(mesh->vertices.coord[4*mesh->tetrahedra.node[4*i+j]+3]<0.0){
+          setDeletedFlag(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..7ccea71216be8d4f9659fc936d61151b48459014
--- /dev/null
+++ b/contrib/hxt/hxt_tetRepair.c
@@ -0,0 +1,539 @@
+#include "hxt_tetRepair.h"
+#include "hxt_tetUtils.h"
+#include "hxt_tetFlag.h"
+#include "hxt_vertices.h"
+#include "predicates.h"
+#include "hxt_sort.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=1; i<nTet*4; i++) {
+      if(triplet[i-1].v[0]==triplet[i].v[0] &&
+         triplet[i-1].v[1]==triplet[i].v[1])
+      {
+        mesh->tetrahedra.neigh[triplet[i-1].v[2]] = triplet[i].v[2];
+        mesh->tetrahedra.neigh[triplet[i].v[2]] = triplet[i-1].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=1; i<nTet*4; i++) {
+      if(pair[i-1].v[0]==pair[i].v[0])
+      {
+        mesh->tetrahedra.neigh[pair[i-1].v[1]] = pair[i].v[1];
+        mesh->tetrahedra.neigh[pair[i].v[1]] = pair[i-1].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(getDeletedFlag(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++)
+    {
+      if(Neigh[j]==HXT_NO_ADJACENT){
+        continue;
+      }
+
+      uint64_t neigh = Neigh[j]/4;
+      unsigned face = Neigh[j]%4;
+
+      if(neigh>=mesh->tetrahedra.num) {
+        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;
+      
+      if(mesh->tetrahedra.neigh[4*neigh+face]!=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+face);
+        errorOccured=1;
+        continue;
+      }
+
+      uint32_t V[3] = { Node[((j+1)&3)], Node[((j+3)&2)], Node[((j&2)^3)]};
+      unsigned l;
+
+      for (l=0; l<3; l++)
+      {
+        if(NeighNode[((face+1)&3)]==V[l] && NeighNode[((face+3)&2)]==V[(l+1)%3] && NeighNode[((face&2)^3)]==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[((face+1)&3)]==V[l] && NeighNode[((face&2)^3)]==V[(l+1)%3] && NeighNode[((face+3)&2)]==V[(l+2)%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((getFacetConstraint(mesh, i,j)!=0) ^ (getFacetConstraint(mesh, neigh, face)!=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[face]!=HXT_GHOST_VERTEX && insphere(vertices[Node[0]].coord,
+      //             vertices[Node[1]].coord,
+      //             vertices[Node[2]].coord,
+      //             vertices[Node[3]].coord,
+      //             vertices[NeighNode[face]].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[face]);
+      //   errorOccured=1;
+      //   continue;
+      // }
+    }
+  }
+
+  if(errorOccured)
+    return HXT_STATUS_ERROR;
+  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){
+      setDeletedFlag(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(getFacetConstraint(mesh, i, j))
+              setFacetConstraint(mesh, 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_tetRepair.h b/contrib/hxt/hxt_tetRepair.h
new file mode 100644
index 0000000000000000000000000000000000000000..0ae279a9955bdaefeac508f9fdf729135a184c40
--- /dev/null
+++ b/contrib/hxt/hxt_tetRepair.h
@@ -0,0 +1,46 @@
+#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);
+
+/** 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_tetUtils.c b/contrib/hxt/hxt_tetUtils.c
new file mode 100644
index 0000000000000000000000000000000000000000..39bd940407a03a30398ccaf9353a9014fda1821d
--- /dev/null
+++ b/contrib/hxt/hxt_tetUtils.c
@@ -0,0 +1,55 @@
+#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(getDeletedFlag(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 && getDeletedFlag(mesh, right)) right--;
+    while(left < right && getDeletedFlag(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 && getDeletedFlag(mesh, left)==0) left++;
+
+  mesh->tetrahedra.num = left;
+
+  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..d637df37a3fb60d04d458ea85ef9b421c56c3444
--- /dev/null
+++ b/contrib/hxt/hxt_tetUtils.h
@@ -0,0 +1,49 @@
+#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);
+
+
+#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..3320d5915d9a5c758a7a63571ef44e82f034a773
--- /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 acxad0 = ac[1]*ad[2] - ac[2]*ad[1];
+  double adxab0 = ad[1]*ab[2] - ad[2]*ab[1];
+  double abxac0 = ab[1]*ac[2] - ab[2]*ac[1];
+  double volume6 = ab[0]*acxad0 + ac[0]*adxab0 + ad[0]*abxac0;
+ 
+   // abort as early as possible
+  if(volume6<=0.0)
+    return 0.0;
+ 
+  double acxad1 = ac[2]*ad[0] - ac[0]*ad[2];
+  double acxad2 = ac[0]*ad[1] - ac[1]*ad[0];
+ 
+  double adxab1 = ad[2]*ab[0] - ad[0]*ab[2];
+  double adxab2 = ad[0]*ab[1] - ad[1]*ab[0];
+ 
+  double abxac1 = ab[2]*ac[0] - ab[0]*ac[2];
+  double abxac2 = ab[0]*ac[1] - ab[1]*ac[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 bcxcd0 = bc[1]*cd[2] - bc[2]*cd[1]; // = acxad0+abxac0+adxab0;
+  double bcxcd1 = bc[2]*cd[0] - bc[0]*cd[2]; // = acxad1+abxac1+adxab1;
+  double bcxcd2 = bc[0]*cd[1] - bc[1]*cd[0]; // = acxad2+abxac2+adxab2;
+
+  double areaSum = sqrt(acxad0*acxad0 + acxad1*acxad1 + acxad2*acxad2)
+                 + sqrt(adxab0*adxab0 + adxab1*adxab1 + adxab2*adxab2)
+                 + sqrt(abxac0*abxac0 + abxac1*abxac1 + abxac2*abxac2)
+                 + sqrt(bcxcd0*bcxcd0 + bcxcd1*bcxcd1 + bcxcd2*bcxcd2);
+
+  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
new file mode 100644
index 0000000000000000000000000000000000000000..d90368f3d88d33dbd98269c59744777da9698614
--- /dev/null
+++ b/contrib/hxt/hxt_tetrahedra.c
@@ -0,0 +1,1872 @@
+#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 DELETED_BUFFER_SIZE 8182
+// #define HXT_DELAUNAY_LOW_MEMORY /* doesn't use any buffer (a lot slower, except if you are at the limit of filling the RAM) */
+
+/* usefull macros */
+#define ABS(x) ((x) >= 0 ? (x) : -(x))
+#define MAX(x,y) ((x)>(y) ? (x) : (y))
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+
+#define HXT_OMP_CHECK(status) do{ HXTStatus _tmp_ = (status); \
+    if(_tmp_<0){ \
+      if(_tmp_>HXT_STATUS_INTERNAL) \
+        HXT_TRACE_MSG(_tmp_, "cannot break OpenMP region -> exiting"); \
+      fflush(stdout); fflush(stderr); \
+      exit(_tmp_); \
+    } \
+  }while(0)
+
+
+typedef struct{
+  uint32_t hxtDeclareAligned node[3];
+  uint16_t flag;
+  uint64_t neigh; // the tet on the other side of the boundar
+} cavityBnd_t;
+
+typedef struct {
+#ifndef HXT_DELAUNAY_LOW_MEMORY
+  uint64_t hxtDeclareAligned Map[1024];
+#endif
+
+  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;
+
+
+/***********************************
+ * create the initial tetrahedron 
+ * surrounded by 4 ghost tetrahedra
+ ***********************************/
+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){
+    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;
+
+  uint32_t i=0, j=1, k=2, l=3;
+  for (i=0; orientation==0.0 && i<nToInsert-3; i++)
+  {
+    for (j=i+1; orientation==0.0 && j<nToInsert-2; j++)
+    {
+      for (k=j+1; orientation==0.0 && k<nToInsert-1; k++)
+      {
+        for (l=k+1; orientation==0.0 && l<nToInsert; l++)
+        {
+          orientation = orient3d(vertices[nodeInfo[i].node].coord,
+                                 vertices[nodeInfo[j].node].coord,
+                                 vertices[nodeInfo[k].node].coord,
+                                 vertices[nodeInfo[l].node].coord);
+        }
+      }
+    }
+  }
+  l--; k--; j--; i--;
+
+
+  if(orientation==0.0){
+    return HXT_ERROR_MSG(HXT_STATUS_FAILED, "all vertices are coplanar");
+  }
+
+  // swap 0<->i  1<->j 2<->k 3<->l
+  {
+    hxtNodeInfo tmp = nodeInfo[i];
+    nodeInfo[i] = nodeInfo[0];
+    nodeInfo[0] = tmp;
+    nodeInfo[0].status = HXT_STATUS_TRUE;
+    i = 0;
+
+    tmp = nodeInfo[j];
+    nodeInfo[j] = nodeInfo[1];
+    nodeInfo[1] = tmp;
+    nodeInfo[1].status = HXT_STATUS_TRUE;
+    j = 1;
+
+    tmp = nodeInfo[k];
+    nodeInfo[k] = nodeInfo[2];
+    nodeInfo[2] = tmp;
+    nodeInfo[2].status = HXT_STATUS_TRUE;
+    k = 2;
+
+    tmp = nodeInfo[l];
+    nodeInfo[l] = nodeInfo[3];
+    nodeInfo[3] = tmp;
+    nodeInfo[3].status = HXT_STATUS_TRUE;
+    l = 3;
+  }
+
+
+  if(orientation < 0.0){
+    uint32_t tmp = i;
+    i = j;
+    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[ 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[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.num = 5;
+
+  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 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=0;
+  passes[0] = nToInsert;
+
+  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;
+  }
+
+  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->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 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 = hxtTetrahedraDoubleSize(mesh);
+    }
+  } // implicit barrier here
+
+  if(status!=HXT_STATUS_OK)
+    HXT_TRACE(status);
+
+  return status;
+}
+
+
+// pragma atomic capture to get tetrahedra.num and update it at the same time before caling this function !
+static inline HXTStatus reserveNewTet(HXTMesh* mesh){
+  if(mesh->tetrahedra.num > mesh->tetrahedra.size){
+    HXT_CHECK( synchronizeReallocation(mesh, NULL, NULL) );
+  }
+
+  return HXT_STATUS_OK;
+}
+
+static inline HXTStatus reserveNewDeleted(TetLocal* 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 reserveNewBnd(TetLocal* local, uint64_t 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;
+}
+/***********************************************/
+
+/************************************
+ * 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
+  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;
+
+  return HXT_STATUS_OK;
+
+}
+
+
+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;
+}
+
+/* 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)
+{
+  double *vtaCoord = mesh->vertices.coord + 4*vta;
+  double vtaNodalSize = nodalSizes[vta];
+
+  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;
+        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;
+
+  double *vtaCoord = vertices[vta].coord;
+  double vtaNodalSize = nodalSizes[vta];
+
+  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;
+}
+
+
+/* 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]);
+
+  local->deleted.num = prevDeleted;
+}
+
+
+/***********************************
+ * 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};
+
+  // 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]) {
+
+        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;
+}
+
+
+/* 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;
+
+  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;
+
+  if(Node[3]==HXT_GHOST_VERTEX){ 
+    double det = orient3d(a,b,c,e);
+
+    if(det!=0.0){
+      return det;
+    }
+
+    // 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;
+  }
+
+  double* const __restrict__ d = vertices[Node[3]].coord;
+
+  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;
+
+  double* const vtaCoord = vertices[vta].coord;
+  unsigned enteringFace = 4;
+
+#ifndef NDEBUG
+  uint64_t TotalCount = 0;
+#endif
+  
+
+  while(1){
+    const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + 4*nextTet;
+    const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + 4*nextTet;
+
+  #ifndef NDEBUG
+    if(curNode[3]==HXT_GHOST_VERTEX){
+      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "walked outside of the domain");
+    }
+  #endif
+
+    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]);
+          }
+
+          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;
+        }
+      }
+    }
+
+    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;
+    }
+
+    //    printf("nextTet %u %g %u %u\n",nextTet,Min, count, neigh);
+    nextTet = curNeigh[neigh]/4;
+    enteringFace = curNeigh[neigh]&3;
+
+  #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
+  }
+}
+
+
+/***********************************
+ * digging cavity
+ ***********************************/
+
+/* 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++;
+}
+
+/* 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;
+}
+
+/* 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 (uint64_t i=0; i<local->ball.num; i++) {
+    if(local->ball.bnd[i].node[2]==HXT_GHOST_VERTEX){
+
+    }
+    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;
+}
+
+
+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;
+
+#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
+
+  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(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);
+
+  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);
+
+  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);
+
+  return HXT_STATUS_OK;
+}
+
+
+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;
+}
+
+
+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");
+
+  // 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;
+  }
+
+  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];
+
+            // 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(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;
+            }
+
+            // 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);
+
+          } while (curTet!=delTet);
+
+          if(!edgeIsSafe) { // we must find a tetrahedron on the opposite side of vta and delete it.
+            in_facet = j;
+            out_facet = k;
+            curTet = delTet;
+
+            uint64_t tetContainingVta = local->deleted.tetID[prevDeleted];
+            uint64_t tetToUndelete = HXT_NO_ADJACENT;
+            double distMax = 0.0;
+            double* vtaCoord = mesh->vertices.coord + 4*vta;
+
+          #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];
+
+            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");
+            }
+          #endif
+
+            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;
+
+              uint32_t* nodes = mesh->tetrahedra.node + 4*curTet;
+              for (out_facet=0; out_facet<3; out_facet++)
+                if(nodes[out_facet]==newV)
+                  break;
+
+              double* coord1 = mesh->vertices.coord + newV;
+              double* coord2 = mesh->vertices.coord + nodes[in_facet];
+
+              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;
+                }
+
+                if(dist>distMax) {
+                  dist = distMax;
+                  tetToUndelete = curTet;
+                }
+              }
+            } while (curTet!=delTet);
+
+            if(tetToUndelete==delTet)
+              exist = 0;
+
+            // printf("undeleting tetrahedron %lu\n", tetToUndelete);
+            mesh->tetrahedra.colors[tetToUndelete] = color;
+            HXT_CHECK( undeleteTetrahedron(local, mesh, vta, tetToUndelete) );
+          }
+        }
+      }
+    }
+  }
+
+  for (uint64_t i=prevDeleted; i<local->deleted.num; i++) {
+    uint64_t delTet = local->deleted.tetID[i];
+    mesh->tetrahedra.colors[delTet] = color;
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+/* 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;
+
+  
+
+  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;
+
+    *edgeConstraint += isAnyEdgeConstrained(mesh, curTet)!=0;
+
+    /* 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) );
+
+    // we unrolled the loop for speed (also because indices are not trivial, we would need a 4X4 array)
+
+    /* 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]);
+      }
+      else{
+        HXT_CHECK( deletedPush(mesh, local, neigh) );
+      }
+    }
+
+    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]);
+      }
+      else{
+        HXT_CHECK( deletedPush(mesh, local, neigh) );
+      }
+    }
+
+    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) );
+      }
+    }
+
+    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;
+
+#ifndef NDEBUG
+  int ghost_is_there = 0;
+#endif
+
+HXT_ASSERT(((size_t) bnd)%SIMD_ALIGN==0);
+HXT_ASSERT(((size_t) verticesID)%SIMD_ALIGN==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;
+    }
+  }
+
+  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]];
+
+    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]];
+    }
+
+  }
+
+  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
+
+  #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;
+  }
+
+  #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){
+
+  uint64_t tlength = 0;
+  const uint64_t middle = blength*3/2; // 3N
+
+  // 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};
+
+  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;
+
+    // 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]];
+
+      // 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;
+
+  uint64_t start = clength - blength;
+
+  // #pragma vector aligned
+  #pragma omp simd
+  for (uint64_t i=0; i<blength; i++)
+  {
+
+    __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;
+
+  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;
+
+  HXT_CHECK( walking2Cavity(mesh, local, curTet, vta) );
+
+  if(nodalSizes!=NULL && filterTet(mesh, nodalSizes, *curTet, vta)){
+    return HXT_STATUS_FALSE;
+  }
+
+  const uint16_t color = mesh->tetrahedra.colors[*curTet];
+  int edgeConstraint = 0;
+  HXTStatus status = diggingACavity(mesh, local, *curTet, vta, &edgeConstraint);
+
+  if(status==HXT_STATUS_INTERNAL){
+    restoreDeleted(mesh, local, prevDeleted, color);
+    return HXT_STATUS_TRYAGAIN;
+  }
+  else{
+    HXT_CHECK(status);
+  }
+
+  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;
+  // }
+
+  // reshape the cavity if it is not star shaped
+  if(!perfectlyDelaunay)
+    HXT_CHECK( reshapeCavityIfNeeded(local, mesh, vta, prevDeleted) );
+
+  if(nodalSizes!=NULL && filterCavity(local, mesh, nodalSizes, vta)) {
+    restoreDeleted(mesh, local, prevDeleted, color);
+    return HXT_STATUS_FALSE;
+  }
+
+
+  if(local->ball.num > local->deleted.num){
+    uint64_t needed = MAX(DELETED_BUFFER_SIZE,local->ball.num)-local->deleted.num;
+
+    uint64_t ntet;
+
+    #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);
+    }
+
+    local->deleted.num+=needed;
+  }
+
+  HXT_CHECK( fillingACavity(mesh, local, verticesID, curTet, vta, color) );
+
+  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;
+  
+
+  /******************************************************
+          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;
+
+    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) );
+    }
+
+    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) );
+
+    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;
+      }
+
+      for (unsigned i=options->numVerticesInMesh < SMALLEST_ROUND; i<npasses; i++) {
+        HXT_CHECK( hxtNodeInfoSort(nodeInfo+passes[i], passes[i+1]-passes[i], nbits) );
+      }
+
+      const uint32_t nodalMin = mesh->vertices.num - nToInsert;
+      double* sizesToInsert = options->nodalSizes + nodalMin;
+
+      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) );
+    }
+  }
+
+  /******************************************************
+        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;
+    }
+    nthreads = MIN(nthreads, 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=(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) );
+      }
+      
+
+      #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);
+      }
+
+      /******************************************************
+      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;
+    }
+  }
+
+  /******************************************************
+                  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;
+      }
+    }
+  }
+  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) );
+
+  /***************************************************************
+    if reordering allowed, remove vertices we could not insert
+  ***************************************************************/
+  if(!noReordering && totalNumSkipped!=0){
+    /* remove deleted vertices and change tetrahedra.node accordingly */
+
+    uint32_t* numInserted;
+    HXT_CHECK( hxtAlignedMalloc(&numInserted, omp_get_max_threads()*sizeof(uint32_t)) );
+
+    uint32_t firstShifted = mesh->vertices.num - nToInsert;
+    uint32_t n = nToInsert;
+
+    // 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
+
+      #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
+
+      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 (int i=0; i<threadID; i++) {
+        start+=numInserted[i];
+      }
+      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++;
+
+        // 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;
+      }
+
+      // 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;
+      }
+    }
+
+    HXT_CHECK( hxtAlignedFree(&numInserted) );
+
+    // 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];
+    }
+
+    if(options->verbosity>1)
+      HXT_INFO("%u vertices removed (vertices not inserted in the mesh are removed when using hxtDelaunay)\n", totalNumSkipped);
+
+    mesh->vertices.num = mesh->vertices.num - totalNumSkipped;
+  }
+
+  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);
+
+  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;
+}
+
+
+/*****************************************
+ * complete the HXTDelaunayOptions struct
+ * when there are missing fields.
+ ****************************************/
+static HXTStatus DelaunayOptionsInit(HXTMesh* mesh,
+                                HXTDelaunayOptions* userOptions,
+                                HXTDelaunayOptions* options,
+                                HXTBbox* bbox){
+HXT_ASSERT(mesh!=NULL);
+
+  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;
+
+    // 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;
+
+    // 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;
+    }
+
+    #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;
+    }
+
+    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(options->numVerticesInMesh <= mesh->vertices.num);
+
+  if(options->bbox==NULL){
+    options->bbox = bbox;
+    hxtBboxInit(bbox);
+    HXT_CHECK( hxtBboxAdd(bbox, mesh->vertices.coord, mesh->vertices.num) );
+  }
+
+  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();
+
+  // 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]);
+
+  return HXT_STATUS_OK;
+}
+
+
+/*****************************************
+ * 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 uint32_t nToInsert = mesh->vertices.num - options.numVerticesInMesh;
+
+  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;
+  }
+
+  HXT_CHECK( parallelDelaunay3D(mesh, &options, nodeInfo, nToInsert, 0) );
+
+  HXT_CHECK( hxtAlignedFree(&nodeInfo) );
+
+  return HXT_STATUS_OK;
+}
+
+
+/************************************************
+ * 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);
+
+  HXTDelaunayOptions options;
+  HXTBbox bbox;
+  HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) );
+
+  if(options.reproducible && nToInsert<2048) // not worth launching threads and having to reorder tets after...
+    options.delaunayThreads = 1;
+
+HXT_ASSERT(options.numVerticesInMesh+nToInsert <= mesh->vertices.num);
+
+  HXT_CHECK( parallelDelaunay3D(mesh, &options, nodeInfo, nToInsert, 1) );
+
+  return HXT_STATUS_OK;
+}
+
diff --git a/contrib/hxt/hxt_tetrahedra.h b/contrib/hxt/hxt_tetrahedra.h
new file mode 100644
index 0000000000000000000000000000000000000000..5511dd5b9359c3d760330006935156493e4e888c
--- /dev/null
+++ b/contrib/hxt/hxt_tetrahedra.h
@@ -0,0 +1,113 @@
+#ifndef _HXT_TETRAHEDRA_
+#define _HXT_TETRAHEDRA_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hxt_mesh.h"
+#include "hxt_vertices.h"
+
+/**
+* \file tetrahedra.h Delaunay tetrahedrization
+* \author Célestin Marot
+*/
+
+/**
+ * \struct HXTDelaunayOptions
+ * 
+ * Options for the Delaunay functions hxtDelaunay() and hxtDelaunaySteadyVertices()
+ * 
+ *
+ */
+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;
+
+
+/**
+ * \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
+ *
+ * \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);
+
+
+/**
+ * \brief Delaunay of a set of vertices
+ * \details This perform the insertion of the vertices
+ * from numVerticesInMesh to mesh->vertices.num\n
+ *
+ * \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 !
+ *
+ * \param mesh: a valid Delaunay mesh
+ * \param options: options to give to the Delaunay algorithm \ref HXTDelaunayOptions
+ */
+HXTStatus hxtDelaunay(HXTMesh* mesh, HXTDelaunayOptions* options);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/contrib/hxt/hxt_vertices.c b/contrib/hxt/hxt_vertices.c
new file mode 100644
index 0000000000000000000000000000000000000000..fa176dcbb9cba886680a0febc5a42f4abca00067
--- /dev/null
+++ b/contrib/hxt/hxt_vertices.c
@@ -0,0 +1,409 @@
+#include "hxt_omp.h"
+#include <string.h>
+#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
+#include <math.h>
+#define nextbefore(x) nextafter(x,0.0);
+#else
+#include <float.h>
+#define nextbefore(x) (x*(1.0-DBL_EPSILON))
+#endif
+
+
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#define SWAP(x,y) do{uint32_t tmp=x; x=y; y=tmp;}while(0)
+#define INVE(x) x=s-1-x // same as x^=s-1 I think
+
+
+/* final coordinate contain n_iter*3 bits
+ * if n < HXT_SORT_SEQUENTIAL_LIMIT hxt_sort makes passes of 8 bit.
+ * Thus making a 9 bit coord is stupid because you need a pass just to sort the last bit
+ * you could have used a 16 bit coord for the same price
+ *
+ *                            multiple of 8: 0 8 16 24 32 40 48 56    64
+ * from this, we get that it's better to use 0 6 15 24 30 39 48 54 or 63 bit
+ * corresponding to a nbr of iteration of    0 2 5  8  10 13 16 18 or 21
+ *
+ * if n > HXT_SORT_SEQUENTIAL_LIMIT, hxt_sort first make passes of  11 bit
+ *
+ *                           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 */
+uint32_t hxtAdvisedHilbertBits(const uint32_t n)
+{
+  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;
+  }
+  else{
+     return MIN(63, (nlog2+10)/11*11/3*3);
+  }
+}
+
+
+// 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(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;
+
+      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 ===========================================
+ *
+ * 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)
+{
+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");
+  
+  if(*nbits>63){
+    *nbits = 63;
+  }
+  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{
+    *nbits = (*nbits+2)/3*3;
+  }
+
+  const uint32_t level = *nbits/3;
+  const double defaultShift[3] = {0.5,0.5,0.5};
+
+  if(shift==NULL)
+    shift=defaultShift;
+
+/* 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];
+  double hxtDeclareAligned min1[3];
+  double hxtDeclareAligned min2[3];
+
+  uint32_t nmax = 1U<<level;
+
+  for (unsigned j=0; j<3; j++) {
+    double diff = bbox->max[j]-bbox->min[j];
+
+    div1[j] = nextbefore(nmax/(2.0*shift[j]*diff));
+    div2[j] = nextbefore(nmax/(2.0*(1.0-shift[j])*diff));
+
+    min1[j] = -bbox->min[j]*div1[j];
+    min2[j] = -(bbox->max[j]-2.0*(1.0-shift[j])*diff)*div2[j];
+
+    mean[j] = bbox->min[j] + shift[j]*diff;
+  }
+
+
+  // const uint32_t invGCTable[8] = {0,1,3,2,7,6,4,5};
+
+#pragma omp parallel for simd
+  for (uint32_t i=0; i<n; i++)
+  {
+    double vx = vertices[i].coord[0];
+    double vy = vertices[i].coord[1];
+    double vz = vertices[i].coord[2];
+
+    if(vx<bbox->min[0] || vx>bbox->max[0] ||
+       vy<bbox->min[1] || vy>bbox->max[1] ||
+       vz<bbox->min[2] || vz>bbox->max[2]) {
+      /* if a tetrahedron contain a vertex that is outside the bounding box,
+         it will not be refined and will never be in any cavity.
+         The vertices outside the bounding box get the value UINT64_MAX as hilbert index
+      */
+      vertices[i].padding.hilbertDist = UINT64_MAX;
+      continue;
+    }
+
+    if(vx < mean[0]){
+      vx = vx*div1[0]+min1[0];
+    }
+    else{
+      vx = vx*div2[0]+min2[0];
+    }
+    if(vy < mean[1]){
+      vy = vy*div1[1]+min1[1];
+    }
+    else{
+      vy = vy*div2[1]+min2[1];
+    }
+    if(vz < mean[2]){
+      vz = vz*div1[2]+min1[2];
+    }
+    else{
+      vz = vz*div2[2]+min2[2];
+    }
+
+    __assume(vx>=0.0);
+    __assume(vy>=0.0);
+    __assume(vz>=0.0);
+
+    uint32_t x = vx;
+    uint32_t y = vy;
+    uint32_t z = vz;
+
+    uint64_t bits;
+
+    #if 1 // this part is for Moore's curve...
+    {
+      uint32_t s = 1U<<(level-1);
+      uint32_t rx = (x & s) != 0;
+      uint32_t ry = (y & s) != 0;
+      uint32_t rz = (z & s) != 0;
+
+      uint32_t invGC = (rx*7)^(ry*3)^rz;
+      // uint32_t invGC = invGCTable[rx*4+ry*2+rz];
+
+      bits = invGC;
+
+      if(!rx){
+        INVE(y);
+        INVE(x);
+      }
+      if(rz){
+        INVE(y);
+      }
+      else{
+        INVE(z);
+      }
+
+      SWAP(x,z);
+      SWAP(x,y);
+    }
+    #endif
+
+    for (int j = level-2; j>=0; j--) {
+      uint32_t s = 1U<<j;
+      uint32_t rx = (x >> j) & 1;
+      uint32_t ry = (y >> j) & 1;
+      uint32_t rz = (z >> j) & 1;
+
+      // the xyz 3 bit number is viewed as gray code that we need to reverse (grayToBinary)
+      uint32_t invGC = (rx*7)^(ry*3)^rz;
+      // uint32_t invGC = invGCTable[rx*4+ry*2+rz];
+
+      __assume(invGC<=7);
+
+      bits = (bits<<3) + invGC;
+
+      if(rx){
+        if(rz){
+          INVE(y);
+        }
+        else{
+            INVE(z);
+            if(ry)
+              SWAP(x,y);
+            else
+              SWAP(y,z);
+        }
+        INVE(x);
+      }
+      else{
+        if(!rz){
+          if(ry){ // GC:3 -- num:2
+            INVE(z);
+            SWAP(x,y);
+            INVE(x);
+          }
+          else{
+            SWAP(y,z);
+          }
+        }
+      }
+
+      SWAP(y,z);
+      SWAP(x,z);
+    }
+    vertices[i].padding.hilbertDist = bits;
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+static inline uint64_t getVertexDist64(HXTVertex* const __restrict__  v, const void* userData)
+{
+  HXT_UNUSED(userData);
+  return v->padding.hilbertDist;
+}
+
+static HXTStatus hxtVerticesSort64(HXTVertex* const __restrict__  vertices, const uint32_t n, const uint64_t distMax)
+{
+  HXTSORT64_UNIFORM(HXTVertex, vertices, n, distMax, getVertexDist64, NULL);
+  return HXT_STATUS_OK;
+}
+
+static inline uint32_t getVertexDist32(HXTVertex* const __restrict__  v, const void* userData)
+{
+  HXT_UNUSED(userData);
+  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);
+  return HXT_STATUS_OK;
+}
+
+
+HXTStatus hxtVerticesSort(HXTVertex* const __restrict__  vertices, const uint32_t n, uint32_t nbits)
+{
+  HXT_ASSERT(vertices!=NULL);
+  if(nbits>64){
+    nbits = 64;
+  }
+  else if(nbits==0){
+    return HXT_STATUS_OK;
+  }
+
+  uint64_t one = 1;
+  if(nbits>32){
+    HXT_CHECK( hxtVerticesSort64(vertices, n, (one<<nbits)-1) );
+  }
+  else{
+    HXT_CHECK( hxtVerticesSort32(vertices, n, (one<<nbits)-1) );
+  }
+
+  return HXT_STATUS_OK;
+}
+
+static inline uint64_t getNodeInfoDist64(hxtNodeInfo*  const __restrict__ nodeInfo, const void* userData)
+{
+  HXT_UNUSED(userData);
+  return nodeInfo->hilbertDist;
+}
+
+static HXTStatus hxtNodeInfoSort64(hxtNodeInfo*  const __restrict__ array, const uint32_t n, const uint64_t distMax)
+{
+  HXTSORT64_UNIFORM(hxtNodeInfo, array, n, distMax, getNodeInfoDist64, NULL);
+  return HXT_STATUS_OK;
+}
+
+static inline uint32_t getNodeInfoDist32(hxtNodeInfo*  const __restrict__ nodeInfo, const void* userData)
+{
+  HXT_UNUSED(userData);
+  return nodeInfo->hilbertDist;
+}
+
+
+static HXTStatus hxtNodeInfoSort32(hxtNodeInfo*  const __restrict__ array, const uint32_t n, const uint32_t distMax)
+{
+  HXTSORT32_UNIFORM(hxtNodeInfo, array, n, distMax, getNodeInfoDist32, NULL);
+  return HXT_STATUS_OK;
+}
+
+
+HXTStatus hxtNodeInfoSort(hxtNodeInfo*  const __restrict__ array, const uint32_t n, uint32_t nbits)
+{
+  HXT_ASSERT(array!=NULL);
+  if(nbits>64){
+    nbits = 64;
+  }
+  else if(nbits==0){
+    return HXT_STATUS_OK;
+  }
+
+  const uint64_t one = 1;
+
+  if(nbits>32){
+    HXT_CHECK( hxtNodeInfoSort64(array, n, (one<<nbits)-1) );
+  }
+  else{
+    HXT_CHECK( hxtNodeInfoSort32(array, n, (one<<nbits)-1) );
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+/*********************************** shuffle functions ***********************************************/
+
+static inline uint32_t fastHash(uint32_t x) {
+  x = ((x >> 16) ^ x) * 0x45d9f3b;
+  x = ((x >> 16) ^ x) * 0x45d9f3b;
+  x = (x >> 16) ^ x;
+  return 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){
+  #pragma omp parallel for simd
+  for (uint32_t i=0; i<n; i++){
+    vertices[i].padding.hilbertDist = fastHash(i);
+  }
+
+  HXT_CHECK( hxtVerticesSort32(vertices, n, UINT32_MAX) );
+  return HXT_STATUS_OK;
+}
+
+/* for the non-static function, use a 22 bit key and a sort with two pass so we don't need to copy */
+HXTStatus hxtNodeInfoShuffle(hxtNodeInfo* const __restrict__ nodeInfo, const uint32_t n){
+  #pragma omp parallel for simd
+  for (uint32_t i=0; i<n; i++){
+    nodeInfo[i].hilbertDist = fastHash(i);
+  }
+
+  HXT_CHECK( hxtNodeInfoSort32(nodeInfo, n, UINT32_MAX) );
+  return HXT_STATUS_OK;
+}
+
+
+
+
diff --git a/contrib/hxt/hxt_vertices.h b/contrib/hxt/hxt_vertices.h
new file mode 100644
index 0000000000000000000000000000000000000000..2550e8034838290c128a21f333075cb113887a8c
--- /dev/null
+++ b/contrib/hxt/hxt_vertices.h
@@ -0,0 +1,110 @@
+#ifndef _HEXTREME_VERTICES_
+#define _HEXTREME_VERTICES_
+
+#include "hxt_mesh.h"
+#include "hxt_bbox.h"
+
+/**
+* \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;
+
+
+/**
+ * \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);
+
+/**
+ * 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
+ */
+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 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 hxtNodeInfoSort(hxtNodeInfo* const array, const uint32_t n, uint32_t nbits);
+
+/**
+ * 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/predicates.c b/contrib/hxt/predicates.c
new file mode 100644
index 0000000000000000000000000000000000000000..c9b7b5405863d38280cc0b489f8ae37431e46baa
--- /dev/null
+++ b/contrib/hxt/predicates.c
@@ -0,0 +1,2771 @@
+/*****************************************************************************/
+/*                                                                           */
+/*  Routines for Arbitrary Precision Floating-point Arithmetic               */
+/*  and Fast Robust Geometric Predicates                                     */
+/*  (predicates.c)                                                           */
+/*                                                                           */
+/*  May 18, 1996                                                             */
+/*                                                                           */
+/*  Placed in the public domain by                                           */
+/*  Jonathan Richard Shewchuk                                                */
+/*  School of Computer Science                                               */
+/*  Carnegie Mellon University                                               */
+/*  5000 Forbes Avenue                                                       */
+/*  Pittsburgh, Pennsylvania  15213-3891                                     */
+/*  jrs@cs.cmu.edu                                                           */
+/*                                                                           */
+/*  This file contains C implementation of algorithms for exact addition     */
+/*    and multiplication of floating-point numbers, and predicates for       */
+/*    robustly performing the orientation and incircle tests used in         */
+/*    computational geometry.  The algorithms and underlying theory are      */
+/*    described in Jonathan Richard Shewchuk.  "Adaptive Precision Floating- */
+/*    Point Arithmetic and Fast Robust Geometric Predicates."  Technical     */
+/*    Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon      */
+/*    University, Pittsburgh, Pennsylvania, May 1996.  (Submitted to         */
+/*    Discrete & Computational Geometry.)                                    */
+/*                                                                           */
+/*  This file, the paper listed above, and other information are available   */
+/*    from the Web page http://www.cs.cmu.edu/~quake/robust.html .           */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Using this code:                                                         */
+/*                                                                           */
+/*  First, read the short or long version of the paper (from the Web page    */
+/*    above).                                                                */
+/*                                                                           */
+/*  Be sure to call exactinit() once, before calling any of the arithmetic   */
+/*    functions or geometric predicates.  Also be sure to turn on the        */
+/*    optimizer when compiling this file.                                    */
+/*                                                                           */
+/*                                                                           */
+/*  Several geometric predicates are defined.  Their parameters are all      */
+/*    points.  Each point is an array of two or three floating-point         */
+/*    numbers.  The geometric predicates, described in the papers, are       */
+/*                                                                           */
+/*    orient2d(pa, pb, pc)                                                   */
+/*    orient2dfast(pa, pb, pc)                                               */
+/*    orient3d(pa, pb, pc, pd)                                               */
+/*    orient3dfast(pa, pb, pc, pd)                                           */
+/*    incircle(pa, pb, pc, pd)                                               */
+/*    incirclefast(pa, pb, pc, pd)                                           */
+/*    insphere(pa, pb, pc, pd, pe)                                           */
+/*    inspherefast(pa, pb, pc, pd, pe)                                       */
+/*                                                                           */
+/*  Those with suffix "fast" are approximate, non-robust versions.  Those    */
+/*    without the suffix are adaptive precision, robust versions.  There     */
+/*    are also versions with the suffices "exact" and "slow", which are      */
+/*    non-adaptive, exact arithmetic versions, which I use only for timings  */
+/*    in my arithmetic papers.                                               */
+/*                                                                           */
+/*                                                                           */
+/*  An expansion is represented by an array of floating-point numbers,       */
+/*    sorted from smallest to largest magnitude (possibly with interspersed  */
+/*    zeros).  The length of each expansion is stored as a separate integer, */
+/*    and each arithmetic function returns an integer which is the length    */
+/*    of the expansion it created.                                           */
+/*                                                                           */
+/*  Several arithmetic functions are defined.  Their parameters are          */
+/*                                                                           */
+/*    e, f           Input expansions                                        */
+/*    elen, flen     Lengths of input expansions (must be >= 1)              */
+/*    h              Output expansion                                        */
+/*    b              Input scalar                                            */
+/*                                                                           */
+/*  The arithmetic functions are                                             */
+/*                                                                           */
+/*    grow_expansion(elen, e, b, h)                                          */
+/*    grow_expansion_zeroelim(elen, e, b, h)                                 */
+/*    expansion_sum(elen, e, flen, f, h)                                     */
+/*    expansion_sum_zeroelim1(elen, e, flen, f, h)                           */
+/*    expansion_sum_zeroelim2(elen, e, flen, f, h)                           */
+/*    fast_expansion_sum(elen, e, flen, f, h)                                */
+/*    fast_expansion_sum_zeroelim(elen, e, flen, f, h)                       */
+/*    linear_expansion_sum(elen, e, flen, f, h)                              */
+/*    linear_expansion_sum_zeroelim(elen, e, flen, f, h)                     */
+/*    scale_expansion(elen, e, b, h)                                         */
+/*    scale_expansion_zeroelim(elen, e, b, h)                                */
+/*    compress(elen, e, h)                                                   */
+/*                                                                           */
+/*  All of these are described in the long version of the paper; some are    */
+/*    described in the short version.  All return an integer that is the     */
+/*    length of h.  Those with suffix _zeroelim perform zero elimination,    */
+/*    and are recommended over their counterparts.  The procedure            */
+/*    fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on   */
+/*    processors that do not use the round-to-even tiebreaking rule) is      */
+/*    recommended over expansion_sum_zeroelim().  Each procedure has a       */
+/*    little note next to it (in the code below) that tells you whether or   */
+/*    not the output expansion may be the same array as one of the input     */
+/*    expansions.                                                            */
+/*                                                                           */
+/*                                                                           */
+/*  If you look around below, you'll also find macros for a bunch of         */
+/*    simple unrolled arithmetic operations, and procedures for printing     */
+/*    expansions (commented out because they don't work with all C           */
+/*    compilers) and for generating random floating-point numbers whose      */
+/*    significand bits are all random.  Most of the macros have undocumented */
+/*    requirements that certain of their parameters should not be the same   */
+/*    variable; for safety, better to make sure all the parameters are       */
+/*    distinct variables.  Feel free to send email to jrs@cs.cmu.edu if you  */
+/*    have questions.                                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#ifdef CPU86
+#include <float.h>
+#endif /* CPU86 */
+#ifdef LINUX
+#include <fpu_control.h>
+#endif /* LINUX */
+
+#include "hxt_tools.h"
+
+#ifdef _MSC_VER
+#pragma fp_contract (off) // disable floating-point contraction
+                          // same as -ffp-contract=off in gcc
+                          // done by -fp-model strict in icc
+#endif
+
+
+/* On some machines, the exact arithmetic routines might be defeated by the  */
+/*   use of internal extended precision floating-point registers.  Sometimes */
+/*   this problem can be fixed by defining certain values to be volatile,    */
+/*   thus forcing them to be stored to memory and rounded off.  This isn't   */
+/*   a great solution, though, as it slows the arithmetic down.              */
+/*                                                                           */
+/* To try this out, write "#define INEXACT volatile" below.  Normally,       */
+/*   however, INEXACT should be defined to be nothing.  ("#define INEXACT".) */
+
+#define INEXACT                          /* Nothing */
+/* #define INEXACT volatile */
+
+#define REAL double                       /* float or double */
+#define REALPRINT doubleprint
+#define REALRAND doublerand
+#define NARROWRAND narrowdoublerand
+#define UNIFORMRAND uniformdoublerand
+
+/* Which of the following two methods of finding the absolute values is      */
+/*   fastest is compiler-dependent.  A few compilers can inline and optimize */
+/*   the fabs() call; but most will incur the overhead of a function call,   */
+/*   which is disastrously slow.  A faster way on IEEE machines might be to  */
+/*   mask the appropriate bit, but that's difficult to do in C.              */
+
+// #define Absolute(a)  ((a) >= 0.0 ? (a) : -(a))
+#define Absolute(a)  fabs(a)
+
+/* Many of the operations are broken up into two pieces, a main part that    */
+/*   performs an approximate operation, and a "tail" that computes the       */
+/*   roundoff error of that operation.                                       */
+/*                                                                           */
+/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(),    */
+/*   Split(), and Two_Product() are all implemented as described in the      */
+/*   reference.  Each of these macros requires certain variables to be       */
+/*   defined in the calling routine.  The variables `bvirt', `c', `abig',    */
+/*   `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because   */
+/*   they store the result of an operation that may incur roundoff error.    */
+/*   The input parameter `x' (or the highest numbered `x_' parameter) must   */
+/*   also be declared `INEXACT'.                                             */
+
+#define Fast_Two_Sum_Tail(a, b, x, y) \
+  bvirt = x - a; \
+  y = b - bvirt
+
+#define Fast_Two_Sum(a, b, x, y) \
+  x = (REAL) (a + b); \
+  Fast_Two_Sum_Tail(a, b, x, y)
+
+#define Fast_Two_Diff_Tail(a, b, x, y) \
+  bvirt = a - x; \
+  y = bvirt - b
+
+#define Two_Sum_Tail(a, b, x, y) \
+  bvirt = (REAL) (x - a); \
+  avirt = x - bvirt; \
+  bround = b - bvirt; \
+  around = a - avirt; \
+  y = around + bround
+
+#define Two_Sum(a, b, x, y) \
+  x = (REAL) (a + b); \
+  Two_Sum_Tail(a, b, x, y)
+
+#define Two_Diff_Tail(a, b, x, y) \
+  bvirt = (REAL) (a - x); \
+  avirt = x + bvirt; \
+  bround = bvirt - b; \
+  around = a - avirt; \
+  y = around + bround
+
+#define Two_Diff(a, b, x, y) \
+  x = (REAL) (a - b); \
+  Two_Diff_Tail(a, b, x, y)
+
+#define Split(a, ahi, alo) \
+  c = (REAL) (splitter * a); \
+  abig = (REAL) (c - a); \
+  ahi = c - abig; \
+  alo = a - ahi
+
+#define Two_Product_Tail(a, b, x, y) \
+  Split(a, ahi, alo); \
+  Split(b, bhi, blo); \
+  err1 = x - (ahi * bhi); \
+  err2 = err1 - (alo * bhi); \
+  err3 = err2 - (ahi * blo); \
+  y = (alo * blo) - err3
+
+#define Two_Product(a, b, x, y) \
+  x = (REAL) (a * b); \
+  Two_Product_Tail(a, b, x, y)
+
+/* Two_Product_Presplit() is Two_Product() where one of the inputs has       */
+/*   already been split.  Avoids redundant splitting.                        */
+
+#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
+  x = (REAL) (a * b); \
+  Split(a, ahi, alo); \
+  err1 = x - (ahi * bhi); \
+  err2 = err1 - (alo * bhi); \
+  err3 = err2 - (ahi * blo); \
+  y = (alo * blo) - err3
+
+/* Two_Product_2Presplit() is Two_Product() where both of the inputs have    */
+/*   already been split.  Avoids redundant splitting.                        */
+
+#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \
+  x = (REAL) (a * b); \
+  err1 = x - (ahi * bhi); \
+  err2 = err1 - (alo * bhi); \
+  err3 = err2 - (ahi * blo); \
+  y = (alo * blo) - err3
+
+/* Square() can be done more quickly than Two_Product().                     */
+
+#define Square_Tail(a, x, y) \
+  Split(a, ahi, alo); \
+  err1 = x - (ahi * ahi); \
+  err3 = err1 - ((ahi + ahi) * alo); \
+  y = (alo * alo) - err3
+
+#define Square(a, x, y) \
+  x = (REAL) (a * a); \
+  Square_Tail(a, x, y)
+
+/* Macros for summing expansions of various fixed lengths.  These are all    */
+/*   unrolled versions of Expansion_Sum().                                   */
+
+#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
+  Two_Sum(a0, b , _i, x0); \
+  Two_Sum(a1, _i, x2, x1)
+
+#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
+  Two_Diff(a0, b , _i, x0); \
+  Two_Sum( a1, _i, x2, x1)
+
+#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
+  Two_One_Sum(a1, a0, b0, _j, _0, x0); \
+  Two_One_Sum(_j, _0, b1, x3, x2, x1)
+
+#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
+  Two_One_Diff(a1, a0, b0, _j, _0, x0); \
+  Two_One_Diff(_j, _0, b1, x3, x2, x1)
+
+#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \
+  Two_One_Sum(a1, a0, b , _j, x1, x0); \
+  Two_One_Sum(a3, a2, _j, x4, x3, x2)
+
+#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \
+  Split(b, bhi, blo); \
+  Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+  Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, x1); \
+  Fast_Two_Sum(_j, _k, x3, x2)
+
+
+#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \
+  Split(a0, a0hi, a0lo); \
+  Split(b0, bhi, blo); \
+  Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \
+  Split(a1, a1hi, a1lo); \
+  Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, _1); \
+  Fast_Two_Sum(_j, _k, _l, _2); \
+  Split(b1, bhi, blo); \
+  Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \
+  Two_Sum(_1, _0, _k, x1); \
+  Two_Sum(_2, _k, _j, _1); \
+  Two_Sum(_l, _j, _m, _2); \
+  Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _n, _0); \
+  Two_Sum(_1, _0, _i, x2); \
+  Two_Sum(_2, _i, _k, _1); \
+  Two_Sum(_m, _k, _l, _2); \
+  Two_Sum(_j, _n, _k, _0); \
+  Two_Sum(_1, _0, _j, x3); \
+  Two_Sum(_2, _j, _i, _1); \
+  Two_Sum(_l, _i, _m, _2); \
+  Two_Sum(_1, _k, _i, x4); \
+  Two_Sum(_2, _i, _k, x5); \
+  Two_Sum(_m, _k, x7, x6)
+
+/* An expansion of length two can be squared more quickly than finding the   */
+/*   product of two different expansions of length two, and the result is    */
+/*   guaranteed to have no more than six (rather than eight) components.     */
+
+#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \
+  Square(a0, _j, x0); \
+  _0 = a0 + a0; \
+  Two_Product(a1, _0, _k, _1); \
+  Two_One_Sum(_k, _1, _j, _l, _2, x1); \
+  Square(a1, _j, _1); \
+  Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2)
+
+/* splitter = 2^ceiling(p / 2) + 1.  Used to split floats in half.           */
+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().
+// They are pre-calcualted and set in exactinit().
+// Added by H. Si, 2012-08-23.
+REAL o3dstaticfilter, o3derrboundA;
+REAL ispstaticfilter, isperrboundA;
+
+
+/*****************************************************************************/
+/*                                                                           */
+/*  exactinit()   Initialize the variables used for exact arithmetic.        */
+/*                                                                           */
+/*  `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in   */
+/*  floating-point arithmetic.  `epsilon' bounds the relative roundoff       */
+/*  error.  It is used for floating-point error analysis.                    */
+/*                                                                           */
+/*  `splitter' is used to split floating-point numbers into two half-        */
+/*  length significands for exact multiplication.                            */
+/*                                                                           */
+/*  I imagine that a highly optimizing compiler might be too smart for its   */
+/*  own good, and somehow cause this routine to fail, if it pretends that    */
+/*  floating-point arithmetic is too much like real arithmetic.              */
+/*                                                                           */
+/*  Don't change this routine unless you fully understand it.                */
+/*                                                                           */
+/*****************************************************************************/
+
+void exactinit(REAL maxx, REAL maxy, REAL maxz)
+{
+  REAL half;
+  REAL check, lastcheck;
+  int everyOther;
+#ifdef LINUX
+  int cword;
+#endif /* LINUX */
+
+#ifdef CPU86
+#ifdef SINGLE
+  _control87(_PC_24, _MCW_PC); /* Set FPU control word for single precision. */
+#else /* not SINGLE */
+  _control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */
+#endif /* not SINGLE */
+#endif /* CPU86 */
+#ifdef LINUX
+#ifdef SINGLE
+  /*  cword = 4223; */
+  cword = 4210;                 /* set FPU control word for single precision */
+#else /* not SINGLE */
+  /*  cword = 4735; */
+  cword = 4722;                 /* set FPU control word for double precision */
+#endif /* not SINGLE */
+  _FPU_SETCW(cword);
+#endif /* LINUX */
+
+  everyOther = 1;
+  half = 0.5;
+  epsilon = 1.0;
+  splitter = 1.0;
+  check = 1.0;
+  /* Repeatedly divide `epsilon' by two until it is too small to add to    */
+  /*   one without causing roundoff.  (Also check if the sum is equal to   */
+  /*   the previous sum, for machines that round up instead of using exact */
+  /*   rounding.  Not that this library will work on such machines anyway. */
+  do {
+    lastcheck = check;
+    epsilon *= half;
+    if (everyOther) {
+      splitter *= 2.0;
+    }
+    everyOther = !everyOther;
+    check = 1.0 + epsilon;
+  } while ((check != 1.0) && (check != lastcheck));
+  splitter += 1.0;
+
+  /* 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.
+
+// Sort maxx < maxy < maxz
+  if (maxx > maxz) {
+    REAL tmp = maxx;
+    maxx = maxz;
+    maxz = tmp;
+  }
+  if (maxy > maxz) {
+    REAL tmp = maxy;
+    maxy = maxz;
+    maxz = tmp;
+  }
+  else if (maxy < maxx) {
+    REAL tmp = maxy;
+    maxy = maxx;
+    maxx = tmp;
+  }
+  o3dstaticfilter = 5.1107127829973299e-15 * maxx * maxy * maxz;
+  ispstaticfilter = 1.2466136531027298e-13 * maxx * maxy * maxz * (maxz * maxz);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  grow_expansion()   Add a scalar to an expansion.                         */
+/*                                                                           */
+/*  Sets h = e + b.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+int grow_expansion(int elen, const REAL *e, REAL b, REAL *h)
+/* e and h can be the same. */
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  int eindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  Q = b;
+  for (eindex = 0; eindex < elen; eindex++) {
+    enow = e[eindex];
+    Two_Sum(Q, enow, Qnew, h[eindex]);
+    Q = Qnew;
+  }
+  h[eindex] = Q;
+  return eindex + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  grow_expansion_zeroelim()   Add a scalar to an expansion, eliminating    */
+/*                              zero components from the output expansion.   */
+/*                                                                           */
+/*  Sets h = e + b.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+int grow_expansion_zeroelim(int elen, const REAL *e, REAL b, REAL *h)
+/* e and h can be the same. */
+{
+  REAL Q, hh;
+  INEXACT REAL Qnew;
+  int eindex, hindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  hindex = 0;
+  Q = b;
+  for (eindex = 0; eindex < elen; eindex++) {
+    enow = e[eindex];
+    Two_Sum(Q, enow, Qnew, hh);
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0)) {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  fast_expansion_sum()   Sum two expansions.                               */
+/*                                                                           */
+/*  Sets h = e + f.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  If round-to-even is used (as with IEEE 754), maintains the strongly      */
+/*  nonoverlapping property.  (That is, if e is strongly nonoverlapping, h   */
+/*  will be also.)  Does NOT maintain the nonoverlapping or nonadjacent      */
+/*  properties.                                                              */
+/*                                                                           */
+/*****************************************************************************/
+
+int fast_expansion_sum(int elen, const REAL *e, int flen, const REAL *f, REAL *h)
+/* h cannot be e or f. */
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  REAL enow, fnow;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  if ((fnow > enow) == (fnow > -enow)) {
+    Q = enow;
+    ++eindex;
+  } else {
+    Q = fnow;
+    ++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, h[0]);
+      ++eindex;
+    } else {
+      Fast_Two_Sum(fnow, Q, Qnew, h[0]);
+      ++findex;
+    }
+    Q = Qnew;
+    hindex = 1;
+    while ((eindex < elen) && (findex < flen)) {
+      enow = e[eindex];
+      fnow = f[findex];
+      if ((fnow > enow) == (fnow > -enow)) {
+        Two_Sum(Q, enow, Qnew, h[hindex]);
+        ++eindex;
+      } else {
+        Two_Sum(Q, fnow, Qnew, h[hindex]);
+        ++findex;
+      }
+      Q = Qnew;
+      hindex++;
+    }
+  }
+  while (eindex < elen) {
+    enow = e[eindex];
+    Two_Sum(Q, enow, Qnew, h[hindex]);
+    ++eindex;
+    Q = Qnew;
+    hindex++;
+  }
+  while (findex < flen) {
+    fnow = f[findex];
+    Two_Sum(Q, fnow, Qnew, h[hindex]);
+    ++findex;
+    Q = Qnew;
+    hindex++;
+  }
+  h[hindex] = Q;
+  return hindex + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  fast_expansion_sum_zeroelim()   Sum two expansions, eliminating zero     */
+/*                                  components from the output expansion.    */
+/*                                                                           */
+/*  Sets h = e + f.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  If round-to-even is used (as with IEEE 754), maintains the strongly      */
+/*  nonoverlapping property.  (That is, if e is strongly nonoverlapping, h   */
+/*  will be also.)  Does NOT maintain the nonoverlapping or nonadjacent      */
+/*  properties.                                                              */
+/*                                                                           */
+/*****************************************************************************/
+
+int fast_expansion_sum_zeroelim(const int elen, const REAL *e, const int flen, const REAL *f, REAL *h)
+/* h cannot be e or f. */
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  INEXACT REAL hh;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  REAL enow, fnow;
+
+  if (elen == 0) {
+    for (int i = 0; i < flen; ++i) h[i] = f[i];
+    return flen;
+  }
+  if (flen == 0) {
+    for (int i = 0; i < elen; ++i) h[i] = e[i];
+    return elen;
+  }
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  if ((fnow > enow) == (fnow > -enow)) {
+    Q = enow;
+    ++eindex;
+  } else {
+    Q = fnow;
+    ++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);
+      ++eindex;
+    } else {
+      Fast_Two_Sum(fnow, Q, Qnew, hh);
+      ++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);
+        ++eindex;
+      } else {
+        Two_Sum(Q, fnow, Qnew, hh);
+        ++findex;
+      }
+      Q = Qnew;
+      if (hh != 0.0) {
+        h[hindex++] = hh;
+      }
+    }
+  }
+  while (eindex < elen) {
+    enow = e[eindex];
+    Two_Sum(Q, enow, Qnew, hh);
+    ++eindex;
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  while (findex < flen) {
+    fnow = f[findex];
+    Two_Sum(Q, fnow, Qnew, hh);
+    ++findex;
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0)) {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  scale_expansion()   Multiply an expansion by a scalar.                   */
+/*                                                                           */
+/*  Sets h = be.  See either version of my paper for details.                */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+int scale_expansion(int elen, REAL *e, REAL b, REAL *h)
+/* e and h cannot be the same. */
+{
+  INEXACT REAL Q;
+  INEXACT REAL sum;
+  INEXACT REAL product1;
+  REAL product0;
+  int eindex, hindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+
+  Split(b, bhi, blo);
+  Two_Product_Presplit(e[0], b, bhi, blo, Q, h[0]);
+  hindex = 1;
+  for (eindex = 1; eindex < elen; eindex++) {
+    enow = e[eindex];
+    Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+    Two_Sum(Q, product0, sum, h[hindex]);
+    hindex++;
+    Two_Sum(product1, sum, Q, h[hindex]);
+    hindex++;
+  }
+  h[hindex] = Q;
+  return elen + elen;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  scale_expansion_zeroelim()   Multiply an expansion by a scalar,          */
+/*                               eliminating zero components from the        */
+/*                               output expansion.                           */
+/*                                                                           */
+/*  Sets h = be.  See either version of my paper for details.                */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+int scale_expansion_zeroelim(const int elen, const REAL *e, const REAL b, REAL *h)
+/* e and h cannot be the same. */
+{
+  INEXACT REAL Q, sum;
+  REAL hh;
+  INEXACT REAL product1;
+  REAL product0;
+  int eindex, hindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+
+  Split(b, bhi, blo);
+  Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
+  hindex = 0;
+  if (hh != 0) {
+    h[hindex++] = hh;
+  }
+  for (eindex = 1; eindex < elen; eindex++) {
+    enow = e[eindex];
+    Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+    Two_Sum(Q, product0, sum, hh);
+    if (hh != 0) {
+      h[hindex++] = hh;
+    }
+    Fast_Two_Sum(product1, sum, Q, hh);
+    if (hh != 0) {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0)) {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+
+/*****************************************************************************/
+/*                                                                           */
+/*  estimate()   Produce a one-word estimate of an expansion's value.        */
+/*                                                                           */
+/*  See either version of my paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+static REAL estimate(const int elen, const REAL *e)
+{
+  REAL Q;
+  int eindex;
+
+  Q = e[0];
+  for (eindex = 1; eindex < elen; eindex++) {
+    Q += e[eindex];
+  }
+  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);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  orient3dfast()   Approximate 3D orientation test.  Nonrobust.            */
+/*  orient3dexact()   Exact 3D orientation test.  Robust.                    */
+/*  orient3dslow()   Another exact 3D orientation test.  Robust.             */
+/*  orient3d()   Adaptive exact 3D orientation test.  Robust.                */
+/*                                                                           */
+/*               Return a positive value if the point pd lies below the      */
+/*               plane passing through pa, pb, and pc; "below" is defined so */
+/*               that pa, pb, and pc appear in counterclockwise order when   */
+/*               viewed from above the plane.  Returns a negative value if   */
+/*               pd lies above the plane.  Returns zero if the points are    */
+/*               coplanar.  The result is also a rough approximation of six  */
+/*               times the signed volume of the tetrahedron defined by the   */
+/*               four 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 orient3d() 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, orient3d() is usually quite */
+/*  fast, but will run more slowly when the input points are coplanar or     */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+
+static REAL orient3dadapt(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, adz, bdz, cdz;
+  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 adet[8], bdet[8], cdet[8];
+  int alen, blen, clen;
+  REAL abdet[16];
+  int ablen;
+  REAL *finnow, *finother, *finswap;
+  REAL fin1[192], fin2[192];
+  int finlength;
+
+  ////////////////////////////////////////////////////////
+  // To avoid uninitialized warnings reported by valgrind.
+  int i;
+  for (i = 0; i < 8; i++) {
+    adet[i] = bdet[i] = cdet[i] = 0.0;
+  }
+  for (i = 0; i < 16; i++) {
+    abdet[i] = 0.0;
+  }
+  ////////////////////////////////////////////////////////
+
+  REAL adxtail, bdxtail, cdxtail;
+  REAL adytail, bdytail, cdytail;
+  REAL adztail, bdztail, cdztail;
+  INEXACT REAL at_blarge, at_clarge;
+  INEXACT REAL bt_clarge, bt_alarge;
+  INEXACT REAL ct_alarge, ct_blarge;
+  REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
+  int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
+  INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
+  INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1;
+  REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
+  REAL adxt_cdy0, adxt_bdy0, bdxt_ady0;
+  INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
+  INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1;
+  REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
+  REAL adyt_cdx0, adyt_bdx0, bdyt_adx0;
+  REAL bct[8], cat[8], abt[8];
+  int bctlen, catlen, abtlen;
+  INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
+  INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
+  REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
+  REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
+  REAL u[4], v[12], w[16];
+  INEXACT REAL u3;
+  int vlength, wlength;
+  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, _k;
+  REAL _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]);
+  adz = (REAL) (pa[2] - pd[2]);
+  bdz = (REAL) (pb[2] - pd[2]);
+  cdz = (REAL) (pc[2] - pd[2]);
+
+  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;
+  alen = scale_expansion_zeroelim(4, bc, adz, 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;
+  blen = scale_expansion_zeroelim(4, ca, bdz, 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;
+  clen = scale_expansion_zeroelim(4, ab, cdz, 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 = o3derrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+  Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+  Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+  Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+  Two_Diff_Tail(pa[2], pd[2], adz, adztail);
+  Two_Diff_Tail(pb[2], pd[2], bdz, bdztail);
+  Two_Diff_Tail(pc[2], pd[2], cdz, cdztail);
+
+  if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
+      && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)
+      && (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0)) {
+    return det;
+  }
+
+  errbound = o3derrboundC * permanent + resulterrbound * det;
+  det += (adz * ((bdx * cdytail + cdy * bdxtail)
+                 - (bdy * cdxtail + cdx * bdytail))
+          + adztail * (bdx * cdy - bdy * cdx))
+       + (bdz * ((cdx * adytail + ady * cdxtail)
+                 - (cdy * adxtail + adx * cdytail))
+          + bdztail * (cdx * ady - cdy * adx))
+       + (cdz * ((adx * bdytail + bdy * adxtail)
+                 - (ady * bdxtail + bdx * adytail))
+          + cdztail * (adx * bdy - ady * bdx));
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  finnow = fin1;
+  finother = fin2;
+
+  if (adxtail == 0.0) {
+    if (adytail == 0.0) {
+      at_b[0] = 0.0;
+      at_blen = 1;
+      at_c[0] = 0.0;
+      at_clen = 1;
+    } else {
+      negate = -adytail;
+      Two_Product(negate, bdx, at_blarge, at_b[0]);
+      at_b[1] = at_blarge;
+      at_blen = 2;
+      Two_Product(adytail, cdx, at_clarge, at_c[0]);
+      at_c[1] = at_clarge;
+      at_clen = 2;
+    }
+  } else {
+    if (adytail == 0.0) {
+      Two_Product(adxtail, bdy, at_blarge, at_b[0]);
+      at_b[1] = at_blarge;
+      at_blen = 2;
+      negate = -adxtail;
+      Two_Product(negate, cdy, at_clarge, at_c[0]);
+      at_c[1] = at_clarge;
+      at_clen = 2;
+    } else {
+      Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
+      Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
+      Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0,
+                   at_blarge, at_b[2], at_b[1], at_b[0]);
+      at_b[3] = at_blarge;
+      at_blen = 4;
+      Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
+      Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
+      Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0,
+                   at_clarge, at_c[2], at_c[1], at_c[0]);
+      at_c[3] = at_clarge;
+      at_clen = 4;
+    }
+  }
+  if (bdxtail == 0.0) {
+    if (bdytail == 0.0) {
+      bt_c[0] = 0.0;
+      bt_clen = 1;
+      bt_a[0] = 0.0;
+      bt_alen = 1;
+    } else {
+      negate = -bdytail;
+      Two_Product(negate, cdx, bt_clarge, bt_c[0]);
+      bt_c[1] = bt_clarge;
+      bt_clen = 2;
+      Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
+      bt_a[1] = bt_alarge;
+      bt_alen = 2;
+    }
+  } else {
+    if (bdytail == 0.0) {
+      Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
+      bt_c[1] = bt_clarge;
+      bt_clen = 2;
+      negate = -bdxtail;
+      Two_Product(negate, ady, bt_alarge, bt_a[0]);
+      bt_a[1] = bt_alarge;
+      bt_alen = 2;
+    } else {
+      Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
+      Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
+      Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0,
+                   bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
+      bt_c[3] = bt_clarge;
+      bt_clen = 4;
+      Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
+      Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
+      Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0,
+                  bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
+      bt_a[3] = bt_alarge;
+      bt_alen = 4;
+    }
+  }
+  if (cdxtail == 0.0) {
+    if (cdytail == 0.0) {
+      ct_a[0] = 0.0;
+      ct_alen = 1;
+      ct_b[0] = 0.0;
+      ct_blen = 1;
+    } else {
+      negate = -cdytail;
+      Two_Product(negate, adx, ct_alarge, ct_a[0]);
+      ct_a[1] = ct_alarge;
+      ct_alen = 2;
+      Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
+      ct_b[1] = ct_blarge;
+      ct_blen = 2;
+    }
+  } else {
+    if (cdytail == 0.0) {
+      Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
+      ct_a[1] = ct_alarge;
+      ct_alen = 2;
+      negate = -cdxtail;
+      Two_Product(negate, bdy, ct_blarge, ct_b[0]);
+      ct_b[1] = ct_blarge;
+      ct_blen = 2;
+    } else {
+      Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
+      Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
+      Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0,
+                   ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
+      ct_a[3] = ct_alarge;
+      ct_alen = 4;
+      Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
+      Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
+      Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0,
+                   ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
+      ct_b[3] = ct_blarge;
+      ct_blen = 4;
+    }
+  }
+
+  bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
+  wlength = scale_expansion_zeroelim(bctlen, bct, adz, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
+  wlength = scale_expansion_zeroelim(catlen, cat, bdz, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
+  wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  if (adztail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, bc, adztail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdztail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, ca, bdztail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdztail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, ab, cdztail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  if (adxtail != 0.0) {
+    if (bdytail != 0.0) {
+      Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
+      Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdztail != 0.0) {
+        Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (cdytail != 0.0) {
+      negate = -adxtail;
+      Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
+      Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdztail != 0.0) {
+        Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+  if (bdxtail != 0.0) {
+    if (cdytail != 0.0) {
+      Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
+      Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adztail != 0.0) {
+        Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (adytail != 0.0) {
+      negate = -bdxtail;
+      Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
+      Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdztail != 0.0) {
+        Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+  if (cdxtail != 0.0) {
+    if (adytail != 0.0) {
+      Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
+      Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdztail != 0.0) {
+        Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (bdytail != 0.0) {
+      negate = -cdxtail;
+      Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
+      Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adztail != 0.0) {
+        Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+
+  if (adztail != 0.0) {
+    wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdztail != 0.0) {
+    wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdztail != 0.0) {
+    wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  return finnow[finlength - 1];
+}
+
+
+REAL orient3d(const REAL* const __restrict__ pa, const REAL* const __restrict__ pb, const REAL* const __restrict__ pc, const REAL* const __restrict__ pd)
+{
+  // return orient3dexact(pa, pb, pc, pd);
+
+  REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  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];
+  adz = pa[2] - pd[2];
+  bdz = pb[2] - pd[2];
+  cdz = pc[2] - pd[2];
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+
+  adxbdy = adx * bdy;
+  bdxady = bdx * ady;
+
+  det = adz * (bdxcdy - cdxbdy)
+      + bdz * (cdxady - adxcdy)
+      + cdz * (adxbdy - bdxady);
+
+  if ((det > o3dstaticfilter) || (-det > o3dstaticfilter)) return det;
+
+  //  printf("coucou %22.15E\n",o3derrboundA);
+
+  permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz)
+            + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz)
+            + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz);
+  errbound = o3derrboundA * permanent;
+  if ((det > errbound) || (-det > errbound)) {
+    return det;
+  }
+
+  // printf("argh det %22.15E %22.15E %22.15E\n",det,permanent,orient3dexact(pa, pb, pc, pd) );
+  // getchar();
+
+  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);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  inspherefast()   Approximate 3D insphere test.  Nonrobust.               */
+/*  insphereexact()   Exact 3D insphere test.  Robust.                       */
+/*  insphereslow()   Another exact 3D insphere test.  Robust.                */
+/*  insphere()   Adaptive exact 3D insphere test.  Robust.                   */
+/*                                                                           */
+/*               Return a positive value if the point pe lies inside the     */
+/*               sphere passing through pa, pb, pc, and pd; a negative value */
+/*               if it lies outside; and zero if the five points are         */
+/*               cospherical.  The points pa, pb, pc, and pd must be ordered */
+/*               so that they have a positive orientation (as defined by     */
+/*               orient3d()), 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 insphere() 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, insphere() is usually quite */
+/*  fast, but will run more slowly when the input points are cospherical or  */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL inspherefast(const REAL* const __restrict__ pa, const REAL* const __restrict__ pb, const REAL* const __restrict__ pc, const REAL* const __restrict__ pd, const REAL* const __restrict__ pe)
+{
+  REAL aex, bex, cex, dex;
+  REAL aey, bey, cey, dey;
+  REAL aez, bez, cez, dez;
+  REAL alift, blift, clift, dlift;
+  REAL ab, bc, cd, da, ac, bd;
+  REAL abc, bcd, cda, dab;
+
+  aex = pa[0] - pe[0];
+  bex = pb[0] - pe[0];
+  cex = pc[0] - pe[0];
+  dex = pd[0] - pe[0];
+  aey = pa[1] - pe[1];
+  bey = pb[1] - pe[1];
+  cey = pc[1] - pe[1];
+  dey = pd[1] - pe[1];
+  aez = pa[2] - pe[2];
+  bez = pb[2] - pe[2];
+  cez = pc[2] - pe[2];
+  dez = pd[2] - pe[2];
+
+  ab = aex * bey - bex * aey;
+  bc = bex * cey - cex * bey;
+  cd = cex * dey - dex * cey;
+  da = dex * aey - aex * dey;
+
+  ac = aex * cey - cex * aey;
+  bd = bex * dey - dex * bey;
+
+  abc = aez * bc - bez * ac + cez * ab;
+  bcd = bez * cd - cez * bd + dez * bc;
+  cda = cez * da + dez * ac + aez * cd;
+  dab = dez * ab + aez * bd + bez * da;
+
+  alift = aex * aex + aey * aey + aez * aez;
+  blift = bex * bex + bey * bey + bez * bez;
+  clift = cex * cex + cey * cey + cez * cez;
+  dlift = dex * dex + dey * dey + dez * dez;
+
+  return (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+}
+
+REAL insphereexact(const REAL* const __restrict__ pa, const REAL* const __restrict__ pb, const REAL* const __restrict__ pc, const REAL* const __restrict__ pd, const REAL* const __restrict__ pe)
+{
+  INEXACT REAL axby1, bxcy1, cxdy1, dxey1, exay1;
+  INEXACT REAL bxay1, cxby1, dxcy1, exdy1, axey1;
+  INEXACT REAL axcy1, bxdy1, cxey1, dxay1, exby1;
+  INEXACT REAL cxay1, dxby1, excy1, axdy1, bxey1;
+  REAL axby0, bxcy0, cxdy0, dxey0, exay0;
+  REAL bxay0, cxby0, dxcy0, exdy0, axey0;
+  REAL axcy0, bxdy0, cxey0, dxay0, exby0;
+  REAL cxay0, dxby0, excy0, axdy0, bxey0;
+  REAL ab[4], bc[4], cd[4], de[4], ea[4];
+  REAL ac[4], bd[4], ce[4], da[4], eb[4];
+  REAL temp8a[8], temp8b[8], temp16[16];
+  int temp8alen, temp8blen, temp16len;
+  REAL abc[24], bcd[24], cde[24], dea[24], eab[24];
+  REAL abd[24], bce[24], cda[24], deb[24], eac[24];
+  int abclen, bcdlen, cdelen, dealen, eablen;
+  int abdlen, bcelen, cdalen, deblen, eaclen;
+  REAL temp48a[48], temp48b[48];
+  int temp48alen, temp48blen;
+  REAL abcd[96], bcde[96], cdea[96], deab[96], eabc[96];
+  int abcdlen, bcdelen, cdealen, deablen, eabclen;
+  REAL temp192[192];
+  REAL det384x[384], det384y[384], det384z[384];
+  int xlen, ylen, zlen;
+  REAL detxy[768];
+  int xylen;
+  REAL adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152];
+  int alen, blen, clen, dlen, elen;
+  REAL abdet[2304], cddet[2304], cdedet[3456];
+  int ablen, cdlen;
+  REAL deter[5760];
+  int deterlen;
+  int i;
+
+  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;
+
+
+  Two_Product(pa[0], pb[1], axby1, axby0);
+  Two_Product(pb[0], pa[1], bxay1, bxay0);
+  Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
+
+  Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+  Two_Product(pc[0], pb[1], cxby1, cxby0);
+  Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
+
+  Two_Product(pc[0], pd[1], cxdy1, cxdy0);
+  Two_Product(pd[0], pc[1], dxcy1, dxcy0);
+  Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
+
+  Two_Product(pd[0], pe[1], dxey1, dxey0);
+  Two_Product(pe[0], pd[1], exdy1, exdy0);
+  Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]);
+
+  Two_Product(pe[0], pa[1], exay1, exay0);
+  Two_Product(pa[0], pe[1], axey1, axey0);
+  Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]);
+
+  Two_Product(pa[0], pc[1], axcy1, axcy0);
+  Two_Product(pc[0], pa[1], cxay1, cxay0);
+  Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
+
+  Two_Product(pb[0], pd[1], bxdy1, bxdy0);
+  Two_Product(pd[0], pb[1], dxby1, dxby0);
+  Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
+
+  Two_Product(pc[0], pe[1], cxey1, cxey0);
+  Two_Product(pe[0], pc[1], excy1, excy0);
+  Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]);
+
+  Two_Product(pd[0], pa[1], dxay1, dxay0);
+  Two_Product(pa[0], pd[1], axdy1, axdy0);
+  Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
+
+  Two_Product(pe[0], pb[1], exby1, exby0);
+  Two_Product(pb[0], pe[1], bxey1, bxey0);
+  Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]);
+
+  temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a);
+  abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       abc);
+
+  temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a);
+  bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       bcd);
+
+  temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a);
+  cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       cde);
+
+  temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a);
+  dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       dea);
+
+  temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a);
+  eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       eab);
+
+  temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a);
+  abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       abd);
+
+  temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a);
+  bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       bce);
+
+  temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a);
+  cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       cda);
+
+  temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a);
+  deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       deb);
+
+  temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a);
+  eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       eac);
+
+  temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, bcde);
+  xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x);
+  ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y);
+  zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet);
+
+  temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, cdea);
+  xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x);
+  ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y);
+  zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet);
+
+  temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, deab);
+  xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x);
+  ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y);
+  zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet);
+
+  temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, eabc);
+  xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x);
+  ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y);
+  zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet);
+
+  temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, abcd);
+  xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x);
+  ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y);
+  zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter);
+
+  return deter[deterlen - 1];
+}
+
+
+REAL insphereadapt(const REAL* const __restrict__ pa, const REAL* const __restrict__ pb, const REAL* const __restrict__ pc, const REAL* const __restrict__ pd, const REAL* const __restrict__ pe,
+                   const REAL permanent)
+{
+  INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
+  REAL det, errbound;
+
+  INEXACT REAL aexbey1, bexaey1, bexcey1, cexbey1;
+  INEXACT REAL cexdey1, dexcey1, dexaey1, aexdey1;
+  INEXACT REAL aexcey1, cexaey1, bexdey1, dexbey1;
+  REAL aexbey0, bexaey0, bexcey0, cexbey0;
+  REAL cexdey0, dexcey0, dexaey0, aexdey0;
+  REAL aexcey0, cexaey0, bexdey0, dexbey0;
+  REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+  INEXACT REAL ab3, bc3, cd3, da3, ac3, bd3;
+  REAL abeps, bceps, cdeps, daeps, aceps, bdeps;
+  REAL temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48];
+  int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len;
+  REAL xdet[96], ydet[96], zdet[96], xydet[192];
+  int xlen, ylen, zlen, xylen;
+  REAL adet[288], bdet[288], cdet[288], ddet[288];
+  int alen, blen, clen, dlen;
+  REAL abdet[576], cddet[576];
+  int ablen, cdlen;
+  REAL fin1[1152];
+  int finlength;
+
+  REAL aextail, bextail, cextail, dextail;
+  REAL aeytail, beytail, ceytail, deytail;
+  REAL aeztail, beztail, ceztail, deztail;
+
+  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;
+
+
+  aex = (REAL) (pa[0] - pe[0]);
+  bex = (REAL) (pb[0] - pe[0]);
+  cex = (REAL) (pc[0] - pe[0]);
+  dex = (REAL) (pd[0] - pe[0]);
+  aey = (REAL) (pa[1] - pe[1]);
+  bey = (REAL) (pb[1] - pe[1]);
+  cey = (REAL) (pc[1] - pe[1]);
+  dey = (REAL) (pd[1] - pe[1]);
+  aez = (REAL) (pa[2] - pe[2]);
+  bez = (REAL) (pb[2] - pe[2]);
+  cez = (REAL) (pc[2] - pe[2]);
+  dez = (REAL) (pd[2] - pe[2]);
+
+  Two_Product(aex, bey, aexbey1, aexbey0);
+  Two_Product(bex, aey, bexaey1, bexaey0);
+  Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+
+  Two_Product(bex, cey, bexcey1, bexcey0);
+  Two_Product(cex, bey, cexbey1, cexbey0);
+  Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+
+  Two_Product(cex, dey, cexdey1, cexdey0);
+  Two_Product(dex, cey, dexcey1, dexcey0);
+  Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]);
+  cd[3] = cd3;
+
+  Two_Product(dex, aey, dexaey1, dexaey0);
+  Two_Product(aex, dey, aexdey1, aexdey0);
+  Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]);
+  da[3] = da3;
+
+  Two_Product(aex, cey, aexcey1, aexcey0);
+  Two_Product(cex, aey, cexaey1, cexaey0);
+  Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]);
+  ac[3] = ac3;
+
+  Two_Product(bex, dey, bexdey1, bexdey0);
+  Two_Product(dex, bey, dexbey1, dexbey0);
+  Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]);
+  bd[3] = bd3;
+
+  temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet);
+
+  temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet);
+
+  temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet);
+
+  temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = isperrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pe[0], aex, aextail);
+  Two_Diff_Tail(pa[1], pe[1], aey, aeytail);
+  Two_Diff_Tail(pa[2], pe[2], aez, aeztail);
+  Two_Diff_Tail(pb[0], pe[0], bex, bextail);
+  Two_Diff_Tail(pb[1], pe[1], bey, beytail);
+  Two_Diff_Tail(pb[2], pe[2], bez, beztail);
+  Two_Diff_Tail(pc[0], pe[0], cex, cextail);
+  Two_Diff_Tail(pc[1], pe[1], cey, ceytail);
+  Two_Diff_Tail(pc[2], pe[2], cez, ceztail);
+  Two_Diff_Tail(pd[0], pe[0], dex, dextail);
+  Two_Diff_Tail(pd[1], pe[1], dey, deytail);
+  Two_Diff_Tail(pd[2], pe[2], dez, deztail);
+  if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0)
+      && (bextail == 0.0) && (beytail == 0.0) && (beztail == 0.0)
+      && (cextail == 0.0) && (ceytail == 0.0) && (ceztail == 0.0)
+      && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) {
+    return det;
+  }
+
+  errbound = isperrboundC * permanent + resulterrbound * det;
+  abeps = (aex * beytail + bey * aextail)
+        - (aey * bextail + bex * aeytail);
+  bceps = (bex * ceytail + cey * bextail)
+        - (bey * cextail + cex * beytail);
+  cdeps = (cex * deytail + dey * cextail)
+        - (cey * dextail + dex * ceytail);
+  daeps = (dex * aeytail + aey * dextail)
+        - (dey * aextail + aex * deytail);
+  aceps = (aex * ceytail + cey * aextail)
+        - (aey * cextail + cex * aeytail);
+  bdeps = (bex * deytail + dey * bextail)
+        - (bey * dextail + dex * beytail);
+  det += (((bex * bex + bey * bey + bez * bez)
+           * ((cez * daeps + dez * aceps + aez * cdeps)
+              + (ceztail * da3 + deztail * ac3 + aeztail * cd3))
+           + (dex * dex + dey * dey + dez * dez)
+           * ((aez * bceps - bez * aceps + cez * abeps)
+              + (aeztail * bc3 - beztail * ac3 + ceztail * ab3)))
+          - ((aex * aex + aey * aey + aez * aez)
+           * ((bez * cdeps - cez * bdeps + dez * bceps)
+              + (beztail * cd3 - ceztail * bd3 + deztail * bc3))
+           + (cex * cex + cey * cey + cez * cez)
+           * ((dez * abeps + aez * bdeps + bez * daeps)
+              + (deztail * ab3 + aeztail * bd3 + beztail * da3))))
+       + 2.0 * (((bex * bextail + bey * beytail + bez * beztail)
+                 * (cez * da3 + dez * ac3 + aez * cd3)
+                 + (dex * dextail + dey * deytail + dez * deztail)
+                 * (aez * bc3 - bez * ac3 + cez * ab3))
+                - ((aex * aextail + aey * aeytail + aez * aeztail)
+                 * (bez * cd3 - cez * bd3 + dez * bc3)
+                 + (cex * cextail + cey * ceytail + cez * ceztail)
+                 * (dez * ab3 + aez * bd3 + bez * da3)));
+
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  return insphereexact(pa, pb, pc, pd, pe);
+}
+
+
+REAL insphere(const REAL* const __restrict__ pa, const REAL* const __restrict__ pb, const REAL* const __restrict__ pc, const REAL* const __restrict__ pd, const REAL* const __restrict__ pe)
+{
+  REAL aex, bex, cex, dex;
+  REAL aey, bey, cey, dey;
+  REAL aez, bez, cez, dez;
+  REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey;
+  REAL aexcey, cexaey, bexdey, dexbey;
+  REAL alift, blift, clift, dlift;
+  REAL ab, bc, cd, da, ac, bd;
+  REAL abc, bcd, cda, dab;
+  REAL det;
+
+
+  aex = pa[0] - pe[0];
+  bex = pb[0] - pe[0];
+  cex = pc[0] - pe[0];
+  dex = pd[0] - pe[0];
+  aey = pa[1] - pe[1];
+  bey = pb[1] - pe[1];
+  cey = pc[1] - pe[1];
+  dey = pd[1] - pe[1];
+  aez = pa[2] - pe[2];
+  bez = pb[2] - pe[2];
+  cez = pc[2] - pe[2];
+  dez = pd[2] - pe[2];
+
+  aexbey = aex * bey;
+  bexaey = bex * aey;
+  ab = aexbey - bexaey;
+  bexcey = bex * cey;
+  cexbey = cex * bey;
+  bc = bexcey - cexbey;
+  cexdey = cex * dey;
+  dexcey = dex * cey;
+  cd = cexdey - dexcey;
+  dexaey = dex * aey;
+  aexdey = aex * dey;
+  da = dexaey - aexdey;
+
+  aexcey = aex * cey;
+  cexaey = cex * aey;
+  ac = aexcey - cexaey;
+  bexdey = bex * dey;
+  dexbey = dex * bey;
+  bd = bexdey - dexbey;
+
+  abc = aez * bc - bez * ac + cez * ab;
+  bcd = bez * cd - cez * bd + dez * bc;
+  cda = cez * da + dez * ac + aez * cd;
+  dab = dez * ab + aez * bd + bez * da;
+
+  alift = aex * aex + aey * aey + aez * aez;
+  blift = bex * bex + bey * bey + bez * bez;
+  clift = cex * cex + cey * cey + cez * cez;
+  dlift = dex * dex + dey * dey + dez * dez;
+
+  det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+
+  if ((det > ispstaticfilter) || (-det > ispstaticfilter)) return det;
+
+  REAL aezplus, bezplus, cezplus, dezplus;
+  REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus;
+  REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus;
+  REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus;
+  REAL permanent, errbound;
+
+  aezplus = Absolute(aez);
+  bezplus = Absolute(bez);
+  cezplus = Absolute(cez);
+  dezplus = Absolute(dez);
+  aexbeyplus = Absolute(aexbey);
+  bexaeyplus = Absolute(bexaey);
+  bexceyplus = Absolute(bexcey);
+  cexbeyplus = Absolute(cexbey);
+  cexdeyplus = Absolute(cexdey);
+  dexceyplus = Absolute(dexcey);
+  dexaeyplus = Absolute(dexaey);
+  aexdeyplus = Absolute(aexdey);
+  aexceyplus = Absolute(aexcey);
+  cexaeyplus = Absolute(cexaey);
+  bexdeyplus = Absolute(bexdey);
+  dexbeyplus = Absolute(dexbey);
+  permanent = ((cexdeyplus + dexceyplus) * bezplus
+               + (dexbeyplus + bexdeyplus) * cezplus
+               + (bexceyplus + cexbeyplus) * dezplus)
+            * alift
+            + ((dexaeyplus + aexdeyplus) * cezplus
+               + (aexceyplus + cexaeyplus) * dezplus
+               + (cexdeyplus + dexceyplus) * aezplus)
+            * blift
+            + ((aexbeyplus + bexaeyplus) * dezplus
+               + (bexdeyplus + dexbeyplus) * aezplus
+               + (dexaeyplus + aexdeyplus) * bezplus)
+            * clift
+            + ((bexceyplus + cexbeyplus) * aezplus
+               + (cexaeyplus + aexceyplus) * bezplus
+               + (aexbeyplus + bexaeyplus) * cezplus)
+            * dlift;
+  errbound = isperrboundA * permanent;
+  if ((det > errbound) || (-det > errbound)) {
+    return det;
+  }
+
+  return insphereadapt(pa, pb, pc, pd, pe, permanent);
+}
diff --git a/contrib/hxt/predicates.h b/contrib/hxt/predicates.h
new file mode 100644
index 0000000000000000000000000000000000000000..35d171f1a57f8499db4e0e3fe3eba11c4253f209
--- /dev/null
+++ b/contrib/hxt/predicates.h
@@ -0,0 +1,72 @@
+#ifndef _ROBUST_PREDICATES_H_
+#define _ROBUST_PREDICATES_H_
+
+#include <hxt_tools.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern double splitter;
+extern double o3dstaticfilter;
+extern double o3derrboundA;
+extern double ispstaticfilter;
+extern double isperrboundA;
+
+double exactinit(double maxx, double maxy, double maxz);
+// double incircle(double *pa, double *pb, double *pc, double *pd);
+
+/** \todo Please comment the freaking variable type you are using. [JP]
+ */
+double insphere(
+  const double* const __restrict__ pa,
+  const double* const __restrict__ pb,
+  const double* const __restrict__ pc,
+  const double* const __restrict__ pd,
+  const double* const __restrict__ pe);
+
+double orient3d(
+  const double* const __restrict__ pa,
+  const double* const __restrict__ pb,
+  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);
+
+int grow_expansion_zeroelim(
+  int elen, const double *e, double b, double *h);
+
+int fast_expansion_sum_zeroelim(
+  const int elen, const double *e,
+  const int flen, const double *f, double *h);
+
+int fast_expansion_sum(
+  int elen, const double *e, int flen, const double *f, double *h);
+
+int fast_expansion_sum_zeroelim(
+  const int elen, const double *e,
+  const int flen, const double *f, double *h);
+
+int scale_expansion(
+  const int elen, const double *e, const double b, double *h);
+
+int scale_expansion_zeroelim(
+  const int elen, const double *e, const double b, double *h);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/hxt/tetgenBR.cxx b/contrib/hxt/tetgenBR.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ec1969f10adfa21b8fb1a9bcf521cf56952243a4
--- /dev/null
+++ b/contrib/hxt/tetgenBR.cxx
@@ -0,0 +1,17347 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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**) NULL, 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 **) NULL, 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)
+{
+#ifndef NDEBUG
+  point torg=NULL, tdest=NULL, tapex=NULL, toppo;
+#else
+  point torg, tdest, tapex, toppo;
+#endif
+  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..13b1903719d396578ce6a7879b2a686582e73c56
--- /dev/null
+++ b/contrib/hxt/tetgenBR.h
@@ -0,0 +1,2590 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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.001;
+    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
+  int 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
+  (void) m; // parameter is unused (suppress warning)
+  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]); // bondtbl[i][j] = (i/4*4 + j)%12
+  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
diff --git a/contrib/mmg3d/CMakeLists.txt b/contrib/mmg3d/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3e12068519ec626cf7503c7c56becf3a1b9e02f0
--- /dev/null
+++ b/contrib/mmg3d/CMakeLists.txt
@@ -0,0 +1,69 @@
+# Gmsh - Copyright (C) 1997-2019 C. Geuzaine, J.-F. Remacle
+#
+# See the LICENSE.txt file for license information. Please report all
+# issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
+
+set(SRC
+./build/sources/mmg3d.c
+./build/sources/inout.c
+./build/sources/mmg3dlib.c
+./build/sources/scalem.c
+./build/sources/outqua.c
+./build/sources/baryct.c
+./build/sources/zaldy.c
+./build/sources/typelt.c
+./build/sources/swaptet.c
+./build/sources/swapar.c
+./build/sources/swap710.c
+./build/sources/swap68.c
+./build/sources/swap56.c
+./build/sources/swap44.c
+./build/sources/swap23.c
+./build/sources/spledg.c
+./build/sources/solmap.c
+./build/sources/simu710.c
+./build/sources/simu68.c
+./build/sources/simu56.c
+./build/sources/simu44.c
+./build/sources/simu23.c
+./build/sources/ratio.c
+./build/sources/queue.c
+./build/sources/quality.c
+./build/sources/pattern.c
+./build/sources/opttyp.c
+./build/sources/opttet.c
+./build/sources/optra4.c
+./build/sources/optlentet.c
+./build/sources/optlen.c
+./build/sources/optlap.c
+./build/sources/optcte.c
+./build/sources/optcoq.c
+./build/sources/optbdry.c
+./build/sources/movevertex.c
+./build/sources/mmg3d9.c
+./build/sources/delaunay.c
+./build/sources/hash.c
+./build/sources/length.c
+./build/sources/mmg3d4.c
+./build/sources/mmg3d1.c
+./build/sources/memory.c
+./build/sources/matrix.c
+./build/sources/locate.c
+./build/sources/librnbg.c
+./build/sources/heap.c
+./build/sources/eigenv.c
+./build/sources/cutelt.c
+./build/sources/coquil.c
+./build/sources/colpoi.c
+./build/sources/chrono.c
+./build/sources/chkmsh.c
+./build/sources/cenrad.c
+./build/sources/cendel.c
+./build/sources/bucket.c
+./build/sources/boulep.c
+./build/sources/analarcutting.c
+./build/sources/analar.c
+)
+
+file(GLOB_RECURSE HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h)
+append_gmsh_src(contrib/mmg3d "${SRC};${HDR}")
diff --git a/contrib/mmg3d/INSTALL.txt b/contrib/mmg3d/INSTALL.txt
new file mode 100644
index 0000000000000000000000000000000000000000..14d9f7844f8caf7b3646b011f9b47c9edbaea7a6
--- /dev/null
+++ b/contrib/mmg3d/INSTALL.txt
@@ -0,0 +1,40 @@
+MMG3D 4.0 installation instructions
+====================================
+
+The simplest way to compile is to use cmake  : 
+
+1) The cmake tool is available at this adress :
+
+http://www.cmake.org/
+
+2) 'cd' to the directory build/ containing :
+CMakeLists.txt 
+a directory named sources/ containing all the MMG3D files.
+
+3) Configure and create the makefile with cmake :  
+
+By default, the configuration is done to create an executable
+of MMG3D. 
+
+NB : By default, cmake find Scotch software : 
+http://www.labri.fr/perso/pelegrin/scotch/scotch_en.html
+
+i) If you use the gui interface of cmake : 'cmake-gui' 
+You can specify if you would compile the shared or static library  
+and if you have or not Scotch software.
+
+OR
+
+ii) 'cmake . ' 
+You can specify the following option :
+-DCOMPIL_STATIC_LIBRARY="ON"  or -DCOMPIL_SHARED_LIBRARY="ON"   
+(by default : OFF)
+-DUSE_SCOTCH="OFF" ' (by default : ON)
+
+
+
+4) Compile MMG3D :
+'make'
+
+
+
diff --git a/contrib/mmg3d/LICENCE.txt b/contrib/mmg3d/LICENCE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e587591e143165eb560c2385f0652ac369fc6595
--- /dev/null
+++ b/contrib/mmg3d/LICENCE.txt
@@ -0,0 +1,621 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/contrib/mmg3d/README.txt b/contrib/mmg3d/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c296704020fbd14f7ffcac34bae3bf1d6ad50c27
--- /dev/null
+++ b/contrib/mmg3d/README.txt
@@ -0,0 +1,8 @@
+The terms under which this copy of the MMG3d 4.0 distribution
+is provided to you are described in file "LICENSE.txt", located
+in the same directory as this file.
+
+If you accept them, please refer to file "INSTALL.txt", also
+located in this directory, for the installation instructions.
+
+
diff --git a/contrib/mmg3d/build/CMakeLists.txt b/contrib/mmg3d/build/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1db395f1670b3a2093234817e386addf64e1117d
--- /dev/null
+++ b/contrib/mmg3d/build/CMakeLists.txt
@@ -0,0 +1,98 @@
+cmake_minimum_required (VERSION 2.6)
+project (MMG3D)     
+
+IF(APPLE) 
+   add_definitions(-static-libgcc -mmacosx-version-min=10.4 -arch ppc -arch i386)  
+
+   # determine if the processor supports 64bit execution
+   EXECUTE_PROCESS(
+      COMMAND sysctl hw.cpu64bit_capable
+      ERROR_QUIET
+      OUTPUT_VARIABLE 64_CMD
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+   )
+   STRING(REGEX REPLACE "^hw.cpu64bit_capable: (.*)" "\\1" 64_BIT "${64_CMD}")
+   #otherwise pbs with linkage SCOTCH
+   SET(CMAKE_OSX_ARCHITECTURES ppc)   
+   
+   # display the results
+   MESSAGE(STATUS "CMAKE_OSX_ARCHITECTURES: " ${CMAKE_OSX_ARCHITECTURES})
+ENDIF(APPLE)  
+
+
+
+# should we use SCOTCH
+option (USE_SCOTCH 
+        "Use SCOTCH TOOL for renumbering" ON) 
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file (
+  "sources/mmg3dConfig.h.in"
+  "sources/mmg3dConfig.h"
+  )
+
+# add SCOTCH library?
+#
+if (USE_SCOTCH)  
+  #Inclusion de SCOTCH
+  find_library(LIBS_SCOTCH scotch)
+  find_library(LIBS_SCOTCHERR scotcherr)
+  find_path(INCLUDE_SCOTCH scotch.h)
+  # IF(LIBS_SCOTCH_FOUND)
+  # 	MESSAGE(STATUS "Looking for SCOTCH - found")
+  # ELSE(SCOTCH_FOUND)
+  # 	MESSAGE(STATUS "Looking for SCOTCH - not found")
+  # ENDIF(SCOTCH_FOUND)   
+  include_directories(${INCLUDE_SCOTCH}) 
+endif (USE_SCOTCH)   
+
+#file sources
+file(
+	GLOB_RECURSE
+	source_files
+	sources/*
+)
+
+add_executable(mmg3d4.0 ${source_files})                                    
+
+option (COMPIL_STATIC_LIBRARY 
+        "Use tutorial provided math implementation" OFF) 
+if (COMPIL_STATIC_LIBRARY)  
+  add_library(mmg3dlib4.0 STATIC ${source_files})
+  target_link_libraries(mmg3dlib4.0 ${M_LIB} ${LIBS_SCOTCH} ${LIBS_SCOTCHERR})
+endif (COMPIL_STATIC_LIBRARY)   
+option (COMPIL_SHARED_LIBRARY 
+        "Use tutorial provided math implementation" OFF) 
+if (COMPIL_SHARED_LIBRARY)  
+  add_library(mmg3dlib4.0 SHARED ${source_files})
+  target_link_libraries(mmg3dlib4.0 ${M_LIB} ${LIBS_SCOTCH} ${LIBS_SCOTCHERR})
+endif (COMPIL_SHARED_LIBRARY)   
+
+find_library(M_LIB m)
+target_link_libraries(mmg3d4.0 ${M_LIB} ${LIBS_SCOTCH} ${LIBS_SCOTCHERR})    
+
+#add testlib   
+if (COMPIL_STATIC_LIBRARY) 
+  #file sources
+  file(
+  	GLOB_RECURSE
+  	source_testlib
+  	libexamples/*
+  )
+  add_executable(testlib ${source_testlib}) 
+  include_directories(sources/)                                    
+  target_link_libraries(testlib mmg3dlib4.0)    
+endif (COMPIL_STATIC_LIBRARY)   
+
+if (COMPIL_SHARED_LIBRARY) 
+  #file sources
+  file(
+  	GLOB_RECURSE
+  	source_testlib
+  	libexamples/*
+  )
+  add_executable(testlib ${source_testlib}) 
+  include_directories(sources/)                                    
+  target_link_libraries(testlib mmg3dlib4.0)    
+endif (COMPIL_SHARED_LIBRARY)
diff --git a/contrib/mmg3d/build/libexamples/main.c b/contrib/mmg3d/build/libexamples/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..f9e29257ec23d58a868ec2ad0fce6591b75618a1
--- /dev/null
+++ b/contrib/mmg3d/build/libexamples/main.c
@@ -0,0 +1,129 @@
+/*Authors Cécile Dobrzynski
+
+Example for using mmg3dlib
+
+*/
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <float.h>
+
+#include "libmmg3d.h"
+int main(int argc,char *argv[]) {
+  MMG_pMesh  mmgMesh;
+  MMG_pSol   mmgSol;
+	int        opt[9],k;
+  
+  fprintf(stdout,"  -- TEST MMG3DLIB \n");
+
+  mmgMesh = (MMG_pMesh)calloc(1,sizeof(MMG_Mesh));
+  assert(mmgMesh);
+
+  /* allocation */
+  mmgMesh->np     = 12;
+  mmgMesh->nt     = 0;
+  mmgMesh->ne     = 12;
+
+
+  mmgMesh->npmax  = 500000;
+  mmgMesh->ntmax  = 1000000;
+  mmgMesh->nemax  = 3000000;
+
+  mmgMesh->point = (MMG_pPoint)calloc(mmgMesh->npmax+1,sizeof(MMG_Point));
+  assert(mmgMesh->point);
+  mmgMesh->tetra = (MMG_pTetra)calloc(mmgMesh->nemax+1,sizeof(MMG_Tetra));
+  assert(mmgMesh->tetra);
+  mmgMesh->tria  = (MMG_pTria)calloc(mmgMesh->ntmax+1,sizeof(MMG_Tria));
+  assert(mmgMesh->tria);
+  mmgMesh->adja = (int*)calloc(4*mmgMesh->nemax+5,sizeof(int));
+  assert(mmgMesh->adja);
+  mmgMesh->disp = (MMG_pDispl)calloc(1,sizeof(MMG_Displ));
+  assert(mmgMesh->disp);
+  mmgMesh->disp->mv = (double*)calloc(3*(mmgMesh->npmax + 1),sizeof(double));
+  assert(mmgMesh->disp->mv);
+  mmgMesh->disp->alpha = (short*)calloc(mmgMesh->npmax+1,sizeof(short));
+  assert(mmgMesh->disp->alpha);
+ 
+  /*coordinates vertices*/ 
+	mmgMesh->point[1].c[0]  = 0.;  mmgMesh->point[1].c[1]  = 0.; mmgMesh->point[1].c[2]  = 0.; mmgMesh->point[1].ref  = 0; 
+	mmgMesh->point[2].c[0]  = 0.5; mmgMesh->point[2].c[1]  = 0;  mmgMesh->point[2].c[2]  = 0;  mmgMesh->point[2].ref  = 0; 
+	mmgMesh->point[3].c[0]  = 0.5; mmgMesh->point[3].c[1]  = 0;  mmgMesh->point[3].c[2]  = 1;  mmgMesh->point[3].ref  = 0; 
+	mmgMesh->point[4].c[0]  = 0;   mmgMesh->point[4].c[1]  = 0;  mmgMesh->point[4].c[2]  = 1;  mmgMesh->point[4].ref  = 0; 
+	mmgMesh->point[5].c[0]  = 0;   mmgMesh->point[5].c[1]  = 1;  mmgMesh->point[5].c[2]  = 0;  mmgMesh->point[5].ref  = 0; 
+	mmgMesh->point[6].c[0]  = 0.5; mmgMesh->point[6].c[1]  = 1;  mmgMesh->point[6].c[2]  = 0;  mmgMesh->point[6].ref  = 0; 
+	mmgMesh->point[7].c[0]  = 0.5; mmgMesh->point[7].c[1]  = 1;  mmgMesh->point[7].c[2]  = 1;  mmgMesh->point[7].ref  = 0; 
+	mmgMesh->point[8].c[0]  = 0;   mmgMesh->point[8].c[1]  = 1;  mmgMesh->point[8].c[2]  = 1;  mmgMesh->point[8].ref  = 0; 
+	mmgMesh->point[9].c[0]  = 1;   mmgMesh->point[9].c[1]  = 0;  mmgMesh->point[9].c[2]  = 0;  mmgMesh->point[9].ref  = 0; 
+	mmgMesh->point[10].c[0] = 1;   mmgMesh->point[10].c[1] = 1;  mmgMesh->point[10].c[2] = 0;  mmgMesh->point[10].ref = 0; 
+	mmgMesh->point[11].c[0] = 1;   mmgMesh->point[11].c[1] = 0;  mmgMesh->point[11].c[2] = 1;  mmgMesh->point[11].ref = 0; 
+	mmgMesh->point[12].c[0] = 1;   mmgMesh->point[12].c[1] = 1;  mmgMesh->point[12].c[2] = 1;  mmgMesh->point[12].ref = 0; 
+  
+  /*tetra*/
+	mmgMesh->tetra[1].v[0]  = 1;  mmgMesh->tetra[1].v[1]  = 2;  mmgMesh->tetra[1].v[2]  = 4;  mmgMesh->tetra[1].v[3]  = 8;  mmgMesh->tetra[1].ref  = 1; 
+	mmgMesh->tetra[2].v[0]  = 8;  mmgMesh->tetra[2].v[1]  = 3;  mmgMesh->tetra[2].v[2]  = 2;  mmgMesh->tetra[2].v[3]  = 7;  mmgMesh->tetra[2].ref  = 1; 
+	mmgMesh->tetra[3].v[0]  = 2;  mmgMesh->tetra[3].v[1]  = 5;  mmgMesh->tetra[3].v[2]  = 6;  mmgMesh->tetra[3].v[3]  = 8;  mmgMesh->tetra[3].ref  = 1; 
+	mmgMesh->tetra[4].v[0]  = 8;  mmgMesh->tetra[4].v[1]  = 5;  mmgMesh->tetra[4].v[2]  = 1;  mmgMesh->tetra[4].v[3]  = 2;  mmgMesh->tetra[4].ref  = 1; 
+	mmgMesh->tetra[5].v[0]  = 2;  mmgMesh->tetra[5].v[1]  = 7;  mmgMesh->tetra[5].v[2]  = 8;  mmgMesh->tetra[5].v[3]  = 6;  mmgMesh->tetra[5].ref  = 1; 
+	mmgMesh->tetra[6].v[0]  = 2;  mmgMesh->tetra[6].v[1]  = 4;  mmgMesh->tetra[6].v[2]  = 3;  mmgMesh->tetra[6].v[3]  = 8;  mmgMesh->tetra[6].ref  = 1; 
+	mmgMesh->tetra[7].v[0]  = 2;  mmgMesh->tetra[7].v[1]  = 9;  mmgMesh->tetra[7].v[2]  = 3;  mmgMesh->tetra[7].v[3]  = 7;  mmgMesh->tetra[7].ref  = 2; 
+	mmgMesh->tetra[8].v[0]  = 7;  mmgMesh->tetra[8].v[1]  = 11; mmgMesh->tetra[8].v[2]  = 9;  mmgMesh->tetra[8].v[3]  = 12; mmgMesh->tetra[8].ref  = 2; 
+	mmgMesh->tetra[9].v[0]  = 9;  mmgMesh->tetra[9].v[1]  = 6;  mmgMesh->tetra[9].v[2]  = 10; mmgMesh->tetra[9].v[3]  = 7;  mmgMesh->tetra[9].ref  = 2; 
+	mmgMesh->tetra[10].v[0] = 7;  mmgMesh->tetra[10].v[1] = 6;  mmgMesh->tetra[10].v[2] = 2;  mmgMesh->tetra[10].v[3] = 9;  mmgMesh->tetra[10].ref = 2;
+	mmgMesh->tetra[11].v[0] = 9;  mmgMesh->tetra[11].v[1] = 12; mmgMesh->tetra[11].v[2] = 7;  mmgMesh->tetra[11].v[3] = 10; mmgMesh->tetra[11].ref = 2; 
+	mmgMesh->tetra[12].v[0] = 9;  mmgMesh->tetra[12].v[1] = 3;  mmgMesh->tetra[12].v[2] = 11; mmgMesh->tetra[12].v[3] = 7;  mmgMesh->tetra[12].ref = 2;
+  
+  
+  /*metric*/
+  mmgSol           = (MMG_pSol)calloc(1,sizeof(MMG_Sol));
+  assert(mmgSol); 
+  mmgSol->offset = 1;
+
+  /*scalaire size*/
+	mmgSol->np = mmgMesh->np;
+	mmgSol->npmax = mmgMesh->npmax;
+  mmgSol->met    = (double*)calloc(mmgSol->npmax+1,(int)mmgSol->offset*sizeof(double));
+  assert(mmgSol->met);  
+  mmgSol->metold = (double*)calloc(mmgSol->npmax+1,mmgSol->offset*sizeof(double));
+  assert(mmgSol->metold);
+	for(k=1 ; k<=mmgMesh->np ; k++) {
+		mmgSol->met[k] = 0.5;           
+	}
+   
+  opt[0]=4; //splitting
+  opt[1]=0; //debug
+  opt[2]=64; //par default 64
+  opt[3]=0;//noswap
+  opt[4]=0;//noinsert
+  opt[5]=0;//nomove
+  opt[6]=5; //imprim
+	opt[7]=3;  //renum
+	opt[8]=500; //renum
+  
+  if(MMG_mmg3dlib(opt,mmgMesh,mmgSol)) {
+		fprintf(stdout,"BAD ENDING OF MMG3DLIB\n");
+  }
+
+  /*save result*/
+	MMG_saveMesh(mmgMesh,"result.mesh");
+
+  /*save metric*/
+	MMG_saveSol(mmgMesh,mmgSol,"result.sol");
+
+  /* free mem */
+  free(mmgMesh->point);
+  free(mmgMesh->disp->alpha);
+  free(mmgMesh->disp->mv);
+  free(mmgMesh->disp);
+  free(mmgMesh->tria);
+  free(mmgMesh->tetra);
+  free(mmgMesh);
+  if ( mmgSol->npfixe )  free(mmgSol->met);
+  free(mmgSol);
+
+
+  return(0);
+}
diff --git a/contrib/mmg3d/build/sources/analar.c b/contrib/mmg3d/build/sources/analar.c
new file mode 100644
index 0000000000000000000000000000000000000000..4f56e8e3f652d3132f40021681c77f0979f31a76
--- /dev/null
+++ b/contrib/mmg3d/build/sources/analar.c
@@ -0,0 +1,357 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+
+
+
+#include "mesh.h"
+
+#define EPS4  1.e-04
+
+extern int MMG_npuiss,MMG_nvol,MMG_npres;
+extern int MMG_nlen,MMG_ncal,MMG_ntopo,MMG_nex;
+extern int MMG_npuisstot,MMG_nvoltot,MMG_nprestot;
+extern int MMG_npdtot;
+       int MMG_nplen,MMG_npref,MMG_bouffe;
+
+int MMG_interp_ani(double *ma,double *mb,double *mp,double t) {
+  double	dma[6],dmb[6],mai[6],mbi[6],mi[6];
+  int		i;
+
+  for (i=0; i<6; i++) {
+    dma[i] = ma[i];
+    dmb[i] = mb[i];
+  }
+  if ( !MMG_invmat(dma,mai) || !MMG_invmat(dmb,mbi) ) {
+    fprintf(stderr,"  ## INTERP INVALID METRIC.\n");
+    return(0);
+  }
+  for (i=0; i<6; i++)
+    mi[i] = (1.0-t)*mai[i] + t*mbi[i];
+    
+  if ( !MMG_invmat(mi,mai) ) {
+    fprintf(stderr,"  ## INTERP INVALID METRIC.\n");
+    return(0);
+  } 
+  
+  for (i=0; i<6; i++)  mp[i] = mai[i];
+
+  return(1);
+}
+
+int MMG_interplog(double *ma,double *mb,double *mp,double *mplog,double t) {
+  double	dma[6],dmb[6],mai[6],mi[6];
+  int		i,ii,jj,kk;
+  double	lambda[3],v[3][3];
+
+  for (i=0; i<6; i++) {
+    dma[i] = ma[i];
+    dmb[i] = mb[i];
+  }
+  
+  for (i=0; i<6; i++)
+    mi[i] = (1.0-t)*dma[i] + t*dmb[i];
+  
+  /*pour metrique log : extraction vp et exponentielle*/
+  if ( !eigenv(1,mi,lambda,v) ) {
+	     puts("pbs eigen interp"); 
+	     return(0);
+   }
+   for (i=0; i<3; i++) lambda[i] = exp(lambda[i]);
+   kk = 0;
+   for (ii=0; ii<3; ii++) {
+     for (jj=ii; jj<3; jj++) {
+       mai[kk] = lambda[0]*v[0][ii]*v[0][jj] + 
+	             lambda[1]*v[1][ii]*v[1][jj] +
+                 lambda[2]*v[2][ii]*v[2][jj]; 
+       kk = kk+1;
+    }                     
+  }
+  
+  /*if ( !MMG_invmat(mi,mai) ) {
+    fprintf(stderr,"  ## INTERP INVALID METRIC.\n");
+    return(0);
+  } */
+  
+  for (i=0; i<6; i++)  mplog[i] = mi[i];  
+  for (i=0; i<6; i++)  mp[i] = mai[i];
+
+  return(1);
+}
+int MMG_interp_iso(double *ma,double *mb,double *mp,double t) {
+
+  *mp = (1.0-t)*(*ma) + t*(*mb);
+  return(1);
+}
+//#define LLONG 1.1
+//#define LSHORT 0.9                          
+/* optimisation based on edge lengths */
+int MMG_analar(pMesh mesh,pSol sol,pBucket bucket,int *na,int *nd,int *nf,int *alert) {
+  pTetra	pt;
+  pPoint	pa,pb;
+  List		list;
+  double	len,coef,siz,t1,declic,*ma,*mb,*mip,*ca,*cb,mp[6],c[3]; 
+  //double  *malog,*mblog,mplog[6];
+  int		  i,k,lon,nad,ndd,npp,npd,ia,ib,ip,ipa,ipb,nedep,base,ifilt;
+  int		  *adja,adj,ret,vois[4],ref,tag,iadr,j,imax;
+  char		tabar,tagedg;
+  int     MMG_ncavity;
+    
+  /* for Delaunay cavity */
+  if ( !MMG_zaldy4(&list.hedg,3*LONMAX) ) {
+    fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM.\n");
+    return(0);
+  }
+
+MMG_npuiss=0;
+MMG_npres=0;
+MMG_nvol=0;    
+MMG_ncavity=0;
+MMG_nplen=0;
+MMG_npref=0;
+MMG_nlen = 0;
+MMG_ncal = 0;
+MMG_ntopo = 0;
+MMG_nex = 0;
+MMG_bouffe = 0;
+
+  npp = 0;
+  nad = 0;
+  ndd = 0;
+  npd = 0;
+  coef  = QDEGRAD;//1.;//QDEGRAD;
+  ifilt = 0;
+  nedep = mesh->ne;
+  base  = ++mesh->flag;
+
+  declic = 1.5/ALPHAD;// 60.*LLONG;
+  
+  for (k=1; k<=nedep; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    //    else if ( pt->flag != base-1 )  continue; 
+    if ( pt->qual < declic ) continue;
+    pt->flag = base-2;                
+        
+    /* mark internal edges */
+    tabar  = 0;
+    tagedg = 0;
+    iadr  = 4*(k-1) + 1;
+    adja  = &mesh->adja[iadr];
+    vois[0]  = adja[0] >> 2;
+    vois[1]  = adja[1] >> 2;
+    vois[2]  = adja[2] >> 2;
+    vois[3]  = adja[3] >> 2;
+    for (i=0; i<4; i++) {
+      adj    = vois[i];
+      ref    = mesh->tetra[adj].ref;
+      tag    = mesh->tetra[adj].flag;
+      if ( !adj || pt->ref != ref ) {
+        tabar |= 1 << MMG_iarf[i][0];
+        tabar |= 1 << MMG_iarf[i][1];
+        tabar |= 1 << MMG_iarf[i][2];
+      }
+      if ( adj && tag == base - 2 ) {
+        tagedg |= 1 << MMG_iarf[i][0];
+        tagedg |= 1 << MMG_iarf[i][1];
+        tagedg |= 1 << MMG_iarf[i][2];
+      }
+      
+    }
+    if ( (tabar == ALL_BDRY) || (tagedg == ALL_BDRY) )  continue;
+    
+    //imax = ((int) pt->qual)%6;
+    imax = 0;
+    
+    for (j=imax; j<imax+6; j++) {
+      i = j;
+      if ( (tabar & 1<<i) || (tagedg & 1<<i) )  continue;
+        
+        /* edge length */
+        ia  = MMG_iare[i][0];
+        ib  = MMG_iare[i][1];
+        ipa = pt->v[ia];
+        ipb = pt->v[ib];
+        
+        ca  = &mesh->point[ipa].c[0];
+        cb  = &mesh->point[ipb].c[0];
+        
+        iadr = (ipa-1)*sol->offset + 1;
+        ma  = &sol->met[iadr];
+        
+        iadr = (ipb-1)*sol->offset + 1;
+        mb  = &sol->met[iadr];        
+              
+        len = MMG_length(ca,cb,ma,mb);
+        
+        if ( len > LLONG && *alert != 1 ) {
+          npp++;
+        
+          siz=0.5;
+
+          /* metric interpolation */
+          if ( sol->offset==1 ) {   
+            if(!MMG_interp(ma,mb,mp,siz) ) continue;
+          }    
+          else {
+            iadr = (ipa-1)*sol->offset + 1;
+            //malog  = &sol->metold[iadr];
+          
+            iadr = (ipb-1)*sol->offset + 1;
+            //mblog  = &sol->metold[iadr];
+            //if ( !MMG_interplog(malog,mblog,mp,mplog,siz) ) continue; 
+            if ( !MMG_interp_ani(ma,mb,mp,siz) ) continue;      
+          }
+          
+          t1   = 1.0 - siz;
+          c[0] = t1*ca[0] +  siz*cb[0];
+          c[1] = t1*ca[1] +  siz*cb[1];
+          c[2] = t1*ca[2] +  siz*cb[2]; 
+          //printf("siz %e new len %e - %e (%e) %d %d\n", siz,MMG_length(ca,c,ma,mb),MMG_length(cb,c,ma,mb),len,(int)(len+0.5),nbp);
+          ip   = MMG_newPt(mesh,c);
+          if ( ip < 1 )  {
+    	    *alert = 1;
+            break;
+          }
+    	    else {
+            iadr = (ip-1)*sol->offset + 1;
+            //mipold  = &sol->metold[iadr];	  
+    	      //memcpy(mipold,mplog,sol->offset*sizeof(double));
+            mip  = &sol->met[iadr];	  
+    	      memcpy(mip,mp,sol->offset*sizeof(double));
+            
+            /* bucket filter */
+            if (!MMG_buckin(mesh,sol,bucket,ip) ) {
+              MMG_delPt(mesh,ip);
+              ifilt++;
+              continue;
+            }
+            
+    	      /* Delaunay kernel */
+            lon = MMG_coquil(mesh,k,i,&list);    
+            lon = MMG_cavity(mesh,sol,k,ip,&list,lon);
+            if ( lon < 1 ) {
+              MMG_delPt(mesh,ip);    
+    	        npd++;
+              if ( lon == -1 ) {  
+                MMG_ncavity++;                
+    				    //printf("cavity pete\n");
+                *alert = 2;
+              } else if ( lon < 0 ) {
+    	          *alert = 1;
+    	          break;
+    	        }
+              else {  	      
+    	          continue;
+    	        }
+    	      }
+    	      else {
+    	        ret = MMG_delone(mesh,sol,ip,&list,lon);
+    	        if ( ret > 0 ) {
+                MMG_addBucket(mesh,bucket,ip);
+                nad++;
+    	          *alert = 0;
+              }
+              else if ( ret == 0 ) {
+    	          MMG_delPt(mesh,ip);
+    	          npd++;
+                *alert = 1;
+                break;
+              }
+    	        else {
+    	          MMG_delPt(mesh,ip);
+    	          npd++;
+    	          MMG_bouffe++;
+    	        }
+    	      } 
+          }
+    	    break;
+        }
+
+        else if ( len < LSHORT ) {
+          npp++;
+    	    pa = &mesh->point[ipa];
+    	    pb = &mesh->point[ipb];
+    	    if ( MMG_colpoi(mesh,sol,k,ia,ib,coef) ) {
+    	      MMG_delBucket(mesh,bucket,ipb);
+            MMG_delPt(mesh,ipb);
+            ndd++; 
+    	      break;
+    	    }
+    	    else if ( MMG_colpoi(mesh,sol,k,ib,ia,coef) ) {
+    	      MMG_delBucket(mesh,bucket,ipa);
+            MMG_delPt(mesh,ipa);
+    	      ndd++;            
+    	      break;
+    	    } 
+        } 
+      }
+      if ( *alert == 1 )  break;
+    }
+
+  *na  = nad;
+  *nd  = ndd;
+  *nf += ifilt;
+  if ( abs(mesh->info.imprim) > 5 || mesh->info.ddebug ) {  
+    printf("analyzed %d \n",npp);
+    printf("rejected colpoi : cal %d  , len %d , topo %d , ex %d\n",MMG_ncal,MMG_nlen,MMG_ntopo,MMG_nex);
+    MMG_npdtot+=npd;
+    MMG_nvoltot+=MMG_nvol;
+    MMG_npuisstot+=MMG_npuiss;
+    MMG_nprestot+=MMG_npres;
+    if (npd>0) {
+      printf("rejected %d : cavity %d vol %d  , puiss %d , pres %d  bouffe %d\n",npd,MMG_ncavity,MMG_nvol,MMG_npuiss,MMG_npres,MMG_bouffe);
+    }
+  }
+
+  if ( *alert == 1 ) {
+    fprintf(stdout,"  ## UNABLE TO CREATE NEW ELEMENT %d , %d\n",
+            mesh->np,mesh->ne);
+  } else *alert = 0;
+  M_free(list.hedg.item);
+  return(1);
+}
+
+
diff --git a/contrib/mmg3d/build/sources/analarcutting.c b/contrib/mmg3d/build/sources/analarcutting.c
new file mode 100644
index 0000000000000000000000000000000000000000..a2f6bef29ec59c553c08af78bf057797174f7b37
--- /dev/null
+++ b/contrib/mmg3d/build/sources/analarcutting.c
@@ -0,0 +1,320 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+
+//#define LLLONG   3.//2.5
+
+int MMG_permar[12][4] = {{0,1,2,3},{0,2,3,1},{2,0,1,3},{0,3,1,2},{1,0,3,2},{3,2,1,0},
+                         {3,0,2,1},{1,2,0,3},
+                         {3,1,0,2},{2,3,0,1},{2,1,3,0},{1,3,2,0}};
+
+int MMG_pointar[64][2]  = {{-1,-1},
+  {0,1},
+  {2,1},
+  {4,22},
+  {6,1},
+  {6,22},
+  {2,22},
+  {10,31},
+  {7,1},  
+  {10,22},
+  {1,22},
+  {10,3},
+  {0,2},
+  {11,33},
+  {4,32},
+  {4,4},
+  {8,1},
+  {0,22},
+  {7,2},
+  {10,32},
+  {11,22},
+  {0,3},
+  {0,33},
+  {6,4},
+  {8,22},
+  {9,31},
+  {4,33},
+  {10,4},
+  {0,32},
+  {0,4},
+  {2,41},
+  {9,5}, 
+  {9,1},
+  {2,2},
+  {5,22},
+  {10,33},
+  {3,22},
+  {2,32},
+  {2,3},
+  {2,4},
+  {7,22},
+  {1,32},
+  {3,31},
+  {1,4},
+  {2,33},
+  {7,41},
+  {5,4},
+  {11,5},
+  {9,22},
+  {6,33},
+  {7,32},
+  {0,41},
+  {0,31},
+  {11,4},
+  {3,4},
+  {7,5},
+  {7,3},
+  {8,4},
+  {7,4},
+  {3,5},
+  {9,4},
+  {1,5},
+  {0,5},
+  {0,6}
+ };
+   
+int MMG_createPoint(pMesh mesh, pSol sol, int ipa, int ipb) { 
+  double    *ca,*cb,*ma,*mb,*mip,mp[6],*mipold,mplog[6],c[3];
+  //double    *malog,*mblog;
+  int       iadr,ip;
+
+  ca  = &mesh->point[ipa].c[0];
+  cb  = &mesh->point[ipb].c[0];
+
+  iadr = (ipa-1)*sol->offset + 1;
+  ma  = &sol->met[iadr];
+  iadr = (ipb-1)*sol->offset + 1;
+  mb  = &sol->met[iadr];        
+
+  c[0] = 0.5*(ca[0] + cb[0]);
+  c[1] = 0.5*(ca[1] + cb[1]);   
+  c[2] = 0.5*(ca[2] + cb[2]);   
+  
+  
+  ip = MMG_newPt(mesh,c);  
+
+  /* metric interpolation */
+  if ( sol->offset==1 ) {   
+    if ( !MMG_interp(ma,mb,mp,0.5) )  return(0);
+  }    
+  else { 
+    iadr = (ipa-1)*sol->offset + 1;
+    //malog  = &sol->metold[iadr];
+
+    iadr = (ipb-1)*sol->offset + 1;
+    //mblog  = &sol->metold[iadr];
+    //if ( !MMG_interplog(malog,mblog,mp,mplog,0.5) ) return(0);      
+    if ( !MMG_interp_ani(ma,mb,mp,0.5) ) return(0);      
+  }
+  iadr = (ip-1)*sol->offset + 1;
+  mipold  = &sol->metold[iadr];   
+  memcpy(mipold,mplog,sol->offset*sizeof(double));
+  mip  = &sol->met[iadr];   
+  memcpy(mip,mp,sol->offset*sizeof(double));
+
+  return(ip);  
+}
+
+extern int ddebug;
+int MMG_analarcutting(pMesh mesh,pSol sol,pHedge hash,int *alert,double* lmoy,double LLLONG) {
+  pTetra    pt;
+  int       k,i,ia,ib,ip,ipa,ipb,iadr,na,ncut;
+  double    *ca,*cb,len,*ma,*mb;  
+  int       nb[7],ne,base;
+  int       ned;
+  int       n1,n2,n3,n4,n5,n6,n22,n31,n32,n33,n41;
+  
+  n1 = n2 = n3 = n4 = n5 = n6 = n22 = n31 = n32 = n33 = n41 = 0;   
+  na     = 0;
+	*alert = 0; 
+  ne     = mesh->ne; 
+  base   = ++mesh->flag;  
+  
+  ned   = 0;
+  *lmoy = 0;
+  for (k=1; k<=ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue; 
+    else if ( pt->flag == base ) continue; //attention si Delaunay avant on peut avoir des trous dans le tab des tets...
+    //normalement on devrait pouvoir juste traiter les tets à base-1... non ?    
+    pt->tabedg = 0; //because used by cendel
+    assert(!pt->tabedg);
+    
+    ncut  = 0;    
+    for (i=0; i<6; i++) {
+      ia  = MMG_iare[i][0];
+      ib  = MMG_iare[i][1];
+      ipa = pt->v[ia];
+      ipb = pt->v[ib];
+      /* already cutted ? */
+      nb[i] = MMG_edgePoint(hash,ipa,ipb);
+      if ( nb[i] ) {
+        //if(nb[i]==6992) printf("already cut %d %d : %d -- %d\n",ipa,ipb,nb[i],k); 
+        pt->tabedg |= 1 << i;
+        ncut++;
+        continue;
+      }
+      ca  = &mesh->point[ipa].c[0];
+      cb  = &mesh->point[ipb].c[0];
+  
+      iadr = (ipa-1)*sol->offset + 1;
+      ma  = &sol->met[iadr];
+      iadr = (ipb-1)*sol->offset + 1;
+      mb  = &sol->met[iadr];        
+
+      /* edge length */  
+      len = MMG_length(ca,cb,ma,mb); 
+      *lmoy += len;
+      ned++;
+      if ( len > LLLONG ) { 
+        ip = MMG_createPoint(mesh,sol,ipa,ipb);
+        //if(ip==6992) printf("on cree %d : %d -- %d %d %d %d\n",ip,k,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+        if ( !ip ) {
+          *alert = 1;
+          return(0);
+        }        
+        /*hash insertion*/ 
+        if ( !MMG_edgePut(hash,ipa,ipb,ip) ) {
+          printf("ahhhhhhhhhhhhhhhhh %d %d\n",ipa,ipb); 
+          exit(0);
+        }
+        nb[i] = ip;
+        pt->tabedg |= 1 << i;
+        na++; 
+        ncut++;                   
+      } 
+    }
+    //if(if(mesh->info.ddebug)) printf("tet %d ncut %d : %d %d\n",k,ncut,pt->tabedg,MMG_pointar[pt->tabedg][1]); 
+    //if(ddebug && ncut) printf("tet %d %d %d %d\n",pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+    if (!ncut) continue;
+    else if ( MMG_pointar[pt->tabedg][1] > -1 ) {
+			if(mesh->info.ddebug){
+				printf("tet %d : %d\n",k,MMG_pointar[pt->tabedg][1]);
+			  printf("pour ce tet ref : %d %d %d %d\n",pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+      }
+      switch(MMG_pointar[pt->tabedg][1]) {
+        case 1: 
+          n1++;
+          MMG_pattern1(mesh,sol,hash,k);
+          break;
+        case 2:
+          n2++;
+          MMG_pattern2(mesh,sol,hash,k);
+          break;
+        case 3:
+          n3++;
+          MMG_pattern3(mesh,sol,hash,k);
+          break;
+        case 4:
+          MMG_pattern4(mesh,sol,hash,k);
+          n4++;
+          break; 
+        case 5:
+          MMG_pattern5(mesh,sol,hash,k);
+          n5++;
+          break;
+        case -1:
+          puts("MMG_analar case -1");
+          exit(0);
+        case 6:
+          MMG_pattern6(mesh,sol,k,nb); 
+          n6++; 
+          break; 
+        case 22:
+          MMG_pattern22(mesh,sol,hash,k); 
+          n22++;
+          break;
+        case 31:
+          MMG_pattern31(mesh,sol,hash,k);  
+          n31++;
+          break;
+        case 32:
+          MMG_pattern32(mesh,sol,hash,k);  
+          n32++;
+          break;
+        case 33:
+          MMG_pattern33(mesh,sol,hash,k);   
+          n33++;
+          break;
+        case 41:
+          MMG_pattern41(mesh,sol,hash,k);   
+          n41++;
+          break;
+          
+      } 
+			//       if ( 1 ){printf("pointar tet 6545 : %d %d %d %d %d\n",
+			//                       MMG_pointar[pt->tabedg][1],mesh->tetra[6545].v[0],mesh->tetra[6545].v[1]
+			//                                         ,mesh->tetra[6545].v[2],mesh->tetra[6545].v[3]);
+			//        printf("bdry ref : %d %d %d %d\n",mesh->tetra[6545].bdryref[0],mesh->tetra[6545].bdryref[1]
+			//                                          ,mesh->tetra[6545].bdryref[2],mesh->tetra[6545].bdryref[3]);}
+  		//if(mesh->ne>=41495) {exit(0);  }
+    
+    }/*end if pointar > -1*/   
+  } 
+  *lmoy /= ned;
+  
+  /*puts("stats cut -------------------"); 
+  printf("1 cut : %8d\n",n1);
+  printf("2 cut : %8d %8d\n",n2,n22);
+  printf("3 cut : %8d %8d %8d %8d\n",n3,n31,n32,n33);
+  printf("4 cut : %8d %8d\n",n4,n41);
+  printf("5 cut : %8d\n",n5);
+  printf("6 cut : %8d\n",n6);
+  printf("---------------------------\n"); */  
+	if ( !na )  return(na);
+#if !defined(_MSC_VER)
+#warning check memory allocation
+#endif
+  //printf("%d cut init --- nb tet %d\n",na,mesh->ne);
+  return(na);
+}
+
+
+
+
+
diff --git a/contrib/mmg3d/build/sources/baryct.c b/contrib/mmg3d/build/sources/baryct.c
new file mode 100644
index 0000000000000000000000000000000000000000..138f158f9e55fcff6eaf17ed6e027657bbeb6d28
--- /dev/null
+++ b/contrib/mmg3d/build/sources/baryct.c
@@ -0,0 +1,112 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define EPST    -1.e-14
+#define EPSR     1.e+14
+
+
+/* compute barycentrics */
+int MMG_baryct(pMesh mesh,pTetra pt,double p[3],double cb[4]) {
+  pPoint	p0,p1,p2,p3;
+  double	bx,by,bz,cx,cy,cz,dx,dy,dz,vx,vy,vz,apx,apy,apz;
+  double	epsra,vol1,vol2,vol3,vol4,dd; 
+
+  p0 = &mesh->point[pt->v[0]];
+  p1 = &mesh->point[pt->v[1]];
+  p2 = &mesh->point[pt->v[2]];
+  p3 = &mesh->point[pt->v[3]];
+
+  /* barycentric */
+  bx  = p1->c[0] - p0->c[0];
+  by  = p1->c[1] - p0->c[1];
+  bz  = p1->c[2] - p0->c[2];
+  cx  = p2->c[0] - p0->c[0];
+  cy  = p2->c[1] - p0->c[1];
+  cz  = p2->c[2] - p0->c[2];
+  dx  = p3->c[0] - p0->c[0];
+  dy  = p3->c[1] - p0->c[1];
+  dz  = p3->c[2] - p0->c[2];
+
+  /* test volume */
+  vx  = cy*dz - cz*dy;
+  vy  = cz*dx - cx*dz;
+  vz  = cx*dy - cy*dx;
+
+  epsra = EPST*(bx*vx + by*vy + bz*vz);
+  apx = p[0] - p0->c[0];
+  apy = p[1] - p0->c[1];
+  apz = p[2] - p0->c[2];
+
+  /* p in 2 */
+  vol2  = apx*vx + apy*vy + apz*vz;
+  if ( epsra > vol2 )  return(0);
+
+  /* p in 3 */
+  vx  = by*apz - bz*apy;
+  vy  = bz*apx - bx*apz;
+  vz  = bx*apy - by*apx;
+  vol3 = dx*vx + dy*vy + dz*vz;
+  if ( epsra > vol3 )  return(0);
+    
+  /* p in 4 */
+  vol4 = -cx*vx - cy*vy - cz*vz;
+  if ( epsra > vol4 )  return(0);
+  
+  /* p in 1 */
+  vol1 = -epsra * EPSR - vol2 - vol3 - vol4;
+  if ( epsra > vol1 )  return(0);
+
+  dd = vol1+vol2+vol3+vol4;
+  if ( dd != 0.0 )  dd = 1.0 / dd;
+  cb[0] = vol1 * dd;
+  cb[1] = vol2 * dd;
+  cb[2] = vol3 * dd;
+  cb[3] = vol4 * dd; 
+
+  return(1);
+}
+
diff --git a/contrib/mmg3d/build/sources/boulep.c b/contrib/mmg3d/build/sources/boulep.c
new file mode 100644
index 0000000000000000000000000000000000000000..001bd54e92f114cba7d7e647e4ad911ed794c9df
--- /dev/null
+++ b/contrib/mmg3d/build/sources/boulep.c
@@ -0,0 +1,206 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+
+/* find all tets sharing P
+   in:  start : tetrahedron containing p 
+        ip    : index of p in start
+        list  : dynamic list structure (allocated outside)
+   out: list  : list of tets */
+int MMG_boulep(pMesh mesh,int start,int ip,pList list) {
+  pTetra	pt,pt1;
+  pPoint	ppt;
+  int		*adja,adj,i,j,indp,iel,iadr,base,ilist,nump;
+  int 		vois[4];
+
+  if ( start < 1 )  return(0);
+  pt   = &mesh->tetra[start];
+  if ( !pt->v[0] )  return(0);
+  nump = pt->v[ip];
+  ppt  = &mesh->point[nump];
+  if ( ppt->tag & M_BDRY || ppt->tag & M_UNUSED )  return(0);
+
+  /* store initial tet */
+  base     = ++mesh->mark;
+  pt->mark = base;
+  ilist    = 1;
+  list->tetra[ilist] = 4*start + ip;
+
+  /* store 3 neighbors sharing P */
+  iadr = (start-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  vois[0]  = adja[0] >> 2;
+  vois[1]  = adja[1] >> 2;
+  vois[2]  = adja[2] >> 2;
+  vois[3]  = adja[3] >> 2;
+  for (i=0; i<4; i++) {
+    if ( i == ip )  continue;
+    adj = vois[i];
+    if ( adj ) {
+      pt1 = &mesh->tetra[adj];
+      if ( pt1->mark != base ) {
+				pt1->mark = base;
+				for (j=0; j<4; j++)
+          if ( pt1->v[j] == nump )  break;
+        ilist++;
+        list->tetra[ilist] = 4*adj + j;
+      }
+    }
+  }
+  if ( ilist < 2 )  return(ilist);
+
+  /* explore list of neighbors */
+  indp = 2;
+  do {
+    iel  = list->tetra[indp] >> 2;
+    pt   = &mesh->tetra[iel];
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    vois[0]  = adja[0] >> 2;
+    vois[1]  = adja[1] >> 2;
+    vois[2]  = adja[2] >> 2;
+    vois[3]  = adja[3] >> 2;
+
+    for (i=0; i<4; i++) {
+      if ( pt->v[i] == nump )  continue;
+      adj = vois[i];
+      if ( adj ) {
+        pt1 = &mesh->tetra[adj];
+        if ( pt1->mark != base ) {
+	  			pt1->mark = base;
+	  			for (j=0; j<4; j++)
+            if ( pt1->v[j] == nump )  break;
+	  			ilist++;
+          list->tetra[ilist] = 4*adj + j;
+       }
+      }
+    }
+    /* overflow */
+    if ( ilist > LONMAX-3 )  return(-ilist);
+  }
+  while ( ++indp <= ilist );
+
+  return(ilist);
+}
+
+
+/* idem boulep for any vertex */
+int MMG_bouleg(pMesh mesh,int start,int ip,pList list) {
+  pTetra	pt,pt1;
+  pPoint	ppt;
+  int		*adja,adj,i,j,indp,iel,iadr,base,ilist,nump;
+  int		vois[4];
+  
+  if ( start < 1 )  return(0);
+  pt   = &mesh->tetra[start];
+  if ( !pt->v[0] )  return(0);
+  nump = pt->v[ip];
+  ppt  = &mesh->point[nump];
+  if ( ppt->tag & M_UNUSED )  return(0);
+
+  /* store initial tet */
+  base     = ++mesh->mark;
+  pt->mark = base;
+  ilist    = 1;
+  list->tetra[ilist] = 4*start + ip;
+
+  /* store 3 neighbors sharing P */
+  iadr = (start-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  vois[0]  = adja[0] >> 2;
+  vois[1]  = adja[1] >> 2;
+  vois[2]  = adja[2] >> 2;
+  vois[3]  = adja[3] >> 2;
+  for (i=0; i<4; i++) {
+    if ( i == ip )  continue;
+    adj = vois[i];
+    if ( adj ) {
+      pt1 = &mesh->tetra[adj];
+      if ( pt1->mark != base ) {
+	pt1->mark = base;
+	for (j=0; j<4; j++)
+          if ( pt1->v[j] == nump )  break;
+        ilist++;
+        list->tetra[ilist] = 4*adj + j;
+      }
+    }
+  }
+  if ( ilist < 2 )  return(ilist);
+
+  /* explore list of neighbors */
+  indp = 2;
+  do {
+    iel  = list->tetra[indp] >> 2;
+    pt   = &mesh->tetra[iel];
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    vois[0]  = adja[0] >> 2;
+    vois[1]  = adja[1] >> 2;
+    vois[2]  = adja[2] >> 2;
+    vois[3]  = adja[3] >> 2;
+
+    for (i=0; i<4; i++) {
+      if ( pt->v[i] == nump )  continue;
+      adj = vois[i];
+      if ( adj ) {
+        pt1 = &mesh->tetra[adj];
+        if ( pt1->mark != base ) {
+	  pt1->mark = base;
+	  for (j=0; j<4; j++)
+            if ( pt1->v[j] == nump )  break;
+	  ilist++;
+          list->tetra[ilist] = 4*adj + j;
+       }
+      }
+    }
+    /* overflow */
+    if ( ilist > LONMAX-3 )  return(-ilist);
+  }
+  while ( ++indp <= ilist );
+
+  return(ilist);
+}
diff --git a/contrib/mmg3d/build/sources/bucket.c b/contrib/mmg3d/build/sources/bucket.c
new file mode 100644
index 0000000000000000000000000000000000000000..7b8938b8dbe571e9d4b3727792000cdeb500cb21
--- /dev/null
+++ b/contrib/mmg3d/build/sources/bucket.c
@@ -0,0 +1,385 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/* create bucket structure and store initial vertices */
+pBucket MMG_newBucket(pMesh mesh,int nmax) {
+  pPoint	ppt;
+  pBucket	bucket;
+  double	dd;
+  int		k,ic,ii,jj,kk;
+
+  /* memory alloc */
+  bucket = (Bucket*)M_malloc(sizeof(Bucket),"newBucket");
+  assert(bucket);
+  bucket->size = nmax;
+  bucket->head = (int*)M_calloc(nmax*nmax*nmax+1,sizeof(int),"newBucket.head");
+  assert(bucket->head);
+  bucket->link = (int*)M_calloc(mesh->npmax+1,sizeof(int),"newBucket.link");
+  assert(bucket->link);
+
+  /* insert vertices */
+  dd = nmax / (double)PRECI;
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+    ii = M_MAX(0,(int)(dd * ppt->c[0])-1);
+    jj = M_MAX(0,(int)(dd * ppt->c[1])-1);
+    kk = M_MAX(0,(int)(dd * ppt->c[2])-1);
+    ic = (kk*nmax + jj)*nmax + ii;
+
+    if ( !bucket->head[ic] )
+      bucket->head[ic] = k;
+    else {
+      bucket->link[k]  = bucket->head[ic];
+      bucket->head[ic] = k;
+    }
+  }
+
+  return(bucket);
+}
+
+
+void MMG_freeBucket(pBucket bucket) {
+  M_free(bucket->head);
+  M_free(bucket->link);
+  M_free(bucket);
+}
+
+
+/* check and eventually insert vertex */
+int MMG_buckin_ani(pMesh mesh,pSol sol,pBucket bucket,int ip) {
+  pPoint	ppt,pp1;
+  double	dd,d2,det,ux,uy,uz,dmi,m1,m2,m3,dx,dy,dz;
+  double	*ma,*mb;
+  int		i,j,k,ii,jj,kk,ic,icc,siz,ip1;
+  int 		iadr,imin,imax,jmin,jmax,kmin,kmax;
+
+  ppt = &mesh->point[ip];
+  siz = bucket->size;
+  dd  = siz / (double)PRECI;
+
+  iadr = (ip-1)*sol->offset + 1;
+  ma   = &sol->met[iadr];
+  dmi  = LFILT*LFILT;
+
+  ii = M_MAX(0,(int)(dd * ppt->c[0])-1);
+  jj = M_MAX(0,(int)(dd * ppt->c[1])-1);
+  kk = M_MAX(0,(int)(dd * ppt->c[2])-1);
+  ic = (kk*siz + jj)*siz + ii;
+
+  /* check current cell */
+  if ( bucket->head[ic] ) {
+    ip1 = bucket->head[ic];
+    pp1 = &mesh->point[ip1];
+    ux = pp1->c[0] - ppt->c[0];
+    uy = pp1->c[1] - ppt->c[1];
+    uz = pp1->c[2] - ppt->c[2];
+    d2 =      ma[0]*ux*ux + ma[3]*uy*uy + ma[5]*uz*uz \
+       + 2.0*(ma[1]*ux*uy + ma[2]*ux*uz + ma[4]*uy*uz);
+    if ( d2 < dmi ) {
+      iadr = (ip1-1)*sol->offset + 1;
+      mb = &sol->met[iadr];
+      d2 =      mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+         + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+      if ( d2 < dmi )  return(0);
+    }
+
+    while ( bucket->link[ip1] ) {
+      ip1 = bucket->link[ip1];
+      pp1 = &mesh->point[ip1];
+      ux = pp1->c[0] - ppt->c[0];
+      uy = pp1->c[1] - ppt->c[1];
+      uz = pp1->c[2] - ppt->c[2];
+      d2 =      ma[0]*ux*ux + ma[3]*uy*uy + ma[5]*uz*uz \
+         + 2.0*(ma[1]*ux*uy + ma[2]*ux*uz + ma[4]*uy*uz);
+      if ( d2 < dmi ) {
+        iadr = (ip1-1)*sol->offset + 1;
+        mb = &sol->met[iadr];
+        d2 =      mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+           + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+        if ( d2 < dmi )  return(0);
+      }
+    }
+  }
+
+  /* bounding box */
+  det = ma[0] * (ma[3]*ma[5] - ma[4]*ma[4]) \
+      - ma[1] * (ma[1]*ma[5] - ma[2]*ma[4]) \
+      + ma[2] * (ma[1]*ma[4] - ma[3]*ma[2]);
+  det = 1.0 / det;
+  m1 = ma[3]*ma[5] - ma[4]*ma[4];
+  m2 = ma[0]*ma[5] - ma[2]*ma[2];
+  m3 = ma[0]*ma[3] - ma[1]*ma[1];
+  if ( det < 0.0 || m1 < 0.0 )
+    return(1);
+  else {
+    dx = LFILT * sqrt(m1 * det) ;
+    dy = LFILT * sqrt(m2 * det) ;
+    dz = LFILT * sqrt(m3 * det) ;
+  }
+
+  imin = (int)(dd * (ppt->c[0]-dx))-1;
+  jmin = (int)(dd * (ppt->c[1]-dy))-1;
+  kmin = (int)(dd * (ppt->c[2]-dz))-1;
+  imax = (int)(dd * (ppt->c[0]+dx))-1;
+  jmax = (int)(dd * (ppt->c[1]+dy))-1;
+  kmax = (int)(dd * (ppt->c[2]+dz))-1;
+
+  imin = M_MAX(0,M_MIN(imin,siz-1));
+  imax = M_MIN(siz-1,M_MAX(0,imax));
+  jmin = M_MAX(0,M_MIN(jmin,siz-1));
+  jmax = M_MIN(siz-1,M_MAX(0,jmax));
+  kmin = M_MAX(0,M_MIN(kmin,siz-1));
+  kmax = M_MIN(siz-1,M_MAX(0,kmax));
+  if ( imin == imax && jmin == jmax && kmin == kmax )  return(1);
+
+  /* explore neighbours */
+  for (k=kmin; k<=kmax; k++)
+    for (j=jmin; j<=jmax; j++) 
+      for (i=imin; i<=imax; i++) {
+        icc = (k*siz + j)*siz + i;
+        ip1 = bucket->head[icc];
+        if ( !ip1 )  continue;
+        pp1 = &mesh->point[ip1];
+        ux = pp1->c[0] - ppt->c[0];
+        uy = pp1->c[1] - ppt->c[1];
+        uz = pp1->c[2] - ppt->c[2];
+        d2 =      ma[0]*ux*ux + ma[3]*uy*uy + ma[5]*uz*uz \
+           + 2.0*(ma[1]*ux*uy + ma[2]*ux*uz + ma[4]*uy*uz);
+        if ( d2 < dmi ) {
+          iadr = (ip1-1)*sol->offset + 1;
+          mb = &sol->met[iadr];
+          d2 =      mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+             + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+          if ( d2 < dmi )  return(0);
+        }
+
+        while ( bucket->link[ip1] ) {
+          ip1 = bucket->link[ip1];
+          pp1 = &mesh->point[ip1];
+          ux = pp1->c[0] - ppt->c[0];
+          uy = pp1->c[1] - ppt->c[1];
+          uz = pp1->c[2] - ppt->c[2];
+          d2 =      ma[0]*ux*ux + ma[3]*uy*uy + ma[5]*uz*uz \
+             + 2.0*(ma[1]*ux*uy + ma[2]*ux*uz + ma[4]*uy*uz);
+          if ( d2 < dmi ) {
+            iadr = (ip1-1)*sol->offset + 1;
+            mb = &sol->met[iadr];
+            d2 =      mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+               + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+            if ( d2 < dmi )  return(0);
+          }
+        }
+      }
+
+  return(1);
+}
+
+
+int MMG_buckin_iso(pMesh mesh,pSol sol,pBucket bucket,int ip) {
+  pPoint	ppt,pp1;
+  double	dd,d2,ux,uy,uz,hpi,hp1,hp2;
+  int		i,j,k,ii,jj,kk,ic,icc,siz,ip1;
+  int 		imin,imax,jmin,jmax,kmin,kmax;
+
+  ppt = &mesh->point[ip];
+  siz = bucket->size;
+  dd  = siz / (double)PRECI;
+  hpi = LFILT * sol->met[ip];
+  hp1 = hpi*hpi;
+
+  ii = M_MAX(0,(int)(dd * ppt->c[0])-1);
+  jj = M_MAX(0,(int)(dd * ppt->c[1])-1);
+  kk = M_MAX(0,(int)(dd * ppt->c[2])-1);
+  ic = (kk*siz + jj)*siz + ii;
+
+  /* check current cell */
+  if ( bucket->head[ic] ) {
+    ip1 = bucket->head[ic];
+    pp1 = &mesh->point[ip1];
+    hp2 = LFILT * sol->met[ip1];
+    ux = pp1->c[0] - ppt->c[0];
+    uy = pp1->c[1] - ppt->c[1];
+    uz = pp1->c[2] - ppt->c[2];
+    d2 = ux*ux + uy*uy + uz*uz;
+    if ( d2 < hp1 || d2 < hp2*hp2 )  {
+//printf("filtre current %d : %e %e %e %e\n",ip1,d2,hp1,d2,hp2*hp2);
+      return(0);
+    }
+
+    while ( bucket->link[ip1] ) {
+      ip1 = bucket->link[ip1];
+      pp1 = &mesh->point[ip1];
+      hp2 = LFILT * sol->met[ip1];
+      ux = pp1->c[0] - ppt->c[0];
+      uy = pp1->c[1] - ppt->c[1];
+      uz = pp1->c[2] - ppt->c[2];
+      d2 = ux*ux + uy*uy + uz*uz;
+      if ( d2 < hp1 || d2 < hp2*hp2 )  {
+//printf("filtre link %d : %e %e %e %e\n",ip1,d2,hp1,d2,hp2*hp2);
+        return(0);
+      }
+    }
+  }
+
+  /* explore neighbors */
+  imin = (int)(dd * (ppt->c[0]-hpi))-1;
+  jmin = (int)(dd * (ppt->c[1]-hpi))-1;
+  kmin = (int)(dd * (ppt->c[2]-hpi))-1;
+  imax = (int)(dd * (ppt->c[0]+hpi))-1;
+  jmax = (int)(dd * (ppt->c[1]+hpi))-1;
+  kmax = (int)(dd * (ppt->c[2]+hpi))-1;
+
+  imin = M_MAX(0,M_MIN(imin,siz-1));
+  imax = M_MIN(siz-1,M_MAX(0,imax));
+  jmin = M_MAX(0,M_MIN(jmin,siz-1));
+  jmax = M_MIN(siz-1,M_MAX(0,jmax));
+  kmin = M_MAX(0,M_MIN(kmin,siz-1));
+  kmax = M_MIN(siz-1,M_MAX(0,kmax));
+  if ( imin == imax && jmin == jmax && kmin == kmax )  return(1);
+
+  for (k=kmin; k<=kmax; k++)
+    for (j=jmin; j<=jmax; j++) 
+      for (i=imin; i<=imax; i++) {
+        icc = (k*siz + j)*siz + i;
+        ip1 = bucket->head[icc];
+        if ( !ip1 )  continue;
+        pp1 = &mesh->point[ip1];
+        hp2 = LFILT * sol->met[ip1];
+        ux = pp1->c[0] - ppt->c[0];
+        uy = pp1->c[1] - ppt->c[1];
+        uz = pp1->c[2] - ppt->c[2];
+        d2 = ux*ux + uy*uy + uz*uz;
+        if ( d2 < hp1 || d2 < hp2*hp2 ) {
+/*	 printf("other cell %d %e < %e -- %e < %e \n",ip1,d2,MMG_length(mesh,sol,ip,ip1),d2,hp2*hp2);
+	 printf("on filtre avec %d : %e %e %e\n",ip1,pp1->c[0],pp1->c[1],pp1->c[2]);
+	*/ return(0);
+	}
+
+        while ( bucket->link[ip1] ) {
+          ip1 = bucket->link[ip1];
+          pp1 = &mesh->point[ip1];
+          hp2 = LFILT * sol->met[ip1];
+          ux = pp1->c[0] - ppt->c[0];
+          uy = pp1->c[1] - ppt->c[1];
+          uz = pp1->c[2] - ppt->c[2];
+          d2 = ux*ux + uy*uy + uz*uz;
+          if ( d2 < hp1 || d2 < hp2*hp2 )  {
+//	    printf("link cell %d %e < %e -- %e < %e \n",ip1,d2,hp1,d2,hp2*hp2);
+	    return(0);
+	  }
+        }
+      }
+
+  return(1);
+}
+
+
+int MMG_addBucket(pMesh mesh,pBucket bucket,int ip) {
+  pPoint	ppt;
+  double	dd;
+  int		ic,ii,jj,kk,siz;
+
+  ppt = &mesh->point[ip];
+  siz = bucket->size;
+  dd  = siz / (double)PRECI;
+
+  ii = M_MAX(0,(int)(dd * ppt->c[0])-1);
+  jj = M_MAX(0,(int)(dd * ppt->c[1])-1);
+  kk = M_MAX(0,(int)(dd * ppt->c[2])-1);
+  ic = (kk*siz + jj)*siz + ii;
+
+  /* store new point */
+  if ( !bucket->head[ic] ) {
+    bucket->head[ic] = ip;
+    bucket->link[ip] = 0;
+  }
+  else {
+    bucket->link[ip] = bucket->head[ic];
+    bucket->head[ic] = ip;
+    assert(ip!=bucket->link[ip]);
+  }
+
+  return(1);
+}
+
+
+int MMG_delBucket(pMesh mesh,pBucket bucket,int ip) {
+  pPoint	ppt;
+  double	dd;
+  int		ic,ii,jj,kk,siz,ip1;
+
+  ppt = &mesh->point[ip];
+  siz = bucket->size;
+  dd  = siz / (double)PRECI;
+
+  ii = M_MAX(0,(int)(dd * ppt->c[0])-1);
+  jj = M_MAX(0,(int)(dd * ppt->c[1])-1);
+  kk = M_MAX(0,(int)(dd * ppt->c[2])-1);
+  ic = (kk*siz + jj)*siz + ii;
+
+  /* remove vertex from cell */
+  if ( bucket->head[ic] ) {
+    if ( bucket->head[ic] == ip ) {
+      bucket->head[ic] = bucket->link[ip];
+      bucket->link[ip] = 0;
+    }
+    else {
+      ip1 = bucket->head[ic];
+      while ( ip1 && bucket->link[ip1] != ip ) {
+        ip1 = bucket->link[ip1];
+      }
+      if ( bucket->link[ip1] == ip ) {
+        bucket->link[ip1] = bucket->link[ip];
+        bucket->link[ip] = 0;
+      }
+    }
+  }
+
+  return(1);
+}
+
diff --git a/contrib/mmg3d/build/sources/cendel.c b/contrib/mmg3d/build/sources/cendel.c
new file mode 100644
index 0000000000000000000000000000000000000000..41eab921bb762dfdfd1b3d6df1780d83ddbeafb7
--- /dev/null
+++ b/contrib/mmg3d/build/sources/cendel.c
@@ -0,0 +1,222 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define SCRIT    0.95
+
+
+int MMG_cendel(pMesh mesh,pSol sol,double declic,int base) {
+  pTetra	pt,pt1;
+  pQueue	queue;
+  List		list;
+  double	crit;
+  int		*adja,adj,iadr,ier,i,j,k,jel,lon,ns,np;
+  int		vois[4],ref,tag;
+  char		tabar,done;
+
+  /* queue on quality */
+  queue = MMG_kiuini(mesh,mesh->nemax,declic,base - 1);
+  assert(queue);
+
+  ns = np = 0;
+  do {
+    k = MMG_kiupop(queue);
+    if ( !k )  break;
+    np++;
+
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    else if ( pt->flag < base - 1) continue;
+    else if ( pt->qual < declic ) continue;
+    
+    /* mark internal edges */
+    tabar  = 0;
+    iadr  = 4*(k-1) + 1;
+    adja  = &mesh->adja[iadr];
+    vois[0]  = adja[0] >> 2;
+    vois[1]  = adja[1] >> 2;
+    vois[2]  = adja[2] >> 2;
+    vois[3]  = adja[3] >> 2;
+    for (i=0; i<4; i++) {
+      adj = vois[i];
+      ref    = mesh->tetra[adj].ref;
+      tag    = mesh->tetra[adj].flag;
+      if ( !adj || pt->ref != ref ) {
+        tabar |= 1 << MMG_iarf[i][0];
+        tabar |= 1 << MMG_iarf[i][1];
+        tabar |= 1 << MMG_iarf[i][2];
+      }
+    }
+    if ( (tabar == ALL_BDRY) )  continue;
+    
+    /* swap for anisotropy */
+    done = 0;  
+    for (i=0; i<6; i++) { 
+      if ( (tabar & 1<<i) )  continue;
+
+      lon  = MMG_coquil(mesh,k,i,&list);
+      if ( lon < 3 || lon > 7 )  continue;
+
+      /* qual crit */
+      crit = pt->qual;
+      for (j=2; j<=lon; j++) {
+        jel  = list.tetra[j] / 6;
+        pt1  = &mesh->tetra[jel];
+        crit = M_MAX(crit,pt1->qual);
+      }
+      crit *= SCRIT; 
+      
+      ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,declic); 
+      if ( ier > 0 ) {
+        ns++;
+        break;
+      }
+      else if ( ier < 0 ) {
+        fprintf(stdout,"     %7d PROPOSED  %7d SWAPPED\n",np,ns);
+        fprintf(stdout,"  ## UNABLE TO SWAP.\n");
+        MMG_kiufree(queue);
+	    return(-ns);
+      } 
+    }
+  }
+  while ( k );
+
+  if ( mesh->info.imprim < - 4 )
+    fprintf(stdout,"     %7d PROPOSED  %7d SWAPPED\n",np,ns);
+
+  MMG_kiufree(queue);
+  return(ns);
+}
+
+int MMG_cendellong(pMesh mesh,pSol sol,double declic,int base) {
+  pTetra	pt,pt1;
+  pQueue	queue;
+  List		list;
+  double	crit,cal;
+  int		*adja,adj,iadr,ier,i,j,k,jel,lon,ns,np;
+  int		vois[4],ref,tag;
+  char		tabar,done;
+  int imin,jj;
+  /* queue on quality */
+  queue = MMG_kiuini(mesh,mesh->nemax,declic,base - 1);
+  assert(queue);
+
+  ns = np = 0;
+  do {
+    k = MMG_kiupop(queue);
+    if ( !k )  break;
+    np++;
+
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    else if ( pt->flag < base - 1) continue;
+    else if ( pt->qual < declic ) continue;
+    
+    /* mark internal edges */
+    tabar  = 0;
+    iadr  = 4*(k-1) + 1;
+    adja  = &mesh->adja[iadr];
+    vois[0]  = adja[0] >> 2;
+    vois[1]  = adja[1] >> 2;
+    vois[2]  = adja[2] >> 2;
+    vois[3]  = adja[3] >> 2;
+    for (i=0; i<4; i++) {
+      adj = vois[i];
+      ref    = mesh->tetra[adj].ref;
+      tag    = mesh->tetra[adj].flag;
+      if ( !adj || pt->ref != ref ) {
+        tabar |= 1 << MMG_iarf[i][0];
+        tabar |= 1 << MMG_iarf[i][1];
+        tabar |= 1 << MMG_iarf[i][2];
+      }
+    }
+    if ( (tabar == ALL_BDRY) )  continue;
+    
+    /* swap for anisotropy */
+    done = 0;  
+    imin = ((int) pt->qual)%6;
+    for (jj=imin; jj<imin+6; jj++) { 
+      i=jj%6;
+      if ( (tabar & 1<<i) )  continue;
+
+      lon  = MMG_coquil(mesh,k,i,&list);
+      if ( lon < 3 || lon > 7 )  continue;
+      //printf("on essaie de swapper %d\n",k); 
+
+      /* qual crit */
+      crit = ( sol->offset==6 ) ? MMG_caltet_ani(mesh,sol,k):MMG_caltet_iso(mesh,sol,k) ; //pt->qual;
+      for (j=2; j<=lon; j++) {
+        jel  = list.tetra[j] / 6;
+        pt1  = &mesh->tetra[jel]; 
+        cal  = ( sol->offset==6 ) ? MMG_caltet_ani(mesh,sol,jel):MMG_caltet_iso(mesh,sol,jel) ;
+        crit = M_MAX(crit,cal);
+      }
+      crit *= SCRIT; 
+      //printf("$$$$$$$$$$$$$ crit %e %e\n",crit,crit/60.); 
+      
+      ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,declic); 
+      //printf("swap ? %d\n",ier);
+      if ( ier > 0 ) {
+        ns++;
+        break;
+      }
+      else if ( ier < 0 ) {
+        fprintf(stdout,"     %7d PROPOSED  %7d SWAPPED\n",np,ns);
+        fprintf(stdout,"  ## UNABLE TO SWAP.\n");
+        MMG_kiufree(queue);
+	    return(-ns);
+      } 
+    }
+  }
+  while ( k );
+
+  if ( mesh->info.imprim < - 4 )
+    fprintf(stdout,"     %7d PROPOSED  %7d SWAPPED\n",np,ns);
+
+  MMG_kiufree(queue);
+  return(ns);
+}
+
diff --git a/contrib/mmg3d/build/sources/cenrad.c b/contrib/mmg3d/build/sources/cenrad.c
new file mode 100644
index 0000000000000000000000000000000000000000..3a3921eaaacb10d7e887ec55a425c9cf02fca9ad
--- /dev/null
+++ b/contrib/mmg3d/build/sources/cenrad.c
@@ -0,0 +1,182 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/* compute circumradius and center */
+int MMG_cenrad_iso(pMesh mesh,double *ct,double *c,double *rad) {
+  double      dd,ux,uy,uz,n1[3],n2[3],n3[3],*c1,*c2,*c3,*c4,pl1,pl2,pl3;
+  double      cc1,cc2,cc3;
+
+  c1 = &ct[0];
+  c2 = &ct[3];
+  c3 = &ct[6];
+  c4 = &ct[9];
+
+  ux = c4[0] - c1[0];
+  uy = c4[1] - c1[1];
+  uz = c4[2] - c1[2];
+  dd = 1.0 / sqrt(ux*ux + uy*uy + uz*uz);
+  n1[0] = ux*dd;
+  n1[1] = uy*dd;
+  n1[2] = uz*dd;
+
+  /* plan: vecteur directeur passant par milieu(1,4) */
+  pl1 = n1[0]*(c4[0]+c1[0]) \
+      + n1[1]*(c4[1]+c1[1]) + n1[2]*(c4[2]+c1[2]);
+
+  ux = c4[0] - c2[0];
+  uy = c4[1] - c2[1];
+  uz = c4[2] - c2[2];
+  dd = 1.0 / sqrt(ux*ux + uy*uy + uz*uz);
+  n2[0] = ux*dd;
+  n2[1] = uy*dd;
+  n2[2] = uz*dd;
+  pl2 = n2[0]*(c4[0]+c2[0]) \
+      + n2[1]*(c4[1]+c2[1]) + n2[2]*(c4[2]+c2[2]);
+
+  ux = c4[0] - c3[0];
+  uy = c4[1] - c3[1];
+  uz = c4[2] - c3[2];
+  dd = 1.0 / sqrt(ux*ux + uy*uy + uz*uz);
+  n3[0] = ux*dd;
+  n3[1] = uy*dd;
+  n3[2] = uz*dd;
+  pl3 = n3[0]*(c4[0]+c3[0]) \
+      + n3[1]*(c4[1]+c3[1]) + n3[2]*(c4[2]+c3[2]);
+
+  /* center = intersection of 3 planes */
+  ux = n2[1]*n3[2] - n2[2]*n3[1];
+  uy = n1[2]*n3[1] - n1[1]*n3[2];
+  uz = n1[1]*n2[2] - n1[2]*n2[1];
+
+  dd = n1[0]*ux + n2[0]*uy + n3[0]*uz;
+  dd = 0.5 / dd;
+
+  cc1 = ux*pl1 + uy*pl2 + uz*pl3;
+  cc2 = pl1 * (n2[2]*n3[0] - n2[0]*n3[2]) \
+     + pl2 * (n1[0]*n3[2] - n3[0]*n1[2]) \
+     + pl3 * (n2[0]*n1[2] - n2[2]*n1[0]);
+  cc3 = pl1 * (n2[0]*n3[1] - n2[1]*n3[0]) \
+     + pl2 * (n3[0]*n1[1] - n3[1]*n1[0]) \
+     + pl3 * (n1[0]*n2[1] - n2[0]*n1[1]);
+
+  c[0] = dd * cc1;
+  c[1] = dd * cc2;
+  c[2] = dd * cc3;
+
+  /* radius (squared) */
+  *rad = (c[0] - c4[0]) * (c[0] - c4[0]) \
+       + (c[1] - c4[1]) * (c[1] - c4[1]) \
+       + (c[2] - c4[2]) * (c[2] - c4[2]);
+
+  return(1);
+}
+
+
+int MMG_cenrad_ani(pMesh mesh,double *ct,double *m,double *c,double *rad) {
+  double      d1,d2,d3,det,dd,ux,uy,uz,vx,vy,vz,wx,wy,wz;
+  double      ax,ay,az,bx,by,bz,cx,cy,cz;
+
+
+  dd =      m[0]*ct[0]*ct[0] + m[3]*ct[1]*ct[1] + m[5]*ct[2]*ct[2] \
+     + 2.0*(m[1]*ct[0]*ct[1] + m[2]*ct[0]*ct[2] + m[4]*ct[1]*ct[2]);
+
+  /* MMG_lengths */
+  d1 =      m[0]*ct[3]*ct[3] + m[3]*ct[4]*ct[4] + m[5]*ct[5]*ct[5] \
+     + 2.0*(m[1]*ct[3]*ct[4] + m[2]*ct[3]*ct[5] + m[4]*ct[4]*ct[5]) - dd;
+
+  d2 =      m[0]*ct[6]*ct[6] + m[3]*ct[7]*ct[7] + m[5]*ct[8]*ct[8] \
+     + 2.0*(m[1]*ct[6]*ct[7] + m[2]*ct[6]*ct[8] + m[4]*ct[7]*ct[8]) - dd;
+
+  d3 =      m[0]*ct[9]*ct[9] + m[3]*ct[10]*ct[10] + m[5]*ct[11]*ct[11] \
+     + 2.0*(m[1]*ct[9]*ct[10] + m[2]*ct[9]*ct[11] + m[4]*ct[10]*ct[11]) - dd;
+
+  ux = ct[3] - ct[0];
+  uy = ct[4] - ct[1];
+  uz = ct[5] - ct[2];
+
+  vx = ct[6] - ct[0];
+  vy = ct[7] - ct[1];
+  vz = ct[8] - ct[2];
+
+  wx = ct[9] - ct[0];
+  wy = ct[10] - ct[1];
+  wz = ct[11] - ct[2];
+
+  /* M.u */
+  ax = m[0]*ux + m[1]*uy + m[2]*uz;
+  ay = m[1]*ux + m[3]*uy + m[4]*uz;
+  az = m[2]*ux + m[4]*uy + m[5]*uz;
+
+  bx = m[0]*vx + m[1]*vy + m[2]*vz;
+  by = m[1]*vx + m[3]*vy + m[4]*vz;
+  bz = m[2]*vx + m[4]*vy + m[5]*vz;
+
+  cx = m[0]*wx + m[1]*wy + m[2]*wz;
+  cy = m[1]*wx + m[3]*wy + m[4]*wz;
+  cz = m[2]*wx + m[4]*wy + m[5]*wz;
+
+  /* center */
+  c[0] = d1 *(by*cz - bz*cy) - d2 * (ay*cz - az*cy) + d3 * (ay*bz - az*by); 
+  c[1] = d1 *(bz*cx - bx*cz) - d2 * (az*cx - ax*cz) + d3 * (az*bx - ax*bz);
+  c[2] = d1 *(bx*cy - by*cx) - d2 * (ax*cy - ay*cx) + d3 * (ax*by - ay*bx);
+
+  det = ax * (by*cz - bz*cy) - ay * (bx*cz - bz*cx) + az * (bx*cy - cx*by);
+  det = 1.0 / (2.0*det);
+
+  c[0] *= det;
+  c[1] *= det;
+  c[2] *= det;
+
+  /* radius (squared) */
+  ux = ct[0] - c[0];
+  uy = ct[1] - c[1];
+  uz = ct[2] - c[2];
+  *rad =      m[0]*ux*ux + m[3]*uy*uy + m[5]*uz*uz \
+       + 2.0*(m[1]*ux*uy + m[2]*ux*uz + m[4]*uy*uz);
+
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/chkmsh.c b/contrib/mmg3d/build/sources/chkmsh.c
new file mode 100644
index 0000000000000000000000000000000000000000..1b492fdb88a83daed2b805f31581786335b70ed4
--- /dev/null
+++ b/contrib/mmg3d/build/sources/chkmsh.c
@@ -0,0 +1,182 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define  EPSLOC   1.00005
+
+
+int MMG_chkmsh(pMesh mesh,int severe,int base) {
+  pPoint	ppt;
+  pTetra	pt1,pt2;
+  List		list;
+  int		*adja,*adja1,adj,adj1,k,kk,l,nk,i,j,ip,iadr,lon,len;
+  unsigned char	voy,voy1;
+
+  for (k=1; k<=mesh->ne; k++) {
+    pt1 = &mesh->tetra[k];
+    if ( !pt1->v[0] )  continue;
+    iadr = (k-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    for (i=0; i<4; i++) {
+      adj = adja[i] / 4;
+      voy = adja[i] % 4;
+      if ( !adj )  continue;
+
+      if ( adj == k ) {
+        fprintf(stdout,"  1. Wrong adjacency %d %d\n",k,adj);
+	printf("k %d: %d %d %d %d\n",k,pt1->v[0],pt1->v[1],pt1->v[2],pt1->v[3]);
+	printf("adj (%d): %d %d %d %d\n",
+	       k,adja[0]/4,adja[1]/4,adja[2]/4,adja[3]/4);
+	exit(1);
+      }
+      pt2 = &mesh->tetra[adj];
+      if ( !pt2->v[0] ) {
+        fprintf(stdout,"  4. Invalid adjacent %d %d\n",adj,k);
+	printf("sommets k   %d: %d %d %d %d\n",
+	       k,pt1->v[0],pt1->v[1],pt1->v[2],pt1->v[3]);
+	printf("sommets adj %d: %d %d %d %d\n",
+	adj,pt2->v[0],pt2->v[1],pt2->v[2],pt2->v[3]);
+	printf("numeros adj %d: %d %d %d %d\n",k,adja[0]/4,adja[1]/4,adja[2]/4,adja[3]/4);
+	exit(1);
+      }
+      iadr  = (adj-1)*4 + 1;
+      adja1 = &mesh->adja[iadr];   
+      adj1  = adja1[voy] / 4;
+      voy1  = adja1[voy] % 4;
+      if ( adj1 != k || voy1 != i ) {
+        fprintf(stdout,"  2. Wrong adjacency %d %d\n",k,adj1);
+	printf("k %d: %d %d %d %d\n",k,pt1->v[0],pt1->v[1],pt1->v[2],pt1->v[3]);
+	printf("a %d: %d %d %d %d\n",
+	       adj,pt2->v[0],pt2->v[1],pt2->v[2],pt2->v[3]);
+	printf("adj(%d): %d %d %d %d\n",
+	       k,adja[0]/4,adja[1]/4,adja[2]/4,adja[3]/4);
+	printf("adj(%d): %d %d %d %d\n",
+	      adj,adja1[0]/4,adja1[1]/4,adja1[2]/4,adja1[3]/4);
+	exit(1);
+      }
+    }
+  }
+  
+  /* Delaunay criterion */
+/*
+  for (k=1; k<=mesh->ne; k++) {
+    pt1 = &mesh->tetra[k];
+    if ( !pt1->v[0] )  continue;
+    iadr = (k-1)*4 + 1; 
+    adja = &mesh->adja[iadr];
+    if ( !cenrad(mesh,k,c,&ray) )  continue;
+
+    for (i=0; i<4; i++) {
+      if ( !adja[i] )  continue;
+      adj = adja[i] / 4;
+      voy = adja[i] % 4;
+      pt2 = &mesh->tetra[adj];
+
+      ppt = &mesh->point[ pt2->v[voy] ];
+      dd = (ppt->c[0] - c[0]) * (ppt->c[0] - c[0]) \
+         + (ppt->c[1] - c[1]) * (ppt->c[1] - c[1]) \
+         + (ppt->c[2] - c[2]) * (ppt->c[2] - c[2]);
+      if ( EPSLOC*EPSLOC*dd < ray ) {
+        fprintf(stdout,"  ## Non-Delaunay mesh:  %.14f < %.14f\n",dd,ray);
+	exit(1);
+      }
+    }
+  }
+*/
+  
+  if ( !severe )  return(1);
+
+  for (k=1; k<=mesh->ne; k++) {
+    pt1 = &mesh->tetra[k];
+    if ( !pt1->v[0] )  continue;
+    else if (pt1->flag < base )  continue;
+    iadr = 4*(k-1) + 1;
+    adja = &mesh->adja[iadr];
+
+    for (i=0; i<4; i++) {
+      adj = (adja[i]-1) / 4 + 1;
+      voy = (adja[i]-1) % 4;
+      if ( !adj )  continue;
+
+      ip  = pt1->v[i];
+      ppt = &mesh->point[ip];
+      if ( ppt->tag & M_UNUSED ) {
+        fprintf(stdout,"  6. Unused vertex %d  %d\n",k,ip);
+	printf("%d %d %d %d\n",pt1->v[0],pt1->v[1],pt1->v[2],pt1->v[3]);
+	exit(1);
+      }
+      lon = MMG_boulep(mesh,k,i,&list);
+      for (l=1; l<=lon; l++) {
+        kk  = list.tetra[l] / 4;
+	nk  = list.tetra[l] % 4;
+	pt2 = &mesh->tetra[kk];
+	if ( pt2->v[nk] != ip ) {
+	  fprintf(stdout,"  5. Wrong ball %d, %d\n",ip,pt2->v[nk]);
+	  exit(1);
+	}
+      }
+      if ( lon < 1 )  continue;
+      len = 0;
+      for (kk=1; kk<=mesh->ne; kk++) {
+        pt2 = &mesh->tetra[kk];
+	if ( !pt2->v[0] )  continue;
+	for (j=0; j<4; j++)
+	  if ( pt2->v[j] == ip ) {
+	    len++;
+	    break;
+	  }
+      }
+      if ( len != lon ) {
+        fprintf(stdout,"  7. Incorrect ball %d: %d %d\n",pt1->v[i],lon,len);
+        exit(1);
+      }
+    }
+  }
+
+  /*fprintf(stdout,"  ** MESH STRUCTURE IS OK\n");*/
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/chrono.c b/contrib/mmg3d/build/sources/chrono.c
new file mode 100644
index 0000000000000000000000000000000000000000..ba4482e177621116c9f4e9f2f0f0ceebe5fb0bae
--- /dev/null
+++ b/contrib/mmg3d/build/sources/chrono.c
@@ -0,0 +1,142 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+/* 
+ *  simulation of a chronograph
+ *  in : tim
+ *  out: tim.dtim = elapsed time in micro-secs
+ *       tim.ptim = elapsed time in secs
+ *       tim.call = number of calls
+ *
+ *  Written by Pascal J. Frey
+ *  email: Pascal.Frey@inria.fr, 1999
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <math.h>
+#include <stdlib.h>
+#include "chrono.h"
+
+
+/* return elapsed time in secs. */
+static double diftim(time_t t2,time_t t1) {
+  struct  tm  *ptm;
+  double  tim;
+  int     hh1,mm1,ss1,hh2,mm2,ss2;
+
+  ptm = localtime(&t1);
+  hh1 = ptm->tm_hour;
+  mm1 = ptm->tm_min;
+  ss1 = ptm->tm_sec;
+
+  ptm = localtime(&t2);
+  hh2 = ptm->tm_hour;
+  mm2 = ptm->tm_min;
+  ss2 = ptm->tm_sec;
+  if ( hh2 < hh1 )  hh2 += 24;
+  
+  tim  = 3600.0*(hh2-hh1);
+  tim += 60.0*(mm2-mm1);
+  tim += ss2-ss1;
+
+  return(tim);
+}
+
+
+/* get system and user times in micro-seconds */
+void  TIM_chrono(int cmode,TIM_mytime *ptt) {
+  time_t tt;
+
+  if ( cmode == RESET ) {
+    ptt->dtim  = clock();
+    ptt->ctim  = 0.0f;
+    ptt->ptim  = 0;
+    ptt->call  = 0;
+  }
+  else {
+    ptt->dtim = difftime(clock(),ptt->dtim);  /* in secs */
+    if ( cmode == ON ) {
+      ptt->ptim = time(NULL);
+      ptt->call++;
+    }
+    else if ( cmode == OFF ) {
+      tt = time(NULL);
+      ptt->ctim += diftim(tt,ptt->ptim);
+      ptt->ptim  = 0;
+    }
+  }
+}
+
+
+/* return time (converted in secs */
+double TIM_gttime(TIM_mytime t) {
+
+  if ( t.ctim < MAXCLK )
+    return(t.dtim / (double)CLOCKS_PER_SEC);
+  else
+    return(t.ctim);
+}
+
+
+/* initialize time table */
+void  TIM_tminit(TIM_mytime *t,int maxtim) {
+  int     k;
+
+  for (k=0; k<maxtim; k++) {
+    t[k].dtim = clock();
+    t[k].ptim = 0;
+    t[k].ctim = 0.0;
+    t[k].call = 0;
+  }
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/contrib/mmg3d/build/sources/chrono.h b/contrib/mmg3d/build/sources/chrono.h
new file mode 100644
index 0000000000000000000000000000000000000000..2df3774828a411dc353b775dd1c0dd0dc658a783
--- /dev/null
+++ b/contrib/mmg3d/build/sources/chrono.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <time.h>
+
+#ifndef  ON
+#define  RESET  0
+#define  ON     1
+#define  OFF    2
+#endif
+
+#define  TIMEMAX   16
+#define  MAXCLK    ( 1073741823. / (double)CLOCKS_PER_SEC )
+
+
+typedef struct TIM_mytime {
+  double    ctim,dtim;
+  time_t    ptim;
+  short     call;
+} TIM_mytime;
+
+
+/* prototypes */
+void   TIM_chrono(int cmode,TIM_mytime *ptt);
+double TIM_gttime(TIM_mytime t);
+void   TIM_tminit(TIM_mytime *t,int maxtim);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/contrib/mmg3d/build/sources/colpoi.c b/contrib/mmg3d/build/sources/colpoi.c
new file mode 100644
index 0000000000000000000000000000000000000000..52c23ad3c52599f3196cdd8cfc32d9eadb4958af
--- /dev/null
+++ b/contrib/mmg3d/build/sources/colpoi.c
@@ -0,0 +1,523 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define LLONG1  1.41//1.9//1.41
+
+int MMG_nlen,MMG_ncal,MMG_ntopo,MMG_nex;
+
+
+/* collapse edge b->a (i.e. remove b) */
+int MMG_colpoi(pMesh mesh,pSol sol,int iel,int ia,int ib,double coef) {
+  pTetra        pt,pt1;
+  pPoint        pa,pb,ppt;
+  double        coor[3],crit,cal;
+  double        solu[6],len,*ca,*cb,*ma,*mb;
+  int          *adja,*adja1,base,i,j,l,kk,nk,adj,voy,iadr,ik,ielv,lon,na,nb;
+  int           adj1,voy1,s1,iadb,iada,ipb,vois[4],vois1[4],ii,jj,iare1,iare2;
+  static List   list;
+
+  pt  = &mesh->tetra[iel];
+  lon = MMG_boulep(mesh,iel,ib,&list);
+  if ( lon < 1 )  return(0);
+  /* topological-geometric checks */
+  base = ++mesh->mark;
+  na = pt->v[ia];
+  nb = pt->v[ib];        
+  pa = &mesh->point[na];
+  pb = &mesh->point[nb];
+  ca = &pa->c[0];
+  
+  crit =  pt->qual;
+  for (l=2; l<=lon; l++) {
+    kk   = list.tetra[l] >> 2;
+    cal  = mesh->tetra[kk].qual;//(sol->offset==6) ? MMG_caltet_ani(mesh,sol,kk) : MMG_caltet_iso(mesh,sol,kk);
+    if ( cal > crit )  crit = cal;
+  }
+  crit *= coef; 
+  //assert(crit < 1e20);
+        
+  /* change coords of pb */
+  iada = (na-1)*sol->offset + 1;
+  ma   = &sol->met[iada];
+  iadb = (nb-1)*sol->offset + 1;
+  memcpy(coor, pb->c,3*sizeof(double));
+  memcpy(pb->c,pa->c,3*sizeof(double));
+  memcpy(solu,&sol->met[iadb],sol->offset*sizeof(double));
+  memcpy(&sol->met[iadb],&sol->met[iada],sol->offset*sizeof(double));
+
+  /* avoid recreating existing elt */
+  for (l=1; l<=lon; l++) {
+    if ( list.tetra[l] < 0 )  continue;
+    kk = list.tetra[l] >> 2;
+    nk = list.tetra[l] % 4;
+
+    pt1 = &mesh->tetra[kk];
+    s1  = pt1->v[0];
+
+    iadr = (kk-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adj  = adja[nk] >> 2;
+    if ( !adj )  continue;
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    vois[0]  = adja[0] >> 2;
+    vois[1]  = adja[1] >> 2;
+    vois[2]  = adja[2] >> 2;
+    vois[3]  = adja[3] >> 2;
+    for (j=0; j<4; j++) 
+      if ( vois[j] == kk ) {
+        pt1 = &mesh->tetra[adj];
+	      s1  = pt1->v[j];
+	      break;
+      }
+    if ( s1 == na ) {
+      MMG_nex++;
+      memcpy(pb->c,coor,3*sizeof(double));
+      memcpy(&sol->met[iadb],solu,sol->offset*sizeof(double));
+      return(0);
+    }
+  }
+
+  /* topological check */
+  for (l=1; l<=lon; l++) {
+    kk = list.tetra[l] >> 2;
+    nk = list.tetra[l] % 4;
+    /* mark elts of shell a-b */
+    pt1 = &mesh->tetra[kk];
+    for (i=0; i<4; i++) {
+      if ( pt1->v[i] == na ) {
+        list.tetra[l] = -list.tetra[l];
+	    pt1->mark     = base;
+	    break;
+      }
+    }
+    
+
+    if ( pt1->mark == base )   continue;
+    /* check lengths */
+    for (i=0; i<3; i++) {
+      ipb  = pt1->v[ MMG_idir[nk][i] ];
+      ppt  = &mesh->point[ipb];
+      cb   = &ppt->c[0];
+      iadr = (ipb-1)*sol->offset + 1;
+      mb   = &sol->met[iadr];   
+      // if(cal < 1e-10) printf("(%e) long %d : %e\n",cal,i,MMG_length(ca,cb,ma,mb),crit/60.);
+      if ( ppt->mark < base ) {
+        len = MMG_length(ca,cb,ma,mb);   
+        if ( len > crit/60. ) {
+          MMG_nlen++;
+          memcpy(pb->c,coor,3*sizeof(double));
+          memcpy(&sol->met[iadb],solu,sol->offset*sizeof(double));
+          //printf("len reject %e %e\n",len,LLONG1);
+          return(0);
+        }
+        ppt->mark = base;
+      }
+    }
+
+    /* check volume of remaining elt */
+    cal =  MMG_voltet(mesh,kk);//(sol->offset==6) ? MMG_caltet_ani(mesh,sol,iel) : MMG_caltet_iso(mesh,sol,iel); 
+    list.qual[l] = MMG_caltet(mesh,sol,kk); 
+    //if( cal < 1e-10 && cal >= 1e-15) printf("cal : %e %e\n",cal,MMG_caltet_ani(mesh,sol,kk));
+    if ( cal < 1e-10/*crit*/ ) {
+      MMG_ncal++;
+      memcpy(pb->c,coor,3*sizeof(double));
+      memcpy(&sol->met[iadb],solu,sol->offset*sizeof(double));  
+//printf("cal reject\n");
+      return(0);
+    }
+    
+  }
+  /* verif topo */ 
+  for (l=1; l<=lon; l++) {
+    ik  = abs(list.tetra[l]);
+    kk  = ik >> 2;
+    nk  = ik % 4;
+    pt1 = &mesh->tetra[kk];
+    if ( pt1->mark != base )  continue;
+
+    iadr = (kk-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    ielv = adja[nk] >> 2;
+    vois[0]  = adja[0] >> 2;
+    vois[1]  = adja[1] >> 2;
+    vois[2]  = adja[2] >> 2;
+    vois[3]  = adja[3] >> 2;
+
+    for (i=0; i<4; i++) {
+      if ( i == nk )  continue;
+      adj = vois[i];
+      pt1 = &mesh->tetra[adj];
+      if ( pt1->mark == base )  continue;
+      adja1 = &mesh->adja[ (adj-1)*4 + 1 ];
+      vois1[0]  = adja1[0] >> 2;
+      vois1[1]  = adja1[1] >> 2;
+      vois1[2]  = adja1[2] >> 2;
+      vois1[3]  = adja1[3] >> 2;
+      for (j=0; j<4; j++) {
+        adj1 = vois1[j];
+        if ( pt1->v[j] == nb && adj1 == ielv ) {
+	        MMG_ntopo++;
+          memcpy(pb->c,coor,3*sizeof(double));
+          memcpy(&sol->met[iadb],solu,sol->offset*sizeof(double));
+	        return(0);
+	      }
+      }
+    }
+  }
+   
+  /* update topo: shell */
+  for (l=1; l<=lon; l++) {
+    if ( list.tetra[l] > 0 )  continue;
+
+    kk = -list.tetra[l] >> 2;
+    nk = -list.tetra[l] % 4;
+
+    pt1 = &mesh->tetra[kk];
+    pt1->qual = list.qual[l];
+    iadr = (kk-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adj  = adja[nk] >> 2;
+    voy  = adja[nk] % 4;
+    adj1 = 0;
+    voy1 = 0;
+    for (j=0; j<4; j++) {
+      if ( pt1->v[j] == na ) {
+        adj1 = adja[j] >> 2;
+	      voy1 = adja[j] % 4;
+        if ( adj1 ) {
+	        iadr = (adj1-1)*4 + 1;
+          adja = &mesh->adja[iadr];
+          adja[voy1] = 4*adj + voy;
+          mesh->tetra[adj1].bdryref[voy1] = pt1->bdryref[nk]; 
+          for(ii=0 ; ii<3 ; ii++) {
+						if(!pt1->bdryinfo[MMG_iarf[nk][ii]]) continue;    
+						iare1 = pt1->v[MMG_iare[MMG_iarf[nk][ii]][0]];
+						iare2 = pt1->v[MMG_iare[MMG_iarf[nk][ii]][1]]; 
+						if(iare1==na) {
+							iare1=nb;  
+						} else if(iare2==na) {
+							iare2=nb;         
+						}
+						else {
+						  continue;
+						}
+						for(jj=0 ; jj<3 ; jj++) {  
+							// printf("iare %d %d -- %d %d\n",iare1,iare2,mesh->tetra[adj1].v[MMG_iare[MMG_iarf[voy1][jj]][0]],
+							// 	mesh->tetra[adj1].v[MMG_iare[MMG_iarf[voy1][jj]][1]]);
+							if((iare1==mesh->tetra[adj1].v[MMG_iare[MMG_iarf[voy1][jj]][0]] &&
+								  iare2==mesh->tetra[adj1].v[MMG_iare[MMG_iarf[voy1][jj]][1]]) ||
+								 (iare2==mesh->tetra[adj1].v[MMG_iare[MMG_iarf[voy1][jj]][0]] &&
+									iare1==mesh->tetra[adj1].v[MMG_iare[MMG_iarf[voy1][jj]][1]])) {
+										mesh->tetra[adj1].bdryinfo[MMG_iarf[voy1][jj]] = pt1->bdryinfo[MMG_iarf[nk][ii]];     
+										break; 		
+							}
+						}
+						assert(jj<3);
+					}
+					
+	      }
+	      break;
+      }
+    }
+    if ( adj ) {
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[voy] = 4*adj1 + voy1;
+      if(!adj1) { 
+        mesh->tetra[adj].bdryref[voy] = pt1->bdryref[j];
+        for(ii=0 ; ii<3 ; ii++) {
+			  	if(!pt1->bdryinfo[MMG_iarf[j][ii]]) continue;
+			  	iare1 = pt1->v[MMG_iare[MMG_iarf[j][ii]][0]];
+			  	iare2 = pt1->v[MMG_iare[MMG_iarf[j][ii]][1]]; 
+			  	if(iare1==na) {
+			  		iare1=nb;  
+			  	} else if(iare2==na)
+			  		iare2=nb;
+			  	else
+			  	  continue; 
+			  	for(jj=0 ; jj<3 ; jj++) {  
+			  		// printf("iare %d %d -- %d %d\n",iare1,iare2,mesh->tetra[adj1].v[MMG_iare[MMG_iarf[voy1][jj]][0]],
+			  		// 	mesh->tetra[adj1].v[MMG_iare[MMG_iarf[voy1][jj]][1]]);
+			  		if((iare1==mesh->tetra[adj].v[MMG_iare[MMG_iarf[voy][jj]][0]] &&
+			  			  iare2==mesh->tetra[adj].v[MMG_iare[MMG_iarf[voy][jj]][1]]) ||
+			  			 (iare2==mesh->tetra[adj].v[MMG_iare[MMG_iarf[voy][jj]][0]] &&
+			  				iare1==mesh->tetra[adj].v[MMG_iare[MMG_iarf[voy][jj]][1]])) {
+			  					mesh->tetra[adj1].bdryinfo[MMG_iarf[voy1][jj]] = pt1->bdryinfo[j];    
+			  					break; 		
+			  		}
+			  	}
+			  	assert(jj<3);
+			  }  
+			}  
+		}
+    MMG_delElt(mesh,kk);
+  }
+
+  /* update topo: ball */
+  for (l=1; l<=lon; l++) {
+    if ( list.tetra[l] < 0 )  continue;
+    kk  = list.tetra[l] >> 2;
+    nk  = list.tetra[l] % 4;
+    pt1 = &mesh->tetra[kk];  
+    pt1->v[nk] = na;
+    pt1->flag  = mesh->flag;
+    pt1->qual  = list.qual[l];
+    pt1->edge &= ~(1 << MMG_arpt[nk][0]);
+    pt1->edge &= ~(1 << MMG_arpt[nk][1]);
+    pt1->edge &= ~(1 << MMG_arpt[nk][2]);
+  }
+
+  /* delete vertex */
+  memcpy(pb->c,coor,3*sizeof(double));
+  pa->mark = mesh->flag;
+
+  return(1);
+}
+/* collapse edge b->a (i.e. remove b) */
+int MMG_colpoi2(pMesh mesh,pSol sol,int iel,int ia,int ib,double coef) {
+  pTetra        pt,pt1;
+  pPoint        pa,pb,ppt;
+  double        coor[3],crit,cal,len;
+  double         solu[6],*ca,*cb,*ma,*mb;
+  int          *adja,*adja1,base,i,j,l,kk,nk,adj,voy,iadr,ik,ielv,lon,na,nb;
+  int           adj1,voy1,s1,ipb,iadb,iada;
+  static List   list;
+
+  pt  = &mesh->tetra[iel];
+  lon = MMG_boulep(mesh,iel,ib,&list);
+  if ( lon < 1 )  return(0);
+
+  /* topological-geometric checks */
+  base = ++mesh->mark;
+  na = pt->v[ia];
+  nb = pt->v[ib];
+  pa = &mesh->point[na];
+  pb = &mesh->point[nb];
+  ca = &pa->c[0];
+
+  crit = pt->qual;
+  for (l=2; l<=lon; l++) {
+    kk   = list.tetra[l] >> 2;
+    cal  = MMG_caltet(mesh,sol,kk);
+    if ( cal > crit )  crit = cal;
+  }
+  crit *= coef;
+
+  /* change coords of pb */
+  iada = (na-1)*sol->offset + 1;
+  ma   = &sol->met[iada];
+  iadb = (nb-1)*sol->offset + 1;
+  memcpy(coor, pb->c,3*sizeof(double));
+  memcpy(pb->c,pa->c,3*sizeof(double));
+  memcpy(solu,&sol->met[iadb],sol->offset*sizeof(double));
+  memcpy(&sol->met[iadb],&sol->met[iada],sol->offset*sizeof(double));
+
+  /* avoid recreating existing elt */
+  for (l=1; l<=lon; l++) {
+    if ( list.tetra[l] < 0 )  continue;
+    kk = list.tetra[l] >> 2;
+    nk = list.tetra[l] % 4;
+
+    pt1 = &mesh->tetra[kk];
+    s1  = pt1->v[0];
+
+    iadr = (kk-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adj  = adja[nk] >> 2;
+    if ( !adj )  continue;
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    for (j=0; j<4; j++) 
+      if ( adja[j]/4 == kk ) {
+        pt1 = &mesh->tetra[adj];
+	      s1  = pt1->v[j];
+	      break;
+      }
+    if ( s1 == na ) {
+      MMG_nex++;
+      memcpy(pb->c,coor,3*sizeof(double));
+      memcpy(&sol->met[iadb],solu,sol->offset*sizeof(double));
+      return(0);
+    }
+  }
+
+  /* topological check */
+  for (l=1; l<=lon; l++) {
+    kk = list.tetra[l] >> 2;
+    nk = list.tetra[l] % 4;
+
+    /* mark elts of shell a-b */
+    pt1 = &mesh->tetra[kk];
+    for (i=0; i<4; i++) {
+      if ( pt1->v[i] == na ) {
+        list.tetra[l] = -list.tetra[l];
+	      pt1->mark     = base;
+	      break;
+      }
+    }
+    /* check volume of remaining elt */
+    if ( pt1->mark == base )   continue;
+    cal = MMG_caltet(mesh,sol,kk);
+    list.qual[l] = cal;
+    if ( cal > crit ) {
+      MMG_ncal++;
+      memcpy(pb->c,coor,3*sizeof(double));
+      memcpy(&sol->met[iadb],solu,sol->offset*sizeof(double));
+//printf("bad quality : %f > %f\n",cal,crit);
+      return(0);
+    }
+    
+    /* check lengths */
+    for (i=0; i<3; i++) {
+      ipb = pt1->v[ MMG_idir[nk][i] ];
+      ppt = &mesh->point[ipb];
+      cb   = &ppt->c[0];
+      iadr = (ipb-1)*sol->offset + 1;
+      mb   = &sol->met[iadr];
+       if ( ppt->mark < base ) {
+        len = MMG_length(ca,cb,ma,mb);
+	      if ( len > 1.51 ) {
+	        MMG_nlen++;
+          memcpy(pb->c,coor,3*sizeof(double));
+          memcpy(&sol->met[iadb],solu,sol->offset*sizeof(double));
+//printf("bad MMG_length : %f > %f\n",len,1.51);
+          return(0);
+        }
+        ppt->mark = base;
+      }
+    }
+  }
+
+  /* verif topo */
+  for (l=1; l<=lon; l++) {
+    ik  = abs(list.tetra[l]);
+    kk  = ik >> 2;
+    nk  = ik % 4;
+    pt1 = &mesh->tetra[kk];
+    if ( pt1->mark != base )  continue;
+
+    iadr = (kk-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    ielv = adja[nk] >> 2;
+
+    for (i=0; i<4; i++) {
+      if ( i == nk )  continue;
+      adj = adja[i] >> 2;
+      pt1 = &mesh->tetra[adj];
+      if ( pt1->mark == base )  continue;
+      adja1 = &mesh->adja[ (adj-1)*4 + 1 ];
+      for (j=0; j<4; j++) {
+        adj1 = adja1[j] >> 2;
+        if ( pt1->v[j] == nb && adj1 == ielv ) {
+	        MMG_ntopo++;
+          memcpy(pb->c,coor,3*sizeof(double));
+          memcpy(&sol->met[iadb],solu,sol->offset*sizeof(double));
+	        return(0);
+	      }
+      }
+    }
+  }
+
+  /* update topo: shell */
+  for (l=1; l<=lon; l++) {
+    if ( list.tetra[l] > 0 )  continue;
+
+    kk = -list.tetra[l] >> 2;
+    nk = -list.tetra[l] % 4;
+
+    pt1 = &mesh->tetra[kk];
+    pt1->qual = list.qual[l];
+    iadr = (kk-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adj  = adja[nk] >> 2;
+    voy  = adja[nk] % 4;
+    adj1 = 0;
+    voy1 = 0;
+    for (j=0; j<4; j++) {
+      if ( pt1->v[j] == na ) {
+        adj1 = adja[j] >> 2;
+	      voy1 = adja[j] % 4;
+        if ( adj1 ) {
+	        iadr = (adj1-1)*4 + 1;
+          adja = &mesh->adja[iadr];
+          adja[voy1] = 4*adj + voy;  
+          mesh->tetra[adj1].bdryref[voy1] = pt1->bdryref[nk];
+	      }
+	      break;
+      }
+    }
+    if ( adj ) {
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[voy] = 4*adj1 + voy1;
+      if(!adj1)  
+        mesh->tetra[adj].bdryref[voy] = pt1->bdryref[j];
+    }
+    MMG_delElt(mesh,kk);
+  }
+
+  /* update topo: ball */
+  for (l=1; l<=lon; l++) {
+    if ( list.tetra[l] < 0 )  continue;
+    kk  = list.tetra[l] >> 2;
+    nk  = list.tetra[l] % 4;
+    pt1 = &mesh->tetra[kk];
+    pt1->v[nk] = na;
+    pt1->flag  = mesh->flag;
+    pt1->qual  = list.qual[l];
+    pt1->edge &= ~(1 << MMG_arpt[nk][0]);
+    pt1->edge &= ~(1 << MMG_arpt[nk][1]);
+    pt1->edge &= ~(1 << MMG_arpt[nk][2]);
+  }
+
+  /* delete vertex */
+  memcpy(pb->c,coor,3*sizeof(double));
+  pa->mark = mesh->flag;
+
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/compil.date b/contrib/mmg3d/build/sources/compil.date
new file mode 100644
index 0000000000000000000000000000000000000000..0e2a9a9299c3dd3a871b60109d25fb197bc4033b
--- /dev/null
+++ b/contrib/mmg3d/build/sources/compil.date
@@ -0,0 +1 @@
+#define COMPIL  " Lun 4 avr 2011 11:23:20 CEST "
diff --git a/contrib/mmg3d/build/sources/coquil.c b/contrib/mmg3d/build/sources/coquil.c
new file mode 100644
index 0000000000000000000000000000000000000000..c351552e47d09e2e099d41dde6a293f7e045cf6e
--- /dev/null
+++ b/contrib/mmg3d/build/sources/coquil.c
@@ -0,0 +1,106 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/* find all tets sharing edge ia of iel */
+int MMG_coquil(pMesh mesh,int iel,int ia,pList list) {
+  pTetra      pt;
+  int        *adja,i,iadr,adj,base,na,nb,ipa,ipb,piv,ilist,kref;
+
+  if ( iel < 1 )  return(0);
+  pt   = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(0);
+
+  base     = ++mesh->mark;
+  pt->mark = base; 
+	kref     = pt->ref;
+  ilist = 1;
+  list->tetra[ilist] = 6*iel + ia;
+
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adj  = adja[ MMG_ifar[ia][0] ] >> 2;
+  piv  = pt->v[ MMG_ifar[ia][1] ];
+  if ( !adj )  return(0);
+
+  na   = pt->v[ MMG_iare[ia][0] ];
+  nb   = pt->v[ MMG_iare[ia][1] ];
+
+  while ( adj != iel ) {
+    pt = &mesh->tetra[adj];
+    pt->mark = base; 
+		if (kref!=pt->ref) return(0);
+		
+    /* identify edge */
+    for (i=0; i<6; i++) {
+      ipa = MMG_iare[i][0];
+      ipb = MMG_iare[i][1];
+      if ( (pt->v[ipa] == na && pt->v[ipb] == nb) ||
+           (pt->v[ipa] == nb && pt->v[ipb] == na))  break;
+    }
+    if(i==6) printf("tetra %d : %d %d %d %d -- %e\n",iel,pt->v[0],pt->v[1],pt->v[2],pt->v[3],pt->qual);
+    assert(i<6);
+
+    ++ilist;
+    if ( ilist > LONMAX-1 )  return(-ilist);
+    list->tetra[ilist] = 6*adj + i;
+
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    if ( pt->v[ MMG_ifar[i][0] ] == piv ) {
+      adj = adja[ MMG_ifar[i][0] ] >> 2;
+      piv = pt->v[ MMG_ifar[i][1] ];
+    }
+    else {
+      adj = adja[ MMG_ifar[i][1] ] >> 2;
+      piv = pt->v[ MMG_ifar[i][0] ];
+    }
+
+    if ( !adj )  return(0);
+  }
+
+  return(ilist);
+}
diff --git a/contrib/mmg3d/build/sources/cutelt.c b/contrib/mmg3d/build/sources/cutelt.c
new file mode 100644
index 0000000000000000000000000000000000000000..54480498fa19f7234d41321c7ea2811bb806bd62
--- /dev/null
+++ b/contrib/mmg3d/build/sources/cutelt.c
@@ -0,0 +1,544 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_cutadd(pMesh mesh,pHedge hed,int icas,int k,int p0,int p1,int p2,int p3,int p4,int p5, int ref) {
+	pTetra		pt;
+	pPoint		ppt,pp0,pp1,pp2,pp3,pp4,pp5;
+
+	pp0 = &mesh->point[p0];
+	pp1 = &mesh->point[p1];
+	pp2 = &mesh->point[p2];
+	pp3 = &mesh->point[p3];
+	pp4 = &mesh->point[p4];
+	pp5 = &mesh->point[p5];
+	mesh->np++;
+	ppt = &mesh->point[mesh->np]; 
+	ppt->c[2] = (1./6.)*(pp0->c[2]+pp1->c[2]+pp2->c[2]+pp3->c[2]+pp4->c[2]+pp5->c[2]);
+	ppt->c[1] = (1./6.)*(pp0->c[1]+pp1->c[1]+pp2->c[1]+pp3->c[1]+pp4->c[1]+pp5->c[1]);
+	ppt->c[0] = (1./6.)*(pp0->c[0]+pp1->c[0]+pp2->c[0]+pp3->c[0]+pp4->c[0]+pp5->c[0]);
+	ppt->ref = pp0->ref;
+	//printf("prisme : %d %d %d %d %d %d + %d\n",p0,p1,p2,p3,p4,p5,mesh->np);
+	if(icas & 1) {
+		//printf("icas %d --> 0 ?\n",icas);
+		pt = &mesh->tetra[k + 1];
+	  pt->v[0] = p0;
+    pt->v[1] = p4;
+	  pt->v[2] = p3;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;
+		pt = &mesh->tetra[k + 2];
+	  pt->v[0] = p0;
+    pt->v[1] = p1;
+	  pt->v[2] = p4;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;
+	} else {
+		if(icas & 4) {
+			//printf("icas %d --> 2 ?\n",icas);
+			
+		} else {
+			MMG_edgePut(hed,p1,p3,2);
+		}   
+		pt = &mesh->tetra[k + 1];
+	  pt->v[0] = p0;
+    pt->v[1] = p1;
+	  pt->v[2] = p3;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;
+		pt = &mesh->tetra[k + 2];
+	  pt->v[0] = p1;
+    pt->v[1] = p4;
+	  pt->v[2] = p3;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;		
+	}
+	
+	if(icas & 8) {
+		//printf("icas %d --> 3 ?\n",icas);
+		pt = &mesh->tetra[k + 3];
+	  pt->v[0] = p1;
+    pt->v[1] = p2;
+	  pt->v[2] = p5;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;
+		pt = &mesh->tetra[k + 4];
+	  pt->v[0] = p1;
+    pt->v[1] = p5;
+	  pt->v[2] = p4;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;
+	} else {
+		if(icas & 32) {
+			//printf("icas %d --> 5 ?\n",icas);
+			
+		} else {
+			MMG_edgePut(hed,p2,p4,2);
+		}   
+		pt = &mesh->tetra[k + 3];
+	  pt->v[0] = p1;
+    pt->v[1] = p2;
+	  pt->v[2] = p4;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;
+		pt = &mesh->tetra[k + 4];
+	  pt->v[0] = p4;
+    pt->v[1] = p2;
+	  pt->v[2] = p5;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;		
+	}
+
+	if(icas & 2) {
+		//printf("icas %d --> 1 ?\n",icas);
+		pt = &mesh->tetra[k + 5];
+	  pt->v[0] = p0;
+    pt->v[1] = p5;
+	  pt->v[2] = p3;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;
+		pt = &mesh->tetra[k + 6];
+	  pt->v[0] = p0;
+    pt->v[1] = p5;
+	  pt->v[2] = p2;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;
+	} else {
+		if(icas & 16) {
+			//printf("icas %d --> 4 ?\n",icas);
+			
+		} else {
+			MMG_edgePut(hed,p2,p3,2);
+		}   
+		pt = &mesh->tetra[k + 5];
+	  pt->v[0] = p0;
+    pt->v[1] = p2;
+	  pt->v[2] = p3;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;
+		pt = &mesh->tetra[k + 6];
+	  pt->v[0] = p2;
+    pt->v[1] = p3;
+	  pt->v[2] = p5;
+	  pt->v[3] = mesh->np;
+	  pt->ref  = ref;		
+	}
+
+	pt = &mesh->tetra[k + 7];
+  pt->v[0] = p3;
+  pt->v[1] = p4;
+  pt->v[2] = p5;
+  pt->v[3] = mesh->np;
+  pt->ref  = ref;
+	pt = &mesh->tetra[k + 8];
+  pt->v[0] = p0;
+  pt->v[1] = p1;
+  pt->v[2] = p2;
+  pt->v[3] = mesh->np;
+  pt->ref  = ref;		
+	
+	return(1);
+}           
+
+int MMG_cuthex(pMesh mesh,pHedge hed,int k,int p0,int p1,int p2,int p3,int p4,int p5,int p6,int p7, int ref) { 
+	pTetra		pt;
+	int				i,nu1,nu2;
+	
+	
+	pt = &mesh->tetra[k+1];
+  pt->v[0] = p0;
+  pt->v[1] = p1;
+  pt->v[2] = p3;
+  pt->v[3] = p7;
+  pt->ref  = ref;
+	for(i=0 ; i<6 ; i++) {
+		nu1 = pt->v[MMG_iare[i][0]];
+		nu2 = pt->v[MMG_iare[i][1]];
+		MMG_edgePut(hed,nu1,nu2,2);
+	}
+  //if((netmp+(k-1)*6+1 ) == 2924) printf("i) %d tet %d %d %d %d\n",k,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+  pt = &mesh->tetra[k+2];
+  pt->v[0] = p7;
+  pt->v[1] = p2;
+  pt->v[2] = p1;
+  pt->v[3] = p6;
+  pt->ref  = ref;
+	for(i=0 ; i<6 ; i++) {
+		nu1 = pt->v[MMG_iare[i][0]];
+		nu2 = pt->v[MMG_iare[i][1]];
+		MMG_edgePut(hed,nu1,nu2,2);
+	}
+  //if((netmp+(k-1)*6+1 + 1) == 2924) printf("ii) tet %d %d %d %d\n",pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+  pt = &mesh->tetra[k+3];  
+  pt->v[0] = p1;
+  pt->v[1] = p4;
+  pt->v[2] = p5;
+  pt->v[3] = p7; 
+	for(i=0 ; i<6 ; i++) {
+		nu1 = pt->v[MMG_iare[i][0]];
+		nu2 = pt->v[MMG_iare[i][1]];
+		MMG_edgePut(hed,nu1,nu2,2);
+	}
+  //if((netmp+(k-1)*6+1 + 2) == 2924) printf("iii) tet %d %d %d %d\n",pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+  pt->ref  = ref;
+  pt = &mesh->tetra[k+4];
+  pt->v[0] = p7;
+  pt->v[1] = p4;
+  pt->v[2] = p0;
+  pt->v[3] = p1;
+  pt->ref  = ref;
+	for(i=0 ; i<6 ; i++) {
+		nu1 = pt->v[MMG_iare[i][0]];
+		nu2 = pt->v[MMG_iare[i][1]];
+		MMG_edgePut(hed,nu1,nu2,2);
+	}
+  //if((netmp+(k-1)*6+1 + 3) == 2924) printf("iv) tet %d %d %d %d\n",pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+  pt = &mesh->tetra[k+5];
+  pt->v[0] = p1;
+  pt->v[1] = p6;
+  pt->v[2] = p7;
+  pt->v[3] = p5;
+  pt->ref  = ref;
+	for(i=0 ; i<6 ; i++) {
+		nu1 = pt->v[MMG_iare[i][0]];
+		nu2 = pt->v[MMG_iare[i][1]];
+		MMG_edgePut(hed,nu1,nu2,2);
+	}
+  pt = &mesh->tetra[k+6];
+  pt->v[0] = p1;
+  pt->v[1] = p3;
+  pt->v[2] = p2;
+  pt->v[3] = p7;
+  pt->ref  = ref;
+	for(i=0 ; i<6 ; i++) {
+		nu1 = pt->v[MMG_iare[i][0]];
+		nu2 = pt->v[MMG_iare[i][1]];
+		MMG_edgePut(hed,nu1,nu2,2);
+	}
+	return(1);
+}
+
+int MMG_cutprism(pMesh mesh,pHedge hed,int k,int p0,int p1,int p2,int p3,int p4,int p5,int ref) {
+	pTetra 	pt;  
+	double	vol;
+	int			t0,t1,t2;
+	char   	icas; 
+	int ddebug;
+ 
+	vol= MMG_quickvol(mesh->point[p0].c,mesh->point[p1].c,mesh->point[p2].c,mesh->point[p3].c);  
+	if(vol<0) {
+		printf("inversion"); 
+		t0 = p0;
+		t1 = p1;
+		t2 = p2;
+		p0 = p3;
+		p1 = p4;
+		p2 = p5;
+		p3 = t0;
+		p4 = t1;
+		p5 = t2;
+	} 
+	if(k==606 || k==605 || k==604 || k==609 || k==608 || k==607) ddebug=1; 
+	else ddebug=0; 
+	ddebug=0;
+	if(ddebug) printf("k = %d : %d %d %d %d %d %d\n",k,p0,p1,p2,p3,p4,p5,p5);      
+	
+	icas = 0;
+
+	//find edge 2 : p1-p3 then edge 0 : 0 4
+	if(!MMG_edgePoint(hed,p1,p3)) {
+		if(MMG_edgePoint(hed,p0,p4))
+			icas |= 1;
+	} else {
+		icas |= 4;
+	}
+	//find edge 5 : p2-p4 then edge 3 : 1 5
+	if(!MMG_edgePoint(hed,p2,p4)) {
+		if(MMG_edgePoint(hed,p1,p5))
+			icas |= 8;
+	} else {
+		icas |= 32;
+	}
+	//find edge 4 : p2-p3 then edge 1 : 0 5
+	if(!MMG_edgePoint(hed,p2,p3)) {
+		if(MMG_edgePoint(hed,p0,p5))
+		icas |= 2;
+	} else {
+		icas |= 16;
+	}
+	if(icas > 55) {
+		fprintf(stdout,"grosgros bug %d\n",icas);
+		exit(0);
+	} 
+	if(ddebug) printf("on trouve %d\n",icas);
+	
+	switch(icas) {
+		case 0: 
+		  if(ddebug) printf("on rajoute %d %d -- %d %d -- %d %d\n",p0,p4,p1,p5,p0,p5);
+	    MMG_edgePut(hed,p2,p4,2);
+	    MMG_edgePut(hed,p1,p3,2);
+	    MMG_edgePut(hed,p3,p2,2);//MMG_edgePut(hed,p2,p3,2);
+		  icas = 52;
+	    break;
+		case 1:
+		  MMG_edgePut(hed,p1,p5,2);
+		  MMG_edgePut(hed,p0,p5,2);
+			icas = 11;//25;
+		  break;
+		case 2:
+	    MMG_edgePut(hed,p1,p5,2);
+	    MMG_edgePut(hed,p0,p4,2);
+	    icas = 11;//14
+	    break;
+		case 3:
+		  MMG_edgePut(hed,p1,p5,2);
+			icas = 11;//35;
+		  break;
+		case 4:
+	    MMG_edgePut(hed,p2,p4,2);//MMG_edgePut(hed,p1,p5,2);
+		  MMG_edgePut(hed,p2,p3,2);//MMG_edgePut(hed,p0,p5,2);
+			icas = 52;//14;
+		  break;
+		case 6:
+		  MMG_edgePut(hed,p1,p5,2);
+		  icas = 14;
+		  break;
+		case 8:
+		  MMG_edgePut(hed,p0,p5,2);
+		  MMG_edgePut(hed,p0,p4,2);
+			icas = 11;//14;
+		  break;
+		case 9:
+		  MMG_edgePut(hed,p0,p5,2);
+			icas = 11;//25;
+		  break;
+		case 10:
+		  MMG_edgePut(hed,p0,p4,2);
+			icas = 11;//14;
+		  break;
+		case 12:
+		  MMG_edgePut(hed,p0,p5,2);
+		  icas = 14;
+		  break;
+		case 16:
+		  MMG_edgePut(hed,p2,p4,2);//MMG_edgePut(hed,p1,p5,2);
+		  MMG_edgePut(hed,p3,p1,2);//MMG_edgePut(hed,p1,p3,2);
+			icas = 52;//28;
+		  break;
+		case 17:
+		  MMG_edgePut(hed,p4,p2,2); 
+			icas = 49;//25;
+		  break;
+		case 20:
+		  MMG_edgePut(hed,p2,p4,2);    //MMG_edgePut(hed,p1,p5,2);
+			icas = 52;//28;
+		  break;
+	  case 24:
+	    MMG_edgePut(hed,p1,p3,2);
+			icas = 28;//25;
+	    break;
+		case 32:
+		  MMG_edgePut(hed,p1,p3,2);//MMG_edgePut(hed,p0,p4,2);
+		  MMG_edgePut(hed,p2,p3,2);//MMG_edgePut(hed,p0,p5,2); 
+			icas = 52;//35;
+		  break;
+		case 33:
+		  MMG_edgePut(hed,p0,p5,2);
+		  icas = 35;
+		  break;
+		case 34:
+		  MMG_edgePut(hed,p0,p4,2);
+		  icas = 35;
+		  break;
+		case 36:
+		  MMG_edgePut(hed,p3,p2,2);
+		  icas = 52;
+		  break;
+		case 48:
+		  MMG_edgePut(hed,p1,p3,2);//MMG_edgePut(hed,p0,p4,2);
+			icas = 52;//49;
+		  break;
+		default:
+			//5,7,11,13,15,18,19,21,22,23,26,27,29,30,31,37,39,40,41,42,43,44,45,46,47,50,51,52,53,54,55: 
+		  //printf("icas imposssss %d\n",icas);
+		  //exit(0);
+		  break;
+		    
+	} 
+	if(ddebug) printf("du coup %d\n",icas);
+	switch(icas) {
+	  case 14: 
+		  pt = &mesh->tetra[k + 1];
+		  pt->v[0] = p5;
+	    pt->v[1] = p1;
+		  pt->v[2] = p2;
+		  pt->v[3] = p0;
+		  pt->ref  = ref;//1;//ref;
+	    pt = &mesh->tetra[k + 2];
+	    pt->v[0] = p3;
+		  pt->v[1] = p5;
+		  pt->v[2] = p1;
+		  pt->v[3] = p0;
+		  pt->ref  = ref;//1;//ref;
+	    pt = &mesh->tetra[k + 3];
+	    pt->v[0] = p3;
+	    pt->v[1] = p4;
+		  pt->v[2] = p1;
+		  pt->v[3] = p5;  
+			pt->ref  = ref;//1;//ref;
+		  break;
+	  case 11://25:    //D3  --> bug!     
+		if(ddebug) printf("on create %d %d %d %d -- %d %d %d %d -- %d %d %d %d\n",p0,p4,p3,p5,p0,p1,p4,p5,p5,p1,p2,p0);
+	   	pt = &mesh->tetra[k + 1];
+			pt->v[0] = p0;
+		  pt->v[1] = p4;
+			pt->v[2] = p3;
+			pt->v[3] = p5;
+			pt->ref  = ref;//3;//ref;
+		  pt = &mesh->tetra[k + 2];
+		  pt->v[0] = p0;
+			pt->v[1] = p1;
+			pt->v[2] = p4;
+			pt->v[3] = p5;
+			pt->ref  = ref;//3;//ref;
+		  pt = &mesh->tetra[k + 3];
+		  pt->v[0] = p5;
+		  pt->v[1] = p1;
+			pt->v[2] = p2;
+			pt->v[3] = p0;
+			pt->ref  = ref;//3;//ref;
+			break;
+		case 28:    //D2
+		 	pt = &mesh->tetra[k + 1];
+			pt->v[0] = p4;
+		  pt->v[1] = p5;
+			pt->v[2] = p1;
+			pt->v[3] = p3;
+			pt->ref  = ref;//2;//ref;
+		  pt = &mesh->tetra[k + 2];
+		  pt->v[0] = p1;
+			pt->v[1] = p2;
+			pt->v[2] = p5;
+			pt->v[3] = p3;
+			pt->ref  = ref;//2;//ref;
+		  pt = &mesh->tetra[k + 3];
+		  pt->v[0] = p2;
+		  pt->v[1] = p3;
+			pt->v[2] = p1;
+			pt->v[3] = p0;
+			pt->ref  = ref;//2;//ref;
+			break;
+		case 35:    //D4 --> ok
+		 	pt = &mesh->tetra[k + 1];
+			pt->v[0] = p0;
+		  pt->v[1] = p4;
+			pt->v[2] = p3;
+			pt->v[3] = p5;
+			pt->ref  = ref;//4;//ref;
+		  pt = &mesh->tetra[k + 2];
+		  pt->v[0] = p0;
+			pt->v[1] = p4;
+			pt->v[2] = p5;
+			pt->v[3] = p2;
+			pt->ref  = ref;//4;//ref;
+		  pt = &mesh->tetra[k + 3];  
+		  pt->v[0] = p0;
+		  pt->v[1] = p2;
+			pt->v[2] = p4;
+			pt->v[3] = p1;
+			pt->ref  = ref;//4;//ref;
+			break;
+		case 52:    
+		 	pt = &mesh->tetra[k + 1];
+			pt->v[0] = p2;
+		  pt->v[1] = p4;
+			pt->v[2] = p5;
+			pt->v[3] = p3;
+			pt->ref  = ref;//6;//ref;
+		  pt = &mesh->tetra[k + 2];
+		  pt->v[0] = p2;
+			pt->v[1] = p4;
+			pt->v[2] = p1;
+			pt->v[3] = p3;
+			pt->ref  = ref;//6;//ref;
+		  pt = &mesh->tetra[k + 3];
+		  pt->v[0] = p3;
+		  pt->v[1] = p0;
+			pt->v[2] = p1;
+			pt->v[3] = p2;
+			pt->ref  = ref;//6;//ref;
+			break;
+		case 49:    //D5
+   	  pt = &mesh->tetra[k + 1];
+		  pt->v[0] = p0;
+	    pt->v[1] = p4;
+		  pt->v[2] = p3;
+		  pt->v[3] = p2;
+		  pt->ref  = ref;//5;//ref;
+	    pt = &mesh->tetra[k + 2];
+	    pt->v[0] = p3;
+		  pt->v[1] = p2;
+		  pt->v[2] = p4;
+		  pt->v[3] = p5;
+		  pt->ref  = ref;//5;//ref;
+	    pt = &mesh->tetra[k + 3];
+	    pt->v[0] = p0;
+	    pt->v[1] = p2;
+		  pt->v[2] = p1;
+		  pt->v[3] = p4;
+			pt->ref  = ref;//5;//ref;
+			break;
+	  default:
+			//5,7,11,13,15,18,19,21,22,23,26,27,29,30,31,37,39,40,41,42,43,44,45,46,47,50,51,52,53,54,55:  
+		  MMG_cutadd(mesh,hed,icas,k,p0,p1,p2,p3,p4,p5,ref);
+		 
+		  //printf("icas imposssss %d\n",icas); 
+			return(0);
+		  //exit(0);
+		  break;
+	}	  	
+	return(1);
+}
\ No newline at end of file
diff --git a/contrib/mmg3d/build/sources/defines.h b/contrib/mmg3d/build/sources/defines.h
new file mode 100644
index 0000000000000000000000000000000000000000..2d71e945a9aa5c2f98be6e6396e7bc4ffd3a5651
--- /dev/null
+++ b/contrib/mmg3d/build/sources/defines.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#define EPS      1.e-06
+#define EPS1     1.e-9
+#define EPS2     1.e-12
+#define EPSOK    1.e-18
+#define EPS30    1.e-30
+
+#define ALPHAC   0.20412415      /* sqrt(6)/12 */  
+#define ALPHAD   0.04811252      /* 1.0/(12*sqrt(3)) */
+#define BETAC    0.03928371      /* sqrt(2)/36 */
+
+#define LLONG    1.3
+#define LSHORT   0.72
+#define LFILT    0.7
+#define QDEGRAD  2.45
+
+#define LONMAX     4096
+#define NPMAX    500000
+#define NTMAX   1000000
+#define NEMAX   3000000
+
+#define PRECI       1
+#define BUCKSIZ    64
+
+#define min(a,b) ( (a) < (b) ? (a) : (b) )
+#define max(a,b) ( (a) < (b) ? (b) : (a) )
+
+#define M_NOTAG    (0)
+#define M_UNUSED   (1 << 0)
+#define M_BDRY     (1 << 1)
+#define M_MOVE     (1 << 2)
+#define M_CAVITY   (1 << 3)
+//#define M_CORNER   (1 << 4)
+//#define M_REQUIRED (1 << 5)
+//#define M_RIDGE_GEO(1 << 6)
+//#define M_RIDGE_REF(1 << 7)
+#define ALL_BDRY   63
+
+#ifdef INT_MAX
+#undef INT_MAX
+#undef SHORT_MAX
+#endif
+#define INT_MAX      0x7fffffff
+#define SHORT_MAX    0x7fff
+
+
+extern unsigned char MMG_idir[4][3];
+extern unsigned char MMG_inxt[7];
+extern unsigned char MMG_iarf[4][3];
+extern unsigned char MMG_iare[6][2];
+extern unsigned char MMG_ifar[6][2];
+extern unsigned char MMG_isar[6][2];
+extern unsigned char MMG_arpt[4][3];
diff --git a/contrib/mmg3d/build/sources/delaunay.c b/contrib/mmg3d/build/sources/delaunay.c
new file mode 100644
index 0000000000000000000000000000000000000000..581df424adf7ad650905cd4f86fafe1db9e072fa
--- /dev/null
+++ b/contrib/mmg3d/build/sources/delaunay.c
@@ -0,0 +1,815 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "eigenv.h"
+#include "mesh.h"
+
+#define  EPSRAD       1.00005
+#define  EPSCON       5.0e-4//1.e-4//1.0e-3
+#define  VOLMIN       1.e-10//1.0e-15  --> vol negatif qd on rejoue
+
+
+int MMG_cas;
+extern int MMG_npuiss,MMG_nvol,MMG_npres;
+
+
+/* cavity -> ball */
+int MMG_delone(pMesh mesh,pSol sol,int ip,pList list,int ilist) {
+  pPoint    ppt;
+  pTetra    pt,pt1;
+  int      *adja,*adjb,i,j,k,l,m,iel,jel,old,v[3],iadr,base,size;
+  int       vois[4],ii,kk,iare1,iare2;
+  short     i1;
+  char      alert;       
+  int tref;  
+  if ( mesh->ne + 2*ilist > mesh->nemax )  return(0);
+  base = mesh->mark; 
+  /* external faces */
+  size = 0;
+  for (k=1; k<=ilist; k++) {
+    old  = list->tetra[k];
+    pt1  = &mesh->tetra[old];
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    vois[0]  = adja[0] >> 2;
+    vois[1]  = adja[1] >> 2;
+    vois[2]  = adja[2] >> 2;
+    vois[3]  = adja[3] >> 2;
+    for (i=0; i<4; i++) {
+      jel = vois[i];
+      if ( !jel || mesh->tetra[jel].mark != base ) {
+        for (j=0; j<3; j++) {
+	      i1  = MMG_idir[i][j];
+	      ppt = &mesh->point[ pt1->v[i1] ];
+	      ppt->tag |= M_CAVITY;
+	    }
+	    size++;
+      }
+    }
+  }
+
+  /* check isolated vertex */
+  alert = 0;
+  for (k=1; k<=ilist; k++) {
+    old  = list->tetra[k];
+    pt1  = &mesh->tetra[old]; 
+    for (i=0; i<4; i++) {
+      ppt = &mesh->point[ pt1->v[i] ]; 
+      if ( !(ppt->tag & M_CAVITY) )  alert = 1;
+    }
+  }
+  /* reset tag */
+  for (k=1; k<=ilist; k++) {
+    old  = list->tetra[k];
+    pt1  = &mesh->tetra[old];
+    for (i=0; i<4; i++) {
+      ppt = &mesh->point[ pt1->v[i] ];
+      ppt->tag &= ~M_CAVITY;
+    }
+  }     
+  if ( alert )  return(-1);
+  /* hash table params */
+  if ( size > 3*LONMAX )  return(0);
+  list->hedg.size  = size;
+  list->hedg.nhmax = 3*size+1;
+  list->hedg.hnxt  = size;
+  memset(list->hedg.item,0,list->hedg.nhmax*sizeof(hedge));
+  for (k=size; k<list->hedg.nhmax; k++)
+    list->hedg.item[k].nxt = k+1;
+
+  for (k=1; k<=ilist; k++) {
+    old  = list->tetra[k];
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    vois[0]  = adja[0];
+    vois[1]  = adja[1];
+    vois[2]  = adja[2];
+    vois[3]  = adja[3];
+    pt   = &mesh->tetra[old];
+
+    for (i=0; i<4; i++) {
+      jel = vois[i] >> 2;
+      j   = vois[i] % 4;
+
+      /* external face */
+      if ( !jel || (mesh->tetra[jel].mark != base) ) {
+        iel = MMG_newElt(mesh);
+        if ( iel < 1 )  return(0);
+        pt1 = &mesh->tetra[iel];
+	      memcpy(pt1,pt,sizeof(Tetra));
+        pt1->v[i] = ip;
+        pt1->qual = MMG_caltet(mesh,sol,iel);   
+        pt1->ref = mesh->tetra[old].ref;
+        if(pt1->qual > 1e+18) {printf("argggg (%d) %d : %e\n",ip,iel,pt1->qual); 
+        printf("pt1 : %d %d %d %d\n",pt1->v[0],pt1->v[1],pt1->v[2],pt1->v[3]);/*exit(0);*/}      
+        pt1->flag = mesh->flag;
+        pt1->edge = 0;     
+				for(ii=0 ; ii<4 ; ii++)
+          pt1->bdryref[ii] = -1; 
+				/*MAJ bdryinfo */
+				for(ii=0 ; ii<6 ; ii++)
+					pt1->bdryinfo[ii] = 0;
+				for(ii=0 ; ii<3 ; ii++) {  
+					if(!pt->bdryinfo[MMG_iarf[i][ii]]) continue;
+					iare1 = pt->v[MMG_iare[MMG_iarf[i][ii]][0]];
+					iare2 = pt->v[MMG_iare[MMG_iarf[i][ii]][1]]; 
+					for(kk=0 ; kk<3 ; kk++) {  
+					  if(((iare1==pt->v[MMG_iare[MMG_iarf[i][kk]][0]]) && (iare2==pt->v[MMG_iare[MMG_iarf[i][kk]][1]]))
+					  || ((iare2==pt->v[MMG_iare[MMG_iarf[i][kk]][0]]) && (iare1==pt->v[MMG_iare[MMG_iarf[i][kk]][1]])) ) {
+					  		pt1->bdryinfo[MMG_iarf[i][kk]] = pt->bdryinfo[MMG_iarf[i][ii]];
+								break;
+					  }        
+				  }          
+					assert(kk<3);
+				}
+        if(!jel || (mesh->tetra[jel].ref != pt1->ref)) {
+          pt1->bdryref[i] = mesh->tetra[old].bdryref[i];
+          if(pt1->bdryref[i]<0) {
+	          printf("delone : pbs sd %d : %d %d %d\n",iel,pt1->v[MMG_idir[i][0]]
+                     ,pt1->v[MMG_idir[i][1]],pt1->v[MMG_idir[i][2]]);exit(0);  
+           }
+        } 				
+        iadr = (iel-1)*4 + 1;
+        adjb = &mesh->adja[iadr];
+        adjb[i] = adja[i];
+        if ( jel ) {
+          iadr = (jel-1)*4 + 1;
+          adjb = &mesh->adja[iadr];
+          adjb[j] = iel*4 + i;
+    	  }
+
+        /* internal faces (p1,p2,ip) */
+        for (j=0; j<4; j++) {
+	        if ( j != i ) {
+            m = 0;
+	          for (l=0; l<3; l++)
+              if ( pt1->v[ MMG_idir[j][l] ] != ip ) {
+	              v[m] = pt1->v[ MMG_idir[j][l] ];
+		            m++;
+	            }
+	          MMG_hashEdge(mesh,&list->hedg,iel,j,v);
+	        }
+	      }
+      }
+    }
+  }
+
+  /* remove old tetra */ 
+  tref = mesh->tetra[list->tetra[1]].ref;
+  for (k=1; k<=ilist; k++) {    
+    if(tref!=mesh->tetra[list->tetra[k]].ref) 
+          printf("arg ref ???? %d %d\n",tref,mesh->tetra[list->tetra[k]].ref);
+    MMG_delElt(mesh,list->tetra[k]);
+  }
+
+  ppt = &mesh->point[ip];
+  ppt->flag = mesh->flag;
+  return(1);
+}
+
+
+/* clone of delone */
+int MMG_delons(pMesh mesh,pSol sol,pQueue queue,int ip,pList list,int ilist,double declic) {
+  pPoint    ppt;
+  pTetra    pt,pt1;
+  int      *adja,*adjb,i,j,k,l,m,iel,jel,old,v[3],iadr,base,size;
+  int       vois[4],ii,kk,iare1,iare2,tref;
+  short     i1;
+  char      alert;
+
+  if ( mesh->ne + 2*ilist > mesh->nemax )  return(0);
+  base = mesh->mark;
+
+  /* external faces */
+  size = 0;
+  for (k=1; k<=ilist; k++) {
+    old  = list->tetra[k];
+    pt1  = &mesh->tetra[old];
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    for (i=0; i<4; i++) {
+      jel = adja[i] >> 2;   
+      if ( !jel || mesh->tetra[jel].mark != base ) { 
+        for (j=0; j<3; j++) {
+          i1  = MMG_idir[i][j];
+          ppt = &mesh->point[ pt1->v[i1] ];
+          ppt->tag |= M_CAVITY;
+	      }
+	     size++;
+       } 
+    }
+  }
+
+  /* check isolated vertex */
+  alert = 0;
+  for (k=1; k<=ilist; k++) {
+    old  = list->tetra[k];
+    pt1  = &mesh->tetra[old];
+    for (i=0; i<4; i++) {
+      ppt = &mesh->point[ pt1->v[i] ];
+      if ( !(ppt->tag & M_CAVITY) )  alert = 1;
+    }
+  }
+  /* reset tag */
+  for (k=1; k<=ilist; k++) {
+    old  = list->tetra[k];
+    pt1  = &mesh->tetra[old];
+    for (i=0; i<4; i++) {
+      ppt = &mesh->point[ pt1->v[i] ];
+      ppt->tag &= ~M_CAVITY;
+    }
+  }
+  if ( alert )  return(-1);
+
+  /* hash table params */
+  if ( size > 3*LONMAX )  return(0);
+  list->hedg.size  = size;
+  list->hedg.nhmax = 3*size+1;
+  list->hedg.hnxt  = size;
+  memset(list->hedg.item,0,list->hedg.nhmax*sizeof(hedge));
+  for (k=size; k<list->hedg.nhmax; k++)
+    list->hedg.item[k].nxt = k+1;
+
+  for (k=1; k<=ilist; k++) {
+    old  = list->tetra[k];
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    vois[0]  = adja[0];
+    vois[1]  = adja[1];
+    vois[2]  = adja[2];
+    vois[3]  = adja[3];
+    pt   = &mesh->tetra[old];
+
+    for (i=0; i<4; i++) {
+      jel = vois[i] >> 2;
+      j   = vois[i] % 4;
+
+      /* external face */
+      if ( !jel || mesh->tetra[jel].mark != base ) {
+        iel = MMG_newElt(mesh);
+        if ( iel < 1 )  return(0);
+        pt1 = &mesh->tetra[iel];
+        pt1->ref = mesh->tetra[old].ref;
+        mesh->point[ip].tmp = iel;
+	    memcpy(pt1,pt,sizeof(Tetra));
+        pt1->v[i] = ip;
+        pt1->qual = MMG_caltet(mesh,sol,iel);
+        pt1->flag = mesh->flag;
+        pt1->edge = 0;
+				for(ii=0 ; ii<4 ; ii++)
+          pt1->bdryref[ii] = -1; 
+				/*MAJ bdryinfo */
+				for(ii=0 ; ii<6 ; ii++)
+					pt1->bdryinfo[ii] = 0;
+				for(ii=0 ; ii<3 ; ii++) {  
+					if(!pt->bdryinfo[MMG_iarf[i][ii]]) continue;
+					iare1 = pt->v[MMG_iare[MMG_iarf[i][ii]][0]];
+					iare2 = pt->v[MMG_iare[MMG_iarf[i][ii]][1]]; 
+					for(kk=0 ; kk<3 ; kk++) {  
+					  if(((iare1==pt->v[MMG_iare[MMG_iarf[i][kk]][0]]) && (iare2==pt->v[MMG_iare[MMG_iarf[i][kk]][1]]))
+					  || ((iare2==pt->v[MMG_iare[MMG_iarf[i][kk]][0]]) && (iare1==pt->v[MMG_iare[MMG_iarf[i][kk]][1]])) ) {
+					  		pt1->bdryinfo[MMG_iarf[i][kk]] = pt->bdryinfo[MMG_iarf[i][ii]];
+								break;
+					  }        
+				  }          
+					assert(kk<3);
+				}
+        if(!jel || (mesh->tetra[jel].ref != pt1->ref)) {
+          pt1->bdryref[i] = mesh->tetra[old].bdryref[i];
+          if(pt1->bdryref[i]<0) {puts("delone : pbs sd");exit(0);  }
+        }
+        iadr = (iel-1)*4 + 1;
+        adjb = &mesh->adja[iadr];
+        adjb[i] = adja[i];
+        if ( jel ) {
+          iadr = (jel-1)*4 + 1;
+          adjb = &mesh->adja[iadr];
+          adjb[j] = iel*4 + i;
+    	}
+        if ( pt1->qual >= declic )
+          MMG_kiuput(queue,iel);
+
+        /* internal faces (p1,p2,ip) */
+        for (j=0; j<4; j++) {
+	      if ( j != i ) {
+            m = 0;
+	        for (l=0; l<3; l++)
+              if ( pt1->v[ MMG_idir[j][l] ] != ip ) {
+	            v[m] = pt1->v[ MMG_idir[j][l] ];
+		        m++;
+	          }
+	        MMG_hashEdge(mesh,&list->hedg,iel,j,v);
+	      }
+	    }
+      }
+    }
+  }
+
+  /* remove old tetra */
+  for (k=1; k<=ilist; k++) {
+    old = list->tetra[k];
+    MMG_delElt(mesh,old);
+    MMG_kiudel(queue,old);
+  }
+
+  ppt = &mesh->point[ip];
+  ppt->flag = mesh->flag;
+  return(1);
+}
+
+
+/* cavity correction for quality */
+int MMG_correction_ani(pMesh mesh,pSol sol,int ip,pList list,int ilist,int nedep) {
+  pPoint	ppt,p1,p2,p3;
+  pTetra	pt;
+  double	dd,det,nn,eps,eps2,ux,uy,uz,vx,vy,vz,v1,v2,v3;
+  double	*ma,*mb,*mc,*md,mm[6],h1,h2,h3;
+  int		*adja,i,j,ipil,iel,lon,iadr,adj,ib,ic,id,base,ncor;
+  int		vois[4];
+
+  ppt  = &mesh->point[ip];
+  if ( ppt->tag & M_UNUSED )  return(ilist);
+  base = mesh->mark;
+  lon  = ilist;
+  eps  = EPSCON;
+  eps2 = eps*eps;
+
+  /* average metric */
+  memset(mm,0,6*sizeof(double));
+  iadr = (ip-1)*sol->offset + 1;
+  ma   = &sol->met[iadr];
+
+  do {
+    ipil = lon;
+    ncor = 0;
+
+    while ( ipil > 0 ) {
+      iel  = list->tetra[ipil];
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      vois[0]  = adja[0] >> 2;
+      vois[1]  = adja[1] >> 2;
+      vois[2]  = adja[2] >> 2;
+      vois[3]  = adja[3] >> 2;
+      pt   = &mesh->tetra[iel];
+
+MMG_cas=0;
+      for (i=0; i<4; i++) {
+        adj = vois[i];
+MMG_cas = 0;
+        if ( adj && mesh->tetra[adj].mark == base)  continue;
+
+        ib = pt->v[ MMG_idir[i][0] ];
+        ic = pt->v[ MMG_idir[i][1] ];
+        id = pt->v[ MMG_idir[i][2] ];
+
+        p1 = &mesh->point[ib];
+        p2 = &mesh->point[ic];
+        p3 = &mesh->point[id];
+
+        ux = p2->c[0] - p1->c[0];
+        uy = p2->c[1] - p1->c[1];
+        uz = p2->c[2] - p1->c[2];
+
+        vx = p3->c[0] - p1->c[0];
+        vy = p3->c[1] - p1->c[1];
+        vz = p3->c[2] - p1->c[2];
+
+        /* volume PABC */
+        v1 = uz*vy - uy*vz;
+        v2 = ux*vz - uz*vx;
+        v3 = uy*vx - ux*vy;
+        dd = v1*(ppt->c[0]-p1->c[0]) + v2*(ppt->c[1]-p1->c[1]) \
+           + v3*(ppt->c[2]-p1->c[2]);
+MMG_cas=1;
+	   //if ( dd < VOLMIN )  break;  
+	   /*test sur le volume avec un eps local*/
+       h1 = ux*ux + uy*uy + uz*uz;
+       h2 = vx*vx + vy*vy + vz*vz;
+       h3 = (p2->c[0] - p3->c[0])*(p2->c[0] - p3->c[0]) + (p2->c[1] - p3->c[1])*(p2->c[1] - p3->c[1])
+          + (p2->c[2] - p3->c[2])*(p2->c[2] - p3->c[2]);
+	     if ( dd < VOLMIN*sqrt(h1*h2*h3) )  break;   
+
+        /* average metric */
+        iadr = (ib-1)*sol->offset + 1;
+        mb   = &sol->met[iadr];
+        iadr = (ic-1)*sol->offset + 1;
+        mc   = &sol->met[iadr];
+        iadr = (id-1)*sol->offset + 1;
+        md   = &sol->met[iadr];
+        for (j=0; j<6; j++)
+          mm[j] = 0.25 * (ma[j]+mb[j]+mc[j]+md[j]);
+          
+        det = mm[0] * ( mm[3]*mm[5] - mm[4]*mm[4]) \
+            - mm[1] * ( mm[1]*mm[5] - mm[2]*mm[4]) \
+            + mm[2] * ( mm[1]*mm[4] - mm[2]*mm[3]);
+        if ( det < EPSOK )  break;
+
+	/* point close to face */
+	/*nn = (v1*v1 + v2*v2 + v3*v3);*/
+MMG_cas=2;
+        nn = mm[0]*v1*v1 + mm[3]*v2*v2 + mm[5]*v3*v3 \
+           + 2.0*(mm[1]*v1*v2 + mm[2]*v1*v3 + mm[4]*v2*v3);
+        /*if ( det*dd*dd*dd*dd*dd*dd < nn * nn * nn * eps2 * eps2 * eps2 )  break;*/
+	/*//prendre le min des valeurs propres
+	eigenv(1,mm,lambda,vv);
+	det = max(lambda[0],max(lambda[1],lambda[2]));
+	if ( det*dd*dd < nn * eps2 )  break;
+	*//*if ( pow(det,1./3.)*dd*dd < nn * eps2 )  break;*/
+	if ( det*dd*dd < nn * eps2 )  break;
+	/*if ( dd*dd < nn * eps2 ) {
+	  printf("en iso      : %e %e    %e %e\n",dd,nn,dd*dd,nn*eps2);
+	  printf("en iso sqrt : %e %e    %e %e\n",dd,nn,dd/sqrt(nn),(sqrt(mm[0]))*(dd/sqrt(nn)));
+
+	  dd1 = mm[0]*v1*v1 + mm[3]*v2*v2 + mm[5]*v3*v3 \
+           + 2.0*(mm[1]*v1*v2 + mm[2]*v1*v3 + mm[4]*v2*v3);
+	   //len carre = (dd*dd/norm(v1v2v3)^2)*dd1/(norm(v1v2v3)^2 
+	  printf("aniso      : %e %e %e %e %e\n",(dd*dd/nn)*dd1/(nn),sqrt(dd*dd*dd1/(nn*sqrt(nn))),det,det*dd*dd,dd1*eps2);
+	  
+	  nn = sqrt(nn);
+	  ph = dd/nn;
+	  v1 /= nn;
+	  v2 /= nn;
+	  v3 /= nn;
+	  xh = ph*v1 + ppt->c[0];
+	  yh = ph*v2 + ppt->c[1];
+	  zh = ph*v3 + ppt->c[2];
+	  
+	  //dist PH dans la met/
+	  ux = xh - ppt->c[0];
+          uy = yh - ppt->c[1];
+          uz = zh - ppt->c[2];
+          dd = ux*ux + uy*uy + uz*uz;
+
+          dd2 =      mm[0]*ux*ux + mm[3]*uy*uy + mm[5]*uz*uz \
+               + 2.0*(mm[1]*ux*uy + mm[2]*ux*uz + mm[4]*uy*uz);
+          if ( dd2 <= 0.0 )  dd2 = 0.0;
+
+          len = sqrt(dd2);
+	  
+	  printf("on trouve len : %e %e %e\n",len,sqrt(eps2)*sqrt(mm[0]),pow(sqrt(eps2)*sqrt(det),1./3.));
+	  printf("len carre %e %e\n",mm[0]*v1*v1*ph*ph + mm[3]*v2*v2*ph*ph + mm[5]*v3*v3*ph*ph,dd2);
+	 exit(0);
+	 break;
+	 }*/
+MMG_cas=0;	
+      }
+      if ( i < 4 ) {
+        if ( ipil <= nedep )  return(0);
+        /* remove iel from list */
+	    pt->mark = base-1;
+	    list->tetra[ipil] = list->tetra[lon];
+        lon--;
+	    ncor = 1;
+	    break;
+      }
+      else
+        ipil--;
+    }
+  }
+  while ( ncor > 0 && lon >= nedep );
+
+  return(lon);
+}
+
+
+/* cavity correction for quality */
+int MMG_correction_iso(pMesh mesh,int ip,pList list,int ilist,int nedep) {
+  pPoint   ppt,p1,p2,p3;
+  pTetra   pt;
+  double   dd,nn,eps,eps2,ux,uy,uz,vx,vy,vz,v1,v2,v3;
+  int     *adja,i,ipil,iel,lon,iadr,adj,ib,ic,id,base,ncor;
+  int	   vois[4];
+  
+  ppt  = &mesh->point[ip];
+  if ( ppt->tag & M_UNUSED )  return(ilist);
+  base = mesh->mark;
+  lon  = ilist;
+  eps  = EPSCON;
+  eps2 = eps*eps;
+  do {
+    ipil = lon;
+    ncor = 0;
+
+    while ( ipil > 0 ) {
+      iel  = list->tetra[ipil];
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      vois[0]  = adja[0] >> 2;
+      vois[1]  = adja[1] >> 2;
+      vois[2]  = adja[2] >> 2;
+      vois[3]  = adja[3] >> 2;
+      pt   = &mesh->tetra[iel];
+MMG_cas=0;
+      for (i=0; i<4; i++) {
+        adj = vois[i];
+MMG_cas = 0;
+        if ( adj && mesh->tetra[adj].mark == base )  continue;
+
+        ib = pt->v[ MMG_idir[i][0] ];
+        ic = pt->v[ MMG_idir[i][1] ];
+        id = pt->v[ MMG_idir[i][2] ];
+
+        p1 = &mesh->point[ib];
+        p2 = &mesh->point[ic];
+        p3 = &mesh->point[id];
+
+        ux = p2->c[0] - p1->c[0];
+        uy = p2->c[1] - p1->c[1];
+        uz = p2->c[2] - p1->c[2];
+
+        vx = p3->c[0] - p1->c[0];
+        vy = p3->c[1] - p1->c[1];
+        vz = p3->c[2] - p1->c[2];
+
+        /* volume PABC */
+        v1 = uz*vy - uy*vz;
+        v2 = ux*vz - uz*vx;
+        v3 = uy*vx - ux*vy;
+        dd = v1*(ppt->c[0]-p1->c[0]) + v2*(ppt->c[1]-p1->c[1]) \
+           + v3*(ppt->c[2]-p1->c[2]);
+MMG_cas=1;   
+//printf("on trouve vol %e <? %e\n",dd,VOLMIN);
+	      if ( dd < VOLMIN )  break;
+
+        /* point close to face */
+        nn = (v1*v1 + v2*v2 + v3*v3);
+MMG_cas=2;                         
+//printf("on trouve close ? %e %e\n",dd*dd,nn*eps2);
+        if ( dd*dd < nn * eps2 )  break;
+MMG_cas=0;	
+      }
+      if ( i < 4 ) {
+        if ( ipil <= nedep )  {/*printf("on veut tout retirer ? %d %d\n",ipil,nedep);*/return(0);   }
+        /* remove iel from list */
+        pt->mark = base-1;
+        list->tetra[ipil] = list->tetra[lon];
+        lon--;
+        ncor = 1;
+        break;
+      }
+      else
+        ipil--;
+    }
+  }
+  while ( ncor > 0 && lon >= nedep );
+
+  return(lon);
+}
+
+
+/* mark elements in cavity */
+int MMG_cavity_ani(pMesh mesh,pSol sol,int iel,int ip,pList list,int lon) {
+  pPoint    ppt;
+  pTetra    pt,pt1,ptc;
+  double    c[3],eps,dd,ray,ux,uy,uz,crit;
+  double    *mj,*mp,ct[12];
+  int       *adja,*adjb,k,adj,adi,voy,i,j,ia,ilist,ipil,jel,iadr,base;
+  int	    vois[4],l;
+  
+  if ( lon < 1 )  return(0);
+  ppt = &mesh->point[ip];
+  if ( ppt->tag & M_UNUSED )  return(0);
+
+  for (k=1; k<=lon; k++)
+    list->tetra[k] = list->tetra[k] / 6;
+
+  /* grow cavity by adjacency */
+  base  = mesh->mark;
+  eps   = EPSRAD * EPSRAD;
+  ilist = lon;
+  ipil  = 1;
+  iadr  = (ip-1)*sol->offset + 1;
+  mp    = &sol->met[iadr];
+
+  do {
+    jel  = list->tetra[ipil];
+    iadr = (jel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    vois[0]  = adja[0];
+    vois[1]  = adja[1];
+    vois[2]  = adja[2];
+    vois[3]  = adja[3];
+    ptc  = &mesh->tetra[jel];
+
+    for (i=0; i<4; i++) {
+      adj = vois[i] >> 2;
+      voy = vois[i] % 4;
+      if ( !adj )  continue;
+      pt  = &mesh->tetra[adj];
+      /* boundary face */
+      if ( pt->mark == base || pt->ref != ptc->ref )  continue;
+      for (j=0,l=0; j<4; j++,l+=3) {
+        memcpy(&ct[l],mesh->point[pt->v[j]].c,3*sizeof(double));      
+      }
+
+
+      /* Delaunay kernel */
+      if ( !MMG_cenrad_ani(mesh,ct,mp,c,&ray) )  continue;
+
+      ux = ppt->c[0] - c[0];
+      uy = ppt->c[1] - c[1];
+      uz = ppt->c[2] - c[2];
+      dd =      mp[0]*ux*ux + mp[3]*uy*uy + mp[5]*uz*uz \
+         + 2.0*(mp[1]*ux*uy + mp[2]*ux*uz + mp[4]*uy*uz);
+      crit = eps * ray;
+      if ( dd > crit )  continue;
+
+      /* mixed metrics */
+      crit = sqrt(dd/ray);
+      for (j=0; j<4; j++) {
+        ia   = pt->v[j];
+        iadr = (ia-1)*sol->offset + 1;
+        mj   = &sol->met[iadr];
+        if ( !MMG_cenrad_ani(mesh,ct,mj,c,&ray) )  continue;
+        ux = ppt->c[0] - c[0];
+        uy = ppt->c[1] - c[1];
+        uz = ppt->c[2] - c[2];
+        dd =      mj[0]*ux*ux + mj[3]*uy*uy + mj[5]*uz*uz \
+           + 2.0*(mj[1]*ux*uy + mj[2]*ux*uz + mj[4]*uy*uz);
+        crit += sqrt(dd/ray);
+      }
+      crit *= EPSRAD;
+      if ( crit > 5.0 ) continue;
+
+      /* lost face(s) */
+      iadr = (adj-1)*4 + 1;
+      adjb = &mesh->adja[iadr];
+
+      for (j=0; j<4; j++) {
+        if ( j == voy )  continue;
+	      adi = adjb[j] >> 2;
+	      if ( !adi )  continue;
+	      pt1 = &mesh->tetra[adi];
+        if ( pt1->mark == base && adi != jel ) {
+          if ( !adi || pt1->ref != mesh->tetra[adi].ref )  break;
+        }
+      }
+      /* store tetra */
+      if ( j == 4 ) {
+        pt->mark = base;
+        ++ilist;
+        list->tetra[ilist] = adj;
+      }
+    }
+    if ( ilist > LONMAX - 3 )  return(-1);
+    ++ipil;
+  }
+  while ( ipil <= ilist );
+
+  /* global overflow */
+  if ( mesh->ne + 2*ilist >= mesh->nemax )
+    ilist = -ilist;
+  else
+    ilist = MMG_correction_ani(mesh,sol,ip,list,ilist,lon);
+
+if(MMG_cas==1) MMG_nvol++;
+else if(MMG_cas==2 || MMG_cas>20) {
+  MMG_npuiss++;
+  if(MMG_cas>20) MMG_npres++;
+}
+
+  return(ilist);
+}
+
+
+int MMG_cavity_iso(pMesh mesh,pSol sol,int iel,int ip,pList list,int lon) {
+  pPoint    ppt;
+  pTetra    pt,pt1,ptc;
+  double    c[3],crit,dd,eps,ray,ct[12];
+  int      *adja,*adjb,k,adj,adi,voy,i,j,ilist,ipil,jel,iadr,base;
+  int       vois[4],l;
+  int tref;
+    
+  if ( lon < 1 )  return(0);
+  ppt = &mesh->point[ip];
+  if ( ppt->tag & M_UNUSED )  return(0);
+
+  tref = mesh->tetra[list->tetra[1]/6].ref;
+#if !defined(_MSC_VER)
+#warning remove this test
+#endif
+  for (k=1; k<=lon; k++)
+    if(tref!=mesh->tetra[list->tetra[k]/6].ref) 
+       printf("pbs coquil %d %d tet %d\n",tref,mesh->tetra[list->tetra[k]/6].ref,list->tetra[k]/6);
+  
+  for (k=1; k<=lon; k++)
+    list->tetra[k] = list->tetra[k] / 6;
+
+  /* grow cavity by adjacency */
+  base  = mesh->mark;
+  eps   = EPSRAD*EPSRAD;
+  ilist = lon;
+  ipil  = 1;
+
+  do {
+    jel  = list->tetra[ipil]; 
+    iadr = (jel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    vois[0]  = adja[0];
+    vois[1]  = adja[1];
+    vois[2]  = adja[2];
+    vois[3]  = adja[3];
+    ptc  = &mesh->tetra[jel];
+
+    for (i=0; i<4; i++) {
+      adj = vois[i] >> 2;
+      voy = vois[i] % 4;
+      if ( !adj )  continue;
+      pt  = &mesh->tetra[adj];
+      /* boundary face */
+      if ( pt->mark == base || pt->ref != ptc->ref )  continue;
+
+      for (j=0,l=0; j<4; j++,l+=3) {
+        memcpy(&ct[l],mesh->point[pt->v[j]].c,3*sizeof(double));      
+      }
+
+      if ( !MMG_cenrad_iso(mesh,ct,c,&ray) )  continue;
+      crit = eps * ray;
+
+      /* Delaunay criterion */
+      dd = (ppt->c[0] - c[0]) * (ppt->c[0] - c[0]) \
+         + (ppt->c[1] - c[1]) * (ppt->c[1] - c[1]) \
+         + (ppt->c[2] - c[2]) * (ppt->c[2] - c[2]);
+      if ( dd > crit )  continue;
+
+      /* lost face(s) */
+      iadr = (adj-1)*4 + 1;
+      adjb = &mesh->adja[iadr];
+
+      for (j=0; j<4; j++) {
+        if ( j == voy )  continue;
+        adi = adjb[j] >> 2;
+        if ( !adi )  continue;
+        pt1 = &mesh->tetra[adi];
+        if ( pt1->mark == base && adi != jel ) {
+          if ( !adi || pt1->ref != mesh->tetra[adi].ref )  break;
+        }
+      }
+      /* store tetra */
+      if ( j == 4 ) {
+        pt->mark = base;
+        ++ilist;
+        list->tetra[ilist] = adj;
+      }
+    }
+    if ( ilist > LONMAX - 3 )  return(-1);
+    ++ipil;
+  }
+  while ( ipil <= ilist );
+  /* global overflow */
+  if ( mesh->ne + 2*ilist >= mesh->nemax )  
+    ilist = -ilist;
+  else
+    ilist = MMG_correction_iso(mesh,ip,list,ilist,lon);
+
+if(MMG_cas==1) MMG_nvol++;
+else if(MMG_cas==2 || MMG_cas>20) {
+  MMG_npuiss++;
+  if(MMG_cas>20) MMG_npres++;
+}
+
+  return(ilist);
+}
diff --git a/contrib/mmg3d/build/sources/eigenv.c b/contrib/mmg3d/build/sources/eigenv.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a4bf0bf71ff67e09942df5e1b638952657e0b3b
--- /dev/null
+++ b/contrib/mmg3d/build/sources/eigenv.c
@@ -0,0 +1,665 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile,
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite,
+y compris les garanties de commercialisation ou
+d’adaptation dans un but spécifique.
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU
+en même temps que ce document.
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+spread under the terms and conditions of the license GNU General Public License
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.
+****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+/* seeking 1.e-05 accuracy */
+#define  EPSD           1.e-15
+#define  EPSD2          1.e-10
+#define  EPS6           5.e-06
+#define  EPS            1.e-06
+#define  EPSX2          2.e-06
+#define  MAXTOU         50
+
+/* check if numbers are equal */
+#define egal(x,y)   ( \
+  (  ((x) == 0.0f) ? (fabs(y) < EPS) : \
+   ( ((y) == 0.0f) ? (fabs(x) < EPS) : \
+     (fabs((x)-(y)) / (fabs(x) + fabs(y)) < EPSX2) )  ) )
+
+
+static double Id[3][3] = {
+  {1.0, 0.0, 0.0},
+  {0.0, 1.0, 0.0},
+  {0.0, 0.0, 1.0} };
+
+
+/* find root(s) of polynomial:  P(x)= x^3+bx^2+cx+d
+   return 1: 3 roots,  2: 2 roots,  3: 1 root */
+static int newton3(double p[4],double x[3]) {
+  double     b,c,d,da,db,dc,epsd;
+  double     delta,fx,dfx,dxx;
+  double     fdx0,fdx1,dx0,dx1,x1,x2;
+  int        it,n;
+
+  /* coeffs polynomial, a=1 */
+  b = p[2];
+  c = p[1];
+  d = p[0];
+  n = 1;
+
+  /* 1st derivative of f */
+  da = 3.0;
+  db = 2.0*b;
+
+  /* solve 2nd order eqn */
+  delta = db*db - 4.0*da*c;
+  epsd  = db*db*EPSD2;
+
+  /* inflexion (f'(x)=0, x=-b/2a) */
+  x1 = -db / 6.0f;
+
+  n = 1;
+  if ( delta > epsd ) {
+    delta = sqrt(delta);
+    dx0   = (-db + delta) / 6.0;
+    dx1   = (-db - delta) / 6.0;
+    /* Horner */
+    fdx0 = d + dx0*(c+dx0*(b+dx0));
+    fdx1 = d + dx1*(c+dx1*(b+dx1));
+
+    if ( fabs(fdx0) < EPSD ) {
+      /* dx0: double root, compute single root */
+      n = 2;
+      x[0] = dx0;
+      x[1] = dx0;
+      x[2] = -b - 2.0*dx0;
+      /* check if P(x) = 0 */
+      fx = d + x[2]*(c+x[2]*(b+x[2]));
+      if ( fabs(fx) > EPSD2 ) {
+#ifdef DEBUG
+        fprintf(stderr,"  ## ERR 9100, newton3: fx= %E\n",fx);
+#endif
+        return(0);
+      }
+      return(n);
+    }
+    else if ( fabs(fdx1) < EPSD ) {
+      /* dx1: double root, compute single root */
+      n = 2;
+      x[0] = dx1;
+      x[1] = dx1;
+      x[2] = -b - 2.0*dx1;
+      /* check if P(x) = 0 */
+      fx = d + x[2]*(c+x[2]*(b+x[2]));
+      if ( fabs(fx) > EPSD2 ) {
+#ifdef DEBUG
+        fprintf(stderr,"  ## ERR 9100, newton3: fx= %E\n",fx);
+#endif
+        return(0);
+      }
+      return(n);
+    }
+  }
+
+  else if ( fabs(delta) < epsd ) {
+    /* triple root */
+    n = 3;
+    x[0] = x1;
+    x[1] = x1;
+    x[2] = x1;
+    /* check if P(x) = 0 */
+    fx = d + x[0]*(c+x[0]*(b+x[0]));
+    if ( fabs(fx) > EPSD2 ) {
+#ifdef DEBUG
+      fprintf(stderr,"  ## ERR 9100, newton3: fx= %E\n",fx);
+#endif
+      return(0);
+    }
+    return(n);
+  }
+
+  else {
+#ifdef DEBUG
+    fprintf(stderr,"  ## ERR 9101, newton3: no real roots\n");
+#endif
+    return(0);
+  }
+
+  /* Newton method: find one root (middle)
+     starting point: P"(x)=0 */
+  x1  = -b / 3.0;
+  dfx =  c + b*x1;
+  fx  = d + x1*(c -2.0*x1*x1);
+  it  = 0;
+  do {
+    x2 = x1 - fx / dfx;
+    fx = d + x2*(c+x2*(b+x2));
+    if ( fabs(fx) < EPSD ) {
+      x[0] = x2;
+      break;
+    }
+    dfx = c + x2*(db + da*x2);
+
+    /* check for break-off condition */
+    dxx = fabs((x2-x1) / x2);
+    if ( dxx < 1.0e-10 ) {
+      x[0] = x2;
+      if ( fabs(fx) > EPSD2 ) {
+        fprintf(stderr,"  ## ERR 9102, newton3, no root found (fx %E).\n",fx);
+        return(0);
+      }
+      break;
+    }
+    else
+      x1 = x2;
+  }
+  while ( ++it < MAXTOU );
+
+  if ( it == MAXTOU ) {
+    x[0] = x1;
+    fx   = d + x1*(c+(x1*(b+x1)));
+    if ( fabs(fx) > EPSD2 ) {
+      fprintf(stderr,"  ## ERR 9102, newton3, no root found (fx %E).\n",fx);
+      return(0);
+    }
+  }
+
+  /* solve 2nd order equation
+     P(x) = (x-sol(1))* (x^2+bb*x+cc)  */
+  db    = b + x[0];
+  dc    = c + x[0]*db;
+  delta = db*db - 4.0*dc;
+
+  if ( delta <= 0.0 ) {
+    fprintf(stderr,"  ## ERR 9103, newton3, det = 0.\n");
+    return(0);
+  }
+
+  delta = sqrt(delta);
+  x[1] = 0.5 * (-db+delta);
+  x[2] = 0.5 * (-db-delta);
+
+#ifdef DEBUG
+    /* check for root accuracy */
+    fx = d + x[1]*(c+x[1]*(b+x[1]));
+    if ( fabs(fx) > EPSD2 ) {
+      fprintf(stderr,"  ## ERR 9104, newton3: fx= %E  x= %E\n",fx,x[1]);
+      return(0);
+    }
+    fx = d + x[2]*(c+x[2]*(b+x[2]));
+    if ( fabs(fx) > EPSD2 ) {
+      fprintf(stderr,"  ## ERR 9104, newton3: fx= %E  x= %E\n",fx,x[2]);
+      return(0);
+    }
+#endif
+
+  return(n);
+}
+
+
+/* find eigenvalues and vectors of a 3x3 symmetric definite
+ * positive matrix
+ * return order of eigenvalues (1,2,3) or 0 if failed */
+int eigenv(int symmat,double *mat,double lambda[3],double v[3][3]) {
+  double    a11,a12,a13,a21,a22,a23,a31,a32,a33;
+  double    aa,bb,cc,dd,ee,ii,vx1[3],vx2[3],vx3[3],dd1,dd2,dd3;
+  double    maxd,maxm,valm,p[4],w1[3],w2[3],w3[3];
+  int       k,n;
+
+  /* default */
+  memcpy(v,Id,9*sizeof(double));
+  if ( symmat ) {
+    lambda[0] = (double)mat[0];
+    lambda[1] = (double)mat[3];
+    lambda[2] = (double)mat[5];
+
+    maxm = fabs(mat[0]);
+    for (k=1; k<6; k++) {
+      valm = fabs(mat[k]);
+      if ( valm > maxm )  maxm = valm;
+    }
+    /* single float accuracy */
+    if ( maxm < EPS6 )  return(1);
+
+    /* normalize matrix */
+    dd  = 1.0 / maxm;
+    a11 = mat[0] * dd;
+    a12 = mat[1] * dd;
+    a13 = mat[2] * dd;
+    a22 = mat[3] * dd;
+    a23 = mat[4] * dd;
+    a33 = mat[5] * dd;
+
+    /* diagonal matrix */
+    maxd = fabs(a12);
+    valm = fabs(a13);
+    if ( valm > maxd )  maxd = valm;
+    valm = fabs(a23);
+    if ( valm > maxd )  maxd = valm;
+    if ( maxd < EPSD )  return(1);
+
+    a21  = a12;
+    a31  = a13;
+    a32  = a23;
+
+    /* build characteristic polynomial
+       P(X) = X^3 - trace X^2 + (somme des mineurs)X - det = 0 */
+    aa = a11*a22;
+    bb = a23*a32;
+    cc = a12*a21;
+    dd = a13*a31;
+    p[0] =  a11*bb + a33*(cc-aa) + a22*dd -2.0*a12*a13*a23;
+    p[1] =  a11*(a22 + a33) + a22*a33 - bb - cc - dd;
+    p[2] = -a11 - a22 - a33;
+    p[3] =  1.0;
+  }
+  else {
+    lambda[0] = (double)mat[0];
+    lambda[1] = (double)mat[4];
+    lambda[2] = (double)mat[8];
+
+    maxm = fabs(mat[0]);
+    for (k=1; k<9; k++) {
+      valm = fabs(mat[k]);
+      if ( valm > maxm )  maxm = valm;
+    }
+    if ( maxm < EPS6 )  return(1);
+
+    /* normalize matrix */
+    dd  = 1.0 / maxm;
+    a11 = mat[0] * dd;
+    a12 = mat[1] * dd;
+    a13 = mat[2] * dd;
+    a21 = mat[3] * dd;
+    a22 = mat[4] * dd;
+    a23 = mat[5] * dd;
+    a31 = mat[6] * dd;
+    a32 = mat[7] * dd;
+    a33 = mat[8] * dd;
+
+    /* diagonal matrix */
+    maxd = fabs(a12);
+    valm = fabs(a13);
+    if ( valm > maxd )  maxd = valm;
+    valm = fabs(a23);
+    if ( valm > maxd )  maxd = valm;
+    valm = fabs(a21);
+    if ( valm > maxd )  maxd = valm;
+    valm = fabs(a31);
+    if ( valm > maxd )  maxd = valm;
+     valm = fabs(a32);
+    if ( valm > maxd )  maxd = valm;
+    if ( maxd < EPSD )  return(1);
+
+    /* build characteristic polynomial
+       P(X) = X^3 - trace X^2 + (somme des mineurs)X - det = 0 */
+    aa = a22*a33 - a23*a32;
+    bb = a23*a31 - a21*a33;
+    cc = a21*a32 - a31*a22;
+    ee = a11*a33 - a13*a31;
+    ii = a11*a22 - a12*a21;
+
+    p[0] =  -a11*aa - a12*bb - a13*cc;
+    p[1] =  aa + ee + ii;
+    p[2] = -a11 - a22 - a33;
+    p[3] =  1.0;
+  }
+
+  /* solve polynomial (find roots using newton) */
+  n = newton3(p,lambda);
+  if ( n <= 0 )  return(0);
+
+  /* compute eigenvectors:
+     an eigenvalue belong to orthogonal of Im(A-lambda*Id) */
+  v[0][0] = 1.0; v[0][1] = v[0][2] = 0.0;
+  v[1][1] = 1.0; v[1][0] = v[1][2] = 0.0;
+  v[2][2] = 1.0; v[2][0] = v[2][1] = 0.0;
+
+  w1[1] = a12;  w1[2] = a13;
+  w2[0] = a21;  w2[2] = a23;
+  w3[0] = a31;  w3[1] = a32;
+
+  if ( n == 1 ) {
+    /* vk = crsprd(wi,wj) */
+    for (k=0; k<3; k++) {
+      w1[0] = a11 - lambda[k];
+      w2[1] = a22 - lambda[k];
+      w3[2] = a33 - lambda[k];
+
+      /* cross product vectors in (Im(A-lambda(i) Id) ortho */
+      vx1[0] = w1[1]*w3[2] - w1[2]*w3[1];
+      vx1[1] = w1[2]*w3[0] - w1[0]*w3[2];
+      vx1[2] = w1[0]*w3[1] - w1[1]*w3[0];
+      dd1    = vx1[0]*vx1[0] + vx1[1]*vx1[1] + vx1[2]*vx1[2];
+
+      vx2[0] = w1[1]*w2[2] - w1[2]*w2[1];
+      vx2[1] = w1[2]*w2[0] - w1[0]*w2[2];
+      vx2[2] = w1[0]*w2[1] - w1[1]*w2[0];
+      dd2    = vx2[0]*vx2[0] + vx2[1]*vx2[1] + vx2[2]*vx2[2];
+
+      vx3[0] = w2[1]*w3[2] - w2[2]*w3[1];
+      vx3[1] = w2[2]*w3[0] - w2[0]*w3[2];
+      vx3[2] = w2[0]*w3[1] - w2[1]*w3[0];
+      dd3    = vx3[0]*vx3[0] + vx3[1]*vx3[1] + vx3[2]*vx3[2];
+
+      /* find vector of max norm */
+      if ( dd1 > dd2 ) {
+        if ( dd1 > dd3 ) {
+          dd1 = 1.0 / sqrt(dd1);
+          v[k][0] = vx1[0] * dd1;
+          v[k][1] = vx1[1] * dd1;
+          v[k][2] = vx1[2] * dd1;
+        }
+	else {
+          dd3 = 1.0 / sqrt(dd3);
+          v[k][0] = vx3[0] * dd3;
+          v[k][1] = vx3[1] * dd3;
+          v[k][2] = vx3[2] * dd3;
+	}
+      }
+      else {
+        if ( dd2 > dd3 ) {
+          dd2 = 1.0 / sqrt(dd2);
+          v[k][0] = vx2[0] * dd2;
+          v[k][1] = vx2[1] * dd2;
+          v[k][2] = vx2[2] * dd2;
+        }
+        else {
+          dd3 = 1.0 / sqrt(dd3);
+          v[k][0] = vx3[0] * dd3;
+          v[k][1] = vx3[1] * dd3;
+          v[k][2] = vx3[2] * dd3;
+        }
+      }
+    }
+  }
+
+  /* (vp1,vp2) double,  vp3 simple root */
+  else if ( n == 2 ) {
+    w1[0] = a11 - lambda[2];
+    w2[1] = a22 - lambda[2];
+    w3[2] = a33 - lambda[2];
+
+    /* cross product */
+    vx1[0] = w1[1]*w3[2] - w1[2]*w3[1];
+    vx1[1] = w1[2]*w3[0] - w1[0]*w3[2];
+    vx1[2] = w1[0]*w3[1] - w1[1]*w3[0];
+    dd1 = vx1[0]*vx1[0] + vx1[1]*vx1[1] + vx1[2]*vx1[2];
+
+    vx2[0] = w1[1]*w2[2] - w1[2]*w2[1];
+    vx2[1] = w1[2]*w2[0] - w1[0]*w2[2];
+    vx2[2] = w1[0]*w2[1] - w1[1]*w2[0];
+    dd2 = vx2[0]*vx2[0] + vx2[1]*vx2[1] + vx2[2]*vx2[2];
+
+    vx3[0] = w2[1]*w3[2] - w2[2]*w3[1];
+    vx3[1] = w2[2]*w3[0] - w2[0]*w3[2];
+    vx3[2] = w2[0]*w3[1] - w2[1]*w3[0];
+    dd3 = vx3[0]*vx3[0] + vx3[1]*vx3[1] + vx3[2]*vx3[2];
+
+    /* find vector of max norm */
+    if ( dd1 > dd2 ) {
+      if ( dd1 > dd3 ) {
+        dd1 = 1.0 / sqrt(dd1);
+        v[2][0] = vx1[0] * dd1;
+        v[2][1] = vx1[1] * dd1;
+        v[2][2] = vx1[2] * dd1;
+      }
+      else {
+        dd3 = 1.0 / sqrt(dd3);
+        v[2][0] = vx3[0] * dd3;
+        v[2][1] = vx3[1] * dd3;
+        v[2][2] = vx3[2] * dd3;
+      }
+    }
+    else {
+      if ( dd2 > dd3 ) {
+        dd2 = 1.0 / sqrt(dd2);
+        v[2][0] = vx2[0] * dd2;
+        v[2][1] = vx2[1] * dd2;
+        v[2][2] = vx2[2] * dd2;
+      }
+      else {
+        dd3 = 1.0 / sqrt(dd3);
+        v[2][0] = vx3[0] * dd3;
+        v[2][1] = vx3[1] * dd3;
+        v[2][2] = vx3[2] * dd3;
+      }
+    }
+
+    /* compute v1 and v2 in Im(A-vp3*Id) */
+    dd1 = w1[0]*w1[0] + w1[1]*w1[1] + w1[2]*w1[2];
+    dd2 = w2[0]*w2[0] + w2[1]*w2[1] + w2[2]*w2[2];
+    if ( dd1 > dd2 ) {
+      dd1 = 1.0 / sqrt(dd1);
+      v[0][0] = w1[0]*dd1;
+      v[0][1] = w1[1]*dd1;
+      v[0][2] = w1[2]*dd1;
+    }
+    else {
+      dd2 = 1.0 / sqrt(dd2);
+      v[0][0] = w2[0]*dd2;
+      v[0][1] = w2[1]*dd2;
+      v[0][2] = w2[2]*dd2;
+    }
+
+    /* 3rd vector orthogonal */
+    v[1][0] = v[2][1]*v[0][2] - v[2][2]*v[0][1];
+    v[1][1] = v[2][2]*v[0][0] - v[2][0]*v[0][2];
+    v[1][2] = v[2][0]*v[0][1] - v[2][1]*v[0][0];
+    dd1 = v[1][0]*v[1][0] + v[1][1]*v[1][1] + v[1][2]*v[1][2];
+    dd1 = 1.0 / sqrt(dd1);
+    v[1][0] *= dd1;
+    v[1][1] *= dd1;
+    v[1][2] *= dd1;
+  }
+
+  lambda[0] *= maxm;
+  lambda[1] *= maxm;
+  lambda[2] *= maxm;
+
+  /* check accuracy */
+  /*-------------------------------------------------------------------
+  if ( ddebug && symmat ) {
+    double  err,tmpx,tmpy,tmpz;
+    float   m[6];
+    int     i,j;
+
+    k = 0;
+    for (i=0; i<3; i++)
+      for (j=i; j<3; j++)
+        m[k++] = lambda[0]*v[i][0]*v[j][0]
+               + lambda[1]*v[i][1]*v[j][1]
+               + lambda[2]*v[i][2]*v[j][2];
+    err = fabs(mat[0]-m[0]);
+    for (i=1; i<6; i++)
+      if ( fabs(m[i]-mat[i]) > err )  err = fabs(m[i]-mat[i]);
+
+    if ( err > 1.e03*maxm ) {
+      printf("\nProbleme eigenv3: err= %f\n",err*maxm);
+      printf("mat depart :\n");
+      printf("%13.6f  %13.6f  %13.6f\n",mat[0],mat[1],mat[2]);
+      printf("%13.6f  %13.6f  %13.6f\n",mat[1],mat[3],mat[4]);
+      printf("%13.6f  %13.6f  %13.6f\n",mat[2],mat[4],mat[5]);
+      printf("mat finale :\n");
+      printf("%13.6f  %13.6f  %13.6f\n",m[0],m[1],m[2]);
+      printf("%13.6f  %13.6f  %13.6f\n",m[1],m[3],m[4]);
+      printf("%13.6f  %13.6f  %13.6f\n",m[2],m[4],m[5]);
+      printf("lambda : %f %f %f\n",lambda[0],lambda[1],lambda[2]);
+      printf(" ordre %d\n",n);
+      printf("\nOrtho:\n");
+      printf("v1.v2 = %.14f\n",
+             v[0][0]*v[1][0]+v[0][1]*v[1][1]+ v[0][2]*v[1][2]);
+      printf("v1.v3 = %.14f\n",
+             v[0][0]*v[2][0]+v[0][1]*v[2][1]+ v[0][2]*v[2][2]);
+      printf("v2.v3 = %.14f\n",
+             v[1][0]*v[2][0]+v[1][1]*v[2][1]+ v[1][2]*v[2][2]);
+
+      printf("Consistency\n");
+      for (i=0; i<3; i++) {
+        tmpx = v[0][i]*m[0] + v[1][i]*m[1]
+             + v[2][i]*m[2] - lambda[i]*v[0][i];
+        tmpy = v[0][i]*m[1] + v[1][i]*m[3]
+             + v[2][i]*m[4] - lambda[i]*v[1][i];
+        tmpz = v[0][i]*m[2] + v[1][i]*m[4]
+             + v[2][i]*m[5] - lambda[i]*v[2][i];
+        printf(" Av %d - lambda %d *v %d = %f %f %f\n",
+        i,i,i,tmpx,tmpy,tmpz);
+
+        printf("w1 %f %f %f\n",w1[0],w1[1],w1[2]);
+        printf("w2 %f %f %f\n",w2[0],w2[1],w2[2]);
+        printf("w3 %f %f %f\n",w3[0],w3[1],w3[2]);
+      }
+      exit(1);
+    }
+  }
+  -------------------------------------------------------------------*/
+
+  return(n);
+}
+
+
+/* eigen value + vector extraction */
+int eigen2(double *mm,double *lambda,double vp[2][2]) {
+  double   m[3],dd,a1,xn,ddeltb,rr1,rr2,ux,uy;
+
+  /* init */
+  ux = 1.0;
+  uy = 0.0;
+
+  /* normalize */
+  memcpy(m,mm,3*sizeof(double));
+  xn = fabs(m[0]);
+  if ( fabs(m[1]) > xn )  xn = fabs(m[1]);
+  if ( fabs(m[2]) > xn )  xn = fabs(m[2]);
+  if ( xn < EPSD2 ) {
+    lambda[0] = lambda[1] = 0.0;
+    vp[0][0] = 1.0;
+    vp[0][1] = 0.0;
+    vp[1][0] = 0.0;
+    vp[1][1] = 1.0;
+    return(1);
+  }
+  xn = 1.0 / xn;
+  m[0] *= xn;
+  m[1] *= xn;
+  m[2] *= xn;
+
+  if ( egal(m[1],0.0) ) {
+    rr1 = m[0];
+    rr2 = m[2];
+    goto vect;
+  }
+
+  /* eigenvalues of jacobian */
+  a1	 = -(m[0] + m[2]);
+  ddeltb = a1*a1 - 4.0 * (m[0]*m[2] - m[1]*m[1]);
+
+  if ( ddeltb < 0.0 ) {
+    fprintf(stderr,"  Delta: %f\n",ddeltb);
+    ddeltb = 0.0;
+  }
+  ddeltb = sqrt(ddeltb);
+
+  if ( fabs(a1) < EPS ) {
+    rr1 = 0.5 * sqrt(ddeltb);
+    rr2 = -rr1;
+  }
+  else if ( a1 < 0.0 ) {
+    rr1 = 0.5 * (-a1 + ddeltb);
+    rr2 = (-m[1]*m[1] + m[0]*m[2]) / rr1;
+  }
+  else if ( a1 > 0.0 ) {
+    rr1 = 0.5 * (-a1 - ddeltb);
+    rr2 = (-m[1]*m[1] + m[0]*m[2]) / rr1;
+  }
+  else {
+    rr1 = 0.5 * ddeltb;
+    rr2 = -rr1;
+  }
+
+vect:
+  xn = 1.0 / xn;
+  lambda[0] = rr1 * xn;
+  lambda[1] = rr2 * xn;
+
+  /* eigenvectors */
+  a1 = m[0] - rr1;
+  if ( fabs(a1)+fabs(m[1]) < EPS ) {
+    if (fabs(lambda[1]) < fabs(lambda[0]) ) {
+      ux = 1.0;
+      uy = 0.0;
+    }
+    else {
+      ux = 0.0;
+      uy = 1.0;
+    }
+  }
+  else if ( fabs(a1) < fabs(m[1]) ) {
+    ux = 1.0;
+    uy = -a1 / m[1];
+  }
+  else if ( fabs(a1) > fabs(m[1]) ) {
+    ux = -m[1] / a1;
+    uy = 1.0;
+  }
+  else if ( fabs(lambda[1]) > fabs(lambda[0]) ) {
+    ux = 0.0;
+    uy = 1.0;
+  }
+  else {
+    ux = 1.0;
+    uy = 0.0;
+  }
+
+  dd = sqrt(ux*ux + uy*uy);
+  dd = 1.0 / dd;
+  if ( fabs(lambda[0]) > fabs(lambda[1]) ) {
+    vp[0][0] =  ux * dd;
+    vp[0][1] =  uy * dd;
+  }
+  else {
+    vp[0][0] =  uy * dd;
+    vp[0][1] = -ux * dd;
+  }
+
+  /* orthogonal vector */
+  vp[1][0] = -vp[0][1];
+  vp[1][1] =  vp[0][0];
+
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/eigenv.h b/contrib/mmg3d/build/sources/eigenv.h
new file mode 100644
index 0000000000000000000000000000000000000000..2f2d806a0d81bd2827dd6256b33b352ca59a9a1c
--- /dev/null
+++ b/contrib/mmg3d/build/sources/eigenv.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+int eigenv(int symmat,double *mat,double lambda[3],double v[3][3]);
+int eigen2(double *mm,double *lambda,double vp[2][2]);
diff --git a/contrib/mmg3d/build/sources/hash.c b/contrib/mmg3d/build/sources/hash.c
new file mode 100644
index 0000000000000000000000000000000000000000..1fa340f024378608ce37ea02053b374b10706f96
--- /dev/null
+++ b/contrib/mmg3d/build/sources/hash.c
@@ -0,0 +1,551 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define KA     31
+#define KB     57
+#define KC     79
+
+#define KTA     7
+#define KTB    11
+#define KTC    13
+
+
+
+/* local data structures */
+typedef struct {
+  int      min,max,sum,iel,nxt;
+} hface;
+
+typedef struct {
+  int      size,hnxt,nhmax;
+  hface   *item;
+} Hface;
+
+
+int MMG_hashTetra(pMesh mesh) {
+  pTetra    pt,pt1;
+  int       k,kk,pp,l,ll,mins,mins1,maxs,maxs1,sum,sum1,iadr;
+  int      *hcode,*link,hsize;  
+  long int  inival;
+  unsigned char  i,ii,i1,i2,i3;
+  unsigned int    key;
+
+  /* default */
+  if ( abs(mesh->info.imprim) > 5 ) {
+    fprintf(stdout,"  ** SETTING ADJACENCIES\n");
+    fflush(stdout);
+  }
+  /* memory alloc */
+  hcode = (int*)M_calloc(mesh->nemax+1,sizeof(int),"hash");
+  assert(hcode);
+  link  = mesh->adja;
+  hsize = mesh->ne;
+
+  /* init */
+  inival = 2147483647;
+  for (k=0; k<=mesh->ne; k++)
+    hcode[k] = -inival;
+  /* build hash table */
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    for (i=0; i<4; i++) {
+      i1 = MMG_idir[i][0];
+      i2 = MMG_idir[i][1];
+      i3 = MMG_idir[i][2];
+      mins = M_MIN(pt->v[i1],pt->v[i2]);
+      mins = M_MIN(mins,pt->v[i3]);
+      maxs = M_MAX(pt->v[i1],pt->v[i2]);
+      maxs = M_MAX(maxs,pt->v[i3]);
+
+      /* compute key */
+      sum = pt->v[i1] + pt->v[i2] + pt->v[i3];
+      key = KA*mins + KB*maxs + KC*sum;
+      key = key % hsize + 1;
+
+      /* insert */
+      iadr = 4*(k-1) + i+1;      
+      link[iadr] = hcode[key];
+      hcode[key] = -iadr;
+    }
+  }
+
+  /* set adjacency */
+  for (l=4*mesh->ne; l>0; l--) {  
+    if ( link[l] >= 0 )  continue;
+    k = ((l-1) >> 2) + 1;
+    i = (l-1) % 4;
+    i1 = MMG_idir[i][0];
+    i2 = MMG_idir[i][1];
+    i3 = MMG_idir[i][2];
+    pt = &mesh->tetra[k];
+
+    sum  = pt->v[i1] + pt->v[i2] + pt->v[i3];
+    mins = M_MIN(pt->v[i1],pt->v[i2]);
+    mins = M_MIN(mins,pt->v[i3]);
+    maxs = M_MAX(pt->v[i1],pt->v[i2]);
+    maxs = M_MAX(maxs,pt->v[i3]);
+
+    /* accross link */
+    ll = -link[l];
+    pp = 0;
+    link[l] = 0;
+    while ( ll != inival ) {
+      kk = ((ll-1) >> 2) + 1;
+      ii = (ll-1) % 4;
+      i1 = MMG_idir[ii][0];
+      i2 = MMG_idir[ii][1];
+      i3 = MMG_idir[ii][2];
+      pt1  = &mesh->tetra[kk];   
+      sum1 = pt1->v[i1] + pt1->v[i2] + pt1->v[i3];
+      if ( sum1 == sum ) {
+        mins1 = M_MIN(pt1->v[i1],pt1->v[i2]);
+        mins1 = M_MIN(mins1,pt1->v[i3]);
+        if ( mins1 == mins ) {
+          maxs1 = M_MAX(pt1->v[i1],pt1->v[i2]);
+          maxs1 = M_MAX(maxs1,pt1->v[i3]);
+          if ( maxs1 == maxs ) {
+            /* adjacent found */
+            if ( pp != 0 )  link[pp] = link[ll];
+            link[l] = 4*kk + ii;
+            link[ll]= 4*k + i;
+            break;
+          }
+        }
+      }
+      pp = ll;
+      ll = -link[ll];
+    }
+  }
+
+  M_free(hcode);
+  return(1);
+}
+
+
+
+/* hash mesh edge v[0],v[1] (face i of iel) */
+int MMG_hashEdge(pMesh mesh,pHedge hash,int iel,int i,int *v) {
+  int       *adja,iadr,jel,j,key,mins,maxs;
+  hedge     *ha;
+
+  /* compute key */
+  if ( v[0] < v[1] ) {
+    mins = v[0];
+    maxs = v[1];
+  }
+  else {
+    mins = v[1];
+    maxs = v[0];
+  }
+  key = KTA*mins + KTB*maxs;
+  key = key % hash->size;
+  ha  = &hash->item[key];
+
+  if ( ha->min ) {
+    /* identical face */
+    if ( ha->min == mins && ha->max == maxs ) {
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[i] = ha->iel;
+
+      jel  = ha->iel >> 2;
+      j    = ha->iel % 4;
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[j] = iel*4 + i;
+      return(1);
+    }
+    else
+      while ( ha->nxt && ha->nxt < hash->nhmax ) {
+        ha = &hash->item[ha->nxt];
+	    if ( ha->min == mins && ha->max == maxs ) {
+          iadr = (iel-1)*4 + 1;
+          adja = &mesh->adja[iadr];
+          adja[i] = ha->iel;
+
+          jel  = ha->iel >> 2;
+          j    = ha->iel % 4;
+          iadr = (jel-1)*4 + 1;
+          adja = &mesh->adja[iadr];
+          adja[j] = iel*4 + i;
+	      return(1);
+	    }
+      }
+    ha->nxt = hash->hnxt;
+    ha      = &hash->item[hash->hnxt];
+    ++hash->hnxt;
+    if ( hash->hnxt == hash->nhmax ) {
+      fprintf(stdout,"  ## Memory alloc problem (edge): %d\n",hash->nhmax);
+      return(0);
+    }
+  }
+
+  /* insert */
+  ha->min = mins;
+  ha->max = maxs;
+  ha->iel = iel*4 + i;
+  ha->nxt = 0;
+
+  return(1);
+}
+
+
+int MMG_inEdge(pHedge hash,int *v,int *iel,int *ia) {
+  int        key,mins,maxs;
+  hedge     *ha;
+
+  /* compute key */
+  if ( v[0] < v[1] ) {
+    mins = v[0];
+    maxs = v[1];
+  }
+  else {
+    mins = v[1];
+    maxs = v[0];
+  }
+  key = KA*mins + KB*maxs;
+  key = key % hash->size;
+
+  ha = &hash->item[key];
+
+  if ( !ha->min )  return(0);
+  else if ( ha->min == mins && ha->max == maxs ) {
+    *iel = ha->iel / 6;
+    *ia  = ha->iel % 6;
+    return(1);
+  }
+  else if ( ha->nxt ) {
+    do {
+      ha = &hash->item[ha->nxt];
+      if ( ha->min == mins && ha->max == maxs ) {
+        *iel = ha->iel / 6;
+        *ia  = ha->iel % 6;
+        return(1);
+      }
+    }
+    while ( ha->nxt && ha->nxt < hash->nhmax );
+  }
+
+  return(0);
+}
+
+
+/* hash triangles and return
+   iel: face stored, 0 problem */
+int MMG_hashFace(Hface *hash,int iel,int *v) {
+  int        key,mins,maxs,sum;
+  hface     *ht;
+
+  mins = M_MIN(v[0],v[1]);
+  mins = M_MIN(mins,v[2]);
+  maxs = M_MAX(v[0],v[1]);
+  maxs = M_MAX(maxs,v[2]);
+
+  /* compute key */
+  sum = v[0] + v[1] + v[2];
+  key = KTA*mins + KTB*maxs + KTC*sum;
+  key = key % hash->size;
+
+  ht = &hash->item[key];
+
+  if ( ht->min ) {
+    if ( ht->min == mins && ht->max == maxs && ht->sum == sum )
+      return(ht->iel);
+    else
+      while ( ht->nxt && ht->nxt < hash->nhmax ) {
+        ht = &hash->item[ht->nxt];
+	if ( ht->min == mins && ht->max == maxs && ht->sum == sum )
+	  return(ht->iel);
+      }
+    ht->nxt = hash->hnxt;
+    ht      = &hash->item[hash->hnxt];
+    ++hash->hnxt;
+    if ( hash->hnxt == hash->nhmax ) {
+      fprintf(stdout,"  ## memory alloc problem (hash)\n");
+      return(0);
+    }
+  }
+
+  ht->min = mins;
+  ht->max = maxs;
+  ht->sum = sum;
+  ht->iel = iel;
+  ht->nxt = 0;
+
+  return(iel);
+}
+
+
+/* hash triangles : put bdryref and assign a tet per triangle*/
+int MMG_seedTria(pMesh mesh) {
+  pTetra    pt,pt1;
+  pTria     ptt;
+  Hface     htri;
+  int      *adja,v[3],i,k,iel,adj,iadr;
+  int ncompt = 0;
+  
+  /* mem alloc */
+  htri.size  = mesh->nt;
+  htri.hnxt  = htri.size;
+  htri.nhmax = (int)(2*htri.size);
+  htri.item  = (hface*)M_calloc(htri.nhmax+1,sizeof(hface),"markTria"); 
+  assert(htri.item);
+
+  for (k=htri.size; k<htri.nhmax; k++)
+    htri.item[k].nxt = k+1;  
+
+  /* store triangles */
+  for (k=1; k<=mesh->nt; k++) {
+    ptt = &mesh->tria[k];
+    iel = MMG_hashFace(&htri,k,ptt->v);
+    if ( !iel )  return(0);
+  }
+
+  /* init seeds */
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    iadr = 4*(k-1) + 1;
+    adja = &mesh->adja[iadr];
+    for (i=0; i<4; i++) {
+      adj = adja[i] >> 2;
+      pt1 = &mesh->tetra[adj];
+      if ( !adj || ((pt->ref != pt1->ref) ) ) { /*&& (k < adj) IL FAUT TRAITER LES 2 TETS POUR LES SD*/ 
+        v[0] = pt->v[ MMG_idir[i][0] ];
+        v[1] = pt->v[ MMG_idir[i][1] ];
+        v[2] = pt->v[ MMG_idir[i][2] ];
+        iel  = MMG_hashFace(&htri,0,v);
+        if ( !iel )  { /*SD BDRY*/
+          if(mesh->info.imprim > 5) printf("on trouve un tr de SD %d : %d %d %d (between %d et %d)\n",++ncompt,v[0],v[1],v[2],
+                    k,adj);
+          pt->bdryref[i] = 10;
+          //return(0);
+        } else { 
+          /*ref bdry or sd bdry*/
+          ptt = &mesh->tria[iel];
+          pt->bdryref[i] = ptt->ref;
+          if ( !ptt->splx )  ptt->splx = k;
+        }
+      } /*else {
+        pt->bdryref[i] = pt->ref;
+      }*/
+    }
+  }
+  M_free(htri.item);
+  return(1);
+}
+
+
+/* mark boundary vertices */
+int MMG_markBdry(pMesh mesh) {
+  pTetra    pt,pt1;
+  pTria     ptt;
+  pPoint    ppt;
+  int      *adja,adj,iadr,k,i,ii,i1,nt;
+
+  //printf("on arrive avec %d\n",mesh->nt);
+  nt = 0;
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    iadr = 4*(k-1) + 1;
+    adja = &mesh->adja[iadr];
+    for (i=0; i<4; i++) {
+      adj = adja[i] >> 2;
+      pt1 = &mesh->tetra[adj];
+      if ( !adj || ((pt->ref != pt1->ref) && (k < adj)) ) { 
+        for (ii=0; ii<3; ii++) {
+          i1  = MMG_idir[i][ii];
+          ppt = &mesh->point[pt->v[i1]];
+          ppt->tag |= M_BDRY;
+        }
+        ++nt;
+        if ( !mesh->nt ) {
+          if ( (nt < mesh->ntmax-1) ) {  
+            ptt = &mesh->tria[nt];
+            ptt->v[0] = pt->v[ MMG_idir[i][0] ];
+            ptt->v[1] = pt->v[ MMG_idir[i][1] ];
+            ptt->v[2] = pt->v[ MMG_idir[i][2] ];   
+            if (pt->bdryref[i]!=-1) {  
+ 							//if(k==13) printf("pour %d (%d) on met %d : %d %d %d\n",k,i,pt->bdryref[i],pt->v[ MMG_idir[i][0] ]
+							//		,pt->v[ MMG_idir[i][1] ],pt->v[ MMG_idir[i][2] ]);
+             ptt->ref = pt->bdryref[i];
+            } else {
+	            if(mesh->info.imprim < -3 )    
+							printf("on a un tr qui n''a pas de ref : %d %d %d of %d %d \n",ptt->v[0],ptt->v[1],ptt->v[2],k,adj);
+							// if(k==10252) {
+							// 	printf("ses ref : %d %d %d %d\n",pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+							// 	printf("face %d : %d %d %d %d\n",i,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+							//   exit(0);
+							// }
+							ptt->ref  = (adj)?M_MIN(pt->ref,pt1->ref):10; 
+							pt->bdryref[i] = (adj)?M_MIN(pt->ref,pt1->ref):10;
+            }
+            if ( !ptt->splx )  ptt->splx = k; 
+          } 
+          else {
+            mesh->nt = nt;
+          }
+        }
+      } else if ( (pt->ref != pt1->ref) ) {  
+        if (pt->bdryref[i]==-1) pt->bdryref[i] = (adj)?M_MIN(pt->ref,pt1->ref):10; 
+			}
+    }
+  }
+
+  if ( !mesh->nt ) {
+    mesh->nt    = nt;
+    mesh->ntnil = mesh->nt + 1;
+    for (k=mesh->ntnil; k<mesh->ntmax-1; k++)
+      mesh->tria[k].v[2] = k+1;
+  }
+  else {
+    //printf("passe-t-on la ?\n");
+    if ( mesh->nt != nt )
+    fprintf(stdout,"  ** WARNING: %d NON-BOUNDARY TRIANGLES : SEVERAL SD CONSIDERED\n",
+            /*abs*/(mesh->nt-nt),nt,mesh->nt);
+
+    MMG_seedTria(mesh); 
+    /*erase triangles*/ 
+    for (k=1; k<=mesh->nt; k++)
+      mesh->tria[k].v[0] = 0;
+     
+  }
+/*printf("on teste\n");
+pTria ptria;  
+for (k=1; k<=mesh->nt; k++) {
+   ptria = &mesh->tria[k];
+if(!(mesh->point[ptria->v[0]].tag & M_BDRY)) printf("pbs0 (%d) %d\n",k,ptria->v[0]); 
+mesh->point[ptria->v[0]].tag |= M_BDRY; 
+mesh->point[ptria->v[1]].tag |= M_BDRY; 
+mesh->point[ptria->v[2]].tag |= M_BDRY; 
+if(!(mesh->point[ptria->v[1]].tag & M_BDRY)) printf("pbs1 (%d) %d\n",k,ptria->v[1]);
+if(!(mesh->point[ptria->v[2]].tag & M_BDRY)) printf("pbs2 (%d) %d\n",k,ptria->v[2]);
+}   */
+  return(1);
+}
+
+/* return 0: no point, np: point stored */ 
+/* edge a-b*/
+int MMG_edgePoint(pHedge hash,int a,int b) {
+  int        key,mins,maxs;
+  hedge     *ha;
+
+  /* compute key */
+  mins = a;
+  maxs = b;
+  if ( a > b ) {
+    mins = b;
+    maxs = a;
+  }
+  key = KA*mins + KB*maxs;
+  key = key % hash->size;
+  ha  = &hash->item[key];
+  //printf("cherche %d %d\n",mins,maxs);
+  if ( !ha->min )  return(0);
+  else if ( ha->min == mins && ha->max == maxs ) {
+    return(ha->iel);
+  }
+  else if ( ha->nxt ) {
+    do {
+      ha = &hash->item[ha->nxt];
+      if ( ha->min == mins && ha->max == maxs ) {
+        return(ha->iel);
+      }
+    }
+    while ( ha->nxt && ha->nxt < hash->nhmax );
+  }
+  return(0);
+}
+
+/*put np on edge a-b*/
+int MMG_edgePut(pHedge hash,int a,int b,int np) {
+  int        key,mins,maxs;
+  hedge     *ha;
+
+  mins = a;
+  maxs = b;
+  if ( a > b ) {
+    mins = b;
+    maxs = a;
+  }                    
+  key = KA*mins + KB*maxs;
+  key = key % hash->size;    
+  ha  = &hash->item[key];
+
+  if ( ha->min ) {
+    /* identical edge */
+    if ( ha->min == mins && ha->max == maxs ) {
+      return(ha->iel);
+    }
+    else {
+      while ( ha->nxt && ha->nxt < hash->nhmax ) {
+        ha = &hash->item[ha->nxt];
+	      if ( ha->min == mins && ha->max == maxs ) {
+	        return(ha->iel);
+	      }
+      }             
+    }
+    ha->nxt = hash->hnxt;
+    ha      = &hash->item[hash->hnxt];
+    ++hash->hnxt;
+    if ( hash->hnxt >= hash->nhmax ) {
+      fprintf(stdout,"  ## Memory alloc problem (edge): %d\n",hash->nhmax);
+      return(0);
+    }
+  }
+  //printf("insert %d %d\n",mins,maxs);
+  /* insert */
+  ha->min = mins;
+  ha->max = maxs;
+  ha->iel = np;
+  ha->nxt = 0;
+
+  return(1);
+}
+
diff --git a/contrib/mmg3d/build/sources/heap.c b/contrib/mmg3d/build/sources/heap.c
new file mode 100644
index 0000000000000000000000000000000000000000..9570ce35024c4dfbc04f7d681eceb33d5e77529f
--- /dev/null
+++ b/contrib/mmg3d/build/sources/heap.c
@@ -0,0 +1,227 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define QUAL      1
+#define VECT      2
+
+
+/* compare functions */
+static int MMG_compVector(pMesh mesh,pHeap heap,int i,int j) {
+  pTetra   pt,pt1;
+  pPoint   pp1,pp2;
+  pDispl   pd;
+  int      dmin1,dmin2;
+  short    k;
+
+  pd    = mesh->disp;
+  pt    = &mesh->tetra[i];
+  pt1   = &mesh->tetra[j];
+  dmin1 = INT_MAX;
+  dmin2 = INT_MAX;
+
+  for(k=0; k<4; k++) {
+    pp1 = &mesh->point[pt->v[k]];
+    if ( pp1->tag & M_MOVE && pd->alpha[pt->v[k]] < dmin1 )
+      dmin1 = pd->alpha[pt->v[k]];
+    pp2 = &mesh->point[pt1->v[k]];
+    if ( pp2->tag & M_MOVE && pd->alpha[pt1->v[k]] < dmin2 )
+      dmin2 = pd->alpha[pt1->v[k]];
+  }
+
+  return( dmin1 > dmin2 );
+  /*return( mesh->tetra[i].move < mesh->tetra[j].move );*/
+}
+
+static int MMG_compQuality(pMesh mesh,pHeap heap,int i,int j) {
+  return( (mesh->tetra[i].qual > mesh->tetra[j].qual) );
+}
+
+static int (*MMG_compare)(pMesh ,pHeap ,int ,int );
+
+
+/* heap management */
+static void MMG_hipup(pMesh mesh,pHeap heap,int k) {
+  int     i,j;
+
+  i = k / 2;
+  j = heap->cell[k];
+
+  while ( (i > 0) && MMG_compare(mesh,heap,j,heap->cell[i]) ) {
+    heap->cell[k] = heap->cell[i];
+    heap->link[heap->cell[i]] = k;
+    k  = i;
+    i /= 2;
+  }
+  heap->cell[k] = j;
+  heap->link[j] = k;
+}
+
+
+static void MMG_hipdown(pMesh mesh,pHeap heap,int k) {
+  int      i,j,n;
+
+  i = heap->cell[k];
+  n = heap->curc / 2;
+
+  while ( k <= n ) {
+    j   = k+k;
+    if ( j < heap->curc ) {
+      if ( MMG_compare(mesh,heap,heap->cell[j+1],heap->cell[j]) )
+        j = j+1;
+    }
+    if ( MMG_compare(mesh,heap,i,heap->cell[j]) )
+      break;
+    heap->cell[k] = heap->cell[j];
+    heap->link[heap->cell[j]] = k;
+    k = j;
+  }
+
+  heap->cell[k] = i;
+  heap->link[i] = k;
+}
+
+
+int MMG_hippop(pMesh mesh,pHeap heap) {
+  int    j;
+
+  if ( heap->curc < 1 )  return(0);
+  j = heap->cell[1];
+  if ( heap->curc > 1 ) {
+    heap->cell[1] = heap->cell[heap->curc];
+    heap->link[ heap->cell[heap->curc--] ] = 1;
+    MMG_hipdown(mesh,heap,1);
+  }
+  else
+    heap->curc--;
+
+  return(j);
+}
+
+
+int MMG_hipput(pMesh mesh,pHeap heap,int k) {
+  if ( heap->curc >= heap->size )  return(0);
+
+  ++heap->curc;
+  heap->cell[heap->curc] = k;
+  heap->link[k]          = heap->curc;
+  MMG_hipup(mesh,heap,heap->curc);
+
+  return(1);
+}
+
+
+void MMG_hiprep(pMesh mesh,pHeap heap,int k) {
+  MMG_hipdown(mesh,heap,heap->link[k]);
+  MMG_hipup(mesh,heap,heap->link[k]);
+}
+
+
+void MMG_hipdel(pMesh mesh,pHeap heap,int k) {
+  if ( heap->link[k] )
+    MMG_hipdown(mesh,heap,heap->link[k]);
+}
+
+
+Heap *MMG_hipini(pMesh mesh,int nemax,short cmpfunc,double declic,int base) {
+  pTetra   pt;
+  pPoint   ppt;
+  pDispl   pd;
+  pHeap    heap;
+  int      i,k,nm,dmin;
+
+  /* mem alloc */
+  heap = (Heap*)M_malloc(sizeof(Heap),"hipini");
+  assert(heap);
+  heap->size = nemax+1;
+  heap->cell = (int*)M_calloc(heap->size,sizeof(int),"hipini");
+  assert(heap->cell);
+  heap->link = (int*)M_calloc(heap->size,sizeof(int),"hipini");
+  assert(heap->link);
+  heap->curc = 0;
+
+  pd  = mesh->disp;
+
+  /* assign function */
+  if ( cmpfunc == QUAL ) {
+    MMG_compare = MMG_compQuality;
+    for (k=1; k<=mesh->ne; k++) {
+      pt = &mesh->tetra[k];
+      if ( !pt->v[0] || pt->qual < declic )    continue;
+      else if ( base > 0 && pt->flag < base )  continue;
+      else if ( !MMG_hipput(mesh,heap,k) )         return(0);
+    }
+  }
+  else {
+    MMG_compare = MMG_compVector;
+    for (k=1; k<=mesh->ne; k++) {
+      pt = &mesh->tetra[k];
+      if ( !pt->v[0] )  continue;
+      dmin = INT_MAX;
+      nm   = 0;
+      for (i=0; i<4; i++) {
+        ppt = &mesh->point[ pt->v[i] ];
+        if ( ppt->tag & M_MOVE ) {
+          if ( pd->alpha[ 3*(pt->v[i]-1) + 1 ] < dmin )  dmin = pd->alpha[ 3*(pt->v[i]-1) + 1 ];
+          nm++;
+        }
+      }
+      if ( nm ) {
+        if ( !MMG_hipput(mesh,heap,k) )  return(0);
+      }
+    }
+  }
+
+  return(heap);
+}
+
+
+void MMG_hipfree(pHeap heap) {
+  M_free(heap->cell);
+  M_free(heap->link);
+  M_free(heap);
+  heap = 0;
+}
diff --git a/contrib/mmg3d/build/sources/inout.c b/contrib/mmg3d/build/sources/inout.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f73da0c925f5bdbc3d9d7680a84608455747063
--- /dev/null
+++ b/contrib/mmg3d/build/sources/inout.c
@@ -0,0 +1,1416 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile,
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite,
+y compris les garanties de commercialisation ou
+d’adaptation dans un but spécifique.
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU
+en même temps que ce document.
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+spread under the terms and conditions of the license GNU General Public License
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.
+****************************************************************************/
+#include "mesh.h"
+
+
+extern short	       MMG_imprim;
+#define sw 4
+#define sd 8
+
+int MMG_swapbin(int sbin)
+{
+	int inv;
+  char *p_in = (char *) &sbin;
+  char *p = (char *)&inv;
+
+
+  p[0] = p_in[3];
+  p[1] = p_in[2];
+  p[2] = p_in[1];
+  p[3] = p_in[0];
+
+  return(inv);
+  /*unsigned char c1, c2, c3, c4;
+
+  c1 = sbin & 255;
+  c2 = (sbin >> 8) & 255;
+  c3 = (sbin >> 16) & 255;
+  c4 = (sbin >> 24) & 255;
+
+  return ((int)c1 << 24) + ((int)c2 << 16) + ((int)c3 << 8) + c4;   */
+
+}
+float MMG_swapf(float sbin)
+{
+  float out;
+  char *p_in = (char *) &sbin;
+  char *p_out = (char *) &out;
+  p_out[0] = p_in[3];
+  p_out[1] = p_in[2];
+  p_out[2] = p_in[1];
+  p_out[3] = p_in[0];
+
+  return(out);
+}
+double MMG_swapd(double sbin)
+{
+  float out;
+  char *p_in = (char *) &sbin;
+  char *p_out = (char *) &out;
+  int i;
+
+  for(i=0;i<8;i++)
+  {
+    p_out[i] = p_in[7-i];
+  }
+  //printf("CONVERTION DOUBLE\n");
+  return(out);
+}
+
+/* read mesh data */
+int MMG_loadMesh(pMesh mesh,char *filename) {
+  FILE*            inm;
+  Hedge            hed,hed2;
+  pPoint       	   ppt;
+  pTetra           pt;
+  pTria            pt1;
+  int              k,dim,ref,bin,bpos,i,tmp;
+  long             posnp,posnt,posne,posnhex,posnpris,posncor,posned,posnq;
+  char            *ptr,data[128],chaine[128];
+  int              nhex,npris,netmp,ncor,ned,nq;
+  int              p0,p1,p2,p3,p4,p5,p6,p7;
+  int              binch,bdim,iswp,nu1,nu2,nimp,ne;
+  float            fc;
+
+
+  posnp = posnt = posne = posnhex = posnpris = 0;
+  netmp = ncor = ned = 0;
+  bin = 0;
+  iswp = 0;
+  mesh->np = mesh->nt = mesh->ne = mesh->ncor = 0;
+  npris = nhex = nq = 0;
+
+  strcpy(data,filename);
+  ptr = strstr(data,".mesh");
+  if ( !ptr ) {
+    strcat(data,".meshb");
+    if( !(inm = fopen(data,"rb")) ) {
+      ptr = strstr(data,".mesh");
+      *ptr = '\0';
+      strcat(data,".mesh");
+      if( !(inm = fopen(data,"r")) ) {
+        fprintf(stderr,"  ** %s  NOT FOUND.\n",data);
+        return(0);
+      }
+    } else {
+      bin = 1;
+    }
+  }
+  else {
+    ptr = strstr(data,".meshb");
+    if( !ptr ) {
+      if( !(inm = fopen(data,"r")) ) {
+        fprintf(stderr,"  ** %s  NOT FOUND.\n",data);
+        return(0);
+      }
+    } else {
+      bin = 1;
+      if( !(inm = fopen(data,"rb")) ) {
+        fprintf(stderr,"  ** %s  NOT FOUND.\n",data);
+        return(0);
+      }
+
+    }
+  }
+
+  fprintf(stdout,"  %%%% %s OPENED\n",data);
+  if (!bin) {
+    strcpy(chaine,"D");
+    while(fscanf(inm,"%s",&chaine[0])!=EOF && strncmp(chaine,"End",strlen("End")) ) {
+      if(!strncmp(chaine,"MeshVersionFormatted",strlen("MeshVersionFormatted"))) {
+        if(fscanf(inm,"%d",&mesh->ver)!=1){ fclose(inm); return(0); }
+        continue;
+      } else if(!strncmp(chaine,"Dimension",strlen("Dimension"))) {
+        if(fscanf(inm,"%d",&dim)!=1){ fclose(inm); return(0); }
+        if(dim!=3) {
+          fprintf(stdout,"BAD DIMENSION : %d\n",dim);
+          return(0);
+        }
+        continue;
+      } else if(!strncmp(chaine,"Vertices",strlen("Vertices"))) {
+        if(fscanf(inm,"%d",&mesh->np)!=1){ fclose(inm); return(0); }
+        posnp = ftell(inm);
+        continue;
+      } else if(!strncmp(chaine,"Triangles",strlen("Triangles"))) {
+        if(fscanf(inm,"%d",&mesh->nt)!=1){ fclose(inm); return(0); }
+        posnt = ftell(inm);
+        continue;
+      } else if(!strncmp(chaine,"Tetrahedra",strlen("Tetrahedra"))) {
+        if(fscanf(inm,"%d",&mesh->ne)!=1){ fclose(inm); return(0); }
+        netmp = mesh->ne;
+        posne = ftell(inm);
+        continue;
+      } else if(!strncmp(chaine,"Hexahedra",strlen("Hexahedra"))) {
+        assert(abs(mesh->info.option)==10);
+        if(fscanf(inm,"%d",&nhex)!=1){ fclose(inm); return(0); }
+				//nhex=0;
+        posnhex = ftell(inm);
+        continue;
+      } else if(!strncmp(chaine,"Pentahedra",strlen("Pentahedra"))) {
+        assert(abs(mesh->info.option)==10);
+        if(fscanf(inm,"%d",&npris)!=1){ fclose(inm); return(0); }
+				//npris=0;
+        posnpris = ftell(inm);
+        continue;
+      } else if(!strncmp(chaine,"Corners",strlen("Corners"))) {
+        if(fscanf(inm,"%d",&ncor)!=1){ fclose(inm); return(0); }
+        posncor = ftell(inm);
+        continue;
+      } else if(!strncmp(chaine,"Edges",strlen("Edges"))) {
+        if(fscanf(inm,"%d",&ned)!=1){ fclose(inm); return(0); }
+        posned = ftell(inm);
+        continue;
+      } else if(abs(mesh->info.option)==10 && !strncmp(chaine,"Quadrilaterals",strlen("Quadrilaterals"))) {
+        if(fscanf(inm,"%d",&nq)!=1){ fclose(inm); return(0); }
+        posnq = ftell(inm);
+        continue;
+      }
+    }
+  } else {
+    bdim = 0;
+    if(fread(&mesh->ver,sw,1,inm)!=1){ fclose(inm); return(0); }
+    iswp=0;
+    if(mesh->ver==16777216)
+      iswp=1;
+    else if(mesh->ver!=1) {
+      fprintf(stdout,"BAD FILE ENCODING\n");
+    }
+    if(fread(&mesh->ver,sw,1,inm)!=1){ fclose(inm); return(0); }
+    if(iswp) mesh->ver = MMG_swapbin(mesh->ver);
+    while(fread(&binch,sw,1,inm)!=0 && binch!=54 ) {
+      if(iswp) binch=MMG_swapbin(binch);
+      if(binch==54) break;
+      if(!bdim && binch==3) {  //Dimension
+        if(fread(&bdim,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos=>20
+        if(iswp) bdim=MMG_swapbin(bdim);
+        if(fread(&bdim,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) bdim=MMG_swapbin(bdim);
+        if(bdim!=3) {
+          fprintf(stdout,"BAD SOL DIMENSION : %d\n",dim);
+          exit(0);
+          return(1);
+        }
+        continue;
+      } else if(!mesh->np && binch==4) {  //Vertices
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        if(fread(&mesh->np,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) mesh->np=MMG_swapbin(mesh->np);
+        posnp = ftell(inm);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+        continue;
+      }  else if(!mesh->nt && binch==6) {//Triangles
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        if(fread(&mesh->nt,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) mesh->nt=MMG_swapbin(mesh->nt);
+        posnt = ftell(inm);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+        continue;
+      } else if(!mesh->ne && binch==8) {
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        if(fread(&mesh->ne,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) mesh->ne=MMG_swapbin(mesh->ne);
+        netmp = mesh->ne;
+        posne = ftell(inm);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+        continue;
+      } else if(!nhex && binch==10) {
+        assert(abs(mesh->info.option)==10);
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        if(fread(&nhex,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) nhex=MMG_swapbin(nhex);
+        posnhex = ftell(inm);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+        continue;
+      } else if(!npris && binch==9) {
+        assert(abs(mesh->info.option)==10);
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        if(fread(&npris,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) npris=MMG_swapbin(npris);
+        posnpris = ftell(inm);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+        continue;
+      } else if(!ncor && binch==13) {
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        if(fread(&ncor,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) ncor=MMG_swapbin(ncor);
+        posncor = ftell(inm);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+        continue;
+      } else if(!ned && binch==5) { //Edges
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        if(fread(&ned,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) ned=MMG_swapbin(ned);
+        posned = ftell(inm);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+        continue;
+      } else {
+        //printf("on traite ? %d\n",binch);
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        //printf("on avance... Nulpos %d\n",bpos);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+      }
+    }
+
+  }
+
+  if ( abs(mesh->info.option)==10 ) {
+    fprintf(stdout,"  -- READING %8d HEXA %8d PRISMS\n",nhex,npris);
+    if(!mesh->ne) netmp = 0;
+    mesh->ne += 6*nhex + 3*npris;
+  }
+
+  if ( abs(mesh->info.imprim) > 5 )
+    fprintf(stdout,"  -- READING DATA FILE %s\n",data);
+
+  if ( !mesh->np || !mesh->ne ) {
+    fprintf(stdout,"  ** MISSING DATA\n");
+    return(0);
+  }
+  if ( !MMG_zaldy(mesh) )  return(0);
+
+  /* read mesh vertices */
+  mesh->npfixe = mesh->np;
+  rewind(inm);
+  fseek(inm,posnp,SEEK_SET);
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if (mesh->ver < 2) { /*float*/
+      if (!bin) {
+        for (i=0 ; i<3 ; i++) {
+          if(fscanf(inm,"%f",&fc)!=1){ fclose(inm); return(0); }
+          ppt->c[i] = (double) fc;
+        }
+        if(fscanf(inm,"%d",&ppt->ref)!=1){ fclose(inm); return(0); }
+      } else {
+        for (i=0 ; i<3 ; i++) {
+          if(fread(&fc,sw,1,inm)!=1){ fclose(inm); return(0); }
+          if(iswp) fc=MMG_swapf(fc);
+          ppt->c[i] = (double) fc;
+        }
+        if(fread(&ppt->ref,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) ppt->ref=MMG_swapbin(ppt->ref);
+      }
+    } else {
+      if (!bin)
+        if(fscanf(inm,"%lf %lf %lf %d",&ppt->c[0],&ppt->c[1],&ppt->c[2],&ppt->ref)!=4){ fclose(inm); return(0); }
+      else {
+        for (i=0 ; i<3 ; i++) {
+          if(fread(&ppt->c[i],sd,1,inm)!=1){ fclose(inm); return(0); }
+          if(iswp) ppt->c[i]=MMG_swapd(ppt->c[i]);
+        }
+        if(fread(&ppt->ref,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) ppt->ref=MMG_swapbin(ppt->ref);
+      }
+    }
+    ppt->tag  = M_UNUSED;
+  }
+
+  /* read mesh triangles */
+  mesh->ntfixe = mesh->nt;
+  rewind(inm);
+  fseek(inm,posnt,SEEK_SET);
+  for (k=1; k<=mesh->nt; k++) {
+    pt1 = &mesh->tria[k];
+    if (!bin)
+      if(fscanf(inm,"%d %d %d %d",&pt1->v[0],&pt1->v[1],&pt1->v[2],&pt1->ref)!=4){ fclose(inm); return(0); }
+    else {
+      for (i=0 ; i<3 ; i++) {
+        if(fread(&pt1->v[i],sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) pt1->v[i]=MMG_swapbin(pt1->v[i]);
+      }
+      if(fread(&pt1->ref,sw,1,inm)!=1){ fclose(inm); return(0); }
+      if(iswp) pt1->ref=MMG_swapbin(pt1->ref);
+    }
+  }
+  /* read mesh quads (option 10)*/
+	if(abs(mesh->info.option)==10) {
+		fprintf(stdout,"     QUADS READING %d\n",nq);
+    mesh->ntfixe += 4*nq;
+    rewind(inm);
+    fseek(inm,posnq,SEEK_SET);
+    for (k=1; k<=nq; k++) {
+      if (!bin)
+        if(fscanf(inm,"%d %d %d %d %d",&p0,&p1,&p2,&p3,&ref)!=5){ fclose(inm); return(0); }
+      else {
+        if(fread(&p0,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) p0=MMG_swapbin(p0);
+        if(fread(&p1,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) p1=MMG_swapbin(p1);
+        if(fread(&p2,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) p2=MMG_swapbin(p2);
+        if(fread(&p3,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) p3=MMG_swapbin(p3);
+        if(fread(&pt1->ref,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) ref=MMG_swapbin(ref);
+      }
+      pt1 = &mesh->tria[++mesh->nt];
+			pt1->v[0] = p0;
+			pt1->v[1] = p1;
+			pt1->v[2] = p2;
+			pt1->ref  = ref;
+      pt1 = &mesh->tria[++mesh->nt];
+			pt1->v[0] = p0;
+			pt1->v[1] = p2;
+			pt1->v[2] = p3;
+			pt1->ref  = ref;
+      pt1 = &mesh->tria[++mesh->nt];
+			pt1->v[0] = p0;
+			pt1->v[1] = p1;
+			pt1->v[2] = p3;
+			pt1->ref  = ref;
+      pt1 = &mesh->tria[++mesh->nt];
+			pt1->v[0] = p1;
+			pt1->v[1] = p2;
+			pt1->v[2] = p3;
+			pt1->ref  = ref;
+
+    }
+  }
+
+	/*read and store edges*/
+  if (ned) {
+	  if ( !MMG_zaldy4(&hed,ned) ) {
+      if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES IGNORED\n");
+			ned = 0;
+    }
+		mesh->ned = ned;
+    rewind(inm);
+    fseek(inm,posned,SEEK_SET);
+    for (k=1; k<=ned; k++) {
+      if (!bin)
+        if(fscanf(inm,"%d %d %d",&nu1,&nu2,&ref)!=3){ fclose(inm); return(0); }
+      else {
+        if(fread(&nu1,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) nu1=MMG_swapbin(nu1);
+        if(fread(&nu2,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) nu2=MMG_swapbin(nu2);
+        if(fread(&ref,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) ref=MMG_swapbin(ref);
+      }
+			if(MMG_edgePut(&hed,nu1,nu2,2)>1) {
+				fprintf(stdout,"  ## WARNING DOUBLE EDGE : %d %d\n",nu1,nu2);
+			}
+    }
+  }
+
+  /* read mesh tetrahedra */
+  mesh->nefixe = mesh->ne;
+  rewind(inm);
+  fseek(inm,posne,SEEK_SET);
+  for (k=1; k<=netmp; k++) {
+    pt = &mesh->tetra[k];
+    if (!bin)
+      if(fscanf(inm,"%d %d %d %d %d",&pt->v[0],&pt->v[1],&pt->v[2],&pt->v[3],&ref)!=5){ fclose(inm); return(0); }
+    else {
+
+      for (i=0 ; i<4 ; i++) {
+        if(fread(&pt->v[i],sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) pt->v[i]=MMG_swapbin(pt->v[i]);
+      }
+      if(fread(&ref,sw,1,inm)!=1){ fclose(inm); return(0); }
+      if(iswp) ref=MMG_swapbin(ref);
+    }
+    pt->ref  = ref;//0;//ref ;
+    for(i=0 ; i<4 ; i++)
+      pt->bdryref[i] = -1;
+
+		if (ned) {
+	    for(i=0 ; i<6 ; i++) {
+				nu1 = pt->v[MMG_iare[i][0]];
+				nu2 = pt->v[MMG_iare[i][1]];
+	      pt->bdryinfo[i] = MMG_edgePoint(&hed,nu1,nu2);
+			}
+
+		} else {
+	    for(i=0 ; i<6 ; i++)
+	      pt->bdryinfo[i] = 0;
+		}
+  }
+  if (ned) M_free(hed.item);
+
+  /*read corners*/
+  if (ncor) {
+    rewind(inm);
+    fseek(inm,posncor,SEEK_SET);
+    mesh->ncor = ncor;
+    for (k=1; k<=ncor; k++) {
+      if (!bin)
+        if(fscanf(inm,"%d",&ref)!=1){ fclose(inm); return(0); }
+      else {
+        if(fread(&ref,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) ref=MMG_swapbin(ref);
+      }
+      ppt = &mesh->point[ref];
+      ppt->geom = M_CORNER;
+    }
+  }
+
+
+  if ( abs(mesh->info.option)==10 ) {
+    if(bin) {
+      printf("NOT SUPPORTED\n");
+      exit(0);
+    }
+    if ( !MMG_zaldy4(&hed2,3*npris+6*nhex) ) {
+      if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : PRISM IGNORED\n");
+      npris = 0;
+      nhex  = 0;
+    }
+
+    /*read hexa and transform to tetra*/
+    rewind(inm);
+    fseek(inm,posnhex,SEEK_SET);
+    for (k=1; k<=nhex; k++) {
+      if(fscanf(inm,"%d %d %d %d %d %d %d %d %d",&p0,&p1,&p2,&p3,&p4,&p5,&p6,&p7,&ref)!=9){ fclose(inm); return(0); }
+      //if(fscanf(inm,"%d %d %d %d %d %d %d %d %d",&p0,&p4,&p2,&p1,&p3,&p5,&p6,&p7,&ref)!=9){ fclose(inm); return(0); }
+      //printf("hex %d : %d %d %d %d %d %d %d %d\n",k,p0,p1,p2,p3,p4,p5,p6,p7);
+      MMG_cuthex(mesh,&hed2,netmp+(k-1)*6,p0,p1,p2,p3,p4,p5,p6,p7,ref);
+    }
+
+    /*read prism and transform to tetra
+		---> compatibility pbs ==> hash edge and switch case*/
+    rewind(inm);
+    fseek(inm,posnpris,SEEK_SET);
+    nimp = 0;
+    ne = netmp+6*nhex;
+    for (k=1; k<=npris; k++) {
+      if(fscanf(inm,"%d %d %d %d %d %d %d",&p0,&p1,&p2,&p3,&p4,&p5,&ref)!=7){ fclose(inm); return(0); }
+      if(!MMG_cutprism(mesh,&hed2,ne,p0,p1,p2,p3,p4,p5,ref)) {
+        if(mesh->info.imprim < 0 ) fprintf(stdout,"DECOMPOSITION PRISM INVALID \n\n");
+        mesh->ne += 5;
+        ne += 8;
+        nimp++;
+        continue;
+      }
+      ne += 3;
+    }
+    if(abs(mesh->info.imprim) > 3 )fprintf(stdout,"     %d INVALID DECOMPOSITION\n\n",nimp);
+  }
+
+  if ( abs(mesh->info.imprim) > 3 ) {
+    fprintf(stdout,"     NUMBER OF GIVEN VERTICES   %8d\n",mesh->npfixe);
+    if ( mesh->ntfixe )
+      fprintf(stdout,"     NUMBER OF GIVEN TRIANGLES  %8d\n",mesh->ntfixe);
+    fprintf(stdout,"     NUMBER OF GIVEN TETRAHEDRA %8d\n",mesh->nefixe);
+    if ( ncor )
+      fprintf(stdout,"     NUMBER OF GIVEN CORNERS    %8d\n",ncor);
+    if ( ned )
+      fprintf(stdout,"     NUMBER OF GIVEN EDGES      %8d\n",ned);
+  }
+ fclose(inm);
+ return(1);
+}
+
+
+/* load solution (metric) */
+int MMG_loadSol(pSol sol,char *filename,int npmax) {
+  FILE       *inm;
+  float       fsol;
+  double      tmp;
+  int         binch,bdim,iswp;
+  int         k,i,isol,type,bin,dim,btyp,bpos;
+  long        posnp;
+  char        *ptr,data[128],chaine[128];
+
+  posnp = 0;
+  bin   = 0;
+  iswp  = 0;
+
+  strcpy(data,filename);
+  ptr = strstr(data,".mesh");
+  if ( ptr )  *ptr = '\0';
+  strcat(data,".solb");
+  if( !(inm = fopen(data,"rb")) ) {
+    ptr  = strstr(data,".solb");
+    *ptr = '\0';
+    strcat(data,".sol");
+    if( !(inm = fopen(data,"r")) ) {
+      fprintf(stderr,"  ** %s  NOT FOUND.\n",data);
+      return(1);
+    }
+  } else {
+    bin = 1;
+  }
+  fprintf(stdout,"  %%%% %s OPENED\n",data);
+
+
+  if(!bin) {
+    strcpy(chaine,"DDD");
+    while(fscanf(inm,"%s",&chaine[0])!=EOF && strncmp(chaine,"End",strlen("End")) ) {
+      if(!strncmp(chaine,"Dimension",strlen("Dimension"))) {
+          if(fscanf(inm,"%d",&dim)!=1){ fclose(inm); return(0); }
+          if(dim!=3) {
+            fprintf(stdout,"BAD SOL DIMENSION : %d\n",dim);
+            return(1);
+          }
+          continue;
+      } else if(!strncmp(chaine,"SolAtVertices",strlen("SolAtVertices"))) {
+        if(fscanf(inm,"%d",&sol->np)!=1){ fclose(inm); return(0); }
+        if(fscanf(inm,"%d",&type)!=1){ fclose(inm); return(0); }
+        if(type!=1) {
+          fprintf(stdout,"SEVERAL SOLUTION => IGNORED : %d\n",type);
+          return(1);
+        }
+        if(fscanf(inm,"%d",&btyp)!=1){ fclose(inm); return(0); }
+        posnp = ftell(inm);
+        break;
+      }
+    }
+  } else {
+    if(fread(&binch,sw,1,inm)!=1){ fclose(inm); return(0); }
+    iswp=0;
+    if(binch==16777216) iswp=1;
+    else if(binch!=1) {
+      fprintf(stdout,"BAD FILE ENCODING\n");
+    }
+    if(fread(&sol->ver,sw,1,inm)!=1){ fclose(inm); return(0); }
+    if(iswp) sol->ver = MMG_swapbin(sol->ver);
+    while(fread(&binch,sw,1,inm)!=EOF && binch!=54 ) {
+      if(iswp) binch=MMG_swapbin(binch);
+      if(binch==54) break;
+      if(binch==3) {  //Dimension
+        if(fread(&bdim,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos=>20
+        if(iswp) bdim=MMG_swapbin(bdim);
+        if(fread(&bdim,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) bdim=MMG_swapbin(bdim);
+        if(bdim!=3) {
+          fprintf(stdout,"BAD SOL DIMENSION : %d\n",dim);
+          exit(0);
+          return(1);
+        }
+        continue;
+      } else if(binch==62) {  //SolAtVertices
+        if(fread(&binch,sw,1,inm)!=1){ fclose(inm); return(0); } //NulPos
+        if(iswp) binch=MMG_swapbin(binch);
+        if(fread(&sol->np,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) sol->np=MMG_swapbin(sol->np);
+        if(fread(&binch,sw,1,inm)!=1){ fclose(inm); return(0); } //nb sol
+        if(iswp) binch=MMG_swapbin(binch);
+        if(binch!=1) {
+          fprintf(stdout,"SEVERAL SOLUTION => IGNORED : %d\n",type);
+          return(1);
+        }
+        if(fread(&btyp,sw,1,inm)!=1){ fclose(inm); return(0); } //typsol
+        if(iswp) btyp=MMG_swapbin(btyp);
+        posnp = ftell(inm);
+        break;
+      } else {
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //Pos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+      }
+    }
+
+  }
+  if ( !sol->np ) {
+    fprintf(stdout,"  ** MISSING DATA\n");
+    return(1);
+  }
+
+  if ( btyp!= 1 && btyp!=3 ) {
+    fprintf(stdout,"  ** DATA IGNORED\n");
+    sol->np = 0;
+    return(1);
+  }
+
+  sol->offset = (btyp==1) ? 1 : 6;
+
+  if ( abs(MMG_imprim) > 5 )
+    fprintf(stdout,"  -- READING DATA FILE %s\n",data);
+
+  if ( !sol->np ) {
+    fprintf(stdout,"  ** MISSING DATA\n");
+    return(0);
+  }
+  sol->npfixe = sol->np;
+  sol->npmax  = npmax;
+  if ( !MMG_zaldy3(sol) )  return(0);
+
+  /* read mesh solutions */
+  sol->npfixe = sol->np;
+  rewind(inm);
+  fseek(inm,posnp,SEEK_SET);
+  for (k=1; k<=sol->np; k++) {
+    isol = (k-1) * sol->offset + 1;
+    if (sol->ver == 1) {
+      for (i=0; i<sol->offset; i++) {
+        if(!bin){
+          if(fscanf(inm,"%f",&fsol)!=1){ fclose(inm); return(0); }
+          sol->met[isol + i] = (double) fsol;
+        } else {
+          if(fread(&fsol,sw,1,inm)!=1){ fclose(inm); return(0); }
+          if(iswp) fsol=MMG_swapf(fsol);
+          sol->met[isol + i] = (double) fsol;
+        }
+      }
+    } else {
+      for (i=0; i<sol->offset; i++) {
+        if(!bin){
+          if(fscanf(inm,"%lf",&sol->met[isol + i])!=1){ fclose(inm); return(0); }
+
+        } else {
+          if(fread(&sol->met[isol + i],sd,1,inm)!=1){ fclose(inm); return(0); }
+          if(iswp) sol->met[isol + i]=MMG_swapd(sol->met[isol + i]);
+        }
+      }
+    }
+    /* MMG_swap data */
+    if ( sol->offset == 6 ) {
+      tmp                = sol->met[isol + 2];
+      sol->met[isol + 2] = sol->met[isol + 3];
+      sol->met[isol + 3] = tmp;
+    }
+  }
+
+  if ( abs(MMG_imprim) > 3 )
+    fprintf(stdout,"     NUMBER OF GIVEN DATA       %8d\n",sol->npfixe);
+
+  fclose(inm);
+  return(1);
+}
+
+
+int MMG_loadVect(pMesh mesh,char *filename,int npmax) {
+  FILE       *inm;
+  pDispl       pd;
+  float       fsol;
+  int         binch,bdim,iswp;
+  int         k,i,type,bin,dim,btyp,bpos,iadr;
+  long        posnp;
+  char        *ptr,data[128],chaine[128];
+
+  pd = mesh->disp;
+
+  posnp = 0;
+  bin   = 0;
+  iswp  = 0;
+
+  strcpy(data,filename);
+  ptr = strstr(data,".mesh");
+  if ( ptr )  *ptr = '\0';
+  strcat(data,".solb");
+  if( !(inm = fopen(data,"rb")) ) {
+    ptr  = strstr(data,".solb");
+    *ptr = '\0';
+    strcat(data,".sol");
+    if( !(inm = fopen(data,"r")) ) {
+      fprintf(stderr,"  ** %s  NOT FOUND.\n",data);
+      return(1);
+    }
+  } else {
+    bin = 1;
+  }
+  fprintf(stdout,"  %%%% %s OPENED\n",data);
+
+
+  if(!bin) {
+    strcpy(chaine,"DDD");
+    while(fscanf(inm,"%s",&chaine[0])!=EOF && strncmp(chaine,"End",strlen("End")) ) {
+      if(!strncmp(chaine,"Dimension",strlen("Dimension"))) {
+          if(fscanf(inm,"%d",&dim)!=1){ fclose(inm); return(0); }
+          if(dim!=3) {
+            fprintf(stdout,"BAD SOL DIMENSION : %d\n",dim);
+            return(1);
+          }
+          continue;
+      } else if(!strncmp(chaine,"SolAtVertices",strlen("SolAtVertices"))) {
+        if(fscanf(inm,"%d",&pd->np)!=1){ fclose(inm); return(0); }
+        if(fscanf(inm,"%d",&type)!=1){ fclose(inm); return(0); }
+        if(type!=1) {
+          fprintf(stdout,"SEVERAL SOLUTION => IGNORED : %d\n",type);
+          return(1);
+        }
+        if(fscanf(inm,"%d",&btyp)!=1){ fclose(inm); return(0); }
+        posnp = ftell(inm);
+        break;
+      }
+    }
+  } else {
+    if(fread(&pd->ver,sw,1,inm)!=1){ fclose(inm); return(0); }
+    iswp=0;
+    if(pd->ver==16777216) iswp=1;
+    else if(pd->ver!=1) {
+      fprintf(stdout,"BAD FILE ENCODING\n");
+    }
+    if(fread(&pd->ver,sw,1,inm)!=1){ fclose(inm); return(0); }
+    if(iswp) pd->ver = MMG_swapbin(pd->ver);
+    while(fread(&binch,sw,1,inm)!=EOF && binch!=54 ) {
+      if(iswp) binch=MMG_swapbin(binch);
+      if(binch==54) break;
+      if(binch==3) {  //Dimension
+        if(fread(&bdim,sw,1,inm)!=1){ fclose(inm); return(0); } //Pos=>20
+        if(iswp) bdim=MMG_swapbin(bdim);
+        if(fread(&bdim,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) bdim=MMG_swapbin(bdim);
+        if(bdim!=3) {
+          fprintf(stdout,"BAD SOL DIMENSION : %d\n",dim);
+          exit(0);
+          return(1);
+        }
+        continue;
+      } else if(binch==62) {  //SolAtVertices
+        if(fread(&binch,sw,1,inm)!=1){ fclose(inm); return(0); } //Pos
+        if(iswp) binch=MMG_swapbin(binch);
+        if(fread(&pd->np,sw,1,inm)!=1){ fclose(inm); return(0); }
+        if(iswp) pd->np=MMG_swapbin(pd->np);
+        if(fread(&binch,sw,1,inm)!=1){ fclose(inm); return(0); } //nb sol
+        if(iswp) binch=MMG_swapbin(binch);
+        if(binch!=1) {
+          fprintf(stdout,"SEVERAL SOLUTION => IGNORED : %d\n",type);
+          return(1);
+        }
+        if(fread(&btyp,sw,1,inm)!=1){ fclose(inm); return(0); } //typsol
+        if(iswp) btyp=MMG_swapbin(btyp);
+        posnp = ftell(inm);
+        break;
+      } else {
+        if(fread(&bpos,sw,1,inm)!=1){ fclose(inm); return(0); } //Pos
+        if(iswp) bpos=MMG_swapbin(bpos);
+        rewind(inm);
+        fseek(inm,bpos,SEEK_SET);
+      }
+    }
+
+  }
+  if ( !pd->np ) {
+    fprintf(stdout,"  ** MISSING DATA\n");
+    return(0);
+  }
+  else if ( pd->np != mesh->np ) {
+    fprintf(stdout,"  ** WRONG DATA\n");
+    return(0);
+  }
+
+  if ( btyp != 2 ) {
+    fprintf(stdout,"  ** DATA IGNORED\n");
+    return(0);
+  }
+
+  if ( abs(mesh->info.imprim) > 5 )
+    fprintf(stdout,"  -- READING DATA FILE %s\n",data);
+
+  /* read mesh solutions */
+  rewind(inm);
+  fseek(inm,posnp,SEEK_SET);
+  for (k=1; k<=pd->np; k++) {
+    iadr = (k - 1) * 3 + 1;
+    if (pd->ver < 2) {
+      for (i=0; i<3; i++) {
+        if(!bin){
+          if(fscanf(inm,"%f",&fsol)!=1){ fclose(inm); return(0); }
+          pd->mv[iadr + i] = (double) fsol;
+        } else {
+          if(fread(&fsol,sw,1,inm)!=1){ fclose(inm); return(0); }
+          if(iswp) fsol=MMG_swapf(fsol);
+          pd->mv[iadr + i] = (double) fsol;
+        }
+      }
+    } else {
+      for (i=0; i<3; i++) {
+        if(!bin){
+          if(fscanf(inm,"%lf",&pd->mv[iadr + i])!=1){ fclose(inm); return(0); }
+        } else {
+          if(fread(&pd->mv[iadr + i],sd,1,inm)!=1){ fclose(inm); return(0); }
+          if(iswp) pd->mv[iadr + i]=MMG_swapd(pd->mv[iadr + i]);
+        }
+      }
+    }
+  }
+
+  if ( abs(mesh->info.imprim) > 3 )
+    fprintf(stdout,"     NUMBER OF GIVEN DATA       %8d\n",pd->np);
+
+  fclose(inm);
+  return(1);
+}
+
+
+/* save mesh to disk */
+int MMG_saveMesh(pMesh mesh,char *filename) {
+  FILE*        inm;
+	Hedge				 hed;
+  pPoint       ppt;
+  pTria        pt1;
+  pTetra       pt;
+  int          i,k,np,ne,nc,ned,*cor,*ed,ref,bin,bpos;
+  char        *ptr,data[128],chaine[128];
+  int          binch,nu1,nu2;
+  mesh->ver = 2; //double precision
+  bin = 0;
+  strcpy(data,filename);
+  ptr = strstr(data,".mesh");
+  if ( !ptr ) {
+    strcat(data,".meshb");
+    if( !(inm = fopen(data,"wb")) ) {
+      ptr  = strstr(data,".mesh");
+      *ptr = '\0';
+      strcat(data,".mesh");
+      if( !(inm = fopen(data,"w")) ) {
+        fprintf(stderr,"  ** UNABLE TO OPEN %s.\n",data);
+        return(0);
+      }
+    } else {
+      bin = 1;
+    }
+  }
+  else {
+    ptr = strstr(data,".meshb");
+    if( ptr ) bin = 1;
+    if( !(inm = fopen(data,"w")) ) {
+      fprintf(stderr,"  ** UNABLE TO OPEN %s.\n",data);
+      return(0);
+    }
+  }
+  fprintf(stdout,"  %%%% %s OPENED\n",data);
+
+  /*entete fichier*/
+  if(!bin) {
+    strcpy(&chaine[0],"MeshVersionFormatted 2\n");
+    fprintf(inm,"%s",chaine);
+    strcpy(&chaine[0],"\n\nDimension 3\n");
+    fprintf(inm,"%s ",chaine);
+  } else {
+    binch = 1; //MeshVersionFormatted
+    fwrite(&binch,sw,1,inm);
+    binch = 2; //version
+    fwrite(&binch,sw,1,inm);
+    binch = 3; //Dimension
+    fwrite(&binch,sw,1,inm);
+    bpos = 20; //Pos
+    fwrite(&bpos,sw,1,inm);
+    binch = 3;
+    fwrite(&binch,sw,1,inm);
+
+  }
+
+  /* compact vertices */
+  if(mesh->ncor) {
+    cor = (int*) M_calloc(mesh->ncor,sizeof(int),"MMG_savemesh");
+    assert(cor);
+  }
+  if(mesh->ned) {
+	  if ( !MMG_zaldy4(&hed,mesh->ned) ) {
+      if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EXPORT EDGES IGNORED\n");
+			mesh->ned = 0;
+    }
+    ed = (int*)M_calloc(2*mesh->ned,sizeof(int),"MMG_savemesh");
+    assert(ed);
+  }
+  np = 0;
+  nc = 0;
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+		ppt->tmp = ++np;
+    if ( ppt->geom & M_CORNER )  cor[nc++] = ppt->tmp;
+  }
+	assert(mesh->ncor==nc);
+  if(!bin) {
+    strcpy(&chaine[0],"\n\nVertices\n");
+    fprintf(inm,"%s",chaine);
+    fprintf(inm,"%d\n",np);
+  } else {
+    binch = 4; //Vertices
+    fwrite(&binch,sw,1,inm);
+    bpos += 12+(1+3*mesh->ver)*4*np; //NullPos
+    fwrite(&bpos,sw,1,inm);
+    fwrite(&np,sw,1,inm);
+  }
+  for(k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+		//if(ppt->tmp==52453) printf("point %d --> %d\n",ppt->tmp,k);
+    if(!bin) {
+      fprintf(inm,"%.15lg %.15lg %.15lg %d\n",ppt->c[0],ppt->c[1],ppt->c[2],ppt->ref);
+    } else {
+      fwrite((unsigned char*)&ppt->c[0],sd,1,inm);
+      fwrite((unsigned char*)&ppt->c[1],sd,1,inm);
+      fwrite((unsigned char*)&ppt->c[2],sd,1,inm);
+      fwrite((unsigned char*)&ppt->ref,sw,1,inm);
+    }
+  }
+
+  /* rebuild triangles tabular and write triangles */
+  mesh->nt = 0;
+  if(MMG_markBdry(mesh)) {
+    if(!bin) {
+      strcpy(&chaine[0],"\n\nTriangles\n");
+      fprintf(inm,"%s",chaine);
+      fprintf(inm,"%d \n",mesh->nt);
+    } else {
+      binch = 6; //Triangles
+      fwrite(&binch,sw,1,inm);
+      bpos += 12+16*mesh->nt; //Pos
+      fwrite(&bpos,sw,1,inm);
+      fwrite(&mesh->nt,sw,1,inm);
+    }
+    for (k=1; k<=mesh->nt; k++) {
+      pt1  = &mesh->tria[k];
+  	    ref  = pt1->ref;
+      if(!bin) {
+        fprintf(inm,"%d %d %d %d\n",mesh->point[pt1->v[0]].tmp,mesh->point[pt1->v[1]].tmp
+    							  ,mesh->point[pt1->v[2]].tmp,ref);
+      } else {
+        fwrite(&mesh->point[pt1->v[0]].tmp,sw,1,inm);
+        fwrite(&mesh->point[pt1->v[1]].tmp,sw,1,inm);
+        fwrite(&mesh->point[pt1->v[2]].tmp,sw,1,inm);
+        fwrite(&ref,sw,1,inm);
+      }
+    }
+  }
+
+  /* write tetrahedra */
+  ne = 0;
+	ned = 0;
+	//printf("avt %d\n",mesh->ned);
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+		if(mesh->ned) {
+		  for (i=0 ; i<6 ; i++) {
+		  	if (pt->bdryinfo[i]) {
+		  		nu1 = pt->v[MMG_iare[i][0]];
+		  		nu2 = pt->v[MMG_iare[i][1]];
+		  		if (MMG_edgePut(&hed,nu1,nu2,2)<=1) {
+		  			ed[2*ned] = (mesh->point[nu1]).tmp;
+		  			ed[2*ned + 1] = (mesh->point[nu2]).tmp;
+		  			ned++;
+		  		}
+		  	}
+		  }
+		}
+	  ne++;
+  }
+	//printf("ned %d\n",ned);
+  if(!bin) {
+    strcpy(&chaine[0],"\n\nTetrahedra\n");
+    fprintf(inm,"%s",chaine);
+    fprintf(inm,"%d\n",ne);
+  } else {
+    binch = 8; //Tetra
+    fwrite(&binch,sw,1,inm);
+    bpos += 12 + 20*ne;//Pos
+    fwrite(&bpos,sw,1,inm);
+    fwrite((unsigned char*)&ne,sw,1,inm);
+  }
+	ne=0;
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+		ne++;
+    ref = pt->ref;
+    if(!bin) {
+      fprintf(inm,"%d %d %d %d %d\n",mesh->point[pt->v[0]].tmp,mesh->point[pt->v[1]].tmp
+  							   ,mesh->point[pt->v[2]].tmp,mesh->point[pt->v[3]].tmp,ref);
+    } else {
+      fwrite(&mesh->point[pt->v[0]].tmp,sw,1,inm);
+      fwrite(&mesh->point[pt->v[1]].tmp,sw,1,inm);
+      fwrite(&mesh->point[pt->v[2]].tmp,sw,1,inm);
+      fwrite(&mesh->point[pt->v[3]].tmp,sw,1,inm);
+      fwrite(&ref,sw,1,inm);
+    }
+  }
+
+  if(mesh->ned) {
+    if(!bin) {
+      strcpy(&chaine[0],"\n\nEdges\n");
+      fprintf(inm,"%s",chaine);
+      fprintf(inm,"%d\n",ned);
+    } else {
+      binch = 5; //Edges
+      fwrite(&binch,sw,1,inm);
+      bpos += 12 + 3*4*ned;//Pos
+      fwrite(&bpos,sw,1,inm);
+      fwrite((unsigned char*)&ned,sw,1,inm);
+    }
+  	  for (k=0; k<ned; k++) {
+   	    ref = 0;
+  	    if(!bin) {
+  	      fprintf(inm,"%d %d %d \n",ed[2*k],ed[2*k+1],ref);
+  	    } else {
+  	      fwrite(&ed[2*k],sw,1,inm);
+  	      fwrite(&ed[2*k+1],sw,1,inm);
+  	      fwrite(&ref,sw,1,inm);
+  	    }
+  	  }
+  	  M_free(hed.item);
+  }
+
+  /* write corners */
+  if(!bin) {
+    strcpy(&chaine[0],"\n\nCorners\n");
+    fprintf(inm,"%s",chaine);
+    fprintf(inm,"%d\n",mesh->ncor);
+  } else {
+    binch = 13; //Corners
+    fwrite(&binch,sw,1,inm);
+    bpos += 12 + 4*mesh->ncor;//Pos
+    fwrite(&bpos,sw,1,inm);
+    fwrite((unsigned char*)&mesh->ncor,sw,1,inm);
+  }
+  for (k=0; k<mesh->ncor; k++) {
+    if(!bin) {
+      fprintf(inm,"%d \n",cor[k]);
+    } else {
+      fwrite(&cor[k],sw,1,inm);
+    }
+  }
+  /*fin fichier*/
+  if(!bin) {
+    strcpy(&chaine[0],"\n\nEnd\n");
+    fprintf(inm,"%s",chaine);
+  } else {
+    binch = 54; //End
+    fwrite(&binch,sw,1,inm);
+  }
+  fclose(inm);
+  if(mesh->ncor) M_free(cor);
+  if ( mesh->info.imprim ) {
+    fprintf(stdout,"     NUMBER OF GIVEN VERTICES   %8d\n",mesh->npfixe);
+    if ( mesh->ntfixe )
+      fprintf(stdout,"     NUMBER OF GIVEN TRIANGLES  %8d\n",mesh->ntfixe);
+    fprintf(stdout,"     NUMBER OF GIVEN ELEMENTS   %8d\n",mesh->nefixe);
+    fprintf(stdout,"     TOTAL NUMBER OF VERTICES   %8d\n",np);
+    fprintf(stdout,"     TOTAL NUMBER OF TRIANGLES  %8d\n",mesh->nt);
+    fprintf(stdout,"     TOTAL NUMBER OF ELEMENTS   %8d\n",ne);
+    if ( mesh->ncor )
+      fprintf(stdout,"     TOTAL NUMBER OF CORNERS    %8d\n",mesh->ncor);
+    if ( mesh->ned )
+      fprintf(stdout,"     TOTAL NUMBER OF EDGES      %8d\n",ned);
+  }
+	//if(ned!=mesh->ned) exit(0);
+  return(1);
+
+}
+
+
+int MMG_saveSol(pMesh mesh,pSol sol,char *filename) {
+  FILE*        inm;
+  pPoint       ppt;
+  float        fsol;
+  double       tmp;
+  int          i,k,nbl,isol,bin,bpos,typ;
+  char        *ptr,data[128],chaine[128];
+  int          binch;
+
+  if ( !sol->np )  return(1);
+  bin = 1;
+  strcpy(data,filename);
+  ptr = strstr(data,".meshb");
+  if ( ptr )  *ptr = '\0';
+  else {
+    ptr = strstr(data,".mesh");
+    if ( ptr ) {
+      *ptr = '\0';
+      bin  = 0;
+    } else {
+	    ptr = strstr(data,".solb");
+	    if ( ptr ) {
+	      *ptr = '\0';
+	      bin  = 1;
+      } else {
+			  ptr = strstr(data,".sol");
+			  if ( ptr ) {
+			    *ptr = '\0';
+			    bin  = 0;
+			  }
+			}
+    }
+  }
+  if ( bin )
+    strcat(data,".solb");
+  else
+    strcat(data,".sol");
+
+  sol->ver = 2;
+  if( bin && !(inm = fopen(data,"wb")) ) {
+    fprintf(stderr,"  ** UNABLE TO OPEN %s.\n",data);
+    return(0);
+  } else {
+    if( !(inm = fopen(data,"w")) ) {
+      fprintf(stderr,"  ** UNABLE TO OPEN %s.\n",data);
+      return(0);
+    }
+  }
+  fprintf(stdout,"  %%%% %s OPENED\n",data);
+
+  /*entete fichier*/
+  if(!bin) {
+    strcpy(&chaine[0],"MeshVersionFormatted 2\n");
+    fprintf(inm,"%s",chaine);
+    strcpy(&chaine[0],"\n\nDimension 3\n");
+    fprintf(inm,"%s ",chaine);
+  } else {
+    binch = 1; //MeshVersionFormatted
+    fwrite(&binch,sw,1,inm);
+    binch = 2; //version
+    fwrite(&binch,sw,1,inm);
+    binch = 3; //Dimension
+    fwrite(&binch,sw,1,inm);
+    bpos = 20; //Pos
+    fwrite(&bpos,sw,1,inm);
+    binch = 3;
+    fwrite(&binch,sw,1,inm);
+
+  }
+
+
+  switch(sol->offset) {
+  case 1:
+	 typ = 1;
+   break;
+  case 6:
+	  typ = 3;
+    break;
+  default:
+    fprintf(stdout,"  ** DATA IGNORED\n");
+    return(0);
+  }
+
+  /* write data */
+  nbl = 0;
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+	nbl++;
+  }
+
+  if(!bin) {
+    strcpy(&chaine[0],"\n\nSolAtVertices\n");
+    fprintf(inm,"%s",chaine);
+    fprintf(inm,"%d\n",nbl);
+    fprintf(inm,"%d %d\n",1,typ);
+  } else {
+    binch = 62; //Vertices
+    fwrite(&binch,sw,1,inm);
+    bpos += 20+(sol->offset*sol->ver)*4*nbl; //Pos
+    fwrite(&bpos,sw,1,inm);
+    fwrite(&nbl,sw,1,inm);
+    binch = 1; //nb sol
+    fwrite(&binch,sw,1,inm);
+    binch = typ; //typ sol
+    fwrite(&binch,sw,1,inm);
+  }
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+    isol = (k-1) * sol->offset + 1;
+    /* swap data */
+    if ( sol->offset == 6 ) {
+      tmp                = sol->met[isol + 2];
+      sol->met[isol + 2] = sol->met[isol + 3];
+      sol->met[isol + 3] = tmp;
+    }
+    if (sol->ver < 2) {
+      if(!bin) {
+        for (i=0; i<sol->offset; i++) {
+          fsol = (float) sol->met[isol + i];
+          fprintf(inm,"%f ",fsol);
+        }
+        fprintf(inm,"\n");
+      } else {
+        for (i=0; i<sol->offset; i++) {
+          fsol = (float) sol->met[isol + i];
+          fwrite(&fsol,sw,1,inm);
+        }
+      }
+    } else {
+      if(!bin) {
+        for (i=0; i<sol->offset; i++)
+          fprintf(inm,"%.15lg ",sol->met[isol + i]);
+        fprintf(inm,"\n");
+      } else {
+        for (i=0; i<sol->offset; i++)
+          fwrite(&sol->met[isol + i],sd,1,inm);
+      }
+
+    }
+  }
+
+  /*fin fichier*/
+  if(!bin) {
+    strcpy(&chaine[0],"\n\nEnd\n");
+    fprintf(inm,"%s",chaine);
+  } else {
+    binch = 54; //End
+    fwrite(&binch,sw,1,inm);
+  }
+  fclose(inm);
+  return(1);
+}
+
+/*save the node speed : coornew-coorold/dt*/
+int MMG_saveVect(pMesh mesh,char *filename) {
+  FILE*        inm;
+  pDispl        pd;
+  pPoint       ppt;
+  double       dsol,dd;
+  int          i,k,nbl,bin,bpos,typ;
+  char        *ptr,data[128],chaine[128];
+  unsigned char binch;
+
+  pd      = mesh->disp;
+  pd->ver = 2;
+
+  bin = 0;
+  strcpy(data,filename);
+  ptr = strstr(data,".meshb");
+  if ( ptr )  *ptr = '\0';
+  else {
+    ptr = strstr(data,".mesh");
+    if ( ptr ) {
+      *ptr = '\0';
+      bin  = 0;
+    }
+  }
+  if ( bin )
+    strcat(data,".o.solb");
+  else
+    strcat(data,".o.sol");
+  if( bin && !(inm = fopen(data,"wb")) ) {
+    fprintf(stderr,"  ** UNABLE TO OPEN %s.\n",data);
+    return(0);
+  } else {
+    if( !(inm = fopen(data,"w")) ) {
+      fprintf(stderr,"  ** UNABLE TO OPEN %s.\n",data);
+      return(0);
+    }
+  }
+  fprintf(stdout,"  %%%% %s OPENED\n",data);
+
+  /*entete fichier*/
+  if(!bin) {
+    strcpy(&chaine[0],"MeshVersionFormatted 2\n");
+    fprintf(inm,"%s",chaine);
+    strcpy(&chaine[0],"\n\nDimension 3\n");
+    fprintf(inm,"%s ",chaine);
+  } else {
+    binch = 1; //MeshVersionFormatted
+    fwrite(&binch,sw,1,inm);
+    binch = pd->ver; //version
+    fwrite(&binch,sw,1,inm);
+    binch = 3; //Dimension
+    fwrite(&binch,sw,1,inm);
+    bpos = 20; //Pos
+    fwrite(&bpos,sw,1,inm);
+    binch = 3;
+    fwrite(&binch,sw,1,inm);
+
+  }
+	typ = 2;
+
+  /* write data */
+  nbl = 0;
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+	nbl++;
+  }
+
+  if(!bin) {
+    strcpy(&chaine[0],"\n\nSolAtVertices\n");
+    fprintf(inm,"%s",chaine);
+    fprintf(inm,"%d\n",nbl);
+    fprintf(inm,"%d %d\n",1,typ);
+  } else {
+    binch = 62; //SolAtVertices
+    fwrite(&binch,sw,1,inm);
+    bpos += 20+(3*pd->ver)*4*nbl; //Pos
+    fwrite(&bpos,sw,1,inm);
+    fwrite(&nbl,sw,1,inm);
+    binch = 1; //nb sol
+    fwrite(&binch,sw,1,inm);
+    binch = typ; //typ sol
+    fwrite(&binch,sw,1,inm);
+  }
+
+
+  dd = mesh->info.delta / (double)PRECI;
+  fprintf(stdout,"        DT %e\n",mesh->info.dt);
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+    for (i=0 ; i<3 ; i++) {
+      dsol = (ppt->c[i] - mesh->disp->cold[3*(k-1) + 1 + i]* dd - mesh->info.min[i])/mesh->info.dt;
+      if(!bin) {
+        fprintf(inm,"%.15lg ",dsol);
+      } else {
+        fwrite(&dsol,sd,1,inm);
+      }
+    }
+    if (!bin) fprintf(inm,"\n");
+  }
+
+
+  /*fin fichier*/
+  if(!bin) {
+    strcpy(&chaine[0],"\n\nEnd\n");
+    fprintf(inm,"%s",chaine);
+  } else {
+    binch = 54; //End
+    fwrite(&binch,sw,1,inm);
+  }
+  fclose(inm);
+
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/length.c b/contrib/mmg3d/build/sources/length.c
new file mode 100644
index 0000000000000000000000000000000000000000..5b3401b7efe784962abb5d387c1baac9d9b39de3
--- /dev/null
+++ b/contrib/mmg3d/build/sources/length.c
@@ -0,0 +1,237 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/* compute aniso edge MMG_length */
+double MMG_long_ani(double *ca,double *cb,double *sa,double *sb) {
+  double   ux,uy,uz,dd1,dd2,len;
+
+  ux = cb[0] - ca[0];
+  uy = cb[1] - ca[1];
+  uz = cb[2] - ca[2];
+
+  dd1 =      sa[0]*ux*ux + sa[3]*uy*uy + sa[5]*uz*uz \
+      + 2.0*(sa[1]*ux*uy + sa[2]*ux*uz + sa[4]*uy*uz);
+  if ( dd1 <= 0.0 )  dd1 = 0.0;
+
+  dd2 =      sb[0]*ux*ux + sb[3]*uy*uy + sb[5]*uz*uz \
+      + 2.0*(sb[1]*ux*uy + sb[2]*ux*uz + sb[4]*uy*uz);
+  if ( dd2 <= 0.0 )  dd2 = 0.0;
+  
+  /*longueur approchee*/   
+  /*precision a 3.5 10e-3 pres*/
+  if(fabs(dd1-dd2) < 0.05 ) {
+    //printf("bonne precision %e \n",sqrt(0.5*(dd1+dd2)) - (sqrt(dd1)+sqrt(dd2)+4.0*sqrt(0.5*(dd1+dd2))) / 6.0 );
+    len = sqrt(0.5*(dd1+dd2));
+    return(len);
+  }
+  len = (sqrt(dd1)+sqrt(dd2)+4.0*sqrt(0.5*(dd1+dd2))) / 6.0;
+
+  return(len);
+}
+
+double MMG_long_ani_init(double *ca,double *cb,double *sa,double *sb) {
+  double   ux,uy,uz,dd1,dd2,len;
+
+  ux = cb[0] - ca[0];
+  uy = cb[1] - ca[1];
+  uz = cb[2] - ca[2];
+
+  dd1 =      sa[0]*ux*ux + sa[3]*uy*uy + sa[5]*uz*uz \
+      + 2.0*(sa[1]*ux*uy + sa[2]*ux*uz + sa[4]*uy*uz);
+  if ( dd1 <= 0.0 )  dd1 = 0.0;
+
+  dd2 =      sb[0]*ux*ux + sb[3]*uy*uy + sb[5]*uz*uz \
+      + 2.0*(sb[1]*ux*uy + sb[2]*ux*uz + sb[4]*uy*uz);
+  if ( dd2 <= 0.0 )  dd2 = 0.0;
+  
+  len = (sqrt(dd1)+sqrt(dd2)+4.0*sqrt(0.5*(dd1+dd2))) / 6.0;
+
+  return(len);
+}
+
+/* compute iso edge MMG_length */
+double MMG_long_iso(double *ca,double *cb,double *ma,double *mb) {
+  double   ux,uy,uz,dd,rap,len;
+  double   sa,sb;
+
+  sa   = *ma;
+  sb   = *mb;
+  
+  ux = cb[0] - ca[0];
+  uy = cb[1] - ca[1];
+  uz = cb[2] - ca[2];
+  dd = sqrt(ux*ux + uy*uy + uz*uz);
+
+  rap = (sb - sa) / sa;
+  if ( fabs(rap) < EPS1 )
+    /*len = dd * (2.0-EPS1) / (2.0*sa);*/
+    len = dd / sa;
+  else
+    /*len = max(dd/sa,dd/sb);*/
+    len = dd * (1.0/sa + 1.0/sb + 8.0 / (sa+sb)) / 6.0;
+
+
+  return(len);
+}
+
+
+/* print histo of edge lengths */
+int MMG_prilen(pMesh mesh,pSol sol) {
+  pTetra      pt;
+  double      lavg,len,ecart,som,lmin,lmax,*ca,*cb,*ma,*mb;
+  int         k,l,lon,navg,ia,ipa,ipb,iamin,ibmin,iamax,ibmax,dep,hl[10];
+  int	      iadr;
+  List        list;
+  static double bd[9] = {0.0, 0.2, 0.5, 0.7071, 0.9, 1.111, 1.4142, 2.0, 5.0 };
+  navg  = 0;
+  lavg  = 0.0;
+  lmin  = 1.e20;
+  lmax  = 0.0;
+  som   = 0.0;
+  dep   = 1;
+  iamin = 0;
+  ibmin = 0;
+  iamax = 0;
+  ibmax = 0;
+
+  for (k=1; k<10; k++)  hl[k] = 0;
+
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+
+    for (ia=0; ia<6; ia++) {
+      lon = MMG_coquil(mesh,k,ia,&list);
+      if ( lon < 2 )  continue;
+      for (l=2; l<=lon; l++)
+        if ( list.tetra[l] < 6*k )  break;
+
+ 	      if ( l <= lon )  continue;
+        
+        ipa = MMG_iare[ia][0];
+        ipb = MMG_iare[ia][1];
+        ca  = &mesh->point[pt->v[ipa]].c[0];
+        cb  = &mesh->point[pt->v[ipb]].c[0];
+
+        iadr = (pt->v[ipa]-1)*sol->offset + 1;
+        ma   = &sol->met[iadr];
+        iadr = (pt->v[ipb]-1)*sol->offset + 1;
+        mb   = &sol->met[iadr];  
+        if(sol->offset==6)
+          len = MMG_long_ani_init(ca,cb,ma,mb);  
+        else
+          len = MMG_length(ca,cb,ma,mb);  
+        navg++;
+        ecart = len; 
+        lavg += len;
+        /* update efficiency index */
+	      if ( ecart > 1.0 )  ecart = 1.0 / ecart; 
+
+        som  += (ecart - 1.0); 
+      
+        /* find largest, smallest edge */
+        if (len < lmin) {
+	        lmin  = len;
+		      iamin = pt->v[ipa];
+		      ibmin = pt->v[ipb];
+        }
+        else if (len > lmax) {
+		      lmax  = len;
+          iamax = pt->v[ipa];
+          ibmax = pt->v[ipb];
+        }
+
+        /* update histogram */
+        if (len < bd[3]) {
+		      if (len > bd[2])       hl[3]++;
+		      else if (len > bd[1])  hl[2]++;
+		      else                   hl[1]++;
+        }
+        else if (len < bd[5]) {
+		      if (len > bd[4])       hl[5]++;
+		      else if (len > bd[3])  hl[4]++;
+        }
+        else if (len < bd[6])    hl[6]++;
+        else if (len < bd[7])    hl[7]++;
+        else if (len < bd[8])    hl[8]++;
+        else                     hl[9]++;
+      }
+      /*    /if ( dep < 0 )  break;*/
+  }
+
+  //if(mesh->info.imprim < 0) { //EMI
+
+  fprintf(stdout,"\n  -- RESULTING EDGE LENGTHS  %d\n",navg);
+  fprintf(stdout,"     AVERAGE LENGTH         %12.4f\n",lavg / (double)navg);
+  fprintf(stdout,"     SMALLEST EDGE LENGTH   %12.4f   %6d %6d\n",
+  	  lmin,iamin,ibmin);
+  fprintf(stdout,"     LARGEST  EDGE LENGTH   %12.4f   %6d %6d \n",
+  	  lmax,iamax,ibmax);
+  fprintf(stdout,"     EFFICIENCY INDEX       %12.4f\n",exp(som/(double)navg));
+  if ( hl[4]+hl[5]+hl[6] )
+    fprintf(stdout,"   %6.2f < L <%5.2f  %8d   %5.2f %%  \n",
+      bd[3],bd[6],hl[4]+hl[5]+hl[6],100.*(hl[4]+hl[5]+hl[6])/(double)navg);
+
+    fprintf(stdout,"\n     HISTOGRAMM\n");
+    if ( hl[1] )
+      fprintf(stdout,"     0.00 < L < 0.20  %8d   %5.2f %%  \n",
+	      hl[1],100.*(hl[1]/(float)navg));
+    if ( lmax > 0.2 ) {
+      for (k=2; k<9; k++) {
+        if ( hl[k] > 0 )
+  	  fprintf(stdout,"   %6.2f < L <%5.2f  %8d   %5.2f %%  \n",
+		  bd[k-1],bd[k],hl[k],100.*(hl[k]/(float)navg));
+      }
+      if ( hl[9] )
+        fprintf(stdout,"     5.   < L         %8d   %5.2f %%  \n",
+	        hl[9],100.*(hl[9]/(float)navg));
+    }
+  
+    //}
+
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/libmmg3d.h b/contrib/mmg3d/build/sources/libmmg3d.h
new file mode 100644
index 0000000000000000000000000000000000000000..4f1afacec9c2ef3675bffb9501d7d2e506fa6782
--- /dev/null
+++ b/contrib/mmg3d/build/sources/libmmg3d.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile,
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite,
+y compris les garanties de commercialisation ou
+d’adaptation dans un but spécifique.
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU
+en même temps que ce document.
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+spread under the terms and conditions of the license GNU General Public License
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.
+****************************************************************************/
+typedef struct {
+  double        c[3];
+  int           mark,tmp;
+  int           ref;
+  unsigned char flag,tag,tmp2;
+	unsigned char geom;
+} MMG_Point;
+typedef MMG_Point * MMG_pPoint;
+
+typedef struct {
+  int           v[4];
+  int           mark;
+  double        qual;
+  int           ref,bdryref[4];
+  unsigned char flag,edge,tabedg;
+	unsigned char bdryinfo[6];
+} MMG_Tetra;
+typedef MMG_Tetra * MMG_pTetra;
+
+typedef struct {
+  int           v[3],splx;
+  int           ref;
+} MMG_Tria;
+typedef MMG_Tria * MMG_pTria;
+
+typedef struct {
+  int      np,ver;
+  double   *mv,*cold;
+  short    *alpha;
+} MMG_Displ;
+typedef MMG_Displ * MMG_pDispl;
+
+typedef struct {
+  unsigned char  ddebug;
+  unsigned char  noswap,noinsert,nomove,bdry;
+  short          imprim,option,memory,rn,rn2;
+  int            bucksiz;
+  double   	     delta,dt;
+  double   	     min[3],max[3];
+
+} MMG_Info;
+
+typedef struct {
+  int      np,ne,nt,ncor,ned,npmax,nemax,ntmax;
+  int      npfixe,nefixe,ntfixe,mark;
+  int      npnil,nenil,ntnil;
+  int     *adja,ver;
+  char    *name,*outf,*move;
+  unsigned char flag,booleen;
+
+  MMG_pPoint   point;
+  MMG_pTetra   tetra;
+  MMG_pTria    tria;
+  MMG_pDispl   disp;
+  MMG_Info     info;
+} MMG_Mesh;
+typedef MMG_Mesh * MMG_pMesh;
+
+typedef struct {
+  int      np,npfixe,npmax,ver;
+  double   *met,hmin,hmax;
+  char     *name;
+  double   *metold;
+  unsigned char offset;
+} MMG_Sol;
+typedef MMG_Sol * MMG_pSol;
+
+/* inout */
+int  MMG_loadMesh(MMG_pMesh ,char *);
+int  MMG_loadSol(MMG_pSol ,char *,int );
+int  MMG_loadVect(MMG_pMesh ,char *,int );
+int  MMG_saveMesh(MMG_pMesh ,char *);
+int  MMG_saveSol(MMG_pMesh ,MMG_pSol ,char *);
+int  MMG_saveVect(MMG_pMesh ,char *);
+
+#ifdef  __cplusplus
+namespace mmg3d{
+extern "C" {
+#endif
+int MMG_mmg3dlib(int opt[9],MMG_pMesh mesh,MMG_pSol sol);
+#ifdef  __cplusplus
+}}
+#endif
diff --git a/contrib/mmg3d/build/sources/libmmg3d_internal.h b/contrib/mmg3d/build/sources/libmmg3d_internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..67be6768cb1065d4847a69ec4ec1faa162329bbc
--- /dev/null
+++ b/contrib/mmg3d/build/sources/libmmg3d_internal.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+typedef MMG_Point Point;
+typedef MMG_Tetra Tetra;
+typedef MMG_Tria  Tria;
+typedef MMG_Displ Displ;
+typedef MMG_Mesh Mesh;
+typedef MMG_Sol Sol;
+
+typedef MMG_pPoint pPoint;
+typedef MMG_pTetra pTetra;
+typedef MMG_pDispl pDispl;
+typedef MMG_pTria pTria;
+typedef MMG_Info Info;
+typedef MMG_pMesh pMesh;
+typedef MMG_pSol pSol;
diff --git a/contrib/mmg3d/build/sources/librnbg.c b/contrib/mmg3d/build/sources/librnbg.c
new file mode 100644
index 0000000000000000000000000000000000000000..bacfb4940bbeeb918b344a2198be690a8fd1bf4a
--- /dev/null
+++ b/contrib/mmg3d/build/sources/librnbg.c
@@ -0,0 +1,461 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+/* librnbg
+ *
+ * Written by Cedric Lachat
+ */  
+#include "mesh.h"
+
+#ifdef USE_SCOTCH
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "librnbg.h"
+
+#define CHECK_SCOTCH(t,m,e) if(0!=t){perror(m);exit(e);}
+
+
+
+
+/* Internal function : biPartBoxCompute
+ * it computes a new numbering of graph vertices, using a bipartitioning.
+ *
+ *  - graf : the input graph
+ *  - vertNbr : the number of vertices
+ *  - boxVertNbr : the number of vertices of each box
+ *  - permVrtTab : the new numbering
+ *  
+ *  returning 0 if OK, 1 else
+ */
+int biPartBoxCompute(SCOTCH_Graph graf, int vertNbr, int boxVertNbr, SCOTCH_Num *permVrtTab) {
+  int boxNbr, vertIdx, boxIdx;
+  SCOTCH_Num tmp, tmp2, *partTab, *partNumTab, *partPrmTab;
+  SCOTCH_Strat strat ;
+
+  /* Computing the number of boxes */
+  boxNbr = vertNbr / boxVertNbr;
+  if (boxNbr * boxVertNbr != vertNbr) {
+    boxNbr = boxNbr + 1;
+  }
+
+
+  /* Initializing SCOTCH functions */
+  CHECK_SCOTCH(SCOTCH_stratInit(&strat), "scotch_stratInit", 0) ; 
+  CHECK_SCOTCH(SCOTCH_stratGraphMap(&strat, "r{job=t,map=t,poli=S,sep=m{type=h,vert=80,low=h{pass=10}f{bal=0.005,move=0},asc=b{bnd=f{bal=0.05,move=0},org=f{bal=0.05,move=0}}}|m{type=h,vert=80,low=h{pass=10}f{bal=0.005,move=0},asc=b{bnd=f{bal=0.05,move=0},org=f{bal=0.05,move=0}}}}"), "scotch_stratGraphMap", 0) ; 
+
+  partTab = (SCOTCH_Num *)M_calloc(vertNbr, sizeof(SCOTCH_Num), "boxCompute");
+
+
+  /* Partionning the graph */
+  CHECK_SCOTCH(SCOTCH_graphPart(&graf, boxNbr, &strat, partTab), "scotch_graphPart", 0);
+
+  partNumTab = (SCOTCH_Num *)M_calloc(boxNbr, sizeof(SCOTCH_Num), "boxCompute");
+
+  if (!memset(partNumTab, 0, boxNbr*sizeof(SCOTCH_Num))) {
+    perror("memset");
+    return 0;
+  }
+
+  /* Computing the number of elements of each box */
+  for( vertIdx = 0 ; vertIdx< vertNbr ;vertIdx++)
+    partNumTab[partTab[vertIdx]] += 1;
+
+
+  /* partition permutation tabular */
+  partPrmTab = (SCOTCH_Num *)M_calloc(vertNbr + 1, sizeof(SCOTCH_Num), "boxCompute");
+
+
+  /* Copying the previous tabular in order to have the index of the first
+   * element of each box
+   * */
+  tmp = partNumTab[0];
+  partNumTab[0] = 0;
+  for(boxIdx = 1; boxIdx < boxNbr ; boxIdx++) {
+    tmp2 = partNumTab[boxIdx];
+    partNumTab[boxIdx] = partNumTab[boxIdx-1] + tmp;
+    tmp = tmp2;
+  }
+
+  /* partPrmTab is built such as each vertex belongs to his box */
+  for( vertIdx = 0;vertIdx< vertNbr;vertIdx++)
+    partPrmTab[partNumTab[partTab[vertIdx]]++] = vertIdx;
+
+
+  /* Infering the new numbering */
+  for (vertIdx = 0; vertIdx < vertNbr ; vertIdx++)
+    permVrtTab[partPrmTab[vertIdx] + 1] = vertIdx + 1;
+
+  M_free(partTab);
+  M_free(partNumTab);
+  M_free(partPrmTab);
+
+  SCOTCH_stratExit(&strat) ;
+  return 0;
+}
+
+
+
+
+/* Internal function : kPartBoxCompute
+ * it computes a new numbering of graph vertices, using a k-partitioning.
+ * Assuming that baseval of the graph is 1
+ *
+ *  - graf : the input graph
+ *  - vertNbr : the number of vertices
+ *  - boxVertNbr : the number of vertices of each box
+ *  - permVrtTab : the new numbering
+ *  
+ *  returning 0 if OK, 1 else
+ */
+int kPartBoxCompute(SCOTCH_Graph graf, int vertNbr, int boxVertNbr, SCOTCH_Num *permVrtTab) {
+  int boxNbr, vertIdx;
+  SCOTCH_Num logMaxVal, SupMaxVal, InfMaxVal, maxVal;
+  char s[200];
+  SCOTCH_Num *sortPartTb;
+  SCOTCH_Strat strat ;
+  SCOTCH_Arch arch;
+
+  /* Computing the number of boxes */
+  boxNbr = vertNbr / boxVertNbr;
+  if (boxNbr * boxVertNbr != vertNbr) {
+    boxNbr = boxNbr + 1;
+  }
+
+
+  /* Initializing SCOTCH functions */
+  CHECK_SCOTCH(SCOTCH_stratInit(&strat), "scotch_stratInit", 0) ; 
+  CHECK_SCOTCH(SCOTCH_archVcmplt(&arch), "scotch_archVcmplt", 0) ; 
+
+  sprintf(s, "m{vert=%d,low=r{job=t,map=t,poli=S,sep=m{type=h,vert=80,low=h{pass=10}f{bal=0.0005,move=80},asc=f{bal=0.005,move=80}}}}", vertNbr / boxVertNbr);
+  CHECK_SCOTCH(SCOTCH_stratGraphMap(&strat, s), "scotch_stratGraphMap", 0) ; 
+
+
+  sortPartTb= (SCOTCH_Num *)M_calloc(2*vertNbr, sizeof(SCOTCH_Num), "boxCompute");
+
+
+  /* Partionning the graph */
+  CHECK_SCOTCH(SCOTCH_graphMap(&graf, &arch, &strat, sortPartTb), "scotch_graphMap", 0);
+
+
+  // Looking for the max value in sortPartTb and computing sortPartTb as
+  // followed : 
+  //  - sortPartTb[2i] is the box value
+  //  - sortPartTb[2i+1] is the vertex number
+  maxVal = sortPartTb[0];
+  for (vertIdx = vertNbr - 1 ; vertIdx >= 0 ; vertIdx--) {
+    sortPartTb[2*vertIdx] = sortPartTb[vertIdx];
+    sortPartTb[2*vertIdx+1] = vertIdx + 1;
+    if (sortPartTb[vertIdx] > maxVal)
+      maxVal = sortPartTb[vertIdx];
+  }
+
+  // Determining the log of MaxVal
+  logMaxVal = 0;
+  while ( maxVal > 0) {
+    logMaxVal++;
+    maxVal >>= 1;
+  }
+
+  // Infering the interval in which box values will be
+  InfMaxVal = logMaxVal << logMaxVal;
+  SupMaxVal = (logMaxVal << (logMaxVal + 1)) - 1;
+
+  // Increasing box values until they are in the previous interval
+  for (vertIdx = 0 ; vertIdx < vertNbr ; vertIdx++) {
+    while (!(sortPartTb[2*vertIdx] >= InfMaxVal && sortPartTb[2*vertIdx] <= SupMaxVal)) {
+      sortPartTb[2*vertIdx] <<= 1;
+    }
+  }
+
+
+
+  // Sorting the tabular, which contains box values and vertex numbers
+  _SCOTCHintSort2asc1(sortPartTb, vertNbr);
+
+
+  /* Infering the new numbering */
+  for (vertIdx = 0; vertIdx < vertNbr ; vertIdx++) {
+    permVrtTab[sortPartTb[2*vertIdx + 1]] = vertIdx + 1;
+  }
+
+  SCOTCH_stratExit(&strat) ;
+  SCOTCH_archExit(&arch) ;
+
+  M_free(sortPartTb);
+
+  return 0;
+}
+
+
+
+/* Function : renumbering
+ *  it modifies the numbering of each node to prevent from cache missing.
+ *
+ *  - boxVertNbr : number of vertices by box
+ *  - mesh : the input mesh which is modified
+ *  
+ *  returning 0 if OK, 1 else
+ */
+int renumbering(int boxVertNbr, MMG_pMesh mesh, MMG_pSol sol) {
+  MMG_pPoint ppt;
+  MMG_pPoint points;
+  MMG_pTria ptri, trias;
+  MMG_pTetra ptet, tetras;
+  SCOTCH_Num edgeNbr;
+  SCOTCH_Num *vertTab, *vendTab, *edgeTab, *permVrtTab;
+  SCOTCH_Graph graf ;
+  int vertNbr, nodeGlbIdx, triaIdx, tetraIdx, ballTetIdx;
+  int i, j, k, addrNew, addrOld;
+  int edgeSiz;
+  int *vertOldTab, *permNodTab, ntreal, nereal, npreal;
+  int      *adja,iadr;
+  double *metNew;
+
+
+  /* Computing the number of vertices and a contiguous tabular of vertices */
+  vertNbr = 0;
+  vertOldTab = (int *)M_calloc(mesh->ne + 1, sizeof(int), "renumbering");
+
+  if (!memset(vertOldTab, 0, sizeof(int)*(mesh->ne+1))) {
+    perror("memset");
+    return 1;
+  }
+
+  for(tetraIdx = 1 ; tetraIdx < mesh->ne + 1 ; tetraIdx++) {
+
+    /* Testing if the tetra exists */
+    if (!mesh->tetra[tetraIdx].v[0]) continue;
+    vertOldTab[tetraIdx] = vertNbr+1;
+    vertNbr++;
+  }
+
+
+  /* Allocating memory to compute adjacency lists */
+  vertTab = (SCOTCH_Num *)M_calloc(vertNbr + 1, sizeof(SCOTCH_Num), "renumbering");
+
+  if (!memset(vertTab, ~0, sizeof(SCOTCH_Num)*(vertNbr + 1))) {
+    perror("memset");
+    return 1;
+  }
+
+  vendTab = (SCOTCH_Num *)M_calloc(vertNbr + 1, sizeof(SCOTCH_Num), "renumbering");
+
+  edgeNbr = 1;
+  edgeSiz = vertNbr*2;
+  edgeTab = (SCOTCH_Num *)M_calloc(edgeSiz, sizeof(SCOTCH_Num), "renumbering");
+
+
+
+  /* Computing the adjacency list for each vertex */
+  for(tetraIdx = 1 ; tetraIdx < mesh->ne + 1 ; tetraIdx++) {
+
+    /* Testing if the tetra exists */
+    if (!mesh->tetra[tetraIdx].v[0]) continue;
+
+
+
+
+
+    iadr = 4*(tetraIdx-1) + 1;
+    adja = &mesh->adja[iadr];
+    for (i=0; i<4; i++) {
+      ballTetIdx = adja[i] >> 2;
+
+      if (!ballTetIdx) continue;
+
+
+
+
+
+
+      /* Testing if one neighbour of tetraIdx has already been added */
+      if (vertTab[vertOldTab[tetraIdx]] < 0)
+        vertTab[vertOldTab[tetraIdx]] = edgeNbr;
+      vendTab[vertOldTab[tetraIdx]] = edgeNbr+1;
+
+      /* Testing if edgeTab memory is enough */
+      if (edgeNbr >= edgeSiz) {
+        edgeSiz += EDGEGAP;
+        edgeTab = (SCOTCH_Num *)M_realloc(edgeTab, edgeSiz * sizeof(SCOTCH_Num), "renumbering");
+      }
+
+      edgeTab[edgeNbr++] = vertOldTab[ballTetIdx];
+    }
+  }
+
+  edgeNbr--;
+
+
+  /* Building the graph by calling Scotch functions */
+
+  SCOTCH_graphInit(&graf) ;
+  CHECK_SCOTCH(SCOTCH_graphBuild(&graf, (SCOTCH_Num) 1, vertNbr, vertTab+1, vendTab+1, NULL, NULL, edgeNbr, edgeTab+1, NULL), "scotch_graphbuild", 0) ;
+  CHECK_SCOTCH(SCOTCH_graphCheck(&graf), "scotch_graphcheck", 0) ;
+
+  permVrtTab = (SCOTCH_Num *)M_calloc(vertNbr + 1, sizeof(SCOTCH_Num), "renumbering");
+
+  CHECK_SCOTCH(kPartBoxCompute(graf, vertNbr, boxVertNbr, permVrtTab), "boxCompute", 0);
+
+  SCOTCH_graphExit(&graf) ;
+
+  M_free(vertTab);
+  M_free(vendTab);
+  M_free(edgeTab);
+
+
+  permNodTab = (int *)M_calloc(mesh->np + 1, sizeof(int), "renumbering");
+
+  /* Computing the new point list and modifying the sol structures*/
+  tetras = (MMG_pTetra)M_calloc(mesh->nemax+1,sizeof(MMG_Tetra),"renumbering");
+
+  points = (MMG_pPoint)M_calloc(mesh->npmax+1,sizeof(MMG_Point),"renumbering");
+
+  metNew = (double*)M_calloc(sol->npmax+1,sol->offset*sizeof(double),"renumbering");
+
+  nereal = 0;
+  npreal = 1;
+  for(tetraIdx = 1 ; tetraIdx < mesh->ne + 1 ; tetraIdx++) {
+    ptet = &mesh->tetra[tetraIdx];
+
+    /* Testing if the tetra exists */
+    if (!ptet->v[0]) continue;
+
+    /* Building the new point list */
+    tetras[permVrtTab[vertOldTab[tetraIdx]]] = *ptet;  
+    nereal++;
+
+    for(j = 0 ; j <= 3 ; j++) {
+
+      nodeGlbIdx = mesh->tetra[tetraIdx].v[j];
+
+      if (permNodTab[nodeGlbIdx]) continue;
+
+      ppt = &mesh->point[nodeGlbIdx];
+
+      if (!(ppt->tag & M_UNUSED)) {
+        /* Building the new point list */
+        permNodTab[nodeGlbIdx] = npreal++;
+
+        points[permNodTab[nodeGlbIdx]] = *ppt;  
+
+        /* Building the new sol met */
+        addrOld = (nodeGlbIdx-1)*sol->offset + 1;
+        addrNew = (permNodTab[nodeGlbIdx]-1)*sol->offset + 1;
+        memcpy(&metNew[addrNew], &sol->met[addrOld], sol->offset*sizeof(double));
+      }
+    }
+  }
+
+
+  M_free(mesh->tetra);
+  mesh->tetra = tetras;
+  mesh->ne = nereal;
+
+  M_free(mesh->point);
+  mesh->point = points;
+  mesh->np    = npreal - 1;
+
+  M_free(sol->met);
+  sol->met = metNew;
+
+  trias = (MMG_pTria)M_calloc(mesh->ntmax+1,sizeof(MMG_Tria),"renumbering");
+
+  ntreal = 1;
+  for(triaIdx = 1 ; triaIdx < mesh->nt + 1 ; triaIdx++) {
+    ptri = &mesh->tria[triaIdx];
+
+    /* Testing if the tetra exists */
+    if (!ptri->v[0]) continue;
+
+    /* Building the new point list */
+    trias[ntreal] = *ptri;  
+    ntreal++;
+  }
+
+  M_free(mesh->tria);
+  mesh->tria = trias;
+  mesh->nt = ntreal - 1;
+
+  mesh->npnil = mesh->np + 1;
+  mesh->nenil = mesh->ne + 1;
+
+  for (k=mesh->npnil; k<mesh->npmax-1; k++)
+    mesh->point[k].tmp  = k+1;
+
+  for (k=mesh->nenil; k<mesh->nemax-1; k++)
+    mesh->tetra[k].v[3] = k+1;
+
+  if ( mesh->nt ) {
+    mesh->ntnil = mesh->nt + 1;
+    for (k=mesh->ntnil; k<mesh->ntmax-1; k++)
+      mesh->tria[k].v[2] = k+1;
+  }
+
+
+
+  /* Modifying the numbering of the nodes of each tetra */
+  for(tetraIdx = 1 ; tetraIdx < mesh->ne + 1 ; tetraIdx++) {
+    if (!mesh->tetra[tetraIdx].v[0]) continue;
+    for(j = 0 ; j <= 3 ; j++) {
+      mesh->tetra[tetraIdx].v[j] = permNodTab[mesh->tetra[tetraIdx].v[j]];
+    }
+  }
+
+  /* Modifying the numbering of the nodes of each triangle */
+  for(triaIdx = 1 ; triaIdx < mesh->nt + 1 ; triaIdx++) {  
+    if (!mesh->tria[triaIdx].v[0]) continue;  
+    for(j = 0 ; j <= 2 ; j++) {
+      mesh->tria[triaIdx].v[j] = permNodTab[mesh->tria[triaIdx].v[j]];
+    } 
+  }
+
+  M_free(permVrtTab);
+
+  return 1;
+}
+#endif
\ No newline at end of file
diff --git a/contrib/mmg3d/build/sources/librnbg.h b/contrib/mmg3d/build/sources/librnbg.h
new file mode 100644
index 0000000000000000000000000000000000000000..97639a447cd5c7914b079c976f758b17d7cdf6a2
--- /dev/null
+++ b/contrib/mmg3d/build/sources/librnbg.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+/* librnbg
+*
+* Written by Cedric Lachat
+*/  
+#ifdef USE_SCOTCH
+
+#ifndef __RENUM__
+#define __RENUM__
+
+#include <scotch.h>
+
+#define HASHPRIME 37
+#define EDGEGAP 100
+
+typedef struct MeshGraphHash_ {
+ int vertNum;
+ int vertEnd;
+} MeshGraphHash;
+
+int renumbering(int vertBoxNbr, MMG_pMesh mesh, MMG_pSol sol) ;
+
+#endif /* __RENUM__ */
+#endif
\ No newline at end of file
diff --git a/contrib/mmg3d/build/sources/locate.c b/contrib/mmg3d/build/sources/locate.c
new file mode 100644
index 0000000000000000000000000000000000000000..c01826d9dc19737d19d862054338453defbf01ec
--- /dev/null
+++ b/contrib/mmg3d/build/sources/locate.c
@@ -0,0 +1,141 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define EPST    -1.e-14
+#define EPSR     1.e+14
+
+
+/* find tetra containg p, starting nsdep */
+int MMG_loctet(pMesh mesh,int nsdep,int base,double *p,double *cb) {
+  pTetra   pt;
+  pPoint   p0,p1,p2,p3;
+  double   bx,by,bz,cx,cy,cz,dx,dy,dz,vx,vy,vz,apx,apy,apz;
+  double   epsra,vol1,vol2,vol3,vol4,dd; 
+  int     *adj,iadr,it,nsfin;
+
+  it    = 0;
+  nsfin = nsdep;
+  /*printf("locateTetra: searching for %f %f %f , init %d\n",p[0],p[1],p[2],nsdep);*/
+  do {
+    if ( !nsfin )  return(0);
+    pt = &mesh->tetra[nsfin];
+    if ( !pt->v[0] )  return(0);
+    if ( pt->mark == base )  return(0);
+    pt->mark = base;
+    iadr = 4*(nsfin-1)+1;
+    adj  = &mesh->adja[iadr];
+    p0   = &mesh->point[pt->v[0]];
+    p1   = &mesh->point[pt->v[1]];
+    p2   = &mesh->point[pt->v[2]];
+    p3   = &mesh->point[pt->v[3]];
+
+    /* barycentric */
+    bx  = p1->c[0] - p0->c[0];
+    by  = p1->c[1] - p0->c[1];
+    bz  = p1->c[2] - p0->c[2];
+    cx  = p2->c[0] - p0->c[0];
+    cy  = p2->c[1] - p0->c[1];
+    cz  = p2->c[2] - p0->c[2];
+    dx  = p3->c[0] - p0->c[0];
+    dy  = p3->c[1] - p0->c[1];
+    dz  = p3->c[2] - p0->c[2];
+
+    /* test volume */
+    vx  = cy*dz - cz*dy;
+    vy  = cz*dx - cx*dz;
+    vz  = cx*dy - cy*dx;
+
+    epsra = EPST*(bx*vx + by*vy + bz*vz);
+    apx = p[0] - p0->c[0];
+    apy = p[1] - p0->c[1];
+    apz = p[2] - p0->c[2];
+
+    /* p in 2 */
+    vol2  = apx*vx + apy*vy + apz*vz;
+    if ( epsra > vol2 ) {
+      nsfin = adj[1]/4;
+      continue;
+    }
+
+    /* p in 3 */
+    vx  = by*apz - bz*apy;
+    vy  = bz*apx - bx*apz;
+    vz  = bx*apy - by*apx;
+    vol3 = dx*vx + dy*vy + dz*vz;
+    if ( epsra > vol3 ) {
+      nsfin = adj[2]/4;
+      continue;
+    }
+    
+    /* p in 4 */
+    vol4 = -cx*vx - cy*vy - cz*vz;
+    if ( epsra > vol4 ) {
+      nsfin = adj[3]/4;
+      continue;
+    }
+    
+    /* p in 1 */
+    vol1 = -epsra * EPSR - vol2 - vol3 - vol4;
+    if ( epsra > vol1 ) {
+      nsfin = adj[0]/4;
+      continue;
+    }
+
+    dd = vol1+vol2+vol3+vol4;
+    if ( dd != 0.0 )  dd = 1.0 / dd;
+    cb[0] = vol1 * dd;
+    cb[1] = vol2 * dd;
+    cb[2] = vol3 * dd;
+    cb[3] = vol4 * dd; 
+    
+    return(nsfin);
+  }
+  while ( ++it <= mesh->ne );
+
+  return(0);
+}
+
diff --git a/contrib/mmg3d/build/sources/matrix.c b/contrib/mmg3d/build/sources/matrix.c
new file mode 100644
index 0000000000000000000000000000000000000000..d09a8212ee377a8f0fe49d28427f5e4b6a82543f
--- /dev/null
+++ b/contrib/mmg3d/build/sources/matrix.c
@@ -0,0 +1,96 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define EPS3    1.e-42
+#define EPS     1e-6
+
+/* invert 3x3 symmetric matrix */
+int MMG_invmat(double *m,double *mi) {
+  double  aa,bb,cc,det,vmin,vmax,maxx;
+  int     k;
+
+  /* check diagonal matrices */
+  vmax = fabs(m[1]);
+  maxx = fabs(m[2]);
+  if( maxx > vmax ) vmax = maxx;
+  maxx = fabs(m[4]);
+  if( maxx > vmax ) vmax = maxx;
+  if ( vmax < EPS ) {
+    mi[0]  = 1./m[0];
+    mi[3]  = 1./m[3];
+    mi[5]  = 1./m[5];
+    mi[1] = mi[2] = mi[4] = 0.0;
+    return(1);
+  }
+
+  /* check ill-conditionned matrix */
+  vmin = vmax = fabs(m[0]);
+  for (k=1; k<6; k++) {
+    maxx = fabs(m[k]);
+    if ( maxx < vmin )  vmin = maxx;
+    else if ( maxx > vmax )  vmax = maxx;
+  }
+  if ( vmax == 0.0 )  return(0);
+  /* compute sub-dets */
+  aa  = m[3]*m[5] - m[4]*m[4];
+  bb  = m[4]*m[2] - m[1]*m[5];
+  cc  = m[1]*m[4] - m[2]*m[3];
+  det = m[0]*aa + m[1]*bb + m[2]*cc;
+  if ( fabs(det) < EPS3 )  return(0);
+  det = 1.0 / det;
+
+  mi[0] = aa*det;
+  mi[1] = bb*det;
+  mi[2] = cc*det;
+  mi[3] = (m[0]*m[5] - m[2]*m[2])*det;
+  mi[4] = (m[1]*m[2] - m[0]*m[4])*det;
+  mi[5] = (m[0]*m[3] - m[1]*m[1])*det;
+
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/memory.c b/contrib/mmg3d/build/sources/memory.c
new file mode 100644
index 0000000000000000000000000000000000000000..9219eb3d56542c51bfae0408bcac8cc32ba5f494
--- /dev/null
+++ b/contrib/mmg3d/build/sources/memory.c
@@ -0,0 +1,284 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+/* file    : memory.c
+ *   C code for memory debugging, to be used in lieu
+ *   of standard memory functions
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "memory.h"
+
+
+typedef struct memstack {
+  size_t   size;
+  void    *ptr;
+  int      nxt;
+  char     call[30];
+} Memstack;
+
+typedef Memstack   * pMemstack;
+
+const int  MAXMEM = 300;
+pMemstack  mstack;
+int        stack,cur;
+
+
+int M_memLeak() {
+  int   i,c=0;
+
+  for (i=1; i<=MAXMEM; i++)
+    if (mstack[i].ptr)  c++;
+  return(c);
+}
+
+/* print out allocated pointers */
+void M_memDump() {
+  size_t  size;
+  int     i,c;
+  static long mega = 1024 * 1024;
+  static long kilo = 1024;
+
+  fprintf(stdout,"\n  -- MEMORY USAGE\n");
+  fprintf(stdout,"  Allocated pointers\n");
+  size = 0;
+  c    = 0;
+  for (i=1; i<=MAXMEM; i++)
+    if ( mstack[i].ptr ) {
+      fprintf(stdout,"   %3d  %3d Pointer %10p  size ",++c,i,mstack[i].ptr);
+      if (mstack[i].size > mega)
+        fprintf(stdout,"   %10d Mbytes  ",(int)(mstack[i].size/mega));
+      else if (mstack[i].size > kilo)
+        fprintf(stdout,"   %10d Kbytes  ",(int)(mstack[i].size/kilo));
+      else 
+        fprintf(stdout,"   %10d  bytes  ",(int)(mstack[i].size));
+      fprintf(stdout,"(%s)\n",mstack[i].call);
+      size += mstack[i].size;
+    }
+  fprintf(stdout,"  Memory leaks    ");
+  if ( size > mega )
+    fprintf(stdout,"  %10d Mbytes  %d pointers\n",(int)(size/mega),c);
+  else if ( size > kilo )
+    fprintf(stdout,"  %10d Kbytes  %d pointers\n",(int)(size/kilo),c);
+  else if ( size )
+    fprintf(stdout,"  %10d bytes   %d pointers\n",(int)size,c);
+}
+
+/* Returns allocated memory space in bytes */
+size_t M_memSize() {
+  size_t size;
+  int    i;
+
+  size = 0;
+  for (i=1; i<=MAXMEM; i++)
+    if ( mstack[i].ptr )
+      size += mstack[i].size;
+  return size;
+}
+
+/* Allocates space for a block of at least size bytes,
+   but does not initialize the space. */
+void *M_malloc(size_t size,char *call) {
+  int   i;
+
+  /* check if first call */
+  if ( !mstack ) {
+    mstack = (Memstack *)calloc((1+MAXMEM),sizeof(Memstack));
+    assert(mstack);
+    for (i=1; i<MAXMEM; i++)
+      mstack[i].nxt    = i+1;
+    cur   = 1;
+    stack = 0;
+  }
+
+  /* store pointer, size */
+  if ( stack < MAXMEM ) {
+    mstack[cur].ptr  = malloc(size);
+    assert(mstack[cur].ptr);
+    mstack[cur].size = size;
+    /* i.e. mstack[cur].call = strdup(call) */
+    /* mstack[cur].call = (char*)malloc((strlen(call)+1) * sizeof(char));
+    assert(mstack[cur].call); */
+    strncpy(mstack[cur].call,call,19);
+    i = cur;
+    cur = mstack[cur].nxt;
+    ++stack;
+#ifdef MEMDEBUG
+    fprintf(stdout,"M_malloc: allocate %p of size %10d         (%g,%d)\n",
+	    mstack[cur].ptr,size,stack,cur);
+#endif
+    return(mstack[i].ptr);
+  }
+  else {
+    fprintf(stderr,"M_malloc: unable to store %10Zd bytes pointer. table full\n",
+	    size);
+    return(0);
+  }
+}
+
+
+/* Allocates space for an array of nelem elements, each of size 
+   elsize bytes, and initializes the space to zeros.  
+   Actual amount of space allocated is >=  nelem * elsize bytes. */
+void *M_calloc(size_t nelem, size_t elsize,char *call) {
+  int    i;
+
+  /* check if first call */
+  if ( !mstack ) {
+    mstack = (Memstack *)calloc((1+MAXMEM),sizeof(Memstack));
+    assert(mstack);
+    for (i=1; i<MAXMEM; i++)
+      mstack[i].nxt    = i+1;
+    cur   = 1;
+    stack = 0;
+  }
+
+  /* store pointer, size */
+  if ( stack < MAXMEM ) {
+    mstack[cur].ptr  = calloc(nelem,elsize);
+    if ( !mstack[cur].ptr )  return(0);
+
+    /*assert(mstack[cur].ptr);*/
+    mstack[cur].size = nelem * elsize;
+    /* mstack[cur].call = (char*)malloc((strlen(call)+1) * sizeof(char));
+    assert(mstack[cur].call); */
+    strncpy(mstack[cur].call,call,19);
+    i   = cur;
+    cur = mstack[cur].nxt;
+    ++stack;
+#ifdef MEMDEBUG
+    fprintf(stdout,"M_calloc: allocate %p of size %d         (%d,%d)\n",
+	    mstack[cur].ptr,nelem*elsize,stack,cur);
+#endif
+    return(mstack[i].ptr);
+  }
+  else {
+    fprintf(stderr,"M_calloc: unable to allocate %10Zd bytes. table full\n",
+	    nelem*elsize);
+    return(0);
+  }
+}
+
+/* Changes the size of the block pointed to by ptr to size bytes 
+   and returns a pointer to the (possibly moved) block. Existing 
+   contents are unchanged up to the lesser of the new and old sizes. */
+void *M_realloc(void *ptr, size_t size,char *call) {
+  int    i;
+
+  if ( !ptr )
+    return 0;
+
+  for (i=1; i<=MAXMEM; i++) {
+    if (ptr == mstack[i].ptr) {
+      /* free(mstack[i].call);
+      mstack[cur].call = (char*)malloc((strlen(call)+1) * sizeof(char));
+      assert(mstack[cur].call); */
+      strncpy(mstack[i].call,call,19);
+      mstack[i].ptr = realloc(mstack[i].ptr,size);
+      if (size)
+	assert(mstack[i].ptr);
+      mstack[i].size = size;
+#ifdef MEMDEBUG
+      fprintf(stdout,"M_realloc: reallocate %p of size %d       (%d)\n",
+	      mstack[i].ptr,mstack[i].size,size);
+#endif
+      return(mstack[i].ptr);
+    }
+  }
+#ifdef MEMDEBUG
+  fprintf(stderr,"M_realloc: pointer %p not found\n",ptr);
+#endif
+  return(0);
+}
+
+/* Deallocates the space pointed to by ptr (a pointer to a block 
+   previously allocated by malloc() and makes the space available
+   for further allocation.  If ptr is NULL, no action occurs. */
+void M_free(void *ptr) {
+  int   i;
+  
+  assert(ptr);
+  for (i=1; i<=MAXMEM; i++) {
+    if (mstack[i].ptr && ptr == mstack[i].ptr) {
+      --stack;
+      free(mstack[i].ptr);
+      mstack[i].ptr  = 0;
+      mstack[i].size = 0;
+      mstack[i].nxt  = cur;
+      mstack[i].call[0]  = '\0';
+      cur = i;
+#ifdef MEMDEBUG
+      fprintf(stdout,"M_free: deallocate %p of size %d       (%d,%d)\n",
+	      ptr,mstack[i].size,stack,cur);
+#endif
+      return;
+    }
+  }
+#ifdef MEMDEBUG
+  fprintf(stderr,"M_free: pointer %p not found\n",ptr);
+#endif
+}
+
+
+/* dump memory requirements */
+void primem(int np) {
+  int memsize;
+
+  memsize = M_memSize();
+  if ( memsize ) {
+    fprintf(stdout,"\n  -- MEMORY REQUIREMENTS\n");
+    if (memsize > 1024*1024)
+      fprintf(stdout,"  Total size :  %10Zd Mbytes",
+	      (long int)(memsize/(1024.*1024.)));
+    else if (memsize > 1024)
+      fprintf(stdout,"  Total size :  %10Zd Kbytes",(long int)(memsize/1024.));
+    else
+      fprintf(stdout,"  Total size :  %10Zd bytes ",(long int)memsize);
+    fprintf(stdout,"    (i.e. %d bytes/point)\n",memsize / np);
+  }
+}
diff --git a/contrib/mmg3d/build/sources/memory.h b/contrib/mmg3d/build/sources/memory.h
new file mode 100644
index 0000000000000000000000000000000000000000..c804eb8708f3538e38eae8a84311251c15a0126e
--- /dev/null
+++ b/contrib/mmg3d/build/sources/memory.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile,
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite,
+y compris les garanties de commercialisation ou
+d’adaptation dans un but spécifique.
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU
+en même temps que ce document.
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+****************************************************************************/
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+spread under the terms and conditions of the license GNU General Public License
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.
+****************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+
+/* prototype (re)definitions */
+void  *M_malloc(size_t size,char *call);
+void  *M_calloc(size_t nelem,size_t elsize,char *call);
+void  *M_realloc(void *ptr, size_t size,char *call);
+void   M_free(void *ptr);
+
+/* ptototypes : tools */
+int    M_memLeak();
+void   M_memDump();
+size_t M_memSize();
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/contrib/mmg3d/build/sources/mesh.h b/contrib/mmg3d/build/sources/mesh.h
new file mode 100644
index 0000000000000000000000000000000000000000..9324545c23121c34656e447d032968130ebd3329
--- /dev/null
+++ b/contrib/mmg3d/build/sources/mesh.h
@@ -0,0 +1,414 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#ifndef _MMG3D_H
+#define _MMG3D_H
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <float.h>
+
+
+#include "chrono.h"
+#include "memory.h"
+
+#include "libmmg3d.h"
+#include "libmmg3d_internal.h"
+#include "mmg3dConfig.h"
+#include "eigenv.h"
+
+#define M_VER "4.0 c"
+#define M_REL "July 20, 2010"
+#define M_STR "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"
+
+#define EPS      1.e-06
+#define EPS1     1.e-9
+#define EPS2     1.e-12
+#define EPSOK    1.e-18
+#define EPS30    1.e-30
+
+#define ALPHAC   0.20412415      /* sqrt(6)/12 */  
+#define ALPHAD   0.04811252      /* 1.0/(12*sqrt(3)) */
+#define BETAC    0.03928371      /* sqrt(2)/36 */   
+#define CALLIM   1.E+35    	    /*valeur de la qual pire*/
+
+
+#define LLONG    1.41
+#define LSHORT   0.68
+#define LFILT    0.7
+#define QDEGRAD  2.45
+
+#define LONMAX     4096//512
+#define NPMAX    500000
+#define NTMAX   1000000
+#define NEMAX   3000000
+
+#define PRECI       1
+#define BUCKSIZ    64
+
+#define M_MIN(a,b) ( (a) < (b) ? (a) : (b) )
+#define M_MAX(a,b) ( (a) < (b) ? (b) : (a) )
+
+#define M_NOTAG     (0)
+#define M_UNUSED    (1 << 0)
+#define M_BDRY      (1 << 1)
+#define M_MOVE      (1 << 2)
+#define M_CAVITY    (1 << 3)
+#define M_CORNER    (1 << 4)
+#define M_REQUIRED  (1 << 5)
+#define M_RIDGE_GEO (1 << 6)
+#define M_RIDGE_REF (1 << 7)
+#define ALL_BDRY   63
+
+/*#ifdef INT_MAX
+#undef INT_MAX
+#undef SHORT_MAX
+#endif
+*/
+#ifndef INT_MAX
+#define INT_MAX      0x7fffffff
+#endif
+#define SHORT_MAX    0x7fff
+
+
+extern unsigned char MMG_idir[4][3];
+extern unsigned char MMG_inxt[7];
+extern unsigned char MMG_iarf[4][3];
+extern unsigned char MMG_iare[6][2];
+extern unsigned char MMG_ifar[6][2];
+extern unsigned char MMG_isar[6][2];
+extern unsigned char MMG_arpt[4][3];
+                             
+
+typedef struct {
+  int      min,max,iel,nxt;
+} hedge;
+typedef struct {
+  int      size,nhmax,hnxt;
+  hedge   *item;  
+} Hedge;
+typedef Hedge * pHedge;
+
+typedef struct {
+  int     *blay,*blref,nblay;
+} Blayer;
+
+
+typedef struct slist {
+  Hedge    hedg;
+  double    qual[LONMAX+1];
+  int      tetra[LONMAX+1];
+} List;
+typedef List * pList;
+
+typedef struct squeue {
+  int    *stack,cur;
+} Queue;
+typedef Queue * pQueue;
+
+typedef struct {
+  int     size,curc;
+  int    *cell;
+  int    *link;
+} Heap;
+typedef Heap * pHeap;
+
+typedef struct {
+  int     size;
+  int    *head;
+  int    *link;
+} Bucket;
+typedef Bucket * pBucket;
+
+/*basic*/
+int MMG_setfunc(int );
+
+int MMG_cutprism(pMesh mesh,pHedge hed,int k,int p0,int p1,int p2,int p3,int p4,int p5,int ref);
+int MMG_cuthex(pMesh mesh,pHedge hed,int k,int p0,int p1,int p2,int p3,int p4,int p5,int p6,int p7, int ref);
+
+int MMG_analar(pMesh ,pSol ,pBucket ,int *,int *,int *,int *);
+int MMG_analarcutting(pMesh ,pSol ,pHedge ,int *,double* ,double );
+int MMG_boulep(pMesh ,int ,int ,pList );
+int MMG_bouleg(pMesh ,int ,int ,pList );
+int MMG_coquil(pMesh ,int ,int ,pList );
+int MMG_cendel(pMesh ,pSol ,double ,int );
+int MMG_spledg(pMesh ,pSol ,pQueue ,pList ,int ,double ,double );
+
+int MMG_interp_ani(double *,double *,double *,double );
+int MMG_interplog(double *,double *,double *,double *,double );
+int MMG_interp_iso(double *,double *,double *,double );
+
+/* delaunay */
+int MMG_correction(pMesh ,int ,pList ,int ,int ,char );
+int MMG_delone(pMesh ,pSol ,int ,pList ,int );
+int MMG_delons(pMesh ,pSol ,pQueue ,int ,pList ,int ,double );
+int MMG_cenrad_ani(pMesh ,double * ,double *,double *,double *); 
+int MMG_cenrad_iso(pMesh ,double * ,double *,double *); 
+
+/*pattern*/
+int MMG_pattern1(pMesh ,pSol ,pHedge ,int );
+int MMG_pattern2(pMesh ,pSol ,pHedge ,int );
+int MMG_pattern3(pMesh ,pSol ,pHedge ,int );
+int MMG_pattern4(pMesh ,pSol ,pHedge ,int );
+int MMG_pattern5(pMesh ,pSol ,pHedge ,int );
+int MMG_pattern6(pMesh ,pSol ,int ,int* );
+int MMG_pattern22(pMesh ,pSol ,pHedge ,int );
+int MMG_pattern31(pMesh ,pSol ,pHedge ,int );
+int MMG_pattern32(pMesh ,pSol ,pHedge ,int );
+int MMG_pattern33(pMesh ,pSol ,pHedge ,int );
+int MMG_pattern41(pMesh ,pSol ,pHedge ,int );
+
+int MMG_colpoi(pMesh ,pSol ,int ,int ,int ,double );
+
+/* hash */
+int  MMG_hashTetra(pMesh );
+int  MMG_hashEdge(pMesh ,pHedge ,int ,int ,int *);
+int  MMG_inEdge(pHedge ,int *,int *,int *);
+int  MMG_markBdry(pMesh );
+int  MMG_edgePoint(pHedge ,int ,int );
+int  MMG_edgePut(pHedge ,int ,int ,int );
+
+
+int  MMG_loctet(pMesh ,int ,int ,double *,double *);
+int  MMG_computeMetric(pMesh ,pSol ,int ,double * );
+
+/* scale */
+int  MMG_doSol(pMesh ,pSol );
+int  MMG_scaleMesh(pMesh ,pSol );
+int  MMG_unscaleMesh(pMesh ,pSol );
+
+int  MMG_mmg3d1(pMesh ,pSol ,int *);
+int  MMG_mmg3d9(pMesh ,pSol ,int *);
+int  MMG_mmg3d4(pMesh ,pSol ,int *);
+
+/* zaldy */
+int  MMG_newPt(pMesh ,double *c);
+int  MMG_newElt(pMesh );
+int  MMG_getnElt(pMesh ,int );
+int  MMG_newTria(pMesh );
+void MMG_delPt(pMesh ,int );
+void MMG_delElt(pMesh ,int );
+void MMG_delTria(pMesh ,int );
+int  MMG_zaldy(pMesh );
+int  MMG_zaldy3(pSol );
+int  MMG_zaldy4(pHedge ,int );
+
+int  MMG_optra4(pMesh ,pSol );
+int  MMG_optcoq(pMesh ,pSol);
+int  MMG_opttet(pMesh ,pSol);
+int  MMG_opttyp(pMesh ,pSol ,double ,int *);
+int  MMG_optbdry(pMesh ,pSol ,int );
+int  MMG_opt2peau(pMesh ,pSol ,pQueue ,int ,double );
+int  MMG_optlap(pMesh ,pSol );
+
+/* swapar */
+int  MMG_swapar(pMesh ,pSol ,pQueue ,List *,int ,double ,double );
+
+int  MMG_simu23(pMesh ,pSol ,int ,int ,double );
+int  MMG_simu32(pMesh ,pSol ,pList ,double );
+int  MMG_swap32(pMesh ,pSol ,pList );
+int  MMG_swap23(pMesh ,pSol ,pQueue ,int ,int ,double );
+
+int  MMG_simu44(pMesh ,pSol ,pList ,double );
+int  MMG_swap44_1(pMesh ,pSol ,pList );
+int  MMG_swap44_2(pMesh ,pSol ,pList );
+
+int  MMG_simu56(pMesh ,pSol ,pList ,double );
+int  MMG_swap56_1(pMesh ,pSol ,pList );
+int  MMG_swap56_2(pMesh ,pSol ,pList );
+int  MMG_swap56_3(pMesh ,pSol ,pList );
+int  MMG_swap56_4(pMesh ,pSol ,pList );
+int  MMG_swap56_5(pMesh ,pSol ,pList );
+
+int MMG_simu68(pMesh ,pSol ,pList ,double );
+int MMG_swap68_1(pMesh ,pSol ,pList );
+int MMG_swap68_2(pMesh ,pSol ,pList );
+int MMG_swap68_3(pMesh ,pSol ,pList );
+int MMG_swap68_4(pMesh ,pSol ,pList );
+int MMG_swap68_5(pMesh ,pSol ,pList );
+int MMG_swap68_6(pMesh ,pSol ,pList );
+int MMG_swap68_7(pMesh ,pSol ,pList );
+int MMG_swap68_8(pMesh ,pSol ,pList );
+int MMG_swap68_9(pMesh ,pSol ,pList );
+int MMG_swap68_10(pMesh ,pSol ,pList );
+int MMG_swap68_11(pMesh ,pSol ,pList );
+int MMG_swap68_12(pMesh ,pSol ,pList );
+int MMG_swap68_13(pMesh ,pSol ,pList );
+int MMG_swap68_14(pMesh ,pSol ,pList );
+
+int MMG_simu710(pMesh ,pSol ,pList ,double );
+int MMG_swap710_1(pMesh ,pSol ,pList );
+int MMG_swap710_2(pMesh ,pSol ,pList );
+int MMG_swap710_3(pMesh ,pSol ,pList );
+int MMG_swap710_4(pMesh ,pSol ,pList );
+int MMG_swap710_5(pMesh ,pSol ,pList );
+int MMG_swap710_6(pMesh ,pSol ,pList );
+int MMG_swap710_7(pMesh ,pSol ,pList );
+int MMG_swap710_8(pMesh ,pSol ,pList );
+int MMG_swap710_9(pMesh ,pSol ,pList );
+int MMG_swap710_10(pMesh ,pSol ,pList );
+int MMG_swap710_11(pMesh ,pSol ,pList );
+int MMG_swap710_12(pMesh ,pSol ,pList );
+int MMG_swap710_13(pMesh ,pSol ,pList );
+int MMG_swap710_14(pMesh ,pSol ,pList );
+int MMG_swap710_15(pMesh ,pSol ,pList );
+int MMG_swap710_16(pMesh ,pSol ,pList );
+int MMG_swap710_17(pMesh ,pSol ,pList );
+int MMG_swap710_18(pMesh ,pSol ,pList );
+int MMG_swap710_19(pMesh ,pSol ,pList );
+int MMG_swap710_20(pMesh ,pSol ,pList );
+int MMG_swap710_21(pMesh ,pSol ,pList );
+int MMG_swap710_22(pMesh ,pSol ,pList );
+int MMG_swap710_23(pMesh ,pSol ,pList );
+int MMG_swap710_24(pMesh ,pSol ,pList );
+int MMG_swap710_25(pMesh ,pSol ,pList );
+int MMG_swap710_26(pMesh ,pSol ,pList );
+int MMG_swap710_27(pMesh ,pSol ,pList );
+int MMG_swap710_28(pMesh ,pSol ,pList );
+int MMG_swap710_29(pMesh ,pSol ,pList );
+int MMG_swap710_30(pMesh ,pSol ,pList );
+int MMG_swap710_31(pMesh ,pSol ,pList );
+int MMG_swap710_32(pMesh ,pSol ,pList );
+int MMG_swap710_33(pMesh ,pSol ,pList );
+int MMG_swap710_34(pMesh ,pSol ,pList );
+int MMG_swap710_35(pMesh ,pSol ,pList );
+int MMG_swap710_36(pMesh ,pSol ,pList );
+int MMG_swap710_37(pMesh ,pSol ,pList );
+int MMG_swap710_38(pMesh ,pSol ,pList );
+int MMG_swap710_39(pMesh ,pSol ,pList );
+int MMG_swap710_40(pMesh ,pSol ,pList );
+int MMG_swap710_41(pMesh ,pSol ,pList );
+int MMG_swap710_42(pMesh ,pSol ,pList );
+
+int  MMG_typelt(pMesh ,int ,int *);
+
+/* quality */
+double MMG_voltet(pMesh ,int );
+double MMG_quickvol(double *,double *,double *,double *);
+int    MMG_prilen(pMesh ,pSol );
+int    MMG_outqua(pMesh ,pSol );
+int    MMG_outquacubic(pMesh ,pSol );
+double MMG_priworst(pMesh , pSol );
+int MMG_ratio(pMesh mesh, pSol sol,char* filename);
+
+int  MMG_chkmsh(pMesh ,int ,int );
+
+/* bucket */
+pBucket MMG_newBucket(pMesh ,int );
+int     MMG_addBucket(pMesh ,pBucket ,int );
+int     MMG_delBucket(pMesh ,pBucket ,int );
+void    MMG_freeBucket(pBucket );
+
+/* heap */
+Heap *MMG_hipini(pMesh ,int ,short ,double ,int );
+void  MMG_hipfree(Heap *);
+int   MMG_hipput(pMesh ,Heap *,int );
+int   MMG_hippop(pMesh ,Heap *);
+void  MMG_hiprep(pMesh ,Heap *,int );
+void  MMG_hipdel(pMesh ,Heap *,int );
+
+/* queue */
+pQueue MMG_kiuini(pMesh ,int ,double ,int );
+int    MMG_kiupop(pQueue );
+int    MMG_kiudel(pQueue ,int );
+int    MMG_kiuput(pQueue ,int );
+void   MMG_kiufree(pQueue );
+
+/* matrices */
+int MMG_invmat(double *,double *);
+
+double MMG_calte3_ani(pMesh mesh,pSol sol,int iel);
+
+/* function pointers */
+double MMG_long_ani(double *,double *,double *,double *);
+double MMG_long_iso(double *,double *,double *,double *);
+double MMG_caltetcubic(pMesh mesh,pSol sol,int iel);
+double MMG_caltetrao(pMesh mesh,pSol sol,int iel);
+double MMG_caltet_ani(pMesh mesh,pSol sol,int iel);
+double MMG_caltet_iso(pMesh mesh,pSol sol,int iel);
+double MMG_calte1_iso(pMesh mesh,pSol sol,int iel);
+double MMG_calte1_ani(pMesh mesh,pSol sol,int iel);
+double MMG_callong(pMesh mesh,pSol sol,int iel);
+int    MMG_caltet2_iso(pMesh mesh,pSol sol,int iel,int ie,double ,double * caltab);
+int    MMG_caltet2_ani(pMesh mesh,pSol sol,int iel,int ie,double ,double * caltab);
+int    MMG_caltet2long_ani(pMesh mesh,pSol sol,int iel,int ie,double crit, double * caltab);
+int    MMG_caltet2long_iso(pMesh mesh,pSol sol,int iel,int ie,double crit, double * caltab);
+int    MMG_buckin_ani(pMesh mesh,pSol sol,pBucket bucket,int ip);
+int    MMG_buckin_iso(pMesh mesh,pSol sol,pBucket bucket,int ip);
+int    MMG_cavity_ani(pMesh mesh,pSol sol,int iel,int ip,pList list,int lon);
+int    MMG_cavity_iso(pMesh mesh,pSol sol,int iel,int ip,pList list,int lon);
+int    MMG_optlen_ani(pMesh mesh,pSol sol,double declic,int base);
+int    MMG_optlen_iso(pMesh mesh,pSol sol,double declic,int base);
+int    MMG_optlen_iso_new(pMesh mesh,pSol sol,double declic,int base);
+int    MMG_optlen_iso2(pMesh mesh,pSol sol,double declic);
+int    MMG_interp_ani(double *,double *,double * ,double );
+int    MMG_interp_iso(double *,double *,double * ,double );
+int    MMG_optlentet_ani(pMesh ,pSol ,pQueue ,double ,int ,int );
+int    MMG_optlentet_iso(pMesh ,pSol ,pQueue ,double ,int ,int );
+int    MMG_movevertex_ani(pMesh ,pSol ,int ,int );
+int    MMG_movevertex_iso(pMesh ,pSol ,int ,int );
+
+
+/* function pointers */
+typedef int (*MMG_Swap)(pMesh ,pSol ,pList );
+MMG_Swap MMG_swpptr;
+double (*MMG_length)(double *,double *,double *,double *);
+double (*MMG_caltet)(pMesh ,pSol ,int );
+double (*MMG_calte1)(pMesh ,pSol ,int );
+int    (*MMG_caltet2)(pMesh ,pSol ,int ,int ,double ,double *);
+int    (*MMG_cavity)(pMesh ,pSol ,int ,int ,pList ,int );
+int    (*MMG_buckin)(pMesh ,pSol ,pBucket ,int );
+int    (*MMG_optlen)(pMesh ,pSol ,double ,int );
+int    (*MMG_interp)(double *,double *,double *,double );
+int    (*MMG_optlentet)(pMesh ,pSol ,pQueue ,double ,int ,int );
+int    (*MMG_movevertex)(pMesh ,pSol ,int ,int );
+
+
+#endif
diff --git a/contrib/mmg3d/build/sources/mmg3d.c b/contrib/mmg3d/build/sources/mmg3d.c
new file mode 100644
index 0000000000000000000000000000000000000000..b394a38a75df6dcadcc5d3c0da8e27d0cb6f3c9a
--- /dev/null
+++ b/contrib/mmg3d/build/sources/mmg3d.c
@@ -0,0 +1,701 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "compil.date"
+#include "mesh.h"
+#include "eigenv.h"
+
+TIM_mytime         MMG_ctim[TIMEMAX];
+short	             MMG_imprim;
+
+unsigned char MMG_idir[4][3] = { {1,2,3}, {0,3,2}, {0,1,3}, {0,2,1} };
+unsigned char MMG_inxt[7]    = { 1,2,3,0,1,2,3 };
+unsigned char MMG_iarf[4][3] = { {5,4,3}, {5,1,2}, {4,2,0}, {3,0,1} };
+unsigned char MMG_iare[6][2] = { {0,1}, {0,2}, {0,3}, {1,2}, {1,3}, {2,3} };
+unsigned char MMG_ifar[6][2] = { {2,3}, {1,3}, {1,2}, {0,3}, {0,2}, {0,1} };
+unsigned char MMG_isar[6][2] = { {2,3}, {3,1}, {1,2}, {0,3}, {2,0}, {0,1} };
+unsigned char MMG_arpt[4][3] = { {0,1,2}, {0,4,3}, {1,3,5}, {2,5,4} };
+
+static void excfun(int sigid) {
+  switch (sigid) {
+  case SIGFPE:
+    fprintf(stderr,"  ## FP EXCEPTION. STOP\n");
+    break;
+  case SIGILL:
+    fprintf(stderr,"  ## ILLEGAL INSTRUCTION. STOP\n");
+    break;
+  case SIGSEGV:
+    fprintf(stderr,"  ## SEGMENTATION FAULT. STOP\n");
+    break;
+  case SIGABRT:
+  case SIGTERM:
+  case SIGINT:
+    fprintf(stderr,"  ## ABNORMAL END. STOP\n");
+    break;
+  }
+  exit(1);
+}
+
+
+static void usage(char *prog) {
+  fprintf(stdout,"usage: %s [-v[n]] [-h] [-m n] [opts..] filein[.mesh] [-out fileout]\n",prog);
+  
+  fprintf(stdout,"\n** Generic options :\n");
+  fprintf(stdout,"-d      Turn on debug mode\n");
+  fprintf(stdout,"-h      Print this message\n");
+  fprintf(stdout,"-v [n]  Tune level of verbosity\n");
+  fprintf(stdout,"-m [n]  Set memory size to n Mbytes\n");
+
+  fprintf(stdout,"\n");
+  fprintf(stdout,"-O [n]  Optimization level\n");
+  fprintf(stdout,"  1      adaptation\n");
+  fprintf(stdout,"  4      use global splitting (warning modify boundary mesh)\n");
+  fprintf(stdout,"  9      moving mesh\n");
+  fprintf(stdout,"  10     transform an hexahedral/prism mesh in a tetrahedral mesh \n");
+  fprintf(stdout," -n      turn off optimisation\n");
+
+  fprintf(stdout,"\n** Misc. options\n");
+  fprintf(stdout,"-bucket [n]    Specify the size of bucket per dimension\n");
+  fprintf(stdout,"-noswap        no edge or face flipping\n");
+  fprintf(stdout,"-nomove        no point relocation\n");
+  fprintf(stdout,"-noinsert      no new point\n");
+  //fprintf(stdout,"-bdry          add points on boundary mesh\n");
+  fprintf(stdout,"-out fileout   Specify output file name\n");  
+#ifdef USE_SCOTCH
+  fprintf(stdout,"-rn n num         Specify the number of vertices by box to renumber nodes and the renumberings\n");
+#endif
+  fprintf(stdout,"-dt dt         to compute the node speed\n");
+
+  exit(1);
+}
+
+
+static int parsar(int argc,char *argv[],pMesh mesh,pSol sol) {
+  int      	i;
+  Info     	*info;
+  char    	*ptr;
+
+  info = &mesh->info;
+
+  i = 1;
+  while ( i < argc ) {
+    if ( *argv[i] == '-' ) {
+      switch(argv[i][1]) {
+      case 'h':  /* on-line help */
+      case '?':
+	      usage(argv[0]);
+	      break;
+
+      case 'b':
+	      if ( !strcmp(argv[i],"-bucket") && i < argc-1 ) {
+	        ++i;
+	        if ( isdigit(argv[i][0]) )
+	          info->bucksiz = atoi(argv[i]);
+	        else
+	          i--;
+	      }
+	      else if ( !strcmp(argv[i],"-bdry") ){  
+          printf("-bdry option discarded\n");
+  	      //info->bdry = 1;
+	      }
+	      else {
+  	      fprintf(stderr,"Missing argument option %s\n",argv[i]);
+	        usage(argv[0]);
+	      }
+        break;
+      
+      case 'd':  /* debug */
+        if ( !strcmp(argv[i],"-dt") ) {
+          ++i;
+          info->dt = atof(argv[i]);
+        } else {
+          info->ddebug = 1;
+        }
+        break;
+
+      case 'i':
+        if ( !strcmp(argv[i],"-in") ) {
+          ++i;
+          mesh->name = argv[i];
+          info->imprim     = 5;
+        }
+        break;
+
+      case 'm':  /* memory */
+	if ( !strcmp(argv[i],"-mov") ) {
+          ++i;
+          mesh->move = argv[i];
+        }
+        else if ( ++i < argc ) {
+	  if ( isdigit(argv[i][0]) )
+	    info->memory = atoi(argv[i]);
+	  else
+	    i--;
+	}
+	else {
+	  fprintf(stderr,"Missing argument option %c\n",argv[i-1][1]);
+	  usage(argv[0]);
+	}
+        break;
+
+      case 'n':
+        if ( !strcmp(argv[i],"-noswap") )
+	  info->noswap = 1;
+	else if( !strcmp(argv[i],"-noinsert") )
+	  info->noinsert = 1;
+	else if( !strcmp(argv[i],"-nomove") )
+	  info->nomove = 1;
+        break;
+
+      case 'o':
+        if ( !strcmp(argv[i],"-out") ) {
+          ++i;
+          mesh->outf = argv[i];
+        }
+        break;
+
+      case 'O':  /* option */
+        if ( ++i < argc ) {
+	  if ( (argv[i][0] == '-' && isdigit(argv[i][1])) ||
+	        argv[i][0] == '0' )
+	    info->option = atoi(argv[i]);
+	  else if ( isdigit(argv[i][0]) )
+	    info->option = atoi(argv[i]);
+	  else
+	    i--;
+	}
+        break;
+
+      case 's':
+        if ( !strcmp(argv[i],"-sol") ) {
+          ++i;
+          sol->name = argv[i];
+        }
+        break; 
+#ifdef USE_SCOTCH
+ /* renumbering begin */
+      case 'r':
+        if ( !strcmp(argv[i],"-rn") ) {
+          if ( ++i < argc ) {
+            if ( isdigit(argv[i][0]) )
+              info->rn = atoi(argv[i]);
+            else {
+              fprintf(stderr,"Missing argument option %s\n",argv[i-1]);
+              usage(argv[0]);
+            }
+            if ( ++i < argc ) {
+              if ( isdigit(argv[i][0]) ) {
+                info->rn2 = atoi(argv[i]);
+                if (! ((info->rn2>=0) && (info->rn2<=3))){
+                  fprintf(stderr,"Wrong argument option %s\n",argv[i-1]);
+                  usage(argv[0]);
+                }
+              }
+              else {
+                fprintf(stderr,"Missing argument option %s\n",argv[i-1]);
+                usage(argv[0]);
+              }
+            }
+            else {
+              fprintf(stderr,"Missing argument option %s\n",argv[i-1]);
+              usage(argv[0]);
+            }
+          }
+          else {
+            fprintf(stderr,"Missing argument option %s\n",argv[i-1]);
+            usage(argv[0]);
+          }
+        }
+        break;
+/* renumbering end */ 
+#endif
+      case 'v':
+        if ( ++i < argc ) {
+	  if ( argv[i][0] == '-' || isdigit(argv[i][0]) )
+	    info->imprim = atoi(argv[i]);
+	  else 
+	    i--;
+	}
+	else {
+	  fprintf(stderr,"Missing argument option %c\n",argv[i-1][1]);
+	  usage(argv[0]);
+	}
+	break;
+
+      default:
+	fprintf(stderr,"  Unrecognized option %s\n",argv[i]);
+	usage(argv[0]);
+      }
+    }
+
+    else {
+      if ( mesh->name == NULL ) {
+        mesh->name = argv[i];
+        info->imprim = 5;
+      }
+      else if ( mesh->outf == NULL )
+        mesh->outf = argv[i];
+      else if ( mesh->move == NULL )
+        mesh->move = argv[i];
+      else {
+        fprintf(stdout,"  Argument %s ignored\n",argv[i]);
+	      usage(argv[0]);
+      }
+    }
+    i++;
+  }
+
+  /* check file names */
+  if ( mesh->name == NULL || info->imprim == -99 ) {
+    fprintf(stdout,"\n  -- PRINT (0 10(advised) -10) ?\n");
+    fflush(stdin);
+    fscanf(stdin,"%d",&i);
+    info->imprim = i;
+  }
+
+  if ( mesh->name == NULL ) {
+    mesh->name = (char *)calloc(128,sizeof(char));
+    assert(mesh->name);
+    fprintf(stdout,"  -- FILE BASENAME ?\n");
+    fflush(stdin); 
+    fscanf(stdin,"%s",mesh->name);
+  }
+  if ( sol->name == NULL ) {
+    sol->name = (char *)calloc(128,sizeof(char));
+    assert(sol->name);
+    strcpy(sol->name,mesh->name);
+  }
+  if ( mesh->outf == NULL ) {
+    mesh->outf = (char *)calloc(128,sizeof(char));
+    assert(mesh->outf);
+    strcpy(mesh->outf,mesh->name);
+    ptr = strstr(mesh->outf,".mesh");
+    if ( ptr ) *ptr = '\0';
+    strcat(mesh->outf,".o.meshb");
+  }
+  if ( abs(info->option) == 9 && mesh->move == NULL ) {
+    mesh->move = (char *)calloc(128,sizeof(char));
+    assert(mesh->move);
+    fprintf(stdout,"  -- DISPLACEMENT FILE ?\n");
+    fflush(stdin); 
+    fscanf(stdin,"%s",mesh->move);
+  }
+
+  return(1);
+}
+ 
+
+int parsop(pMesh mesh) {
+  int      i,ret;
+  char    *ptr,data[256];
+  FILE    *in;
+
+  strcpy(data,mesh->name);
+  ptr = strstr(data,".mesh");
+  if ( ptr )  *ptr = '\0';
+  strcat(data,".mmg");
+  in = fopen(data,"r");
+  if ( !in ) {
+    sprintf(data,"%s","DEFAULT.mmg");
+    in = fopen(data,"r");
+    if ( !in )  return(1);
+  }
+  fprintf(stdout,"  %%%% %s OPENED\n",data);
+
+  while ( !feof(in) ) {
+    ret = fscanf(in,"%s",data);
+    if ( !ret || feof(in) )  break;
+    for (i=0; i<strlen(data); i++) data[i] = tolower(data[i]);
+/*
+    if ( !strcmp(data,"blayer") ) {
+      fscanf(in,"%d",&dummi);
+      if ( dummi ) {
+        mesh->blayer.nblay = dummi;
+        mesh->blayer.blay  = (int*)calloc(dummi+1,sizeof(int));
+        assert(mesh->blayer.blay);
+        mesh->blayer.blref = (int*)calloc(dummi+1,sizeof(int));
+        assert(mesh->blayer.blref);
+        for (j=0; j<=dummi; j++)
+          fscanf(in,"%d %d",&mesh->blayer.blay[j],&mesh->blayer.blref[j]);
+      }
+    }
+*/
+    fprintf(stderr,"  ** UNKNOWN KEYWORD %s\n",data);
+  }
+  fclose(in);
+
+  return(1);
+}
+
+
+void endcod() {
+  double   ttot,ttim[TIMEMAX];
+  int      k,call[TIMEMAX];
+
+  TIM_chrono(OFF,&MMG_ctim[0]);
+  
+  for (k=0; k<TIMEMAX; k++) {
+    call[k] = MMG_ctim[k].call;
+    ttim[k] = MMG_ctim[k].call ? TIM_gttime(MMG_ctim[k]) : 0.0;
+  }
+  ttot    = ttim[1]+ttim[2]+ttim[3]+ttim[4];
+  ttim[0] = M_MAX(ttim[0],ttot);
+
+  if ( abs(MMG_imprim) > 5 ) {
+    fprintf(stdout,"\n  -- CPU REQUIREMENTS\n");
+    fprintf(stdout,"  in/out    %8.2f %%    %3d. calls,   %7.2f sec/call\n",
+	    100.*ttim[1]/ttim[0],call[1],ttim[1]/(float)call[1]);
+    fprintf(stdout,"  analysis  %8.2f %%    %3d. calls,   %7.2f sec/call\n",
+	    100.*ttim[2]/ttim[0],call[2],ttim[2]/(float)call[2]);
+    fprintf(stdout,"  optim     %8.2f %%    %3d. calls,   %7.2f sec/call\n",
+	    100.*ttim[3]/ttim[0],call[3],ttim[3]/(float)call[3]);
+    fprintf(stdout,"  total     %8.2f %%    %3d. calls,   %7.2f sec/call\n",
+	    100.*ttot/ttim[0],call[0],ttot/(float)call[0]);
+  }
+  fprintf(stdout,"\n   ELAPSED TIME  %.2f SEC.  (%.2f)\n",ttim[0],ttot);
+}
+
+
+/* set function pointers */
+int MMG_setfunc(int type) {
+  if ( type == 6 ) {
+    MMG_length     = MMG_long_ani;
+    MMG_cavity     = MMG_cavity_ani;
+    MMG_caltet     = MMG_caltet_ani;  //MMG_callong;//MMG_caltet_ani;
+    MMG_calte1     = MMG_calte1_ani;
+    MMG_caltet2    = MMG_caltet2_ani;//MMG_caltet2long_ani;
+    MMG_buckin     = MMG_buckin_ani;
+    MMG_optlen     = MMG_optlen_ani;
+    MMG_interp     = MMG_interp_ani;
+    MMG_optlentet  = MMG_optlentet_ani;
+    MMG_movevertex = MMG_movevertex_ani;
+  }
+  else if ( type == 1 ) {
+    MMG_length     = MMG_long_iso;
+    MMG_cavity     = MMG_cavity_iso;
+    MMG_caltet     = MMG_caltet_iso; //MMG_callong;
+    MMG_calte1     = MMG_calte1_iso;
+    MMG_caltet2    = MMG_caltet2_iso; //MMG_caltet2long_iso;//MMG_caltet2_iso;
+    MMG_buckin     = MMG_buckin_iso;
+    MMG_optlen     = MMG_optlen_iso;
+    MMG_interp     = MMG_interp_iso;
+    MMG_optlentet  = MMG_optlentet_iso;
+    MMG_movevertex = MMG_movevertex_iso;
+  }
+  else if ( type != 3 ) {
+    fprintf(stdout,"  ** WRONG DATA TYPE\n");
+    return(0);
+  }
+  return(1);
+}
+
+/* /\* /\\* *\/ */
+/* /\* int main(int argc,char *argv[]) { *\/ */
+/* /\*   pMesh      	mesh; *\/ */
+/* /\*   pSol       	sol; *\/ */
+/* /\*   Info     	*info; *\/ */
+/* /\*   int        	alert; *\/ */
+/* /\* int k,iadr,i,jj,kk,ii; *\/ */
+/* /\* double	lambda[3],v[3][3],*mold,*m; *\/ */
+
+/* /\*   fprintf(stdout,"  -- MMG3d, Release %s (%s) \n",M_VER,M_REL); *\/ */
+/* /\*   fprintf(stdout,"     Copyright (c) LJLL/IMB, 2010\n"); *\/ */
+/* /\*   fprintf(stdout,"    %s\n",COMPIL); *\/ */
+
+/* /\*   signal(SIGABRT,excfun); *\/ */
+/* /\*   signal(SIGFPE,excfun); *\/ */
+/* /\*   signal(SIGILL,excfun); *\/ */
+/* /\*   signal(SIGSEGV,excfun); *\/ */
+/* /\*   signal(SIGTERM,excfun); *\/ */
+/* /\*   signal(SIGINT,excfun); *\/ */
+/* /\*   atexit(endcod); *\/ */
+
+/* /\*   TIM_tminit(MMG_ctim,TIMEMAX); *\/ */
+/* /\*   TIM_chrono(ON,&MMG_ctim[0]); *\/ */
+
+/* /\*   mesh = (pMesh)M_calloc(1,sizeof(Mesh),"main"); *\/ */
+/* /\*   assert(mesh); *\/ */
+/* /\*   sol  = (pSol)M_calloc(1,sizeof(Sol),"main"); *\/ */
+/* /\*   assert(sol); *\/ */
+/* /\*   sol->offset = 1; *\/ */
+
+
+/* /\*   info = &mesh->info; *\/ */
+
+/* /\*   info->imprim   = -99; *\/ */
+/* /\*   info->memory   = -1; *\/ */
+/* /\*   info->ddebug   = 0; *\/ */
+/* /\*   info->rn2      = 3; *\/ */
+/* /\*   info->rn       = 500; *\/ */
+/* /\*   info->option   = 1; *\/ */
+/* /\*   alert          = 0; *\/ */
+/* /\*   info->bucksiz  = 0; *\/ */
+/* /\*   info->noswap   = 0; *\/ */
+/* /\*   info->nomove   = 0; *\/ */
+/* /\*   info->noinsert = 0;  *\/ */
+/* /\*   info->dt       = 1.; *\/ */
+/* /\*   info->bdry     = 0; *\/ */
+
+/* /\*   if ( !parsar(argc,argv,mesh,sol) )  return(1); *\/ */
+/* /\*   MMG_imprim = info->imprim; *\/ */
+  
+
+/* /\*   if ( MMG_imprim )   fprintf(stdout,"\n  -- INPUT DATA\n"); *\/ */
+/* /\*   TIM_chrono(ON,&MMG_ctim[1]); *\/ */
+/* /\*   if ( !MMG_loadMesh(mesh,mesh->name) )  return(1);  *\/ */
+/* /\*   if ( !MMG_loadSol(sol,sol->name,mesh->npmax) )  return(1); *\/ */
+/* /\*   if ( sol->np && sol->np != mesh->np ) { *\/ */
+/* /\*     fprintf(stdout,"  ## WARNING: WRONG SOLUTION NUMBER. IGNORED\n"); *\/ */
+/* /\*     sol->np = 0; *\/ */
+/* /\*   } *\/ */
+
+/* /\*   if ( !parsop(mesh) )  return(1); *\/ */
+
+/* /\*   if ( abs(info->option) == 9 && !MMG_loadVect(mesh,mesh->move,mesh->np) )  return(0); *\/ */
+
+/* /\*   if ( !MMG_setfunc(sol->offset) ) return(1); *\/ */
+/* /\*   if ( !MMG_scaleMesh(mesh,sol) )  return(1); *\/ */
+/* /\*   TIM_chrono(OFF,&MMG_ctim[1]); *\/ */
+/* /\*   if ( MMG_imprim ) *\/ */
+/* /\*     fprintf(stdout,"  -- DATA READING COMPLETED.     %.2f sec.\n", *\/ */
+/* /\*             TIM_gttime(MMG_ctim[1])); *\/ */
+  
+/* /\*   if ( abs(MMG_imprim) > 3 )  { *\/ */
+/* /\*     alert = MMG_outqua(mesh,sol); *\/ */
+/* /\*     if(alert) { *\/ */
+/* /\*       fprintf(stdout,"\n \n    ## INVALID MESH. STOP\n"); *\/ */
+/* /\*       exit(1);   *\/ */
+/* /\*     } *\/ */
+/* /\*     if(MMG_imprim < 0) MMG_outquacubic(mesh,sol); *\/ */
+/* /\*   } *\/ */
+
+/* /\*   fprintf(stdout,"\n  %s\n   MODULE MMG3D-LJLL/IMB : %s (%s)  %s\n  %s\n", *\/ */
+/* /\*           M_STR,M_VER,M_REL,sol->offset == 1 ? "ISO" : "ANISO",M_STR); *\/ */
+/* /\*   fprintf(stdout,"  MAXIMUM NUMBER OF POINTS    (NPMAX) : %8d\n",mesh->npmax); *\/ */
+/* /\*   fprintf(stdout,"  MAXIMUM NUMBER OF TRIANGLES (NTMAX) : %8d\n",mesh->ntmax); *\/ */
+/* /\*   fprintf(stdout,"  MAXIMUM NUMBER OF ELEMENTS  (NEMAX) : %8d\n",mesh->nemax); *\/ */
+
+
+/* /\*   TIM_chrono(ON,&MMG_ctim[2]); *\/ */
+/* /\*   if ( MMG_imprim )   fprintf(stdout,"\n  -- PHASE 1 : ANALYSIS\n"); *\/ */
+/* /\*   if ( !MMG_hashTetra(mesh) )    return(1); *\/ */
+/* /\*   if ( !MMG_markBdry(mesh) )     return(1); *\/ */
+/* /\*   if (abs(mesh->info.option)==10) { *\/ */
+/* /\*     MMG_saveMesh(mesh,"tetra.mesh"); *\/ */
+/* /\*     return(0); *\/ */
+/* /\*   }            *\/ */
+
+
+/* /\*   if ( !sol->np  &&  !MMG_doSol(mesh,sol) )  return(1); *\/ */
+/* /\*   TIM_chrono(OFF,&MMG_ctim[2]); *\/ */
+/* /\*   if ( MMG_imprim ) *\/ */
+/* /\*     fprintf(stdout,"  -- PHASE 1 COMPLETED.     %.2f sec.\n", *\/ */
+/* /\*             TIM_gttime(MMG_ctim[2])); *\/ */
+
+/* /\*   if ( info->ddebug )  MMG_chkmsh(mesh,1,1); *\/ */
+  
+/* /\*   if ( abs(MMG_imprim) > 4 )  { *\/ */
+/* /\* 	  MMG_prilen(mesh,sol); *\/ */
+/* /\*     MMG_ratio(mesh,sol,NULL);         *\/ */
+/* /\*   }                        *\/ */
+   
+/* /\* #ifdef USE_SCOTCH *\/ */
+/* /\*   /\\* renumbering begin *\\/ *\/ */
+/* /\* 	  /\\*check enough vertex to renum*\\/  *\/ */
+/* /\*    if ( (info->rn2 & 1) && (mesh->np/2. > info->rn)) {  *\/ */
+/* /\*      TIM_chrono(ON,&MMG_ctim[5]);  *\/ */
+/* /\*      if ( MMG_imprim < -6) *\/ */
+/* /\*        fprintf(stdout,"renumbering"); *\/ */
+/* /\*      renumbering(info->rn, mesh, sol);   *\/ */
+    
+/* /\*      if ( !MMG_hashTetra(mesh) )    return(1);     *\/ */
+/* /\*      TIM_chrono(OFF,&MMG_ctim[5]);  *\/ */
+/* /\*      if ( MMG_imprim < -6) *\/ */
+/* /\*        fprintf(stdout,"  -- PHASE RENUMBERING COMPLETED.     %.2f sec.\n", *\/ */
+/* /\*                TIM_gttime(MMG_ctim[5])); *\/ */
+/* /\*      if ( info->ddebug ) MMG_chkmsh(mesh,1,0); *\/ */
+/* /\*    } *\/ */
+/* /\*    /\\* renumbering end *\\/ *\/ */
+/* /\* #endif  *\/ */
+/* /\*   /\\* mesh optimization *\\/ *\/ */
+/* /\*   if ( info->option ) { *\/ */
+/* /\*     TIM_chrono(ON,&MMG_ctim[3]); *\/ */
+/* /\*     if ( MMG_imprim )   fprintf(stdout,"\n  -- PHASE 2 : UNIT MESH\n"); *\/ */
+/* /\*     if ( abs(info->option) == 9 ) { *\/ */
+/* /\*       if(!MMG_mmg3d9(mesh,sol,&alert)) { *\/ */
+/* /\*         if ( !MMG_unscaleMesh(mesh,sol) )  return(1); *\/ */
+/* /\*         MMG_saveMesh(mesh,mesh->outf); *\/ */
+/* /\* 	    MMG_saveSol(mesh,sol,mesh->outf); *\/ */
+/* /\* 	    return(1); *\/ */
+/* /\*       } *\/ */
+/* /\*       /\\*puts("appel 1"); *\/ */
+/* /\*       MMG_mmg3d1(mesh,sol,&alert);*\\/  *\/ */
+/* /\*       for (k=1; k<=mesh->np; k++) { *\/ */
+/* /\*         iadr = (k-1)*sol->offset + 1; *\/ */
+/* /\*         m    = &sol->met[iadr];       *\/ */
+/* /\*         /\\*calcul du log de M*\\/ *\/ */
+/* /\*         if ( !eigenv(1,m,lambda,v) ) { *\/ */
+/* /\*              puts("pbs eigen");  *\/ */
+/* /\*          return(0); *\/ */
+/* /\*         } *\/ */
+/* /\*         for (i=0; i<3; i++) lambda[i] = log(lambda[i]); *\/ */
+/* /\*         mold    = &sol->metold[iadr];  *\/ */
+/* /\*         kk = 0; *\/ */
+/* /\*       for (ii=0; ii<3; ii++) { *\/ */
+/* /\*            for (jj=ii; jj<3; jj++) { *\/ */
+/* /\*               mold[kk] = lambda[0]*v[0][ii]*v[0][jj] +  *\/ */
+/* /\*                          lambda[1]*v[1][ii]*v[1][jj] + *\/ */
+/* /\*                          lambda[2]*v[2][ii]*v[2][jj];  *\/ */
+/* /\*               kk = kk+1; *\/ */
+/* /\*            }                      *\/ */
+/* /\*          } *\/ */
+/* /\*       } *\/ */
+/* /\*     }  *\/ */
+    
+/* /\*     if(!info->noinsert) { *\/ */
+/* /\*       if(abs(info->option) == 4){ *\/ */
+/* /\*         MMG_mmg3d4(mesh,sol,&alert); *\/ */
+/* /\*       } else { *\/ */
+/* /\*         MMG_mmg3d1(mesh,sol,&alert); *\/ */
+/* /\*       } *\/ */
+/* /\*     } *\/ */
+      
+/* /\*     TIM_chrono(OFF,&MMG_ctim[3]); *\/ */
+/* /\*     if ( MMG_imprim ) *\/ */
+/* /\*       fprintf(stdout,"  -- PHASE 2 COMPLETED.     %.2f sec.\n", *\/ */
+/* /\*               TIM_gttime(MMG_ctim[3])); *\/ */
+/* /\*   } *\/ */
+/* /\* /////////////////////////////////////   *\/ */
+/* /\* /\\*MMG_caltet     = MMG_caltetrao; *\/ */
+/* /\* for(k=1 ; k<=mesh->ne ; k++) { *\/ */
+/* /\* 	if(!mesh->tetra[k].v[0]) continue; *\/ */
+/* /\* 	mesh->tetra[k].qual = MMG_caltet(mesh,sol,k);   *\/ */
+/* /\* } *\\/ *\/ */
+/* /\* /////////////////////////////////////   *\/ */
+/* /\*   /\\* mesh regularisation *\\/ *\/ */
+/* /\*   if ( info->option > -1 ) { *\/ */
+/* /\* #ifdef USE_SCOTCH *\/ */
+/* /\*     /\\* renumbering begin *\\/ *\/ */
+/* /\*     /\\*MMG_chkmsh(mesh,0,-1);  *\/ */
+/* /\*     puts("3er chk ok"); *\/ */
+/* /\*     *\\/ *\/ */
+/* /\*     if ( (info->rn2 & 2) && (mesh->np/2. > info->rn))  {  *\/ */
+/* /\*       TIM_chrono(ON,&MMG_ctim[6]);  *\/ */
+/* /\*       if ( MMG_imprim < -6) *\/ */
+/* /\*         fprintf(stdout,"renumbering");  *\/ */
+/* /\* 			renumbering(info->rn, mesh, sol); *\/ */
+/* /\*       if ( !MMG_hashTetra(mesh) )    return(1);     *\/ */
+/* /\*       TIM_chrono(OFF,&MMG_ctim[6]);  *\/ */
+/* /\*       if ( MMG_imprim < -6) *\/ */
+/* /\*         fprintf(stdout,"  -- PHASE RENUMBERING COMPLETED.     %.2f sec.\n", *\/ */
+/* /\*               TIM_gttime(MMG_ctim[6])); *\/ */
+/* /\*       if ( info->ddebug ) MMG_chkmsh(mesh,1,0); *\/ */
+/* /\*     }  *\/ */
+/* /\*     /\\* renumbering end *\\/    *\/ */
+/* /\* #endif *\/ */
+/* /\*     TIM_chrono(ON,&MMG_ctim[4]); *\/ */
+/* /\*     if ( MMG_imprim )  fprintf(stdout,"\n  -- PHASE 3 : OPTIMIZATION\n"); *\/ */
+/* /\*     if ( !alert )  { *\/ */
+/* /\*       if ( info->option == 9 ) {  *\/ */
+/* /\*          MMG_optra4(mesh,sol);  *\/ */
+/* /\*       } else { *\/ */
+/* /\*          MMG_optra4(mesh,sol);  *\/ */
+/* /\*       } *\/ */
+/* /\*     } *\/ */
+    
+/* /\*     if ( info->ddebug )  MMG_chkmsh(mesh,1,1); *\/ */
+/* /\*     TIM_chrono(OFF,&MMG_ctim[4]); *\/ */
+/* /\*     if ( MMG_imprim ) *\/ */
+/* /\*       fprintf(stdout,"  -- PHASE 3 COMPLETED.     %.2f sec.\n", *\/ */
+/* /\*               TIM_gttime(MMG_ctim[4])); *\/ */
+/* /\*   } *\/ */
+/* /\* /////////////////////////////////////   *\/ */
+/* /\* /\\*MMG_caltet     = MMG_caltet_iso; *\/ */
+/* /\* for(k=1 ; k<=mesh->ne ; k++) { *\/ */
+/* /\* 	mesh->tetra[k].qual = MMG_caltet(mesh,sol,k);   *\/ */
+/* /\* } *\\/ *\/ */
+/* /\* /////////////////////////////////////   *\/ */
+
+/* /\*   if ( info->option > -1 || abs(MMG_imprim) > 3 ) { *\/ */
+/* /\*     MMG_outqua(mesh,sol); *\/ */
+/* /\*     if(MMG_imprim < 0) MMG_outquacubic(mesh,sol); *\/ */
+/* /\*     MMG_prilen(mesh,sol); *\/ */
+/* /\*     MMG_ratio(mesh,sol,NULL); *\/ */
+/* /\*   } *\/ */
+/* /\*   fprintf(stdout,"\n  %s\n   END OF MODULE MMG3D \n  %s\n",M_STR,M_STR); *\/ */
+/* /\*   if ( alert ) *\/ */
+/* /\*     fprintf(stdout,"\n  ## WARNING: INCOMPLETE MESH  %d , %d\n", *\/ */
+/* /\*             mesh->np,mesh->ne); *\/ */
+
+/* /\*   if ( MMG_imprim )  fprintf(stdout,"\n  -- WRITING DATA FILE %s\n",mesh->outf); *\/ */
+/* /\*   TIM_chrono(ON,&MMG_ctim[1]); *\/ */
+/* /\*   if ( !MMG_unscaleMesh(mesh,sol) )  return(1); *\/ */
+/* /\*   MMG_saveMesh(mesh,mesh->outf); *\/ */
+/* /\*   if ( info->option == 9 ) { *\/ */
+/* /\*     MMG_saveSol(mesh,sol,mesh->outf); *\/ */
+/* /\*     MMG_saveVect(mesh,mesh->move);     *\/ */
+/* /\*   } *\/ */
+/* /\*   else *\/ */
+/* /\*     MMG_saveSol(mesh,sol,mesh->outf); *\/ */
+/* /\*   TIM_chrono(OFF,&MMG_ctim[1]); *\/ */
+/* /\*   if ( MMG_imprim )  fprintf(stdout,"  -- WRITING COMPLETED\n"); *\/ */
+
+/* /\*   /\\* free mem *\\/ *\/ */
+/* /\*   M_free(mesh->point); *\/ */
+/* /\*   M_free(mesh->tria); *\/ */
+/* /\*   M_free(mesh->tetra); *\/ */
+/* /\*   /\\*la desallocation de ce pointeur plante dans certains cas...*\\/ *\/ */
+/* /\*   M_free(mesh->adja); *\/ */
+/* /\*   M_free(mesh->disp->alpha); *\/ */
+/* /\*   M_free(mesh->disp->mv); *\/ */
+/* /\*   M_free(mesh->disp); *\/ */
+
+/* /\*   if ( sol->npfixe )  M_free(sol->met); *\/ */
+/* /\*   M_free(sol); *\/ */
+
+/* /\*   if ( MMG_imprim < -4 || info->ddebug )  M_memDump(); *\/ */
+/* /\*   M_free(mesh); *\/ */
+/* /\*   return(0); *\/ */
+/* /\* } *\/ */
diff --git a/contrib/mmg3d/build/sources/mmg3d1.c b/contrib/mmg3d/build/sources/mmg3d1.c
new file mode 100644
index 0000000000000000000000000000000000000000..68e18083a5988d80445dbabb343db99d2db8279f
--- /dev/null
+++ b/contrib/mmg3d/build/sources/mmg3d1.c
@@ -0,0 +1,192 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+extern TIM_mytime         MMG_ctim[TIMEMAX];
+
+int MMG_npdtot,MMG_npuisstot,MMG_nvoltot,MMG_nprestot;
+int MMG_npuiss,MMG_nvol,MMG_npres,MMG_npd;
+
+int MMG_cendellong(pMesh mesh,pSol sol,double declic,int base);
+
+int MMG_mmg3d1(pMesh mesh,pSol sol,int *alert) {
+  pBucket	bucket;
+  int		base,na,nd,nf,nna,nnd,dd,it,maxtou;
+  int   naold,ndold;
+//double q,declicw;
+//pTetra pt;      
+//int  nw;
+
+  if ( abs(mesh->info.imprim) > 3 )
+    fprintf(stdout,"  ** SIZE OPTIMIZATION\n");
+  if ( mesh->info.imprim < 0 ) {
+    MMG_outqua(mesh,sol);
+    MMG_prilen(mesh,sol);
+  }
+
+  base   = mesh->flag;
+  *alert = 0;
+
+  nna = 0;
+  nnd = 0;
+  nf  = 0;
+  it  = 0;
+  maxtou = 100;
+MMG_npdtot=0;
+MMG_npuisstot=0;
+MMG_nprestot=0;
+MMG_nvoltot=0;
+
+  /* 2. field points */
+  if ( mesh->info.imprim < -4 ) {
+    MMG_prilen(mesh,sol);
+    fprintf(stdout,"  -- FIELD POINTS\n");
+  }
+  /* create filter */
+  bucket = MMG_newBucket(mesh,M_MAX(mesh->info.bucksiz,BUCKSIZ));
+  if ( !bucket )  return(0);
+  
+  naold = ndold = 0;
+  do {
+    base = mesh->flag;
+    nf   = 0;
+        
+    MMG_analar(mesh,sol,bucket,&na,&nd,&nf,alert);    
+    nna += na;
+    nnd += nd;
+
+    if ( *alert ) {
+      if ( nd < 1000 )  break;
+      else  *alert = 0;
+    }
+    
+    /*test avec comme critere de qualite les longueurs*/
+    /*if( it < 7 && !(it%3) ) {
+      ns = 0; 
+      declic = 120.; //attention c'est 60*len  
+      if ( !*alert && !mesh->info.noswap ) {
+          declicw = 180.;  
+          nw += MMG_opttyp(mesh,sol,declicw,&alert);
+          ns = MMG_cendellong(mesh,sol,declic,-1);
+        if ( ns < 0 ) {
+          *alert = 1;
+      	  ns    = -ns;
+        }
+      }
+      if ( mesh->info.imprim && ns )
+        fprintf(stdout,"     %8d SWAPPED\n",ns);    
+      //puts("on arrete la");exit(0); 
+    }
+    
+    if( it > 5 ) {
+      
+      
+      //printf("on traite moins 1%% : %d %d %e\n",na,nd,(na+nd)/(double)mesh->np); 
+      //printf("delold/ins %e %e\n",ndold / (double) (na+1),naold / (double) (nd+1));
+      
+      if( it > 10 ) {
+        q = ndold / (double) (na+1);
+        if( q < 1.7 && q > 0.57) {
+          break;
+        }
+        q = naold / (double) (nd+1);
+        if( q < 1.7 && q > 0.57) {
+          break;
+        }        
+      }
+      q = ndold / (double) (na+1);
+      if( q < 1.1 && q > 0.9) {
+        break;
+      }
+      q = naold / (double) (nd+1);
+      if( q < 1.1 && q > 0.9) {
+        break;
+      }
+    }
+    naold = na;
+    ndold = nd;
+    */
+    
+    if ( it > 5 ) {
+      dd = abs(nd-na);
+      if ( dd < 5 || dd < 0.05*nd )   break;
+      else if ( it > 12 && nd >= na )  break;
+    }
+    if ( na + nd > 0 && mesh->info.imprim )
+      fprintf(stdout,"     %8d INSERTED   %8d REMOVED   %8d FILTERED\n",
+              na,nd,nf);
+    }
+    while ( na+nd > 0 && ++it < maxtou );
+
+
+  if ( nna+nnd && mesh->info.imprim ) {
+    fprintf(stdout,"     %7d INSERTED  %7d REMOVED  %7d FILTERED\n",nna,nnd,nf);
+  }
+
+if(MMG_npdtot>0) { 
+fprintf(stdout,"    REJECTED : %5d\n",MMG_npdtot);
+fprintf(stdout,"          VOL      : %6.2f %%    %5d \n",
+	100*(MMG_nvoltot/(float)
+MMG_npdtot),MMG_nvoltot); 
+fprintf(stdout,"          PUISS    : %6.2f %%    %5d \n",
+	100*(MMG_npuisstot/(float) MMG_npdtot),MMG_npuisstot);
+fprintf(stdout,"         PROCHE    : %6.2f %%    %5d \n",
+	100*(MMG_nprestot/(float) MMG_npuisstot),MMG_nprestot);	
+MMG_npdtot=0;
+MMG_npuisstot=0;
+MMG_nvoltot=0;  
+} 
+  if ( mesh->info.imprim < 0 ) {
+    MMG_outqua(mesh,sol);
+    MMG_prilen(mesh,sol);
+  }
+
+  M_free(bucket->head);
+  M_free(bucket->link);
+  M_free(bucket);
+
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/mmg3d4.c b/contrib/mmg3d/build/sources/mmg3d4.c
new file mode 100644
index 0000000000000000000000000000000000000000..67791e2f88826b25f4c6590779fa3af444e766dc
--- /dev/null
+++ b/contrib/mmg3d/build/sources/mmg3d4.c
@@ -0,0 +1,237 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_npuiss,MMG_nvol,MMG_npres;
+int MMG_nlen,MMG_ncal,MMG_ntopo,MMG_nex;
+int MMG_npuisstot,MMG_nvoltot,MMG_nprestot;
+int MMG_npdtot;
+int MMG_nplen,MMG_npref,MMG_bouffe;
+
+int ddebug;
+
+int MMG_mmg3d4(pMesh mesh,pSol sol,int *alert) {
+  Hedge    hash;
+  pBucket	 bucket; 
+  double   declic;
+  int		   base,na,nd,ns,nna,nnd,nns,dd,it,nf,maxtou; 
+  double   lmoy,LLLONG;
+  if ( abs(mesh->info.imprim) > 3 )
+    fprintf(stdout,"  ** SIZE OPTIMIZATION\n");
+  if ( mesh->info.imprim < 0 ) {
+    MMG_outqua(mesh,sol);
+    MMG_prilen(mesh,sol);
+  }
+
+  base   = mesh->flag;
+  *alert = 0;
+  maxtou = 10;
+  nna = nns = nnd = 0;
+  it  = 0;
+  declic = 3. / ALPHAD;  
+  lmoy = 10.;
+  LLLONG = 1.5;
+  
+  nna = 10;
+  do { 
+    na  = nd  = ns  = 0; 
+    if(0) ddebug = 1;
+    else ddebug = 0;
+    
+    if(!(it%2) ) {
+      bucket = MMG_newBucket(mesh,M_MAX(mesh->info.bucksiz,BUCKSIZ));
+      if ( !bucket )  return(0);
+      
+      MMG_analar(mesh,sol,bucket,&na,&nd,&nf,alert);      
+      if ( abs(mesh->info.imprim) > 5 ) 
+        fprintf(stdout,"     %7d INSERTED  %7d REMOVED   %7d FILTERED\n",na,nd,nf);  
+
+	    M_free(bucket->head);
+	    M_free(bucket->link);
+	    M_free(bucket);
+        
+    } else {
+        ++mesh->flag;
+    }
+    //printf("IT %d $$$$$$$$$$$ LLLONG  %9.3f\n",it,LLLONG); 
+    nna = nns = nnd = 0; 
+      
+    /*splitting*/
+    if ( !mesh->info.noinsert && (!*alert)  ) {
+      /* store points on edges */
+      if ( !MMG_zaldy4(&hash,mesh->np) ) {
+        if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM.\n"); 
+        *alert = 2;
+        break;
+      }
+          
+      nna = MMG_analarcutting(mesh,sol,&hash,alert,&lmoy,LLLONG); 
+      if ( abs(mesh->info.imprim) > 5 ) { printf("lmoy %9.5f\n",lmoy); }
+      /*puts("--------------------------------------");
+      puts("--------------------------------------");
+      puts("--------------------------------------");
+      */
+      if ( *alert ) {
+        fprintf(stdout," \n\n ** UNABLE TO CUT (analarcutting)\n");
+        fprintf(stdout," ** RETRY WITH -m > %6d \n\n",mesh->info.memory);
+        MMG_saveMesh(mesh,"crash.meshb");
+        MMG_saveSol(mesh,sol,"crash.solb"); 
+        exit(0);
+      }
+      M_free(hash.item);        
+    }
+    else if ( *alert )  nna = 0;  
+    /* adjacencies */ 
+    if ( nna /*|| it == (maxtou-1)*/ ) {
+      mesh->nt = 0;
+      if ( !MMG_hashTetra(mesh) )  return(0);
+      if ( !MMG_markBdry(mesh) )   return(0);
+    }
+    // printf("chkmsh\n");
+    // MMG_saveMesh(mesh,"chk.mesh");
+    //MMG_chkmsh(mesh,1,-1);
+		//if(it==1)exit(0);		
+     /* delaunization */
+    if ( !mesh->info.noswap && (nna || na) ) {  
+      nns   =  MMG_cendel(mesh,sol,declic,base);
+    }
+
+    /* deletion */
+    /*if ( 0 && nna ) {
+      nnd   = MMG_colvert(mesh,sol,base);
+    } */
+    if ( nna+nnd+nns && abs(mesh->info.imprim) > 3 )
+      fprintf(stdout,"     %7d INSERTED  %7d REMOVED   %7d FLIPPED\n",nna+na,nnd+nd,nns);
+    
+  }
+  while ( na+nd+nns+nna+nnd > 0 && ++it < maxtou && lmoy > 1.3);
+
+  if ( nna+nnd+nns && abs(mesh->info.imprim) < 4 ) {
+    fprintf(stdout,"     %7d INSERTED  %7d REMOVED %7d FLIPPED\n",nna,nnd,nns);
+  }
+
+  if ( mesh->info.imprim < 0 ) {
+    MMG_outqua(mesh,sol);
+    MMG_prilen(mesh,sol);
+  }
+
+	//return(1);
+	//MMG_saveMesh(mesh,"aprescut.mesh");
+	fprintf(stdout,"    ---\n");
+  
+  /*analyze standard*/
+    base   = mesh->flag;
+    *alert = 0;
+
+    nna = 0;
+    nnd = 0;
+    nf  = 0;
+    it  = 0;
+    maxtou = 100;
+    MMG_npdtot=0;
+    MMG_npuisstot=0;
+    MMG_nprestot=0;
+    MMG_nvoltot=0;
+
+    /* 2. field points */
+    if ( mesh->info.imprim < -4 ) {
+      MMG_prilen(mesh,sol);
+      fprintf(stdout,"  -- FIELD POINTS\n");
+    }
+
+    /* create filter */
+    bucket = MMG_newBucket(mesh,M_MAX(mesh->info.bucksiz,BUCKSIZ));
+    if ( !bucket )  return(0);
+
+    do {
+      MMG_analar(mesh,sol,bucket,&na,&nd,&nf,alert);    
+      nna += na;
+      nnd += nd;
+      if ( *alert ) {
+        if ( nd < 1000 )  break;
+        else  *alert = 0;
+      }
+      if ( it > 5 ) {
+        dd = abs(nd-na);
+        if ( dd < 5 || dd < 0.05*nd )   break;
+        else if ( it > 12 && nd >= na )  break;
+      }
+      if ( na+nd && abs(mesh->info.imprim) > 3 )
+        fprintf(stdout,"     %7d INSERTED  %7d REMOVED   %7d FILTERED\n",na,nd,nf);    
+			// MMG_saveMesh(mesh,"chk.mesh");
+			// //if(it==1) exit(0);
+    }
+    while ( na+nd > 0 && ++it < maxtou );
+
+    if ( nna+nnd && abs(mesh->info.imprim) < 3 ) {
+      fprintf(stdout,"     %7d INSERTED  %7d REMOVED  %7d FILTERED\n",na,nd,nf);
+    }
+
+  if(MMG_npdtot>0) { 
+  fprintf(stdout,"    REJECTED : %5d\n",MMG_npdtot);
+  fprintf(stdout,"          VOL      : %6.2f %%    %5d \n",
+  	100*(MMG_nvoltot/(float)
+  MMG_npdtot),MMG_nvoltot); 
+  fprintf(stdout,"          PUISS    : %6.2f %%    %5d \n",
+  	100*(MMG_npuisstot/(float) MMG_npdtot),MMG_npuisstot);
+  fprintf(stdout,"         PROCHE    : %6.2f %%    %5d \n",
+  	100*(MMG_nprestot/(float) MMG_npuisstot),MMG_nprestot);	
+  MMG_npdtot=0;
+  MMG_npuisstot=0;
+  MMG_nvoltot=0;  
+  } 
+    if ( mesh->info.imprim < 0 ) {
+      MMG_outqua(mesh,sol);
+      MMG_prilen(mesh,sol);
+    }
+
+    M_free(bucket->head);
+    M_free(bucket->link);
+    M_free(bucket);
+  
+
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/mmg3d9.c b/contrib/mmg3d/build/sources/mmg3d9.c
new file mode 100644
index 0000000000000000000000000000000000000000..1a9d16f1d8c775ef650b97f4da813a9840f89ea9
--- /dev/null
+++ b/contrib/mmg3d/build/sources/mmg3d9.c
@@ -0,0 +1,528 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+extern int MMG_nlen,MMG_ncal,MMG_ntopo,MMG_nex;
+
+/*essaie d'enlever les tets ayant plus d'une face de peau pour faciliter le bouger*/
+int MMG_optfacespeau(pMesh mesh,pSol sol) {
+  double  	declic;
+  pTetra	pt;
+  pQueue    queue;
+  int      	it,maxtou,i,iadr,*adja,npeau,nwtot,nw,k;
+  
+  declic = 1.7 / ALPHAD;
+  maxtou = 10;
+  it     = 0;
+
+  do {
+    queue = MMG_kiuini(mesh,mesh->nemax,declic,-1);
+    assert(queue);
+    nw    = 0;
+    nwtot = 0;
+    do {
+      k = MMG_kiupop(queue);
+      pt = &mesh->tetra[k];
+      if ( !k )  break;
+      if ( !pt->v[0] )  continue;
+      iadr = 4*(k-1) + 1;
+      adja = &mesh->adja[iadr];
+      /*optim bdry tetra*/
+      npeau = 0;
+      for(i=0 ; i<4 ; i++) {
+        if(!adja[i])npeau++;	 
+      }
+      if(npeau>1 ) {
+        nwtot++;
+        //if(mesh->info.imprim<0) printf("%d faces de peau!!!! %d %e\n",npeau,k,mesh->tetra[k].qual * ALPHAD);
+	    nw += MMG_opt2peau(mesh,sol,queue,k,declic);
+        continue;
+      }    
+    } 
+    while (k);
+    fprintf(stdout,"      %7d / %7d BDRY TETS\n",nw,nwtot);
+    MMG_kiufree(queue);
+  }
+  while ( nw && ++it < maxtou );
+ 
+ return(1);
+}
+
+/*collapse*/
+int MMG_colps(pMesh mesh,pSol sol,int *nd) {
+  pTetra    pt,pt1;
+  pPoint    pa,pb;
+  double    len,*ca,*cb,*ma,*mb;
+  float     coef;
+  int       i,k,l,jel,num,lon,ndd,npp,ia,ib,ipa,ipb,nedep,base;
+  int      *adja,adj,iadr;
+  List      list;
+  char      tabar;
+
+MMG_nlen = 0;
+MMG_ncal = 0;
+MMG_ntopo = 0;
+MMG_nex = 0;
+
+  npp = 0;
+  ndd = 0;
+  coef  = QDEGRAD;
+  nedep = mesh->ne;
+  base  = ++mesh->flag;
+  /*Try collapse*/
+  for (k=1; k<=nedep; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    else if ( pt->flag != base-1 )  continue;
+
+    /* mark internal edges */
+    tabar = 0;
+    iadr  = 4*(k-1) + 1;
+    adja  = &mesh->adja[iadr];
+    for (i=0; i<4; i++) {
+      adj = adja[i] >> 2;
+      if ( !adj || pt->ref != mesh->tetra[adj].ref ) {
+  	tabar |= 1 << MMG_iarf[i][0];
+  	tabar |= 1 << MMG_iarf[i][1];
+  	tabar |= 1 << MMG_iarf[i][2];
+     }
+    }
+    if ( tabar == ALL_BDRY )  continue;
+
+    /* internal edge */
+    for (i=0; i<6; i++) {
+      if ( tabar & 1<<i )  continue;
+      else if ( pt->edge & 1 << i )  continue;
+
+      /* edge length */
+      ia  = MMG_iare[i][0];
+      ib  = MMG_iare[i][1];
+      ipa = pt->v[ia];
+      ipb = pt->v[ib];
+      pa  = &mesh->point[ipa];
+      pb  = &mesh->point[ipb];
+      ca  = &pa->c[0];
+      cb  = &pb->c[0];
+      iadr = (ipa-1)*sol->offset + 1;
+      ma  = &sol->met[iadr];
+
+      iadr = (ipb-1)*sol->offset + 1;
+      mb  = &sol->met[iadr];
+      
+      /* coquille */
+      lon = MMG_coquil(mesh,k,i,&list);
+      for (l=1; l<=lon; l++) {
+  	    jel = list.tetra[l] / 6;
+  	    num = list.tetra[l] % 6;
+  	    pt1 = &mesh->tetra[jel];
+  	    pt1->edge |= 1 << num;
+      }
+      if ( lon < 3 )  continue;
+      len = MMG_length(ca,cb,ma,mb);
+  
+      if ( len < LSHORT ) {
+  	    npp++;
+  	    pa = &mesh->point[ipa];
+  	    pb = &mesh->point[ipb];
+  	    if ( MMG_colpoi(mesh,sol,k,ia,ib,coef) ) {
+  	      MMG_delPt(mesh,ipb);
+  	      ndd++;
+  	      break;
+  	    }
+  	    else if ( MMG_colpoi(mesh,sol,k,ib,ia,coef) ) {
+  	      MMG_delPt(mesh,ipa);
+  	      ndd++;
+  	      break;
+  	    }
+      }
+    }
+  }
+
+        *nd = ndd;
+printf("analyzed %d \n",npp);
+printf("rejected colpoi : cal %d  , len %d , topo %d , ex %d\n",MMG_ncal,MMG_nlen,MMG_ntopo,MMG_nex);
+
+  if ( *nd > 0 && abs(mesh->info.imprim) > 2 )
+    fprintf(stdout,"	%8d REMOVED  \n",*nd);
+
+  return(1);
+
+}
+
+
+/* dichotomy: check if nodes can move */
+int MMG_dikomv(pMesh mesh,pSol sol,short t) {
+  pTetra    pt;
+  pPoint    ppt;
+  pDispl    pd;
+  double    vol,c[4][3];
+  double    alpha,coor[3];
+  int       k,i,nm;
+/*double hmax,*mp,h1,lambda[3],dd;  
+int iadr;  
+Info     *info;
+*/
+  pd = mesh->disp;
+
+  alpha = (double)t / SHORT_MAX;
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    for (i=0; i<4; i++) {
+      ppt      = &mesh->point[ pt->v[i] ];
+      ppt->tmp = k;
+      if ( ppt->tag & M_MOVE ) {
+        c[i][0] = ppt->c[0] + alpha * pd->mv[3*(pt->v[i]-1) + 1 + 0];
+        c[i][1] = ppt->c[1] + alpha * pd->mv[3*(pt->v[i]-1) + 1 + 1];
+        c[i][2] = ppt->c[2] + alpha * pd->mv[3*(pt->v[i]-1) + 1 + 2];
+      }
+      else
+        memcpy(c[i],ppt->c,3*sizeof(double));
+    }
+
+    vol = MMG_quickvol(c[0],c[1],c[2],c[3]);
+    if ( vol < 1e-24/*EPS2*/ )  {
+      if(mesh->info.imprim < 0) printf("vol reject %d %e %e\n",k,vol,pt->qual * ALPHAD);
+      return(0);
+    }
+  }
+  /* update metrics */
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if(ppt->tag & M_UNUSED) continue;
+//#warning a mettre ou pas?    
+    if(ppt->tag & M_BDRY) continue;
+    if ( ppt->tag & M_MOVE ) {
+      coor[0] = ppt->c[0] + alpha * pd->mv[3*(k-1) + 1 + 0];
+      coor[1] = ppt->c[1] + alpha * pd->mv[3*(k-1) + 1 + 1];
+      coor[2] = ppt->c[2] + alpha * pd->mv[3*(k-1) + 1 + 2];
+if(MMG_computeMetric(mesh,sol,k,coor) == -1) ;//printf("point %d not found\n",k);
+// /*pour mettre une metrique analytique*/
+//       iadr = (k-1)*sol->offset + 1;
+//       mp   = &sol->met[iadr];
+//             
+//       info = &mesh->info;
+// 
+//     /* normalize metrics */
+//     dd = (double)PRECI / info->delta;
+// 
+//       hmax = 0.5;
+//       h1 = hmax * fabs(1-exp(-fabs((coor[2] * 1./dd + info->min[2])+4))) + 0.008;
+//       lambda[2]=1./(h1*h1);
+//       lambda[1]=1./(hmax*hmax);
+//       lambda[0]=1./(hmax*hmax);
+//      dd = 1.0 / (dd*dd);
+// 
+//       mp[0]=dd*lambda[0];
+//       mp[1]=0;
+//       mp[3]=dd*lambda[1];
+//       mp[2]=0;
+//       mp[4]=0;
+//       mp[5]=dd*lambda[2];
+// /*fin metrique analytique*/      
+   }
+  }
+
+  /* update point coords */
+  nm = 0;
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_MOVE ) {
+      ppt->c[0] += alpha * pd->mv[3*(k-1) + 1 + 0];
+      ppt->c[1] += alpha * pd->mv[3*(k-1) + 1 + 1];
+      ppt->c[2] += alpha * pd->mv[3*(k-1) + 1 + 2];
+      pd->alpha[k]  = t;
+      if ( t == SHORT_MAX )  ppt->tag &= ~M_MOVE;
+      nm++;
+    }
+  }
+
+  if ( mesh->info.imprim < 0 )  fprintf(stdout,"     %7d NODES UPDATED\n",nm);
+  return(nm);
+}
+
+
+/* check if displacement ok */
+int MMG_chkmov(pMesh mesh,char level) {
+  pTetra     pt;
+  pPoint     ppt;
+  pDispl     pd;
+  double     vol;
+  int        k,nc;
+
+  pd  = mesh->disp;
+
+  nc = 0;
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_MOVE ) {
+      if ( pd->alpha[k] < SHORT_MAX )  return(0);
+      ppt->tag &= ~M_MOVE;
+      nc++;
+    }
+  }
+
+  /* check element validity */
+  if ( level > 0 ) {
+    for (k=1 ; k<=mesh->ne; k++) {
+      pt = &mesh->tetra[k];
+      if ( !pt->v[0] )  continue;
+      vol = MMG_voltet(mesh,k);
+      if ( vol < 0.0 )  return(0);
+    }
+  }
+
+  return(1);
+}
+
+int MMG_optra9(pMesh mesh,pSol sol) {
+  double	declicw;
+  double	declic;
+  int		base,nm,ns,it,maxtou,alert,nw,k;
+  
+  /* optim coquil */
+  alert  = 0;
+  maxtou = 10;
+  it     = 0;
+    
+  for (k=1; k<=mesh->ne; k++) mesh->tetra[k].flag = mesh->flag;
+  for (k=1; k<=mesh->np; k++) mesh->point[k].flag = mesh->flag;
+ 
+  declicw = 5./ALPHAD;
+  declic  = 1.1 / ALPHAD;//1.1 pour mmg3d
+
+  do {
+    for (k=1; k<=mesh->ne; k++) mesh->tetra[k].flag = mesh->flag;
+    for (k=1; k<=mesh->np; k++) mesh->point[k].flag = mesh->flag;
+    base = ++mesh->flag;
+    
+    ns = 0;
+    if ( !alert && !mesh->info.noswap ) {
+      ns = MMG_cendel(mesh,sol,declic,base);
+      if ( ns < 0 ) {
+        alert = 1;
+    	ns    = -ns;
+      }
+    }
+    nw = 0;
+    /*if (!mesh->info.noinsert)  nw = MMG_pretreat(mesh,sol,declicw,&alert);
+    */
+    /*sur des surfaces courbes, il existe des tetras ayant  4 points de peau avec Q=3*/  
+    if (1/*!mesh->info.noswap*/ /*&& (it < 10)*/ )  {
+	  nw += MMG_opttyp(mesh,sol,declicw,&alert);   
+    }
+
+    nm = MMG_optlen(mesh,sol,declic,base);  
+//    if(abs(mesh->info.option)!=9 || !mesh->disp) if(it<2) MMG_optlap(mesh,sol);
+    
+    if ( mesh->info.imprim< -4 && nw+ns+nm )
+      fprintf(stdout,"     %8d IMPROVED  %8d SWAPPED  %8d MOVED\n",nw,ns,nm);
+  }
+  while ( ns+nm/*(ns && (ns > 0.005*mesh->ne || it < 5))*/ && ++it < maxtou );
+ 
+  return(1);
+}
+
+
+int MMG_mmg3d9(pMesh mesh,pSol sol,int *alert) {   
+  pPoint    ppt;
+  pTetra   pt;
+  double   declic;
+  double    *m,*mold,qworst;
+  int      k,nm,iter,maxiter;
+  int      base,nnd,nns,sit,maxtou,ns,iadr,iold;
+  short    t,i,it,alpha;
+  
+  if ( abs(mesh->info.imprim) > 3 )
+    fprintf(stdout,"  ** MOVING MESH\n");
+  
+  /*alloc et init metold*/
+  sol->metold = (double*)M_calloc(sol->npmax+1,sol->offset*sizeof(double),"MMG_mmg3d9");
+  assert(sol->metold);
+  mesh->disp->cold = (double*)M_calloc(3*(mesh->npmax + 1),sizeof(double),"MMG_mmg3d9");
+  assert(mesh->disp->cold);
+  for (k=1; k<=mesh->np; k++) {
+     ppt = &mesh->point[k];
+	 if ( ppt->tag & M_UNUSED ) continue;
+     mesh->disp->cold[3*(k-1) + 1 + 0] = ppt->c[0];
+     mesh->disp->cold[3*(k-1) + 1 + 1] = ppt->c[1];
+     mesh->disp->cold[3*(k-1) + 1 + 2] = ppt->c[2];
+   }
+  
+  switch (sol->offset) {
+  case 1:
+    for (k=1; k<=sol->np; k++)  {
+      sol->metold[k] = sol->met[k];
+    }
+    break;
+
+  case 6:
+    for (k=1; k<=mesh->np; k++) {
+      iadr = (k-1)*sol->offset + 1;
+      m    = &sol->met[iadr];
+      mold = &sol->metold[iadr];
+      for (i=0; i<sol->offset; i++)  mold[i] = m[i];
+    }
+    break;
+  default:
+    fprintf(stderr,"  ## SPECIFIC DATA NOT USED.\n");
+    exit(2);
+  }
+
+  
+  /* move grid nodes */
+  t       = SHORT_MAX;
+  alpha   = 0;
+  iter    = 0;
+  maxiter = 200;
+  declic  = 1.1/ALPHAD;
+  iold    = 1;
+  
+  /* move grid nodes */
+  t = SHORT_MAX;
+  if (  MMG_dikomv(mesh,sol,t) ) {
+    if ( mesh->info.imprim )  fprintf(stdout,"     %7d NODES MOVED\n",mesh->np);
+  }
+  else {
+    fprintf(stdout,"     TRYING DICHO\n");
+    while (alpha < SHORT_MAX && iter < maxiter) {
+      t = SHORT_MAX - alpha;
+      i = 0;
+      do {
+        nm = MMG_dikomv(mesh,sol,t);
+        if ( nm )  {
+	      alpha += t;
+	      break;
+	    } 
+	    t = t >> 1;     
+      } while (i++ < 10);
+  
+      /*si pas de mouvement 2 iter de suite stop*/
+      if ( (i==11) && (iold==11)) {
+        fprintf(stdout,"  NO MOVEMENT ## UNCOMPLETE DISPLACEMENT\n");
+        return(0);
+       }
+       iold = i;
+       /* update quality */
+	   qworst = 0.;
+       for (k=1; k<=mesh->ne; k++) {
+         pt = &mesh->tetra[k];
+		 if ( !pt->v[0] ) continue;
+         pt->qual = MMG_caltet(mesh,sol,k);
+		 qworst = M_MAX(qworst,pt->qual);
+       }
+      
+     
+      if ( mesh->info.imprim && nm )
+        fprintf(stdout,"     %7d NODES MOVED\n",nm);
+
+      printf("%%%% ITER %d alpha (%d) %d < %d\n",iter,i,alpha,SHORT_MAX);
+            
+      if ( i>1 ) {
+        fprintf(stdout,"     CAN'T MOVED\n");
+        if(!mesh->info.noswap) MMG_optfacespeau(mesh,sol);
+        nnd = 0;
+        nns = 0;
+        sit = 0;
+        maxtou = 10;
+        do {
+          it   = 0;
+          base = mesh->flag;
+		  ns   = 0;//MMG_cendel(mesh,sol,declic,base);
+          if(!mesh->info.noswap) MMG_optfacespeau(mesh,sol);
+          if ( ns < 0 ) {
+            *alert = 1;
+            ns     = -ns;
+          }
+          nns += ns;
+          if ( ns && abs(mesh->info.imprim) > 4 )
+            fprintf(stdout,"      %7d SWAPPED\n",ns);
+        }
+        while ( ns && ++sit < maxtou );
+
+        if ( nnd+nns && abs(mesh->info.imprim) < 3 ) {
+          fprintf(stdout,"     %7d REMOVED  %7d SWAPPED\n",nnd,nns);
+        }
+      }
+      
+      if ( qworst < 10./ALPHAD ) { 
+        MMG_optra4(mesh,sol);
+      } else {
+		MMG_optra9(mesh,sol);
+      }
+      if(!mesh->info.noswap) MMG_optfacespeau(mesh,sol);
+      MMG_outqua(mesh,sol);
+      iter++;
+
+    }
+
+    /* check mesh */
+    if ( iter==maxiter && !MMG_chkmov(mesh,1) ) {
+      fprintf(stdout,"  ## UNCOMPLETE DISPLACEMENT\n");
+      return(0);
+    }
+  }
+  
+  if(!mesh->info.noswap) MMG_optfacespeau(mesh,sol);
+
+  /* update quality */
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( pt->v[0] )
+      pt->qual = MMG_caltet(mesh,sol,k);
+  }
+
+  if ( mesh->info.imprim < 0 ) {
+    MMG_outqua(mesh,sol);
+    MMG_prilen(mesh,sol);
+  }
+  
+ // M_free(sol->metold);
+/*  M_free(mesh->disp);
+  mesh->disp = 0;
+  */return(1);
+}
+
diff --git a/contrib/mmg3d/build/sources/mmg3dConfig.h b/contrib/mmg3d/build/sources/mmg3dConfig.h
new file mode 100644
index 0000000000000000000000000000000000000000..6fc584d764994ca3d96393facbd7e6adbff67be8
--- /dev/null
+++ b/contrib/mmg3d/build/sources/mmg3dConfig.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR 
+#define Tutorial_VERSION_MINOR 
+
+
diff --git a/contrib/mmg3d/build/sources/mmg3dlib.c b/contrib/mmg3d/build/sources/mmg3dlib.c
new file mode 100644
index 0000000000000000000000000000000000000000..f613e1e39efa0fa8aef80f143b3687f27d074a83
--- /dev/null
+++ b/contrib/mmg3d/build/sources/mmg3dlib.c
@@ -0,0 +1,497 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+/*
+mmg3dlib(int option, ): to use mmg3d via a library
+
+option = 
+  fprintf(stdout,"  1      adaptation\n");
+  fprintf(stdout,"  9      moving mesh\n");
+  fprintf(stdout," -n      turn off optimisation\n");
+
+*/
+#include "compil.date"
+#include "mesh.h"
+#include "eigenv.h"
+
+TIM_mytime         MMG_ctim[TIMEMAX];
+short	       MMG_imprim;
+
+static void MMG_excfun(int sigid) {
+  switch (sigid) {
+  case SIGFPE:
+    fprintf(stderr,"  ## FP EXCEPTION. STOP\n");
+    break;
+  case SIGILL:
+    fprintf(stderr,"  ## ILLEGAL INSTRUCTION. STOP\n");
+    break;
+  case SIGSEGV:
+    fprintf(stderr,"  ## SEGMENTATION FAULT. STOP\n");
+    break;
+  case SIGABRT:
+  case SIGTERM:
+  case SIGINT:
+    fprintf(stderr,"  ## ABNORMAL END. STOP\n");
+    break;
+  }
+  exit(1);
+}
+
+void MMG_endcod() {
+  double   ttot,ttim[TIMEMAX];
+  int      k,call[TIMEMAX];
+
+  TIM_chrono(OFF,&MMG_ctim[0]);
+  
+  for (k=0; k<TIMEMAX; k++) {
+    call[k] = MMG_ctim[k].call;
+    ttim[k] = MMG_ctim[k].call ? TIM_gttime(MMG_ctim[k]) : 0.0;
+  }
+  ttot    = ttim[1]+ttim[2]+ttim[3]+ttim[4];
+  ttim[0] = M_MAX(ttim[0],ttot);
+
+  if ( abs(MMG_imprim) > 5 ) {
+    fprintf(stdout,"\n  -- CPU REQUIREMENTS\n");
+    fprintf(stdout,"  in/out    %8.2f %%    %3d. calls,   %7.2f sec/call\n",
+	    100.*ttim[1]/ttim[0],call[1],ttim[1]/(float)call[1]);
+    fprintf(stdout,"  analysis  %8.2f %%    %3d. calls,   %7.2f sec/call\n",
+	    100.*ttim[2]/ttim[0],call[2],ttim[2]/(float)call[2]);
+    fprintf(stdout,"  optim     %8.2f %%    %3d. calls,   %7.2f sec/call\n",
+	    100.*ttim[3]/ttim[0],call[3],ttim[3]/(float)call[3]);
+    fprintf(stdout,"  total     %8.2f %%    %3d. calls,   %7.2f sec/call\n",
+	    100.*ttot/ttim[0],call[0],ttot/(float)call[0]);
+  }
+  fprintf(stdout,"\n   ELAPSED TIME  %.2f SEC.  (%.2f)\n",ttim[0],ttot);
+}
+
+
+int MMG_inputdata(pMesh mesh,pSol sol) {
+  pPoint 	ppt;
+  int		k;
+  
+  
+  mesh->npfixe = mesh->np;
+  mesh->ntfixe = mesh->nt;
+  mesh->nefixe = mesh->ne;
+  
+  /* keep track of empty links */
+  mesh->npnil = mesh->np + 1;
+  mesh->nenil = mesh->ne + 1;
+  for (k=mesh->npnil; k<mesh->npmax-1; k++)
+    mesh->point[k].tmp  = k+1;
+  for (k=mesh->nenil; k<mesh->nemax-1; k++)
+    mesh->tetra[k].v[3] = k+1;
+  if ( mesh->nt ) {
+    mesh->ntnil = mesh->nt + 1;
+    for (k=mesh->ntnil; k<mesh->ntmax-1; k++)
+      mesh->tria[k].v[2] = k+1;
+  }
+  /*tag points*/
+  for (k=1; k<=mesh->np; k++) {
+      ppt = &mesh->point[k];
+      ppt->tag  = M_UNUSED;
+  }
+ return(1);
+}
+
+int MMG_tassage(pMesh mesh,pSol sol) {
+  pTetra	pt,ptnew;
+  pTria		pt1;
+  pPoint	ppt,pptnew;
+  int 		np,k,ne,nbl,isol,isolnew,i;
+  
+  /*rebuild triangles*/
+	MMG_markBdry(mesh);
+
+  /* compact vertices */
+  np=0;
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+    ppt->tmp = ++np;
+  }
+  
+ 
+  /* compact triangles */
+  for (k=1; k<=mesh->nt; k++) {
+    pt1  = &mesh->tria[k];
+    pt1->v[0] = mesh->point[pt1->v[0]].tmp;
+    pt1->v[1] = mesh->point[pt1->v[1]].tmp;
+    pt1->v[2] = mesh->point[pt1->v[2]].tmp;
+  }
+ 
+  /* compact tetrahedra */
+  ne  = 0;
+  nbl = 1;
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  {
+      continue;
+    }
+		for(i=0 ; i<4 ; i++)
+		  pt->bdryref[i] = -1;
+    pt->v[0] = mesh->point[pt->v[0]].tmp;
+    pt->v[1] = mesh->point[pt->v[1]].tmp;
+    pt->v[2] = mesh->point[pt->v[2]].tmp;
+    pt->v[3] = mesh->point[pt->v[3]].tmp; 
+		ne++; 
+			//     if(k!=nbl) {
+			// printf("on voudrait tasser\n");
+			//       ptnew = &mesh->tetra[nbl];
+			//       memcpy(ptnew,pt,sizeof(Tetra));
+			//       nbl++;
+			//     }               
+			// 
+		// for(i=0 ; i<4 ; i++)
+		//   ptnew->bdryref[i] = 0;
+		//     if(k != nbl) {
+		//       memset(pt,0,sizeof(Tetra));
+		//       pt->qual = 0.0;
+		//       pt->edge = 0; 
+		//     }
+		//     nbl++;
+  }
+  // mesh->ne = ne;
+  
+  /* compact metric */
+  nbl = 1;
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+    isol    = (k-1) * sol->offset + 1;    
+    isolnew = (nbl-1) * sol->offset + 1;
+    
+    for (i=0; i<sol->offset; i++)
+      sol->met[isolnew + i] = sol->met[isol + i];
+    ++nbl;
+  }
+  
+   
+  /*compact vertices*/
+   np  = 0;
+   nbl = 1;
+   for (k=1; k<=mesh->np; k++) {
+     ppt = &mesh->point[k];
+     if ( ppt->tag & M_UNUSED )  continue;
+     pptnew = &mesh->point[nbl];
+     memcpy(pptnew,ppt,sizeof(Point));
+     ppt->tag   &= ~M_UNUSED;
+     assert(ppt->tmp == nbl);
+     np++;
+     if(k != nbl) {
+       ppt = &mesh->point[k];
+       memset(ppt,0,sizeof(Point));
+       ppt->tag    = M_UNUSED;
+     }
+     nbl++;
+   }
+   mesh->np = np;
+   sol->np  = np;
+   
+  for(k=1 ; k<=mesh->np ; k++)
+    mesh->point[k].tmp = 0;
+    
+  mesh->npnil = mesh->np + 1;
+  for (k=mesh->npnil; k<mesh->npmax-1; k++)
+    mesh->point[k].tmp  = k+1;
+  
+  mesh->nenil = mesh->ne + 1;
+  for (k=mesh->nenil; k<mesh->nemax-1; k++)
+    mesh->tetra[k].v[3] = k+1;
+ 
+  mesh->ntnil = mesh->nt + 1;
+  for (k=mesh->ntnil; k<mesh->ntmax-1; k++)
+    mesh->tria[k].v[2] = k+1;
+  
+
+  
+ return(1);
+}
+
+int MMG_mmg3dlib(int opt[9],MMG_pMesh mesh,MMG_pSol sol) {
+  int           alert;
+  Info     	*info;
+  short		imprim;
+  int k,iadr,i,jj,kk,ii,im;
+  double	lambda[3],v[3][3],*mold,*m;
+  //fprintf(stdout,"  %s \n", M_STR);
+  if (opt[6] < 0) fprintf(stdout,"  -- START MMG3d (%d ELEMS)\n", mesh->ne) ;
+  if (opt[6] < 0) fprintf(stdout,"  -- MMG3d, Release %s (%s) \n",M_VER,M_REL);
+  if (opt[6] < -10) fprintf(stdout,"     Copyright (c) LJLL/IMB, 2010\n");
+  if (opt[6] < -10) fprintf(stdout,"    %s\n",COMPIL);
+  
+  
+  signal(SIGABRT,MMG_excfun);
+  signal(SIGFPE,MMG_excfun);
+  signal(SIGILL,MMG_excfun);
+  signal(SIGSEGV,MMG_excfun);
+  signal(SIGTERM,MMG_excfun);
+  signal(SIGINT,MMG_excfun);
+ /* atexit(MMG_endcod);
+*/
+
+  TIM_tminit(MMG_ctim,TIMEMAX);
+  TIM_chrono(ON,&MMG_ctim[0]);
+  TIM_chrono(OFF,&MMG_ctim[0]);
+  /* default values */
+  info = &mesh->info;
+
+  info->imprim   = opt[6];
+  info->memory   = 0;
+  info->ddebug   = opt[1];
+  info->option   = opt[0];
+  info->bucksiz  = opt[2];
+  info->noswap   = opt[3];
+  info->nomove   = opt[5];
+  info->noinsert = opt[4];
+  info->rn2      = opt[7];//3;
+  info->rn       = opt[8];//500;
+  alert          = 0;
+  info->dt       = 1.;
+  info->bdry     = 0;
+
+  imprim         = info->imprim;
+  MMG_imprim   = imprim;
+ 
+  if ( imprim )   fprintf(stdout,"\n  -- INPUT DATA\n");
+  TIM_chrono(ON,&MMG_ctim[1]);
+  MMG_inputdata(mesh,sol);
+  if ( sol->np && sol->np != mesh->np ) {
+    fprintf(stdout,"  ## WARNING: WRONG SOLUTION NUMBER. IGNORED\n");
+    sol->np = 0;
+  }
+  /*read displacement*/
+  if ( abs(info->option) == 9 && !mesh->disp)  {
+    //M_free(mesh->adja);
+    fprintf(stdout,"  ## WARNING: NO DISPLACEMENT. IGNORED\n");
+    return(0);
+  }  
+  if ( !MMG_setfunc(sol->offset) ) return(1);
+  if ( !MMG_scaleMesh(mesh,sol) )  return(1);
+  TIM_chrono(OFF,&MMG_ctim[1]);
+  if ( imprim < -10)
+    fprintf(stdout,"  -- DATA READING COMPLETED.     %.2f sec.\n",
+            TIM_gttime(MMG_ctim[1]));
+
+  alert = MMG_outqua(mesh,sol);
+  if(alert) {
+    fprintf(stdout,"\n \n    ## INVALID MESH. STOP\n");
+    exit(1);  
+  }
+  if(MMG_imprim < 0) MMG_outquacubic(mesh,sol);
+
+  if (info->imprim  < -10){
+    fprintf(stdout,"\n  %s\n   MODULE MMG3D-LJLL/IMB : %s (%s)  %s\n  \n",
+	    M_STR,M_VER,M_REL,sol->offset == 1 ? "ISO" : "ANISO");
+    fprintf(stdout,"  MAXIMUM NUMBER OF POINTS    (NPMAX) : %8d\n",mesh->npmax);
+    fprintf(stdout,"  MAXIMUM NUMBER OF TRIANGLES (NTMAX) : %8d\n",mesh->ntmax);
+    fprintf(stdout,"  MAXIMUM NUMBER OF ELEMENTS  (NEMAX) : %8d\n",mesh->nemax);
+  }
+
+  /* mesh analysis */
+  TIM_chrono(ON,&MMG_ctim[2]);
+  if ( MMG_imprim < 0 )   fprintf(stdout,"\n  -- PHASE 1 : ANALYSIS\n");
+  if ( !MMG_hashTetra(mesh) )    return(1);
+  if ( !MMG_markBdry(mesh) )     return(1);
+  if (abs(mesh->info.option)==10) {
+    MMG_saveMesh(mesh,"tetra.mesh");
+    return(0);
+  }           
+  if ( !sol->np) {
+    fprintf(stdout,"  WARNING NO METRIC FOUND %d\n",sol->np);    
+    im = 1;
+    if(!MMG_doSol(mesh,sol) ) return(1);
+  } else
+    im = 0;
+    
+  TIM_chrono(OFF,&MMG_ctim[2]);
+  if ( MMG_imprim < 0 )
+    fprintf(stdout,"  -- PHASE 1 COMPLETED.     %.2f sec.\n",
+            TIM_gttime(MMG_ctim[2]));
+  if ( info->ddebug )  MMG_chkmsh(mesh,1,1);
+  
+  if ( abs(MMG_imprim) > 4 )  {
+	  MMG_prilen(mesh,sol);
+    MMG_ratio(mesh,sol,NULL);        
+  }                       
+
+#ifdef USE_SCOTCH
+  /* renumbering begin */
+   if ( info->rn2 & 1 )  { 
+     TIM_chrono(ON,&MMG_ctim[5]); 
+     if ( MMG_imprim < -6)
+       fprintf(stdout,"renumbering");
+     renumbering(info->rn, mesh, sol);  
+    
+     if ( !MMG_hashTetra(mesh) )    return(1);    
+     TIM_chrono(OFF,&MMG_ctim[5]); 
+     if ( MMG_imprim < -6)
+       fprintf(stdout,"  -- PHASE RENUMBERING COMPLETED.     %.2f sec.\n",
+               TIM_gttime(MMG_ctim[5]));
+     if ( info->ddebug ) MMG_chkmsh(mesh,1,0);
+   }
+   /* renumbering end */
+#endif 
+
+  /* mesh optimization */
+  if ( info->option ) {
+    TIM_chrono(ON,&MMG_ctim[3]);
+    if ( MMG_imprim < 0 )   fprintf(stdout,"\n  -- PHASE 2 : UNIT MESH\n");
+    if ( abs(info->option) == 9 ) {  
+      if(!MMG_mmg3d9(mesh,sol,&alert)) {
+        if ( !MMG_unscaleMesh(mesh,sol) )  return(1);
+        MMG_saveMesh(mesh,"errormoving.mesh");
+	      //MMG_saveSol(mesh,sol,mesh->outf);
+	      return(1);
+      }
+      /*puts("appel 1");
+      MMG_mmg3d1(mesh,sol,&alert);*/ 
+      for (k=1; k<=mesh->np; k++) {
+        iadr = (k-1)*sol->offset + 1;
+        m    = &sol->met[iadr];      
+        /*calcul du log de M*/
+        if ( !eigenv(1,m,lambda,v) ) {
+             puts("pbs eigen"); 
+         return(0);
+        }
+        for (i=0; i<3; i++) lambda[i] = log(lambda[i]);
+        mold    = &sol->metold[iadr]; 
+        kk = 0;
+      for (ii=0; ii<3; ii++) {
+           for (jj=ii; jj<3; jj++) {
+              mold[kk] = lambda[0]*v[0][ii]*v[0][jj] + 
+                         lambda[1]*v[1][ii]*v[1][jj] +
+                         lambda[2]*v[2][ii]*v[2][jj]; 
+              kk = kk+1;
+           }                     
+         }
+      }
+    } 
+    
+    if(!info->noinsert) {
+      if(abs(info->option) == 4){ 
+        MMG_mmg3d4(mesh,sol,&alert);
+      } else {
+        MMG_mmg3d1(mesh,sol,&alert);
+      }
+    }
+      
+    TIM_chrono(OFF,&MMG_ctim[3]);
+    if ( MMG_imprim < 0 )
+      fprintf(stdout,"  -- PHASE 2 COMPLETED.     %.2f sec.\n",
+              TIM_gttime(MMG_ctim[3]));
+  }
+
+  /* mesh regularisation */
+  if ( info->option > -1 ) {
+#ifdef USE_SCOTCH
+    /* renumbering begin */
+    /*MMG_chkmsh(mesh,0,-1); 
+    puts("3er chk ok");
+    */
+    if ( info->rn2 & 2 )  { 
+      TIM_chrono(ON,&MMG_ctim[6]); 
+      if ( MMG_imprim < -6)
+        fprintf(stdout,"renumbering"); 
+			renumbering(info->rn, mesh, sol);
+      if ( !MMG_hashTetra(mesh) )    return(1);    
+      TIM_chrono(OFF,&MMG_ctim[6]); 
+      if ( MMG_imprim < -6)
+        fprintf(stdout,"  -- PHASE RENUMBERING COMPLETED.     %.2f sec.\n",
+              TIM_gttime(MMG_ctim[6]));
+      if ( info->ddebug ) MMG_chkmsh(mesh,1,0);
+    } 
+    /* renumbering end */   
+#endif
+    TIM_chrono(ON,&MMG_ctim[4]);
+    if ( MMG_imprim < 0)  fprintf(stdout,"\n  -- PHASE 3 : OPTIMIZATION\n");
+    if ( !alert )  {
+      if ( info->option == 9 ) { 
+         MMG_optra4(mesh,sol); 
+      } else {
+         MMG_optra4(mesh,sol); 
+      }
+    }
+    
+    if ( info->ddebug )  MMG_chkmsh(mesh,1,1);
+    TIM_chrono(OFF,&MMG_ctim[4]);
+    if ( MMG_imprim <  0)
+      fprintf(stdout,"  -- PHASE 3 COMPLETED.     %.2f sec.\n",
+              TIM_gttime(MMG_ctim[4]));
+  }
+
+
+  if ( info->option > -1 || abs(MMG_imprim) > 3 ) {
+    MMG_outqua(mesh,sol);
+    if(MMG_imprim < 0) MMG_outquacubic(mesh,sol);
+    MMG_prilen(mesh,sol);
+    MMG_ratio(mesh,sol,NULL);
+  }
+  if ( MMG_imprim ) fprintf(stdout,"  -- END MMG3D (%d ELEMS)\n", mesh->ne);
+  if ( alert )
+    fprintf(stdout,"\n  ## WARNING: INCOMPLETE MESH  %d , %d\n",
+            mesh->np,mesh->ne);
+
+  if ( MMG_imprim )  fprintf(stdout,"\n  -- SAVING DATA FILE \n");
+  TIM_chrono(ON,&MMG_ctim[1]);
+  if ( !MMG_unscaleMesh(mesh,sol) )  return(1);
+  MMG_tassage(mesh,sol);
+  
+  TIM_chrono(OFF,&MMG_ctim[1]);
+  if ( MMG_imprim ) {
+    fprintf(stdout,"     NUMBER OF GIVEN VERTICES   %8d\n",mesh->npfixe);
+    if ( mesh->ntfixe )
+      fprintf(stdout,"     NUMBER OF GIVEN TRIANGLES  %8d\n",mesh->ntfixe);
+    fprintf(stdout,"     NUMBER OF GIVEN ELEMENTS   %8d\n",mesh->nefixe);
+    fprintf(stdout,"     TOTAL NUMBER OF VERTICES   %8d\n",mesh->np);
+    fprintf(stdout,"     TOTAL NUMBER OF TRIANGLES  %8d\n",mesh->nt);
+  }
+
+  if ( MMG_imprim )  fprintf(stdout,"  -- SAVING COMPLETED\n");
+
+  if ( MMG_imprim < -4 || info->ddebug )  M_memDump();
+
+  return(alert);
+}
diff --git a/contrib/mmg3d/build/sources/movevertex.c b/contrib/mmg3d/build/sources/movevertex.c
new file mode 100644
index 0000000000000000000000000000000000000000..207afc28f4f8c1ace239d3bfd5ec1803f5af7cbe
--- /dev/null
+++ b/contrib/mmg3d/build/sources/movevertex.c
@@ -0,0 +1,366 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_movevertex_ani(pMesh mesh,pSol sol,int k,int ib) {
+  pTetra 	pt,pt1;
+  pPoint 	ppa,ppb,p1,p2,p3;
+  List  	list;
+  int    	j,iadr,ipb,iter,maxiter,l,lon,iel,i1,i2,i3;
+  double  	*mp,coe;
+  double 	ax,ay,az,bx,by,bz,nx,ny,nz,dd,len,qual,oldc[3];    
+  assert(k);
+  assert(ib<4);
+  pt = &mesh->tetra[k];
+  ppa  = &mesh->point[pt->v[ib]];
+  iadr = (pt->v[ib]-1)*sol->offset + 1;
+  mp   = &sol->met[iadr];
+  
+  /*compute normal*/
+  i1 = pt->v[MMG_idir[ib][0]];
+  i2 = pt->v[MMG_idir[ib][1]];
+  i3 = pt->v[MMG_idir[ib][2]];
+  p1 = &mesh->point[i1];
+  p2 = &mesh->point[i2];
+  p3 = &mesh->point[i3];
+  
+  ax = p3->c[0] - p1->c[0];
+  ay = p3->c[1] - p1->c[1];
+  az = p3->c[2] - p1->c[2];
+
+  bx = p2->c[0] - p1->c[0];
+  by = p2->c[1] - p1->c[1];
+  bz = p2->c[2] - p1->c[2];
+
+  nx = (ay*bz - az*by);
+  ny = (az*bx - ax*bz);
+  nz = (ax*by - ay*bx);
+    
+  dd = sqrt(nx*nx+ny*ny+nz*nz);  
+  dd = 1./dd;
+  nx *=dd;
+  ny *=dd;
+  nz *=dd;
+  len = 0;
+  for (j=0; j<3; j++) {
+    ipb = pt->v[ MMG_idir[ib][j] ];
+    ppb = &mesh->point[ipb];
+
+    ax  = ppb->c[0] - ppa->c[0];
+    ay  = ppb->c[1] - ppa->c[1];
+    az  = ppb->c[2] - ppa->c[2];
+
+    dd =       mp[0]*ax*ax + mp[3]*ay*ay + mp[5]*az*az \
+  	+ 2.0*(mp[1]*ax*ay + mp[2]*ax*az + mp[4]*ay*az);
+    assert(dd>0);
+    len += sqrt(dd);
+  }	
+    
+  dd  = 1.0 / (double)3.;
+  len = 1.0 / len;
+  len *= dd;
+  memcpy(oldc,ppa->c,3*sizeof(double));
+  	    
+  lon = MMG_boulep(mesh,k,ib,&list);
+  if(mesh->info.imprim < 0 ) if(lon < 4 && lon) printf("lon petit : %d\n",lon);
+  if(!lon) return(0);
+  
+  coe	  = 1.;
+  iter    = 0;
+  maxiter = 20;
+  do {
+    ppa->c[0] = oldc[0] + coe * nx * len;
+    ppa->c[1] = oldc[1] + coe * ny * len;
+    ppa->c[2] = oldc[2] + coe * nz * len;
+
+    for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      pt1 = &mesh->tetra[iel];
+
+      qual = MMG_caltet(mesh,sol,iel);
+       if ( !((qual < pt1->qual) || (qual < pt->qual /2.)) )  break;
+       list.qual[l] = qual;
+
+     }
+     if ( l > lon )  break;
+     coe *= 0.5;
+   }
+   while ( ++iter <= maxiter );
+   if ( iter > maxiter) {
+     memcpy(ppa->c,oldc,3*sizeof(double));
+     return(0);
+   }
+   
+   for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      pt1 = &mesh->tetra[iel];
+      pt1->qual = list.qual[l];
+//    if ( pt1->qual < declic )
+//	MMG_kiudel(queue,iel);
+   }
+   return(1);
+ 
+}
+
+
+int MMG_movevertex_iso(pMesh mesh,pSol sol,int k,int ib) {
+  pTetra pt,pt1;
+  pPoint ppa,ppb,p1,p2,p3;
+  List  list;
+  int    j,iadr,ipb,iter,maxiter,l,lon,iel,i1,i2,i3;
+  double  hp,coe,crit;
+  double ax,ay,az,bx,by,bz,nx,ny,nz,dd,len,qual,oldc[3];
+  
+  assert(k);
+  assert(ib<4);
+  pt = &mesh->tetra[k];
+   
+  ppa  = &mesh->point[pt->v[ib]];
+  iadr = (pt->v[ib]-1)*sol->offset + 1;
+  hp   = sol->met[iadr];
+  
+  /*compute normal*/
+  i1 = pt->v[MMG_idir[ib][0]];
+  i2 = pt->v[MMG_idir[ib][1]];
+  i3 = pt->v[MMG_idir[ib][2]];
+  p1 = &mesh->point[i1];
+  p2 = &mesh->point[i2];
+  p3 = &mesh->point[i3];
+  
+  ax = p3->c[0] - p1->c[0];
+  ay = p3->c[1] - p1->c[1];
+  az = p3->c[2] - p1->c[2];
+
+  bx = p2->c[0] - p1->c[0];
+  by = p2->c[1] - p1->c[1];
+  bz = p2->c[2] - p1->c[2];
+
+  nx = (ay*bz - az*by);
+  ny = (az*bx - ax*bz);
+  nz = (ax*by - ay*bx);
+    
+  dd = sqrt(nx*nx+ny*ny+nz*nz); 
+  dd = 1./dd;
+  nx *=dd;
+  ny *=dd;
+  nz *=dd;
+  len = 0;
+  for (j=0; j<3; j++) {
+    ipb = pt->v[ MMG_idir[ib][j] ];
+    ppb = &mesh->point[ipb];
+
+    ax  = ppb->c[0] - ppa->c[0];
+    ay  = ppb->c[1] - ppa->c[1];
+    az  = ppb->c[2] - ppa->c[2];
+
+    dd    =   sqrt(ax*ax +ay*ay +az*az);
+    len  +=   dd/hp;
+  }	
+   
+  dd  = 1.0 / (double)3.;
+  len *= dd; 
+  if(len > 0.) len = 1.0 / len;
+  else printf("MMG_movevertex len %e\n",len);
+
+  memcpy(oldc,ppa->c,3*sizeof(double));
+  	    
+  lon = MMG_boulep(mesh,k,ib,&list);
+  if(mesh->info.imprim < 0) if(lon < 4 && lon) printf("lon petit : %d\n",lon);
+  if(!lon) return(0);
+
+  /*qual crit*/
+  crit = pt->qual;
+  for (l=2; l<=lon; l++) {
+    iel = list.tetra[l] >> 2;
+    pt1 = &mesh->tetra[iel];
+    if ( pt1->qual > crit )  crit = pt1->qual;
+  }
+ /* if ( (crit > 100/ALPHAD) ) {
+    crit *= 1.1;
+  } else 
+   */ crit *= 0.99;
+  
+  coe	  = 1.;
+  iter    = 0;
+  maxiter = 20;
+  do {
+
+    ppa->c[0] = oldc[0] + coe * nx * len;
+    ppa->c[1] = oldc[1] + coe * ny * len;
+    ppa->c[2] = oldc[2] + coe * nz * len;
+    for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      pt1 = &mesh->tetra[iel];
+      qual = MMG_caltet(mesh,sol,iel);
+      if ( qual > crit )  break;
+       list.qual[l] = qual;
+
+     }
+     if ( l > lon )  break;
+     coe *= 0.5;
+   }
+   while ( ++iter <= maxiter );
+   if ( iter > maxiter) {
+     memcpy(ppa->c,oldc,3*sizeof(double));
+     return(-2);
+   }
+   
+   for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      pt1 = &mesh->tetra[iel];
+      pt1->qual = list.qual[l];
+//    if ( pt1->qual < declic )
+//	MMG_kiudel(queue,iel);
+   }
+   return(1);
+ 
+}
+
+
+int MMG_movevertexbdry(pMesh mesh,pSol sol,int k,int ib) {
+  pTetra 	pt,pt1;
+  pPoint 	ppa,ppb,p1,p2,p3;
+  List  	list;
+  int    	j,ipb,iter,maxiter,l,lon,iel,i1,i2,i3;
+  double    coe,crit;
+  double 	ax,ay,az,bx,by,bz,nx,ny,nz,dd,len,qual,oldc[3];
+  
+  assert(k);
+  assert(ib<4);
+  pt = &mesh->tetra[k];
+    
+  ppa  = &mesh->point[pt->v[ib]];
+  
+  /*compute normal*/
+  i1 = pt->v[MMG_idir[ib][0]];
+  i2 = pt->v[MMG_idir[ib][1]];
+  i3 = pt->v[MMG_idir[ib][2]];
+  p1 = &mesh->point[i1];
+  p2 = &mesh->point[i2];
+  p3 = &mesh->point[i3];
+  
+  ax = p3->c[0] - p1->c[0];
+  ay = p3->c[1] - p1->c[1];
+  az = p3->c[2] - p1->c[2];
+
+  bx = p2->c[0] - p1->c[0];
+  by = p2->c[1] - p1->c[1];
+  bz = p2->c[2] - p1->c[2];
+
+  nx = (ay*bz - az*by);
+  ny = (az*bx - ax*bz);
+  nz = (ax*by - ay*bx);
+    
+  dd = sqrt(nx*nx+ny*ny+nz*nz);  
+  dd = 1./dd;
+  nx *=dd;
+  ny *=dd;
+  nz *=dd;
+  len = 0;
+  for (j=0; j<3; j++) {
+    ipb = pt->v[ MMG_idir[ib][j] ];
+    ppb = &mesh->point[ipb];
+
+    ax  = ppb->c[0] - ppa->c[0];
+    ay  = ppb->c[1] - ppa->c[1];
+    az  = ppb->c[2] - ppa->c[2];
+
+    dd   = ax*ax + ay*ay + az*az;
+    len += sqrt(dd);
+  }	
+    
+  dd  = 1.0 / (double)3.;
+  len = 1.0 / len;
+  len *= dd;
+  memcpy(oldc,ppa->c,3*sizeof(double));
+  	    
+  lon = MMG_boulep(mesh,k,ib,&list);
+  if(mesh->info.imprim < 0 ) if(lon < 4 && lon) printf("lon petit : %d\n",lon);
+  if(!lon) return(0);
+  crit = pt->qual;
+  for (l=2; l<=lon; l++) {
+    iel = list.tetra[l] >> 2;
+    pt1 = &mesh->tetra[iel];
+    if ( pt1->qual > crit )  crit = pt1->qual;
+  }
+  coe	  = 0.5;
+  iter    = 0;
+  maxiter = 50;
+  do {
+    ppa->c[0] = oldc[0] + coe * nx * len;
+    ppa->c[1] = oldc[1] + coe * ny * len;
+    ppa->c[2] = oldc[2] + coe * nz * len;
+
+    for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      pt1 = &mesh->tetra[iel];
+
+      qual = MMG_caltet(mesh,sol,iel);
+      if ( !((qual < crit)) )  break;
+      list.qual[l] = qual;
+
+    }
+    if ( l > lon )  break;
+    coe *= 0.5;
+  }
+  while ( ++iter <= maxiter );
+  if ( iter > maxiter) {
+    memcpy(ppa->c,oldc,3*sizeof(double));
+    return(0);
+  }
+   
+   for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      pt1 = &mesh->tetra[iel];
+      pt1->qual = list.qual[l];
+//    if ( pt1->qual < declic )
+//	MMG_kiudel(queue,iel);
+   }
+   return(1);
+ 
+}
+
+
diff --git a/contrib/mmg3d/build/sources/optbdry.c b/contrib/mmg3d/build/sources/optbdry.c
new file mode 100644
index 0000000000000000000000000000000000000000..71152ccec560924002e60c2096aa2ecbb11de8a9
--- /dev/null
+++ b/contrib/mmg3d/build/sources/optbdry.c
@@ -0,0 +1,272 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+#define OCRIT    0.99
+
+int MMG_movevertexbdry(pMesh mesh,pSol sol,int k,int ib);
+int MMG_colpoi2(pMesh mesh,pSol sol,int iel,int ia,int ib,double coef);
+
+int MMG_optbdry(pMesh mesh,pSol sol,int k) {
+  pTetra pt;
+  int    ia,ib,i,*adja,iadr,ipb;
+
+  
+  
+  pt = &mesh->tetra[k];
+  iadr = 4*(k-1) + 1;
+  adja = &mesh->adja[iadr];
+      
+  /*essai de collapse du point qui n'est pas sur la peau*/
+  for(i=0 ; i<4 ; i++) if(!adja[i]) break;
+  
+  ib  = i;
+  ipb = pt->v[ib];
+  if(!mesh->info.noinsert) {
+    for(i=1 ; i<4 ; i++) {
+      ia = (ib+i)%4;
+      if(MMG_colpoi2(mesh,sol,k,ia,ib,QDEGRAD)) {
+        MMG_delPt(mesh,ipb);
+        break;
+      }
+    }
+  } else {
+    i=4;
+  }
+  
+  /*try to move the 4th vertex*/
+  if(i==4) {
+//if(k==402140) printf("colpoi refused, try move %d %d %d\n",k,ib,pt->v[ib]);
+    if(!MMG_movevertexbdry(mesh,sol,k,ib)) return(0);
+    return(2);
+  }
+   
+  return(1);
+    
+}
+int MMG_opt2peau(pMesh mesh,pSol sol,pQueue queue,int k,double declic) {
+  pTetra    pt,pt1;
+  pPoint    pa,pb,pc,pd;
+  List      list;
+  double    abx,aby,abz,acx,acy,acz,adx,ady,adz,v1,v2,v3,vol;
+  double    bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz,h[6];
+  double     crit;
+  double    s[4],dd,rapmin,rapmax;
+  int       i,ia,ib,ic,id,iarmax,iarmin;
+  int       lon,l,iel,ier;
+
+  ier = 0;
+  
+  pt = &mesh->tetra[k];
+  if ( !pt->v[0] )  return(-1);
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+  pa = &mesh->point[ia];
+  pb = &mesh->point[ib];
+  pc = &mesh->point[ic];
+  pd = &mesh->point[id];
+
+  /* volume */
+  abx = pb->c[0] - pa->c[0]; 
+  aby = pb->c[1] - pa->c[1]; 
+  abz = pb->c[2] - pa->c[2]; 
+
+  acx = pc->c[0] - pa->c[0]; 
+  acy = pc->c[1] - pa->c[1]; 
+  acz = pc->c[2] - pa->c[2]; 
+
+  adx = pd->c[0] - pa->c[0]; 
+  ady = pd->c[1] - pa->c[1]; 
+  adz = pd->c[2] - pa->c[2]; 
+
+  v1  = acy*adz - acz*ady;
+  v2  = acz*adx - acx*adz;
+  v3  = acx*ady - acy*adx;
+  vol = abx * v1 + aby * v2 + abz * v3;
+
+  /* max edge */
+  h[0] = abx*abx + aby*aby + abz*abz;
+  h[1] = acx*acx + acy*acy + acz*acz;
+  h[2] = adx*adx + ady*ady + adz*adz;
+
+  bcx = pc->c[0] - pb->c[0];
+  bcy = pc->c[1] - pb->c[1];
+  bcz = pc->c[2] - pb->c[2];
+
+  bdx = pd->c[0] - pb->c[0];
+  bdy = pd->c[1] - pb->c[1];
+  bdz = pd->c[2] - pb->c[2];
+
+  cdx = pd->c[0] - pc->c[0];
+  cdy = pd->c[1] - pc->c[1];
+  cdz = pd->c[2] - pc->c[2];
+
+  h[3] = bcx*bcx + bcy*bcy + bcz*bcz;
+  h[4] = bdx*bdx + bdy*bdy + bdz*bdz;
+  h[5] = cdx*cdx + cdy*cdy + cdz*cdz;
+
+  /* face areas */
+  dd = cdy*bdz - cdz*bdy; 
+  s[0] = dd * dd;
+  dd = cdz*bdx - cdx*bdz;
+  s[0] = s[0] + dd * dd;
+  dd = cdx*bdy - cdy*bdx;
+  s[0] = s[0] + dd * dd;
+  s[0] = sqrt(s[0]);
+
+  s[1] = sqrt(v1*v1 + v2*v2 + v3*v3);
+
+  dd = bdy*adz - bdz*ady;
+  s[2] = dd * dd;
+  dd = bdz*adx - bdx*adz;
+  s[2] = s[2] + dd * dd;
+  dd = bdx*ady - bdy*adx;
+  s[2] = s[2] + dd * dd;
+  s[2] = sqrt(s[2]);
+
+  dd = aby*acz - abz*acy;
+  s[3] = dd * dd;
+  dd = abz*acx - abx*acz;
+  s[3] = s[3] + dd * dd;
+  dd = abx*acy - aby*acx;
+  s[3] = s[3] + dd * dd;
+  s[3] = sqrt(s[3]);
+
+  /* classification */
+  rapmin = h[0];
+  rapmax = h[0];
+  iarmin = 0;
+  iarmax = 0;
+  for (i=1; i<6; i++) {
+    if ( h[i] < rapmin ) {
+      rapmin = h[i];
+      iarmin = i;
+    }
+    else if ( h[i] > rapmax ) {
+      rapmax = h[i];
+      iarmax = i;
+    }
+  }
+  rapmin = sqrt(rapmin);
+  rapmax = sqrt(rapmax);
+  
+  if(mesh->info.imprim < -9) printf("edge : %d %d\n",pt->v[MMG_iare[iarmax][0]],pt->v[MMG_iare[iarmax][1]]);
+  /*split edge*/
+  lon = MMG_coquil(mesh,k,iarmax,&list);
+  if(mesh->info.imprim < 0) {
+    //printf("lon %d\n",lon);
+    //if(!lon) printf("colle peau, edge peau\n");
+  }
+  
+  if(!lon) {  
+   for(i=0 ; i<6 ; i++) {
+     lon = MMG_coquil(mesh,k,i,&list);
+     if ( lon > 2 ) {
+       if ( !MMG_zaldy4(&list.hedg,3*LONMAX) ) {
+         fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM MMG_optbdry.\n");
+         MMG_kiufree(queue);
+         return(0);
+       }
+       crit = pt->qual;
+       for (l=2; l<=lon; l++) {
+         iel = list.tetra[l] / 6;
+         pt1 = &mesh->tetra[iel];
+         if ( pt1->qual > crit )  crit = pt1->qual;
+       }
+       crit *= OCRIT;
+       //crit = min(1000/ALPHAD,crit*1.3);
+       ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,1e9);
+       if(ier) {/*printf("on a reussi a l'enlever par MMG_swap\n");*/break;}
+       if ( ier == 0 && !mesh->info.noinsert) { 
+         crit = M_MIN(100./ALPHAD,crit*1.5);
+         ier = MMG_spledg(mesh,sol,queue,&list,lon,/*1.8**/crit,declic);
+       }
+       if(ier) {/*printf("on a reussi a l'enlever par split \n");*/break;}
+      
+       M_free(list.hedg.item);
+      }
+    }
+    
+    //M_free(list.hedg.item);
+
+    if(ier) {
+      M_free(list.hedg.item);
+      return(1);
+    }
+    else return(0);
+  } else {
+ 
+    if ( !MMG_zaldy4(&list.hedg,3*LONMAX) ) {
+      fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM MMG_optbdry.\n");
+      MMG_kiufree(queue);
+      return(0);
+    }
+    if ( lon > 2 ) {
+      crit = pt->qual;
+      for (l=2; l<=lon; l++) {
+        iel = list.tetra[l] / 6;
+        pt1 = &mesh->tetra[iel];
+        if ( pt1->qual > crit )  crit = pt1->qual;
+      }
+      crit *= OCRIT;
+      // crit = min(1000/ALPHAD,crit*1.3);
+      ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,1e9);
+      if ( ier == 0 && !mesh->info.noinsert) {
+        crit = M_MIN(100./ALPHAD,crit*1.5);
+        ier = MMG_spledg(mesh,sol,queue,&list,lon,/*1.8**/crit,declic);
+      }
+    }
+   
+  
+    M_free(list.hedg.item);
+    if(ier) return(1);
+    else return(0);
+ }
+  return(1);
+    
+}
diff --git a/contrib/mmg3d/build/sources/optcoq.c b/contrib/mmg3d/build/sources/optcoq.c
new file mode 100644
index 0000000000000000000000000000000000000000..c63a74d25f647a4a0d3e0e51f43b53f5538a59f9
--- /dev/null
+++ b/contrib/mmg3d/build/sources/optcoq.c
@@ -0,0 +1,120 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define SCRIT    0.95
+
+
+/* optim coquilles 3 a 7 */
+int MMG_optcoq(pMesh mesh,pSol sol) {
+  pTetra     pt,pt1;
+  pPoint     p1,p2;
+  List       list;
+  double     crit;
+  int       *adja,adj,iadr,i,j,k,ia,ib,jel,lon,ns,nss,it,maxtou;
+  char       tabar;
+int npp;
+
+  maxtou = 10;
+  nss    = 0;
+  it     = 0;
+
+  do {
+    ns  = 0;
+    npp =0;
+    
+    for (k=1; k<=mesh->ne; k++) {
+      pt = &mesh->tetra[k];
+      if ( !pt->v[0] )  continue;
+npp++;
+      /* mark internal edges */
+      tabar = 0;
+      iadr  = 4*(k-1) + 1;
+      adja  = &mesh->adja[iadr];
+      for (i=0; i<4; i++) {
+        adj = adja[i] >> 2;
+        if ( !adj || pt->ref != mesh->tetra[adj].ref ) {
+          tabar |= 1 << MMG_iarf[i][0];
+          tabar |= 1 << MMG_iarf[i][1];
+          tabar |= 1 << MMG_iarf[i][2];
+        }
+      }
+      if ( tabar == ALL_BDRY )  continue;
+
+      /* longest edge */
+      for (i=0; i<6; i++) {
+        if ( tabar & 1<<i )  continue;
+
+        ia = pt->v[ MMG_iare[i][0] ];
+        ib = pt->v[ MMG_iare[i][1] ];
+        p1 = &mesh->point[ia];
+        p2 = &mesh->point[ib];
+
+        lon  = MMG_coquil(mesh,k,i,&list);
+        if ( lon < 3 || lon > 7 )  continue;
+
+        /* qual crit */
+        crit = pt->qual;
+        for (j=2; j<=lon; j++) {
+          jel  = list.tetra[j] / 6;
+          pt1  = &mesh->tetra[jel];
+          if ( pt1->qual > crit )  crit = pt1->qual;
+        }
+        crit *= SCRIT;
+        /*
+        if ( MMG_swapar(mesh,sol,0,k,i,&list,lon,crit) ) {
+          ns++;
+          break;
+        }*/
+      }
+    }
+printf("  prop %d   swapped %d\n",npp,ns);
+    nss += ns;
+  }
+  while ( ns > 0 && ++it < maxtou );
+
+  return(nss);
+}
diff --git a/contrib/mmg3d/build/sources/optcte.c b/contrib/mmg3d/build/sources/optcte.c
new file mode 100644
index 0000000000000000000000000000000000000000..6651abd33bc15b4d7a109046a14e5a05b762d4c4
--- /dev/null
+++ b/contrib/mmg3d/build/sources/optcte.c
@@ -0,0 +1,275 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define  HQCOEF    0.45
+#define  HCRIT     0.8
+
+int MMG_optlentmp(pMesh mesh,pSol sol,double declic,int base) {
+  pTetra    pt,pt1;
+  pPoint    ppa,ppb;
+  pQueue    queue;
+  List      list;
+  double    cal,ctg,cx,cy,cz,ux,uy,uz,cpx,cpy,cpz,coe;
+  double    oldc[3],dd,dd1,dd2,len,lmi,lma;
+  double    *mp,*mb;
+  int       i,j,k,l,iel,lon,nm;
+  int       ipa,ipb,nb,nk,npp,iadr,iter,maxtou;
+
+  /* queueing tetrahedra */
+  queue = MMG_kiuini(mesh,mesh->ne,declic,base);
+  assert(queue);
+  mesh->flag++;
+
+  maxtou = 5;
+  nm  = 0;
+  npp = 0;
+  do {
+    k = MMG_kiupop(queue);
+    if ( !k )  break;
+
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+
+    for (i=0; i<4; i++) {
+      ipa = pt->v[i];
+      ppa = &mesh->point[ipa];
+      if ( ppa->tag & M_BDRY )  continue;
+      else if ( ppa->flag > mesh->flag )  continue;
+      npp++;
+
+      lon = MMG_boulep(mesh,k,i,&list);
+      if ( lon < 4 )  continue;
+
+      /* optimal point */
+      iadr = (ipa-1)*sol->offset + 1;
+      mp   = &sol->met[iadr];
+      cx  = 0.0;
+      cy  = 0.0;
+      cz  = 0.0;
+      nb  = 0;
+      cal = pt->qual;
+      lmi = LSHORT;
+      lma = LLONG;
+      for (l=1; l<=lon; l++) {
+        iel = list.tetra[l] >> 2;
+        nk  = list.tetra[l] % 4;
+        pt1 = &mesh->tetra[iel];
+        if ( pt1->qual > cal )  cal = pt1->qual;
+
+        for (j=0; j<3; j++) {
+          ipb = pt1->v[ MMG_idir[nk][j] ];
+          ppb = &mesh->point[ipb];
+
+          iadr = (ipb-1)*sol->offset + 1;
+          mb   = &sol->met[iadr];
+
+          ux  = ppb->c[0] - ppa->c[0];
+          uy  = ppb->c[1] - ppa->c[1];
+          uz  = ppb->c[2] - ppa->c[2];
+
+          dd1 =      mp[0]*ux*ux + mp[3]*uy*uy + mp[5]*uz*uz \
+              + 2.0*(mp[1]*ux*uy + mp[2]*ux*uz + mp[4]*uy*uz);
+          
+          dd2 =      mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+              + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+          len = 0.5*(dd1+dd2);
+          if ( len < lmi )      lmi = len;
+          else if (len > lma )  lma = len;
+
+          /* optimal point */
+          len = 1.0 / sqrt(dd1);
+          cx += ppa->c[0] + ux * len;
+          cy += ppa->c[1] + uy * len;
+          cz += ppa->c[2] + uz * len;
+          nb++;
+        }
+      }
+      if ( nb < 3 )  continue;
+      dd  = 1.0 / (double)nb;
+      cpx = cx*dd - ppa->c[0];
+      cpy = cy*dd - ppa->c[1];
+      cpz = cz*dd - ppa->c[2];
+
+      /* adjust position */
+      coe  = HQCOEF;
+      iter = 1;
+      if ( cal > 10.0 / ALPHAD )
+        ctg = cal * HCRIT;
+      else if ( cal > 5.0 / ALPHAD )
+        ctg = cal * 0.95;
+      else
+        ctg = cal * 0.975;
+      memcpy(oldc,ppa->c,3*sizeof(double));
+
+ctg = 1000. / ALPHAD;
+
+      do {
+        ppa->c[0] = oldc[0] + coe * cpx;
+        ppa->c[1] = oldc[1] + coe * cpy;
+        ppa->c[2] = oldc[2] + coe * cpz;
+
+        for (l=1; l<=lon; l++) {
+          iel = list.tetra[l] >> 2;
+          nk  = list.tetra[l] % 4;
+          pt1 = &mesh->tetra[iel];
+
+          cal = MMG_caltet(mesh,sol,iel);
+          if ( cal > ctg )  break;
+          list.qual[l] = cal;
+
+          /* check MMG_length */
+          for (j=0; j<3; j++) {
+            ipb = pt1->v[ MMG_idir[nk][j] ];
+            ppb = &mesh->point[ipb];
+
+            iadr = (ipb-1)*sol->offset + 1;
+            mb   = &sol->met[iadr];
+
+            ux  = ppb->c[0] - ppa->c[0];
+            uy  = ppb->c[1] - ppa->c[1];
+            uz  = ppb->c[2] - ppa->c[2];
+
+            dd1 =      mp[0]*ux*ux + mp[3]*uy*uy + mp[5]*uz*uz \
+                + 2.0*(mp[1]*ux*uy + mp[2]*ux*uz + mp[4]*uy*uz);
+          
+            dd2 =      mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+                + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+            len = 0.5*(dd1+dd2);
+
+            if ( len < lmi || len > lma )  break;
+          }
+          if ( j < 3 )  break;
+        }
+        if ( l > lon )  break;
+        coe *= 0.5;
+      }
+      while ( ++iter <= maxtou );
+      if ( iter > maxtou ) {
+        memcpy(ppa->c,oldc,3*sizeof(double));
+        continue;
+      }
+
+      /* update tetra */
+      for (l=1; l<=lon; l++) {
+        iel = list.tetra[l] >> 2;
+        nk  = list.tetra[l] % 4;
+        pt1 = &mesh->tetra[iel];
+        pt1->qual = list.qual[l];
+        pt1->flag = mesh->flag;
+        /*if ( pt1->qual < declic )
+          MMG_kiudel(queue,iel);
+        else if ( coe > 0.1 )
+          MMG_kiuput(queue,iel); */
+      }
+
+      /* interpol metric */
+      ppa->flag = mesh->flag + 1;
+      nm++;
+      break;
+    }
+  }
+  while ( k );
+
+  if ( mesh->info.imprim < -4 )
+    fprintf(stdout,"     %7d PROPOSED  %7d MOVED\n",npp,nm);
+
+  MMG_kiufree(queue);
+  return(nm);
+}
+
+
+
+
+
+int MMG_optcte(pMesh mesh,pSol sol) {
+  double	declicw;
+  double	declic;
+  int		base,nm,ns,it,maxtou,alert,nw;
+
+
+  alert  = 0;
+
+  /* optim coquil */
+  declic = 1. / ALPHAD;
+  maxtou = 10;
+  base   = -1;
+  it     = 0;
+
+  do {
+  
+    nw = 0;
+    declicw = 5./ALPHAD;
+//    if (!mesh->info.noswap) nw = MMG_opttyp(mesh,sol,declicw,&alert);   
+    
+    nm = MMG_optlentmp(mesh,sol,declic,-1);
+//     ns = 0;
+//     if ( !alert && !mesh->info.noswap ) {
+//       ns = MMG_cendel(mesh,sol,declic,base);
+//       if ( ns < 0 ) {
+//         alert = 1;
+// 	ns    = -ns;
+//       }
+//     }
+//     base = ++mesh->flag;
+//     
+//     if ( !mesh->disp ) if(it<2) MMG_optlap(mesh,sol);
+    
+ns = 0;    
+    if ( mesh->info.imprim && nm+ns )
+      fprintf(stdout,"     %8d MOVED  %8d SWAPPED\n",nm,ns);
+  }
+  while ( nm > 0.01*mesh->np && ++it < maxtou );
+    
+ 
+  MMG_outqua(mesh,sol);
+  MMG_prilen(mesh,sol);
+  puts("-------- APPEL MMG_optra4");
+  MMG_optra4(mesh,sol);
+  
+  return(1);
+}
+
diff --git a/contrib/mmg3d/build/sources/optlap.c b/contrib/mmg3d/build/sources/optlap.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a81bbba3af390306fc2e98ef73512b300e43a22
--- /dev/null
+++ b/contrib/mmg3d/build/sources/optlap.c
@@ -0,0 +1,284 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define  LLAMBDA   0.33  /*0.33*/
+#define  LMU       0.331   /*0.34*/
+
+int MMG_optlap(pMesh mesh,pSol sol) {
+  pTetra    pt,pt1;
+  pPoint    ppt,pptb,ppta;
+  pDispl    pdisp;
+  List      list;
+  int       it,i,k,lon,l,iel,ipt;
+  int       maxiter,ipta,iptb,ipt0,ipt1,ipt2,ipt3;
+  double    vol,ax,ay,az,bx,by,bz;
+  double    *nv,res,dd,ox,oy,oz,declic;
+
+  if(!mesh->disp) {
+	  mesh->disp = (pDispl)M_calloc(1,sizeof(Displ),"MMG_zaldy.displ");
+	  assert(mesh->disp);
+	  mesh->disp->mv = (double*)M_calloc(3*(mesh->npmax + 1),sizeof(double),"MMG_zaldy.displ");
+	  assert(mesh->disp->mv);
+	  mesh->disp->alpha = (short*)M_calloc(mesh->npmax+1,sizeof(short),"MMG_zaldy.displ");
+	  assert(mesh->disp->alpha);
+  }
+  maxiter = 3;  
+  nv = (double*)M_calloc(mesh->np+1,3*sizeof(double),"movlap.nv");
+  assert(nv);
+  
+  pdisp         = mesh->disp;	
+
+  it  = 1;
+  declic = 3./ALPHAD;
+  do {
+  
+    /*initialisation*/
+    for(i = 1 ; i<=mesh->np ; i++) {
+      ppt          = &mesh->point[i];
+      pdisp->alpha[i] = 0;
+      pdisp->mv[3*(i-1) + 1 + 0] = 0.;
+      pdisp->mv[3*(i-1) + 1 + 1] = 0.;
+      pdisp->mv[3*(i-1) + 1 + 2] = 0.;
+      
+    }
+
+    /*1st stage : laplacian*/    
+    for(k = 1 ; k<=mesh->ne ; k++) {
+      pt = &mesh->tetra[k];
+      if (!pt->v[0]) continue;
+      if (pt->qual > declic) continue;
+
+      for(i=0 ; i<6 ; i++) {
+        ipta   = pt->v[MMG_iare[i][0]];
+        ppta   = &mesh->point[ipta];
+        
+		iptb   = pt->v[MMG_iare[i][1]];
+        pptb   = &mesh->point[iptb];
+	
+		if(!(ppta->tag & M_BDRY)) {
+	  		pdisp->mv[3*(ipta-1) + 1 + 0] += pptb->c[0];
+	  		pdisp->mv[3*(ipta-1) + 1 + 1] += pptb->c[1];
+	  		pdisp->mv[3*(ipta-1) + 1 + 2] += pptb->c[2];
+	  		pdisp->alpha[ipta]++;
+		}
+		if(!(pptb->tag & M_BDRY)) {
+	  		pdisp->mv[3*(iptb-1) + 1 + 0] += ppta->c[0];
+	  		pdisp->mv[3*(iptb-1) + 1 + 1] += ppta->c[1];
+	  		pdisp->mv[3*(iptb-1) + 1 + 2] += ppta->c[2];
+	  		pdisp->alpha[iptb]++;
+		}
+      }
+    }
+
+    for(i=1 ; i<=mesh->np ; i++) {  
+      ppt           = &mesh->point[i];
+      if(pdisp->alpha[i]) {
+        dd            = 1./(double) pdisp->alpha[i];
+        pdisp->mv[3*(i-1) + 1 + 0] *= dd;
+        pdisp->mv[3*(i-1) + 1 + 1] *= dd;
+        pdisp->mv[3*(i-1) + 1 + 2] *= dd;
+        nv[3*(i-1) + 1] = ppt->c[0] + LLAMBDA * (ppt->c[0] - pdisp->mv[3*(i-1) + 1 + 0]);
+        nv[3*(i-1) + 2] = ppt->c[1] + LLAMBDA * (ppt->c[1] - pdisp->mv[3*(i-1) + 1 + 1]);
+        nv[3*(i-1) + 3] = ppt->c[2] + LLAMBDA * (ppt->c[2] - pdisp->mv[3*(i-1) + 1 + 2]);
+      } else {
+        nv[3*(i-1) + 1] = ppt->c[0];
+        nv[3*(i-1) + 2] = ppt->c[1];
+        nv[3*(i-1) + 3] = ppt->c[2];
+      
+      }
+      pdisp->alpha[i] = 0;
+      pdisp->mv[3*(i-1) + 1 + 0] = 0.;
+      pdisp->mv[3*(i-1) + 1 + 1] = 0.;
+      pdisp->mv[3*(i-1) + 1 + 2] = 0.;
+      
+    }
+
+    /*2nd stage : anti-laplacian*/
+    for(k = 1 ; k<=mesh->ne ; k++) {
+      pt = &mesh->tetra[k];
+      if (!pt->v[0]) continue;
+      if (pt->qual > declic) continue;
+      
+      for(i=0 ; i<6 ; i++) {
+        ipta   = pt->v[MMG_iare[i][0]];
+        ppta   = &mesh->point[ipta];
+       
+    	iptb   = pt->v[MMG_iare[i][1]];
+        pptb   = &mesh->point[iptb];
+	
+	if(!(ppta->tag & M_BDRY)) {
+	  pdisp->mv[3*(ipta-1) + 1 + 0] += nv[3*(iptb-1) + 1];
+	  pdisp->mv[3*(ipta-1) + 1 + 1] += nv[3*(iptb-1) + 2];
+	  pdisp->mv[3*(ipta-1) + 1 + 2] += nv[3*(iptb-1) + 3];
+	  pdisp->alpha[ipta]++;
+	}
+	if(!(pptb->tag & M_BDRY)) {
+	  pdisp->mv[3*(iptb-1) + 1 + 0] += nv[3*(ipta-1) + 1];
+	  pdisp->mv[3*(iptb-1) + 1 + 1] += nv[3*(ipta-1) + 2];
+	  pdisp->mv[3*(iptb-1) + 1 + 2] += nv[3*(ipta-1) + 3];
+	  pdisp->alpha[iptb]++;
+	}
+      }
+    }
+    
+    res= 0.;
+    for(i=1 ; i<=mesh->np ; i++) {  
+      ppt           = &mesh->point[i];
+      if(pdisp->alpha[i]) {
+        dd            = 1./(double) pdisp->alpha[i];
+        pdisp->mv[3*(i-1) + 1 + 0] *= dd;
+        pdisp->mv[3*(i-1) + 1 + 1] *= dd;
+        pdisp->mv[3*(i-1) + 1 + 2] *= dd;
+        ox = nv[3*(i-1) + 1];
+        oy = nv[3*(i-1) + 2];
+        oz = nv[3*(i-1) + 3];
+        nv[3*(i-1) + 1] = nv[3*(i-1) + 1] - LMU * (nv[3*(i-1) + 1] - pdisp->mv[3*(i-1) + 1 + 0]);
+        nv[3*(i-1) + 2] = nv[3*(i-1) + 2] - LMU * (nv[3*(i-1) + 2] - pdisp->mv[3*(i-1) + 1 + 1]);
+        nv[3*(i-1) + 3] = nv[3*(i-1) + 3] - LMU * (nv[3*(i-1) + 3] - pdisp->mv[3*(i-1) + 1 + 2]);
+	
+        dd = (nv[3*(i-1) + 1]-ox)*(nv[3*(i-1) + 1]-ox) 
+           + (nv[3*(i-1) + 2]-oy)*(nv[3*(i-1) + 2]-oy)
+	   + (nv[3*(i-1) + 3]-oz)*(nv[3*(i-1) + 3]-oz);
+        res +=dd;     
+      
+      } 
+      
+      
+      pdisp->alpha[i] = 0;
+      pdisp->mv[3*(i-1) + 1 + 0] = 0.;
+      pdisp->mv[3*(i-1) + 1 + 1] = 0.;
+      pdisp->mv[3*(i-1) + 1 + 2] = 0.;
+    }    
+/*    printf("---------- iter %d  res %e\r",it,res);
+    fflush(stdout); 
+*/    
+    /*check new coor*/
+    for(k = 1 ; k<=mesh->ne ; k++) {
+      pt = &mesh->tetra[k];
+      if(!pt->v[0]) continue;
+      
+      for(i=0 ; i<4 ; i++) {
+        ipt   = pt->v[i];
+        ppt   = &mesh->point[ipt];
+    	if(ppt->tag & M_BDRY) continue;
+	    if(ppt->tmp2) continue;
+	    ppt->tmp2 = 1;
+	    lon = MMG_boulep(mesh,k,i,&list);
+        for (l=1; l<=lon; l++) {
+          iel    = list.tetra[l] >> 2;
+          pt1    = &mesh->tetra[iel];
+	      ipt0   = 3*(pt1->v[0] - 1);
+	      ipt1   = 3*(pt1->v[1] - 1);
+	      ipt2   = 3*(pt1->v[2] - 1);
+	      ipt3   = 3*(pt1->v[3] - 1);
+	  
+          ax = nv[ipt2 + 1] - nv[ipt0 + 1];
+          ay = nv[ipt2 + 2] - nv[ipt0 + 2];
+          az = nv[ipt2 + 3] - nv[ipt0 + 3];
+  
+          bx = nv[ipt3 + 1] - nv[ipt0 + 1];
+          by = nv[ipt3 + 2] - nv[ipt0 + 2];
+          bz = nv[ipt3 + 3] - nv[ipt0 + 3];
+  
+          vol = (nv[ipt1 + 1] - nv[ipt0 + 1]) * (ay*bz - az*by) \
+              + (nv[ipt1 + 2] - nv[ipt0 + 2]) * (az*bx - ax*bz) \
+              + (nv[ipt1 + 3] - nv[ipt0 + 3]) * (ax*by - ay*bx);
+          if(vol < 0) {/*printf("reject1 %e\n",vol);*/break;}
+        }
+	if(l<=lon) {
+	  memcpy(&pdisp->mv[3*(ipt-1) + 1],ppt->c,3*sizeof(double));
+          for (l=1; l<=lon; l++) {
+            iel    = list.tetra[l] >> 2;
+            pt1    = &mesh->tetra[iel];
+	    ipt0   = 3*(pt1->v[0] - 1);
+	    ipt1   = 3*(pt1->v[1] - 1);
+	    ipt2   = 3*(pt1->v[2] - 1);
+	    ipt3   = 3*(pt1->v[3] - 1);
+	  
+            ax = nv[ipt2 + 1] - nv[ipt0 + 1];
+            ay = nv[ipt2 + 2] - nv[ipt0 + 2];
+            az = nv[ipt2 + 3] - nv[ipt0 + 3];
+  
+            bx = nv[ipt3 + 1] - nv[ipt0 + 1];
+            by = nv[ipt3 + 2] - nv[ipt0 + 2];
+            bz = nv[ipt3 + 3] - nv[ipt0 + 3];
+  
+            vol = (nv[ipt1 + 1] - nv[ipt0 + 1]) * (ay*bz - az*by) \
+                + (nv[ipt1 + 2] - nv[ipt0 + 2]) * (az*bx - ax*bz) \
+                + (nv[ipt1 + 3] - nv[ipt0 + 3]) * (ax*by - ay*bx);
+            if(vol < 0) {/*printf("reject %e\n",vol);*/break;}
+          }
+	  if(l<=lon) break;
+	}
+      }
+      if(i<4) break;
+    }  
+    if(k > mesh->ne) { 
+      /*update coor*/
+      for(i=1 ; i<=mesh->np ; i++) {
+        ppt   = &mesh->point[i];
+        ppt->c[0] = nv[3*(i-1) + 1];
+        ppt->c[1] = nv[3*(i-1) + 2];
+        ppt->c[2] = nv[3*(i-1) + 3];
+      }
+      for(k=1 ; k<=mesh->ne ; k++) {
+        pt = &mesh->tetra[k];
+	if(!pt->v[0]) continue;
+	pt->qual = MMG_caltet(mesh,sol,k);
+      }
+      if(mesh->info.imprim < 0) fprintf(stdout,"              LAPLACIAN : %8f\n",res);      
+    } else {     
+      if(mesh->info.imprim < 0) fprintf(stdout,"              NO LAPLACIAN\n");
+      break;
+    }
+    if(res<1e-5) break;
+   
+  } while(it++ < maxiter);
+
+  M_free(nv);
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/optlen.c b/contrib/mmg3d/build/sources/optlen.c
new file mode 100644
index 0000000000000000000000000000000000000000..ab8b07a26ac5f90a86d4c387f87b35330e2a3b7a
--- /dev/null
+++ b/contrib/mmg3d/build/sources/optlen.c
@@ -0,0 +1,610 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define  HQCOEF    0.9 
+#define  HCRIT     0.98
+
+double MMG_rao(pMesh mesh,int k,int inm);
+int MMG_optlen_ani(pMesh mesh,pSol sol,double declic,int base) {
+  pTetra    pt,pt1;
+  pPoint    ppa,ppb;
+  pQueue    queue;
+  List      list;
+  double    cal,ctg,cx,cy,cz,ux,uy,uz,cpx,cpy,cpz,coe;
+  double    oldc[3],dd,dd1,dd2,len,lmi,lma;
+  double    *mp,*mb,wcal;
+  int       i,j,k,l,iel,lon,nm;
+  int       ipa,ipb,nb,nk,npp,iadr,iter,maxtou;
+
+  /* queueing tetrahedra */
+  queue = MMG_kiuini(mesh,mesh->ne,declic,base - 1);
+  assert(queue);
+  
+  maxtou = 3;
+  nm     = 0;
+  npp    = 0;
+  wcal   = 5. / ALPHAD;
+  
+  do {
+    k = MMG_kiupop(queue);
+    if ( !k )  break;
+
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    else if ( pt->flag != base - 1 )  continue;
+    
+    for (i=0; i<4; i++) {
+      ipa = pt->v[i];
+      ppa = &mesh->point[ipa];
+      if ( ppa->tag & M_BDRY )  continue;
+      else if ( ppa->flag != base - 1)  continue;
+      npp++;
+
+      lon = MMG_boulep(mesh,k,i,&list);
+      if ( lon < 4 )  continue;
+
+      /* optimal point */
+      iadr = (ipa-1)*sol->offset + 1;
+      mp   = &sol->met[iadr];
+      cx  = 0.0;
+      cy  = 0.0;
+      cz  = 0.0;
+      nb  = 0;
+      cal = pt->qual;
+      lmi = LSHORT;
+      lma = LLONG;
+      for (l=1; l<=lon; l++) {
+        iel = list.tetra[l] >> 2;
+        nk  = list.tetra[l] % 4;
+        pt1 = &mesh->tetra[iel];
+        if ( pt1->qual > cal )  cal = pt1->qual;
+
+        for (j=0; j<3; j++) {
+          ipb = pt1->v[ MMG_idir[nk][j] ];
+          ppb = &mesh->point[ipb];
+
+          iadr = (ipb-1)*sol->offset + 1;
+          mb   = &sol->met[iadr];
+
+          ux  = ppb->c[0] - ppa->c[0];
+          uy  = ppb->c[1] - ppa->c[1];
+          uz  = ppb->c[2] - ppa->c[2];
+
+          dd1 =      mp[0]*ux*ux + mp[3]*uy*uy + mp[5]*uz*uz \
+              + 2.0*(mp[1]*ux*uy + mp[2]*ux*uz + mp[4]*uy*uz);
+          
+          dd2 =      mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+              + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+          len = 0.5*(dd1+dd2);
+          if ( len < lmi )      lmi = len;
+          else if (len > lma )  lma = len;
+
+          /* optimal point */
+          len = 1.0 / sqrt(dd1);
+          cx += ppa->c[0] + ux * len;
+          cy += ppa->c[1] + uy * len;
+          cz += ppa->c[2] + uz * len;
+          nb++;
+        }
+      }
+      if ( nb < 3 )  continue;
+      dd  = 1.0 / (double)nb;
+      cpx = cx*dd - ppa->c[0];
+      cpy = cy*dd - ppa->c[1];
+      cpz = cz*dd - ppa->c[2];
+
+      /* adjust position */
+      coe  = HQCOEF;
+      iter = 1;
+      /*if ( cal > 10.0 / ALPHAD )
+        ctg = cal * HCRIT;
+      else*/ 
+        ctg = cal * HCRIT;//0.975;
+      memcpy(oldc,ppa->c,3*sizeof(double));
+
+      do {
+        ppa->c[0] = oldc[0] + coe * cpx;
+        ppa->c[1] = oldc[1] + coe * cpy;
+        ppa->c[2] = oldc[2] + coe * cpz;
+
+        for (l=1; l<=lon; l++) {
+          iel = list.tetra[l] >> 2;
+          nk  = list.tetra[l] % 4;
+          pt1 = &mesh->tetra[iel];
+
+          cal = MMG_caltet(mesh,sol,iel);
+          if ( cal > ctg )  break;
+          list.qual[l] = cal;
+
+          /* check length */
+        /*  for (j=0; j<3; j++) {
+            ipb = pt1->v[ MMG_idir[nk][j] ];
+            ppb = &mesh->point[ipb];
+            
+            iadr = (ipb-1)*sol->offset + 1;
+            mb   = &sol->met[iadr];
+
+            ux  = ppb->c[0] - ppa->c[0];
+            uy  = ppb->c[1] - ppa->c[1];
+            uz  = ppb->c[2] - ppa->c[2];
+
+            dd1 =      mp[0]*ux*ux + mp[3]*uy*uy + mp[5]*uz*uz \
+                + 2.0*(mp[1]*ux*uy + mp[2]*ux*uz + mp[4]*uy*uz);
+          
+            dd2 =      mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+                + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+            len = 0.5*(dd1+dd2);
+
+            if ( len < lmi || len > lma )  break;
+          }
+          if ( j < 3 )  break;*/
+        }
+        if ( l > lon )  break;
+        coe *= 0.5;
+      }
+      while ( ++iter <= maxtou );
+      if ( iter > maxtou ) {
+        memcpy(ppa->c,oldc,3*sizeof(double));
+	    ppa->flag = base - 2;
+        continue;
+      }
+
+      /* update tetra */
+      for (l=1; l<=lon; l++) {
+        iel = list.tetra[l] >> 2;
+        nk  = list.tetra[l] % 4;
+        pt1 = &mesh->tetra[iel];
+        pt1->qual = list.qual[l];
+        pt1->flag = base;
+		for(i=0; i<4; i++)  mesh->point[pt1->v[i]].flag = base; 
+        if ( pt1->qual < declic )
+          MMG_kiudel(queue,iel);
+        else if ( coe > 0.1 )
+          MMG_kiuput(queue,iel); 
+      }
+
+      /* interpol metric */
+      ppa->flag = base + 1;
+      nm++;
+      break;
+    }
+  }
+  while ( k );
+
+  if ( mesh->info.imprim < -4 )
+    fprintf(stdout,"     %7d PROPOSED  %7d MOVED\n",npp,nm);
+
+  MMG_kiufree(queue);
+  return(nm);
+}
+
+
+/* optimise using heap */
+int MMG_optlen_iso(pMesh mesh,pSol sol,double declic,int base) {
+  pTetra    pt,pt1;
+  pPoint    ppa,ppb;
+  pQueue    queue;
+  List      list;
+  double    oldc[3],cal,ctg,cx,cy,cz,ux,uy,uz,cpx,cpy,cpz,coe,dd,len;
+  double    hb,hp,*ca,*cb;
+  int       i,j,k,l,iel,lon,nm;
+  int       ipa,ipb,nb,nk,npp,iadr,iter,maxtou;
+int nrj;    
+double tmp1,tmp2,tmp3;
+  /* queue on quality */
+  queue = MMG_kiuini(mesh,mesh->ne,declic,base - 1);
+  assert(queue);
+
+  maxtou = 10;
+  nm     = 0;
+  npp    = 0;
+nrj = 0;  
+  do {
+    k = MMG_kiupop(queue);
+    if ( !k )  break;
+    npp++;
+
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+   //	else if ( pt->flag != base - 1 ) continue;
+    for (i=0; i<4; i++) {
+      ipa = pt->v[i];
+      ppa = &mesh->point[ipa];
+      if ( ppa->tag & M_BDRY )  continue;
+    //  else if ( ppa->flag != base - 1 )  continue;
+
+      lon = MMG_boulep(mesh,k,i,&list);
+      if ( lon < 4 )  continue;
+
+      /* optimal point */
+      ca   = &ppa->c[0];
+      iadr = (ipa-1)*sol->offset + 1;
+      hp   = sol->met[iadr];
+      cx   = 0.0;
+      cy   = 0.0;
+      cz   = 0.0;
+      nb   = 0;
+      cal  = pt->qual;
+      for (l=1 ; l<=lon; l++) {
+        iel = list.tetra[l] >> 2;
+        nk  = list.tetra[l] % 4;
+        pt1 = &mesh->tetra[iel];
+        if ( pt1->qual > cal )  cal = pt1->qual;
+		tmp1 = 0;
+		tmp2 = 0;
+		tmp3 = 0;
+        for (j=0; j<3; j++) {
+          ipb  = pt1->v[ MMG_idir[nk][j] ];
+          ppb  = &mesh->point[ipb]; 
+/*if(ppb->mark < 0) continue;
+ppb->mark = -ppb->mark;*/      
+//printf("on prend en compte point %d\n",ipb);
+	      cb   = &ppb->c[0];
+          iadr = (ipb-1)*sol->offset + 1;
+          hb   = sol->met[iadr];
+
+          len = MMG_length(ca,cb,&hp,&hb); 
+
+/*len = MMG_length(&(mesh->point[pt1->v[MMG_idir[nk][0]]].c[0]),&(mesh->point[pt1->v[MMG_idir[nk][1]]].c[0]),&hp,&hb); 
+len += MMG_length(&(mesh->point[pt1->v[MMG_idir[nk][0]]].c[0]),&(mesh->point[pt1->v[MMG_idir[nk][2]]].c[0]),&hp,&hb); 
+len += MMG_length(&(mesh->point[pt1->v[MMG_idir[nk][1]]].c[0]),&(mesh->point[pt1->v[MMG_idir[nk][2]]].c[0]),&hp,&hb); 
+len /= 3.;
+len = 1./len;
+len *=  MMG_length(ca,cb,&hp,&hb); */  	
+          /* optimal point */
+	      ux  = ppb->c[0] - ppa->c[0];
+          uy  = ppb->c[1] - ppa->c[1];
+          uz  = ppb->c[2] - ppa->c[2];
+          cx += ppa->c[0] + ux*(1. - 1./len);//ux * len;
+          cy += ppa->c[1] + uy*(1. - 1./len);//uy * len;
+          cz += ppa->c[2] + uz*(1. - 1./len);//uz * len; 
+		tmp1 +=ux*(1. - 1./len);
+		tmp2 +=uy*(1. - 1./len);
+		tmp3 +=uz*(1. - 1./len);
+/*memcpy(oldc,ppa->c,3*sizeof(double));
+ppa->c[0] = oldc[0] + ux*(1. - 1./len);//ppb->c[0] - (ux/MMG_length(oldc,cb,&hp,&hb))*len;
+ppa->c[1] = oldc[1] + uy*(1. - 1./len);//ppb->c[1] - (uy/MMG_length(oldc,cb,&hp,&hb))*len;
+ppa->c[2] = oldc[2] + uz*(1. - 1./len);//ppb->c[2] - (uz/MMG_length(oldc,cb,&hp,&hb))*len; 
+printf("%d len %e (%e)\n",j,MMG_length(ca,cb,&hp,&hb),len);
+memcpy(ppa->c,oldc,3*sizeof(double));            
+  */ 	
+          nb++;
+        }
+/*check amelioration*/
+/*memcpy(oldc,ppa->c,3*sizeof(double));
+ppa->c[0] = oldc[0] + tmp1/3.;
+ppa->c[1] = oldc[1] + tmp2/3.;
+ppa->c[2] = oldc[2] + tmp3/3.;
+if(MMG_caltet(mesh,sol,iel) > pt1->qual) {
+	printf("oups %d -- cal of %d ( %d ) %e > %e\n",nb,iel,ipa,pt1->qual,MMG_caltet(mesh,sol,iel)); 
+
+	//exit(0);
+	}
+else {
+//printf("%d -- cal of %d ( %d ) %e > %e\n",nb,iel,ipa,pt1->qual,MMG_caltet(mesh,sol,iel)); 
+}
+memcpy(ppa->c,oldc,3*sizeof(double));
+*/  
+      }
+/*for (l=1 ; l<=lon; l++) {
+  iel = list.tetra[l] >> 2;
+  nk  = list.tetra[l] % 4;
+  pt1 = &mesh->tetra[iel];
+
+  for (j=0; j<3; j++) {
+    ipb  = pt1->v[ MMG_idir[nk][j] ];
+    ppb  = &mesh->point[ipb]; 
+    ppb->mark = abs(ppb->mark);
+  }
+} */
+
+      if ( nb < 3 )  continue;
+      dd  = 1.0 / (double)nb;
+      cpx = cx*dd - ppa->c[0];
+      cpy = cy*dd - ppa->c[1];
+      cpz = cz*dd - ppa->c[2];
+
+      /* adjust position */
+      coe  = HQCOEF;
+      iter = 1; 
+      if(cal > 100./ALPHAD) {
+        ctg  = 0.99 * cal;  
+      } else {
+        ctg  = cal * HCRIT;   
+      }
+      memcpy(oldc,ppa->c,3*sizeof(double));
+      do {
+        ppa->c[0] =/* (1. - coe) * */oldc[0] + coe * cpx;
+        ppa->c[1] =/* (1. - coe) * */oldc[1] + coe * cpy;
+        ppa->c[2] =/* (1. - coe) * */oldc[2] + coe * cpz;
+
+        for (l=1; l<=lon; l++) {
+          iel = list.tetra[l] >> 2;
+          nk  = list.tetra[l] % 4;
+          pt1 = &mesh->tetra[iel];
+        
+          cal = MMG_caltet(mesh,sol,iel);
+          if ( cal > ctg )  break;
+          list.qual[l] = cal;
+        }
+        if ( l > lon )  break;
+        coe *= 0.5;
+      }
+      while ( ++iter <= maxtou );
+      if ( iter > maxtou ) {
+        memcpy(ppa->c,oldc,3*sizeof(double));
+	    ppa->flag = base - 2;   
+	    //if(k==154529) printf("cal(%d) %e %e %e\n",iel,cal,ctg,ctg/0.99);
+		/*exit(0);*/ nrj++;	
+        continue;
+      }
+
+      /* update tetra */
+      for (l=1; l<=lon; l++) {
+        iel = list.tetra[l] >> 2;
+        nk  = list.tetra[l] % 4;
+        pt1 = &mesh->tetra[iel];
+        pt1->qual = list.qual[l];
+        pt1->flag = base;
+	    for(i=0; i<4; i++)  mesh->point[pt1->v[i]].flag = base; 
+        //if(iel==154529) printf("on a traite %d (%d) %e %d\n",iel,k,pt1->qual,iter);
+
+        if ( pt1->qual < declic )
+          MMG_kiudel(queue,iel);
+        else if ( coe > 0.1 )
+          MMG_kiuput(queue,iel);
+      }
+
+      /* interpol metric */
+      ppa->flag = base + 1;
+      nm++;
+      break;
+    }
+  }
+  while ( k );
+
+  if ( mesh->info.imprim < - 4 )
+    fprintf(stdout,"     %7d PROPOSED  %7d MOVED %d REJ \n",npp,nm,nrj);
+
+  MMG_kiufree(queue);
+  return(nm);
+}
+
+/* optimise using heap : point position on normal*/
+int MMG_optlen_iso_new(pMesh mesh,pSol sol,double declic,int base) {
+  pTetra    pt,pt1;
+  pPoint    ppa,ppb,p1,p2,p3;
+  pQueue    queue;
+  List      list;
+  double    oldc[3],cal,ctg,cx,cy,cz,ux,uy,uz,cpx,cpy,cpz,coe,dd,len;
+  double    hb,hp,*ca,ax,ay,az,bx,by,bz,nx,ny,nz;
+  int       i,j,k,l,iel,lon,nm;
+  int       ipa,ipb,nb,nk,npp,iadr,iter,maxtou;
+int nrj,i1,i2,i3; 
+//double co1,co2,ddd;
+  /* queue on quality */
+  queue = MMG_kiuini(mesh,mesh->ne,declic,base - 1);
+  assert(queue);
+
+  maxtou = 3;
+  nm     = 0;
+  npp    = 0;
+nrj = 0;  
+  do {
+    k = MMG_kiupop(queue);
+    if ( !k )  break;
+    npp++;
+
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+   	//else if ( pt->flag != base - 1 ) continue;
+    
+    for (i=0; i<4; i++) {
+      ipa = pt->v[i];
+      ppa = &mesh->point[ipa];
+      if ( ppa->tag & M_BDRY )  continue;
+      //else if ( ppa->flag != base - 1 )  continue;
+
+      lon = MMG_boulep(mesh,k,i,&list);
+      if ( lon < 4 )  continue;
+
+      /* optimal point : length 1 sur la normal a la face*/
+      ca   = &ppa->c[0];
+      iadr = (ipa-1)*sol->offset + 1;
+      hp   = sol->met[iadr];
+      cx   = 0.0;
+      cy   = 0.0;
+      cz   = 0.0;
+      ux   = 0.0;
+      uy   = 0.0;
+      uz   = 0.0;
+      nb   = 0;
+      cal  = pt->qual;  
+      for (l=1 ; l<=lon; l++) {
+        iel = list.tetra[l] >> 2;
+        nk  = list.tetra[l] % 4;
+        pt1 = &mesh->tetra[iel];
+        if ( pt1->qual > cal )  cal = pt1->qual;
+	
+//printf("lon %d cal %e %e\n",kel,pt1->qual,MMG_caltet_iso(mesh,sol,iel));
+//printf("boule :   %d : %e %e %e\n",l,pt1->qual,pt1->qual*ALPHAD,MMG_rao(mesh,iel,0));
+				
+        /*compute normal*/
+        i1 = pt->v[MMG_idir[nk][0]];
+        i2 = pt->v[MMG_idir[nk][1]];
+        i3 = pt->v[MMG_idir[nk][2]];
+        p1 = &mesh->point[i1];
+        p2 = &mesh->point[i2];
+        p3 = &mesh->point[i3];
+  
+        ax = p3->c[0] - p1->c[0];
+        ay = p3->c[1] - p1->c[1];
+        az = p3->c[2] - p1->c[2];
+
+        bx = p2->c[0] - p1->c[0];
+        by = p2->c[1] - p1->c[1];
+        bz = p2->c[2] - p1->c[2];
+
+        nx = (ay*bz - az*by);
+        ny = (az*bx - ax*bz);
+        nz = (ax*by - ay*bx);
+    
+        dd = sqrt(nx*nx+ny*ny+nz*nz); 
+        dd = 1./dd;
+        nx *= dd;
+        ny *= dd;
+        nz *= dd; 
+
+		len = 0;
+        ux  = 0;
+        uy  = 0;
+        uz  = 0;
+        for (j=0; j<3; j++) {
+          ipb  = pt1->v[ MMG_idir[nk][j] ];
+          ppb  = &mesh->point[ipb];
+          iadr = (ipb-1)*sol->offset + 1;
+          hb   = sol->met[iadr];  
+          
+          ax  = ppb->c[0] - ppa->c[0];
+          ay  = ppb->c[1] - ppa->c[1];
+          az  = ppb->c[2] - ppa->c[2]; 
+          
+          /* centre de gravite de la face*/
+          ux += ppb->c[0];
+          uy += ppb->c[1];
+          uz += ppb->c[2];          
+
+          dd    =   sqrt(ax*ax +ay*ay +az*az);
+          len  +=   dd/hb;
+        }
+        dd  = 1.0 / (double)3.;
+        len *= dd; 
+        ux  *= dd;
+        uy  *= dd;
+        uz  *= dd;
+        if(len > 0.) len = 1.0 / len;
+        else printf("optlennew len %e\n",len);		
+       
+        /* optimal point */
+        cx += ux + nx * len;
+        cy += uy + ny * len;
+        cz += uz + nz * len;
+        nb++;        
+
+      }
+      if ( nb < 3 )  continue;
+      dd  = 1.0 / (double)nb;
+      cpx = cx*dd;
+      cpy = cy*dd;
+      cpz = cz*dd;
+
+      /* adjust position */
+      coe  = HQCOEF;
+      iter = 1;
+      if(cal > 100./ALPHAD) {
+        ctg  = 0.99 * cal;  
+      } else {
+        ctg  = cal * HCRIT;   
+      }
+      maxtou =  10;
+	  memcpy(oldc,ppa->c,3*sizeof(double));
+      do {
+        ppa->c[0] = (1. - coe) *oldc[0] + coe * cpx;
+        ppa->c[1] = (1. - coe) *oldc[1] + coe * cpy;
+        ppa->c[2] = (1. - coe) *oldc[2] + coe * cpz;
+
+        for (l=1; l<=lon; l++) {
+          iel = list.tetra[l] >> 2;
+          nk  = list.tetra[l] % 4;
+          pt1 = &mesh->tetra[iel];
+        
+          cal = MMG_caltet(mesh,sol,iel); 
+//printf("l %d cal %e %e\n",l,cal,MMG_rao(mesh,iel,0));
+          if ( cal > ctg )  break;
+          list.qual[l] = cal;
+        }
+        if ( l > lon )  break;
+		coe *= 0.5;
+      }
+      while ( ++iter <= maxtou );
+      if ( iter > maxtou ) {
+        memcpy(ppa->c,oldc,3*sizeof(double));
+	    ppa->flag = base - 2;   
+//printf("arg  cal %e %e %e\n",cal,ctg,ctg/0.98);  
+		nrj++;	
+        continue;
+      }
+
+      /* update tetra */
+      for (l=1; l<=lon; l++) {
+        iel = list.tetra[l] >> 2;
+        nk  = list.tetra[l] % 4;
+        pt1 = &mesh->tetra[iel];
+        pt1->qual = list.qual[l];
+        pt1->flag = base;
+	    for(i=0; i<4; i++)  mesh->point[pt1->v[i]].flag = base; 
+
+        if ( pt1->qual < declic )
+          MMG_kiudel(queue,iel);
+        else if ( coe > 0.1 )
+          MMG_kiuput(queue,iel);
+      }
+
+      /* interpol metric */
+      ppa->flag = base + 1;
+      nm++;
+      break;
+    }
+  }
+  while ( k );
+
+  if ( mesh->info.imprim < - 4 )
+    fprintf(stdout,"     %7d PROPOSED  %7d MOVED %d REJ \n",npp,nm,nrj);
+
+  MMG_kiufree(queue);
+  return(nm);
+}
diff --git a/contrib/mmg3d/build/sources/optlentet.c b/contrib/mmg3d/build/sources/optlentet.c
new file mode 100644
index 0000000000000000000000000000000000000000..380ed42c2f2dbcf14722cd9d2de90765f7d3f039
--- /dev/null
+++ b/contrib/mmg3d/build/sources/optlentet.c
@@ -0,0 +1,349 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define  HQCOEF    0.45
+#define  HCRIT     0.8
+
+
+int MMG_optlentet_ani(pMesh mesh,pSol sol,pQueue queue,double declic,int base,int k) {
+  pTetra    pt,pt1;
+  pPoint    ppa,ppb;
+  List      list;
+  double    cal,ctg,cx,cy,cz,ux,uy,uz,cpx,cpy,cpz,coe;
+  double    oldc[3],dd,dd1,dd2,len,lmi,lma;
+  double    *mp,*mb;
+  int       i,j,l,iel,lon;
+  int       ipa,ipb,nb,nk,iadr,iter,maxtou;
+
+  pt = &mesh->tetra[k];
+  if ( !pt->v[0] )  return(0);
+
+
+  maxtou = 2;
+
+  for (i=0; i<4; i++) {
+    ipa = pt->v[i];
+    ppa = &mesh->point[ipa];
+    if ( ppa->tag & M_BDRY )  continue;
+    else if ( ppa->flag > mesh->flag )  continue;
+
+    lon = MMG_boulep(mesh,k,i,&list);
+    if ( lon < 4 )  continue;
+
+    /* optimal point */
+    iadr = (ipa-1)*sol->offset + 1;
+    mp   = &sol->met[iadr];
+    cx  = 0.0;
+    cy  = 0.0;
+    cz  = 0.0;
+    nb  = 0;
+    cal = pt->qual;
+    lmi = LSHORT;
+    lma = LLONG;
+    for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      nk  = list.tetra[l] % 4;
+      pt1 = &mesh->tetra[iel];
+      if ( pt1->qual > cal )  cal = pt1->qual;
+
+      for (j=0; j<3; j++) {
+  	ipb = pt1->v[ MMG_idir[nk][j] ];
+  	ppb = &mesh->point[ipb];
+
+  	iadr = (ipb-1)*sol->offset + 1;
+  	mb   = &sol->met[iadr];
+
+  	ux  = ppb->c[0] - ppa->c[0];
+  	uy  = ppb->c[1] - ppa->c[1];
+  	uz  = ppb->c[2] - ppa->c[2];
+
+  	dd1 =	   mp[0]*ux*ux + mp[3]*uy*uy + mp[5]*uz*uz \
+  	    + 2.0*(mp[1]*ux*uy + mp[2]*ux*uz + mp[4]*uy*uz);
+  	
+  	dd2 =	   mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+  	    + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+  	len = 0.5*(dd1+dd2);
+  	if ( len < lmi )      lmi = len;
+  	else if (len > lma )  lma = len;
+
+  	/* optimal point */
+  	len = 1.0 / sqrt(dd1);
+  	cx += ppa->c[0] + ux * len;
+  	cy += ppa->c[1] + uy * len;
+  	cz += ppa->c[2] + uz * len;
+  	nb++;
+      }
+    }
+    if ( nb < 3 )  continue;
+    dd  = 1.0 / (double)nb;
+    cpx = cx*dd - ppa->c[0];
+    cpy = cy*dd - ppa->c[1];
+    cpz = cz*dd - ppa->c[2];
+
+    /* adjust position */
+    coe  = HQCOEF;
+    iter = 1;
+    if ( cal > 10.0 / ALPHAD )
+      ctg = cal * HCRIT;
+    else if ( cal > 5.0 / ALPHAD )
+      ctg = cal * 0.95;
+    else
+      ctg = cal * 0.975;
+    memcpy(oldc,ppa->c,3*sizeof(double));
+
+    do {
+      ppa->c[0] = oldc[0] + coe * cpx;
+      ppa->c[1] = oldc[1] + coe * cpy;
+      ppa->c[2] = oldc[2] + coe * cpz;
+
+      for (l=1; l<=lon; l++) {
+  	iel = list.tetra[l] >> 2;
+  	nk  = list.tetra[l] % 4;
+  	pt1 = &mesh->tetra[iel];
+
+  	cal = MMG_caltet(mesh,sol,iel);
+  	if ( cal > ctg )  break;
+  	list.qual[l] = cal;
+
+  	/* check length */
+  	for (j=0; j<3; j++) {
+  	  ipb = pt1->v[ MMG_idir[nk][j] ];
+  	  ppb = &mesh->point[ipb];
+
+  	  iadr = (ipb-1)*sol->offset + 1;
+  	  mb   = &sol->met[iadr];
+
+  	  ux  = ppb->c[0] - ppa->c[0];
+  	  uy  = ppb->c[1] - ppa->c[1];
+  	  uz  = ppb->c[2] - ppa->c[2];
+
+  	  dd1 =      mp[0]*ux*ux + mp[3]*uy*uy + mp[5]*uz*uz \
+  	      + 2.0*(mp[1]*ux*uy + mp[2]*ux*uz + mp[4]*uy*uz);
+  	
+  	  dd2 =      mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+  	      + 2.0*(mb[1]*ux*uy + mb[2]*ux*uz + mb[4]*uy*uz);
+  	  len = 0.5*(dd1+dd2);
+
+  	  if ( len < lmi || len > lma )  break;
+  	}
+  	if ( j < 3 )  break;
+      }
+      if ( l > lon )  break;
+      coe *= 0.5;
+    }
+    while ( ++iter <= maxtou );
+    if ( iter > maxtou ) {
+      memcpy(ppa->c,oldc,3*sizeof(double));
+      continue;
+    }
+
+    /* update tetra */
+    for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      nk  = list.tetra[l] % 4;
+      pt1 = &mesh->tetra[iel];
+      pt1->qual = list.qual[l]; 
+      pt1->flag = mesh->flag;
+      if ( pt1->qual < declic )
+  	MMG_kiudel(queue,iel);
+      else 
+  	MMG_kiuput(queue,iel); 
+    }
+
+    /* interpol metric */
+    ppa->flag = mesh->flag + 1;
+    break;
+  }
+  
+  
+  if(i!=4) return(1);
+  
+  return(0);
+}
+
+
+int MMG_optlentet_iso(pMesh mesh,pSol sol,pQueue queue,double declic,int base,int k) {
+  pTetra    pt,pt1;
+  pPoint    ppa,ppb;
+  List      list;
+  double    cal,ctg,cx,cy,cz,ux,uy,uz,cpx,cpy,cpz,coe;
+  double    oldc[3],dd,len,lmi,lma,*ca,*cb,hp,hb;
+  int       i,j,l,iel,lon;
+  int       ipa,ipb,nb,nk,iadr,iter,maxtou;
+
+  pt = &mesh->tetra[k];
+  if ( !pt->v[0] )  return(0);
+  maxtou = 5;
+
+  for (i=0; i<4; i++) {
+    ipa = pt->v[i];
+    ppa = &mesh->point[ipa];
+    if ( ppa->tag & M_BDRY )  continue;
+    else if ( ppa->flag > mesh->flag )  continue;
+
+    lon = MMG_boulep(mesh,k,i,&list);
+    if ( lon < 4 )  continue;
+     
+    /* optimal point */
+    ca   = &ppa->c[0];
+    iadr = (ipa-1)*sol->offset + 1;
+    hp   = sol->met[iadr];
+    cx   = 0.0;
+    cy   = 0.0;
+    cz   = 0.0;
+    nb   = 0;
+    cal  = pt->qual;
+    lmi  = 0.6; //LSHORT;
+    lma  = 1.4; //LLONG;
+    for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      nk  = list.tetra[l] % 4;
+      pt1 = &mesh->tetra[iel];
+      if ( pt1->qual > cal )  cal = pt1->qual;
+
+      for (j=0; j<3; j++) {
+    	ipb  = pt1->v[ MMG_idir[nk][j] ];
+    	ppb  = &mesh->point[ipb];
+    	iadr = (ipb-1)*sol->offset + 1;
+    	hb   = sol->met[iadr];
+        cb   = &ppb->c[0]; 
+    	len  = MMG_length(ca,cb,&hp,&hb);
+        if ( len < lmi )       lmi = len;
+    	else if ( len > lma )  lma = len;
+
+        /* optimal point */
+        ux  = cb[0] - ca[0];
+    	uy  = cb[1] - ca[1];
+    	uz  = cb[2] - ca[2];
+
+        len = 1.0 / len;
+    	cx += ca[0] + ux * len;
+    	cy += ca[1] + uy * len;
+    	cz += ca[2] + uz * len;
+    	nb++;
+      }
+    }
+    if ( nb < 3 )  continue;
+    dd  = 1.0 / (double)nb;
+    cpx = cx*dd - ca[0];
+    cpy = cy*dd - ca[1];
+    cpz = cz*dd - ca[2];
+
+    /* adjust position */
+    coe  = HQCOEF;
+    iter = 1;
+    if ( cal > 10.0 / ALPHAD )
+      ctg = cal * HCRIT;
+    else if ( cal > 5.0 / ALPHAD )
+      ctg = cal * 0.95;
+    else
+      ctg = cal * 0.9975;
+    memcpy(oldc,ppa->c,3*sizeof(double));
+
+    do {
+      ca[0] = oldc[0] + coe * cpx;
+      ca[1] = oldc[1] + coe * cpy;
+      ca[2] = oldc[2] + coe * cpz;
+
+      for (l=1; l<=lon; l++) {
+    	iel = list.tetra[l] >> 2;
+    	nk  = list.tetra[l] % 4;
+    	pt1 = &mesh->tetra[iel];
+      
+    	cal = MMG_caltet(mesh,sol,iel);
+    	if ( cal > ctg )  break;
+    	list.qual[l] = cal;
+
+    	/* check length */
+    	for (j=0; j<3; j++) {
+    	  ipb = pt1->v[ MMG_idir[nk][j] ];
+    	  ppb = &mesh->point[ipb];
+          cb  = &ppb->c[0];
+	   
+    	  iadr = (ipb-1)*sol->offset + 1;
+    	  hb   = sol->met[iadr];
+          len  = MMG_length(ca,cb,&hp,&hb);
+          if ( len < lmi || len > lma ) {
+            break;
+    	  }
+        }
+    	if ( j < 3 )  break;
+      }
+      if ( l > lon )  break;
+      coe *= 0.5;
+    }
+    while ( ++iter <= maxtou );
+    if ( iter > maxtou ) {
+      memcpy(ppa->c,oldc,3*sizeof(double));
+      continue;
+    }
+
+    /* update tetra */
+    for (l=1; l<=lon; l++) {
+      iel = list.tetra[l] >> 2;
+      nk  = list.tetra[l] % 4;
+      pt1 = &mesh->tetra[iel];
+      pt1->qual = list.qual[l];
+      pt1->flag = mesh->flag;
+
+      if ( pt1->qual < declic )
+    	MMG_kiudel(queue,iel);
+      else 
+    	MMG_kiuput(queue,iel);
+    }
+
+
+    /* interpol metric */
+    ppa->flag = mesh->flag + 1;
+    break;
+  }
+  
+  
+  if(i!=4) return(1);
+  
+  return(0);
+}
diff --git a/contrib/mmg3d/build/sources/optra4.c b/contrib/mmg3d/build/sources/optra4.c
new file mode 100644
index 0000000000000000000000000000000000000000..5719c4bc45f5fb86d4552208e737867cd3ea2a8a
--- /dev/null
+++ b/contrib/mmg3d/build/sources/optra4.c
@@ -0,0 +1,106 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_pretreat(pMesh ,pSol ,double ,int *);
+
+int MMG_optra4(pMesh mesh,pSol sol) {
+  double	declicw;
+  double	declic;
+  int		base,nm,ns,it,maxtou,alert,nw,nwold,k;
+  
+  /* optim coquil */
+  alert  = 0;
+  maxtou = 20;//0;
+  it     = 0;
+  
+  MMG_caltet = ( sol->offset==6 ) ? MMG_caltet_ani:MMG_caltet_iso;
+  MMG_caltet2 = ( sol->offset==6 ) ? MMG_caltet2_ani:MMG_caltet2_iso;
+    
+  for (k=1; k<=mesh->ne; k++) {
+    mesh->tetra[k].flag = mesh->flag;
+    mesh->tetra[k].qual = MMG_caltet(mesh,sol,k);
+  }
+  for (k=1; k<=mesh->np; k++) mesh->point[k].flag = mesh->flag;
+  declicw = 5./ALPHAD;
+  declic  = 1.5/ ALPHAD;
+            
+  do {
+    base = ++mesh->flag;
+//MMG_ratio(mesh,sol,NULL); 
+    ns = 0;
+    if ( !alert && !mesh->info.noswap ) {
+        ns = MMG_cendel(mesh,sol,declic,base);
+      if ( ns < 0 ) {
+        alert = 1;
+    	ns    = -ns;
+      }
+    }
+    nw = 0;
+    /*if (!mesh->info.noinsert)  nw = MMG_pretreat(mesh,sol,declicw,&alert);
+    */
+    /*sur des surfaces courbes, il existe des tetras ayant  4 points de peau avec Q=3*/
+    if ( it < 10 )  {
+      nwold = nw;
+      nw += MMG_opttyp(mesh,sol,declicw,&alert);
+	  declicw *= 1.05;
+         
+    }
+	nm = 0;    
+    if (!mesh->info.nomove) {          
+        nm = MMG_optlen(mesh,sol,declic,base);  
+    }
+    
+    //if(!mesh->info.nomove && it<2) MMG_optlap(mesh,sol);
+    if ( mesh->info.imprim < -10 && nw+ns+nm )
+      fprintf(stdout,"     %8d IMPROVED  %8d SWAPPED  %8d MOVED\n",nw,ns,nm);     
+    }
+  //while ( (ns && ((ns > 0.005*mesh->ne /*&& nwold > nw*/) || it < 5)) && ++it < maxtou );
+  while ( ns+nm && ++it < maxtou );
+ 
+  return(1);
+}
+
diff --git a/contrib/mmg3d/build/sources/opttet.c b/contrib/mmg3d/build/sources/opttet.c
new file mode 100644
index 0000000000000000000000000000000000000000..eabe29da115cf986b2cf3c241f0d4e74fbd48004
--- /dev/null
+++ b/contrib/mmg3d/build/sources/opttet.c
@@ -0,0 +1,133 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int    MMG_swaptet(pMesh mesh,pSol sol,pQueue queue, double declic,int iel);
+
+int MMG_opttet(pMesh mesh,pSol sol) {
+  pQueue     queue;
+  double    declic;
+  int      base,nm,ns,it,maxtou,alert,ier,k;
+double worst;
+double declicw;
+int iterworst,nw;
+  worst = 1e20;
+
+
+
+  /* optim tet */
+  alert = 0;
+  declic = 1.7 / ALPHAD; //ALPHAD cte pour qualite interne
+  maxtou = 10;
+  base   = -1;
+  it     = 0;
+
+  do {
+   ns = 0;
+   nm = 0;
+   
+   iterworst = 0;
+   nw = 0;
+   declicw = 9./ALPHAD;
+   do {
+//printf("--------------- iter %d : declicw %e\n",iterworst,declicw*ALPHAD);
+//MMG_outqua(mesh,sol);
+     if (!mesh->info.noswap) 
+     	nw = MMG_opttyp(mesh,sol,declicw,&alert);
+   } while((iterworst++ < 0) && nw);
+   MMG_outqua(mesh,sol);
+   puts("  ");
+   
+   worst = MMG_priworst(mesh,sol);
+   
+   /* queueing tetrahedra */
+   queue = MMG_kiuini(mesh,mesh->ne,declic,base);
+   assert(queue);
+   mesh->flag++;
+   do {
+     k = MMG_kiupop(queue);
+     if ( !k )  break;
+     /*try MMG_swap*/
+     ier = 0;
+     if((!mesh->info.noswap)) ier = MMG_swaptet(mesh,sol,queue,declic,k);
+     if(ier < 0) {
+       alert = 1;
+     } else if(ier) {
+       ns++;
+       continue;
+     }
+     
+     /*try move*/
+     if(MMG_optlentet(mesh,sol,queue,declic,base,k)) {
+       nm++;
+       continue;
+     } 
+     
+     }
+   while ( k );
+    
+   MMG_kiufree(queue);
+   base = ++mesh->flag;
+
+   if ( mesh->info.imprim && nm+ns )
+     fprintf(stdout,"     %8d MOVED  %8d SWAPPED\n",nm,ns);
+  }
+  while ( nm > 0.01*mesh->np && ++it < maxtou );
+
+printf("------------------------ on est arrive a maxtou ?: %d %d\n",it,maxtou);
+   worst = MMG_priworst(mesh,sol);
+
+// #warning A ENLEVER
+//   for(it=1 ; it<=mesh->ne ; it++) {
+//     pt = &mesh->tetra[it];
+//     if(pt->qual < declic) continue;
+//     pt->ref = ALPHAD*pt->qual;
+//   } 
+
+  return(1);
+}
+
+    
diff --git a/contrib/mmg3d/build/sources/opttyp.c b/contrib/mmg3d/build/sources/opttyp.c
new file mode 100644
index 0000000000000000000000000000000000000000..15b499e4e1bc568132c8ed9c3e732ad2d6bacb54
--- /dev/null
+++ b/contrib/mmg3d/build/sources/opttyp.c
@@ -0,0 +1,821 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define OCRIT    0.99
+#undef  QDEGRAD
+#define QDEGRAD 2.
+
+int MMG_colpoi2(pMesh mesh,pSol sol,int iel,int ia,int ib,double coef);
+
+/* optimize mesh quality based on element type */
+int MMG_opttyp(pMesh mesh,pSol sol,double declic,int *alert) {
+  pTetra     pt,pt1;
+  pPoint     pa,pb;
+  List       list;
+  pQueue     queue;
+  double      crit;
+  double     len,*ca,*cb,*ma,*mb;
+  int       *adja,i,k,l,iar,ia,ib,iel,iadr,ier,item[2],nd,base,ityp,np;
+  int        npeau,cs[10],ds[10],dsb[3],lon,sombdry,dpeau;
+  int        ipa,ipb,iarmin,iarmax,ndd,nbt,j;
+double LLONG2;
+
+  /*classification des mauvais : 0 si pas sur la peau*/
+  /*				 1 si au moins une face de peau */
+  /*				 2 si au moins une arete sur la peau */
+  if(mesh->info.imprim < -5 ) {
+    puts("  ");
+ 
+    nd = 0;
+    memset(cs,0,10*sizeof(int)); 
+    memset(ds,0,10*sizeof(int));
+    nbt = 0;
+    for(k=1 ; k<=mesh->ne ; k++) {
+      pt = &mesh->tetra[k];
+//if(k== 40117) printf("tetra %d %e : %d %d %d %d\n",k,pt->qual*ALPHAD,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+      if(!pt->v[0] || pt->qual < declic) continue;
+      nbt++;
+//if(k== 40117) printf("tetra %d %e : %d %d %d %d\n",k,pt->qual*ALPHAD,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+   
+      iadr = 4*(k-1) + 1;
+      adja = &mesh->adja[iadr];
+      for(i=0 ; i<4 ; i++) {
+        if(!adja[i])break;     
+      }
+      if(i==4) {
+         for(i = 0 ; i<6 ; i++) 
+           if(!MMG_coquil(mesh,k,i,&list)) break;
+
+         if ( i==6 )  cs[0]++;
+         else cs[2]++;
+      } else cs[1]++;
+    }
+
+    printf("  tetra interne        = %5d / %5d  %6.2f %%\n",cs[0],nbt,100.0*cs[0]/nbt);
+    printf("  tetra face peau      = %5d / %5d  %6.2f %%\n",cs[1],nbt,100.0*cs[1]/nbt);
+    printf("  tetra arete peau     = %5d / %5d  %6.2f %%\n",cs[2],nbt,100.0*cs[2]/nbt);
+  
+    printf("\n");
+  
+  }
+  
+  
+  /*traitement des mauvais*/
+  base  = mesh->flag;
+  ier   = 0;
+  queue = MMG_kiuini(mesh,mesh->nemax,declic,base - 1);
+  assert(queue);
+
+  memset(cs,0,10*sizeof(int)); 
+  memset(ds,0,10*sizeof(int));
+  memset(dsb,0,3*sizeof(int));
+  nd   = 0;
+
+  if ( !MMG_zaldy4(&list.hedg,3*LONMAX) ) {
+    fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM MMG_OPTTYP.\n");
+    MMG_kiufree(queue);
+    return(0);
+  }
+
+  np = 0;
+  do {
+    k = MMG_kiupop(queue);
+    if ( !k )  break;
+    np++;
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    else if ( pt->qual < declic )  continue;
+	  else if ( pt->flag != base - 1 ) continue;
+	
+    LLONG2 = 0.1;
+
+    crit = pt->qual * OCRIT;
+    ityp = MMG_typelt(mesh,k,item);
+cs[ityp]++;
+//if(k==175494) printf("tetra %d (%d) %e : %d %d %d %d\n",k,ityp,pt->qual*ALPHAD,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+    iadr = 4*(k-1) + 1;
+    adja = &mesh->adja[iadr];
+    /*optim bdry tetra*/
+    npeau = 0;
+    for(i=0 ; i<4 ; i++) {
+      if(!adja[i])npeau++;     
+    }
+      
+    if(npeau>1 && !mesh->info.noswap) {
+      if(mesh->info.imprim<-4) printf("%d faces de peau!!!! %d (typ %d) %e\n",npeau,k,ityp,pt->qual * ALPHAD);
+      dsb[0]++;
+      if(MMG_opt2peau(mesh,sol,queue,k,declic)){ 
+        nd++; 
+        continue;
+      }
+    }  
+    if(npeau) {       
+      dpeau = MMG_optbdry(mesh,sol,k);
+      dsb[dpeau]++;
+      if(dpeau) { 
+        nd++;
+        ds[ityp]++;
+	    continue;
+      }
+    } 
+    if(mesh->info.noswap) continue;
+    switch(ityp) {
+    case 1:  /* sliver */
+      iar = item[0];
+      lon = MMG_coquil(mesh,k,iar,&list);
+      if ( lon > 2 ) {
+        crit = pt->qual;
+        for (l=2; l<=lon; l++) {
+          iel = list.tetra[l] / 6;
+          pt1 = &mesh->tetra[iel];
+          if ( pt1->qual > crit )  crit = pt1->qual;
+        }
+        /*impossible de dégrader à cause des swap 4-4*/
+        crit *= OCRIT;
+	    ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,declic);
+        if ( ier == 0 && !mesh->info.noinsert) {
+		  if ( MMG_voltet(mesh,k) < 5.e-9 ) {
+		    crit *= 2.;
+		    crit = M_MIN(crit,8e+8);
+		  } 
+          ia  = MMG_iare[iar][0];
+          ib  = MMG_iare[iar][1];
+          ipa = pt->v[ia];
+          ipb = pt->v[ib];
+          ca  = &mesh->point[ipa].c[0];
+          cb  = &mesh->point[ipb].c[0];
+          
+	      iadr = (ipa-1)*sol->offset + 1;
+          ma   = &sol->met[iadr];
+          iadr = (ipb-1)*sol->offset + 1;
+          mb   = &sol->met[iadr];
+	  
+	      len = MMG_length(ca,cb,ma,mb);
+	      if(len > LLONG2)
+	        ier = MMG_spledg(mesh,sol,queue,&list,lon,crit,declic);
+	      
+	      if ( ier ) {
+   	        pt1  = &mesh->tetra[mesh->point[ier].tmp];  
+		    for(j=0 ; j<4 ; j++) {
+		      if(pt1->v[j] == ier) break;	
+		    }
+		    assert(j<4);
+            ier = MMG_movevertex(mesh,sol,mesh->point[ier].tmp,j);
+		    if ( ier==1 ) {
+		    } else {
+		      ier = 1;
+		    }
+	      }
+	    }
+	    if ( ier > 0 ) {
+           nd++;
+          ds[1]++;
+          break;
+        }
+	    else if ( ier < 0 ) {
+	      *alert = 1;
+	      break;
+	    }
+      }
+      for(i=0 ; i<6 ; i++) {
+      	if(i==item[0]) continue;
+        lon = MMG_coquil(mesh,k,i,&list);
+        if ( lon > 2 ) {
+          crit = pt->qual;
+          for (l=2; l<=lon; l++) {
+            iel = list.tetra[l] / 6;
+            pt1 = &mesh->tetra[iel];
+            if ( pt1->qual > crit )  crit = pt1->qual;
+          }
+          crit *= OCRIT;
+          
+          ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,declic);
+
+	      if ( ier == 0 && !mesh->info.noinsert) {
+           	/*if ( MMG_voltet(mesh,k) < 5.e-9 ) {     //degrade trop les maillages!!!
+			  crit *= 2.;
+			  crit = M_MIN(crit,8e+8);
+			} */
+		    ia  = MMG_iare[i][0];
+            ib  = MMG_iare[i][1];
+            ipa = pt->v[ia];
+            ipb = pt->v[ib];
+            ca  = &mesh->point[ipa].c[0];
+            cb  = &mesh->point[ipb].c[0];
+          
+	        iadr = (ipa-1)*sol->offset + 1;
+            ma   = &sol->met[iadr];
+            iadr = (ipb-1)*sol->offset + 1;
+            mb   = &sol->met[iadr];
+	  
+	        len = MMG_length(ca,cb,ma,mb);
+	        if(len > LLONG2)
+	          ier = MMG_spledg(mesh,sol,queue,&list,lon,crit,declic);	  
+	        
+	        if ( ier ) {
+		      pt1  = &mesh->tetra[mesh->point[ier].tmp];
+		      for(j=0 ; j<4 ; j++) {
+		        if(pt1->v[j] == ier) break;	
+		      }
+		      assert(j<4);
+			  ier = MMG_movevertex(mesh,sol,mesh->point[ier].tmp,j);
+		      if ( ier==1 ) {
+		      } else {
+				ier = 1;
+		      }
+		    } /*end if ier for movevertex*/         
+          } /*end if ier == 0 */
+	  
+	      if ( ier > 0 ) {
+            nd++;
+            ds[1]++;
+	        break;
+          }
+	      else if ( ier < 0 )
+	        *alert = 1; 
+        } /*end if lon==2*/
+      } /*end for i*/
+      if(i<6) break;
+      /*move vertex*/
+      for(i=0 ; i<4 ; i++) {
+        if(MMG_movevertex(mesh,sol,k,i)) {
+          nd++;
+	      ds[ityp]++;
+	      break;
+        }	  
+      }
+      break;
+
+    case 2:  /* chapeau */
+      /*on essaie de decoller le point de l'arete*/
+      iar = item[0];
+      if((sol->offset!=1) && MMG_movevertex(mesh,sol,k,iar)) {
+        nd++;
+	ds[ityp]++;
+      } else {
+        for(i=0 ; i<4 ; i++) {
+	  if(iar==i) continue;
+          if((sol->offset!=1) && MMG_movevertex(mesh,sol,k,i)) {
+            nd++;
+	    ds[ityp]++;
+	    break;
+          }	  
+	}
+      }
+      break;
+
+    case 3:  /* aileron */
+      iar = item[1];
+      ier = MMG_simu23(mesh,sol,k,iar,crit);
+      if ( ier > 0 ) {
+       	MMG_swap23(mesh,sol,queue,k,iar,crit);
+	nd++;
+        ds[ityp]++;
+	break;
+      }
+      else if ( ier < 0 ) {
+	*alert = 1;
+	break;
+      }
+
+      iar = item[0];
+      lon = MMG_coquil(mesh,k,iar,&list);
+//if(!lon) printf("arete de bord!!!!!!!!!!!!!\n");
+      if ( lon > 2 ) {
+        crit = pt->qual;
+        for (l=2; l<=lon; l++) {
+          iel = list.tetra[l] / 6;
+          pt1 = &mesh->tetra[iel];
+          if ( pt1->qual > crit )  crit = pt1->qual;
+        }
+        crit *= OCRIT;
+        ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,declic);
+	if ( ier == 0 && !mesh->info.noinsert) {
+          ia  = MMG_iare[iar][0];
+          ib  = MMG_iare[iar][1];
+          ipa = pt->v[ia];
+          ipb = pt->v[ib];
+          ca  = &mesh->point[ipa].c[0];
+          cb  = &mesh->point[ipb].c[0];
+          
+	  iadr = (ipa-1)*sol->offset + 1;
+          ma   = &sol->met[iadr];
+          iadr = (ipb-1)*sol->offset + 1;
+          mb   = &sol->met[iadr];
+	  
+	  len = MMG_length(ca,cb,ma,mb);
+	  if(len > LLONG2)
+	    ier = MMG_spledg(mesh,sol,queue,&list,lon,crit,declic);
+	  
+	}
+ 	if ( ier > 0 ) {
+          nd++;
+          ds[3]++;
+        }
+	else if ( ier < 0 )
+	  *alert = 1;
+      }
+      
+      if(ier) break;
+      
+//       /*try collapse one edge*/
+//       iar = item[0];
+//       for(i=0 ; i<6 ; i++) {
+// 	ia = MMG_iare[i][0];
+// 	ib = MMG_iare[i][1];
+// 	ipa = pt->v[ia];
+// 	ipb = pt->v[ib];
+//         if(MMG_colpoi2(mesh,sol,k,ia,ib,QDEGRAD)) {
+//          MMG_delPt(mesh,ipb);
+// 	  //printf("1 : on a reussi a supprimer le tetra mauvais \n");
+//           break;
+//         } else if(MMG_colpoi2(mesh,sol,k,ib,ia,QDEGRAD)) {
+//           MMG_delPt(mesh,ipa);
+// 	  //printf("2 : on a reussi a supprimer le tetra mauvais\n");
+//           break;
+//         }
+// 	
+//       }
+
+      /*move vertex*/
+      for(i=0 ; i<4 ; i++) {
+        if(MMG_movevertex(mesh,sol,k,i)) {
+          nd++;
+	  ds[ityp]++;
+	  break;
+        }	  
+      }
+
+      break;
+
+    case 4:  /* berlingot */
+      iar = item[0];
+      ia  = MMG_iare[iar][0];
+      ib  = MMG_iare[iar][1];
+      ipa = pt->v[ia];
+      ipb = pt->v[ib];
+      pa  = &mesh->point[ipa];
+      pb  = &mesh->point[ipb];
+      if ( !mesh->info.noinsert && !(pb->tag & M_BDRY) && MMG_colpoi2(mesh,sol,k,ia,ib,QDEGRAD) ) {
+        nd++;
+        MMG_delPt(mesh,ipb);
+        ds[ityp]++;
+        break;
+      } 
+      else if( !mesh->info.noinsert && !(pa->tag & M_BDRY) && MMG_colpoi2(mesh,sol,k,ib,ia,QDEGRAD) ) {
+        nd++;
+        MMG_delPt(mesh,ipa);
+        ds[ityp]++;
+        break; 
+      }
+      if( (sol->offset!=1) && !(pb->tag & M_BDRY)) {
+        if(MMG_movevertex(mesh,sol,k,ib)) {
+	  nd++;
+	  ds[ityp]++;
+	  break;
+	}
+      }	
+      if( (sol->offset!=1) && !(pa->tag & M_BDRY)) {
+        if(MMG_movevertex(mesh,sol,k,ia)) {
+	  nd++;
+	  ds[ityp]++;
+	  break;
+	}
+      }	
+
+      break;
+
+    case 5:  /* 0 good face: split largest edge + node move */
+      break;
+
+    case 6:  /* O good face: move away closest vertices */
+      /*collapse item[0]*/
+      ier = 0;
+      iar = item[0];
+      ia  = MMG_iare[iar][0];
+      ib  = MMG_iare[iar][1];
+      ipa = pt->v[ia];
+      ipb = pt->v[ib];
+      ca  = &mesh->point[ipa].c[0];
+      cb  = &mesh->point[ipb].c[0];
+      iadr = (ipa-1)*sol->offset + 1;
+      ma  = &sol->met[iadr];
+      iadr = (ipb-1)*sol->offset + 1;
+      mb  = &sol->met[iadr];
+      if ( (MMG_length(ca,cb,ma,mb) < LSHORT) ) {
+        pa  = &mesh->point[ipa];
+        pb  = &mesh->point[ipb];
+        if ( !mesh->info.noinsert && !(pb->tag & M_BDRY) && MMG_colpoi2(mesh,sol,k,ia,ib,QDEGRAD) ) {
+          ier = 2;
+	  nd++;
+          MMG_delPt(mesh,ipb);
+          ds[ityp]++;
+          break;
+        } 
+        else if( !mesh->info.noinsert && !(pa->tag & M_BDRY) && MMG_colpoi2(mesh,sol,k,ib,ia,QDEGRAD) ) {
+          ier = 2;
+	  nd++;
+          MMG_delPt(mesh,ipa);
+          ds[ityp]++;
+          break; 
+        }
+      }
+       
+      /*split item[1]*/
+      iar = item[1];
+      lon = MMG_coquil(mesh,k,iar,&list);
+      if ( lon <= 2 ) break;
+      
+      crit = pt->qual;
+      for (l=2; l<=lon; l++) {
+        iel = list.tetra[l] / 6;
+        pt1 = &mesh->tetra[iel];
+        if ( pt1->qual > crit )  crit = pt1->qual;
+      }  
+      crit *= OCRIT;
+      ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,declic);
+      if ( ier == 0 && !mesh->info.noinsert) {
+          ia  = MMG_iare[iar][0];
+          ib  = MMG_iare[iar][1];
+          ipa = pt->v[ia];
+          ipb = pt->v[ib];
+          ca  = &mesh->point[ipa].c[0];
+          cb  = &mesh->point[ipb].c[0];
+          
+	  iadr = (ipa-1)*sol->offset + 1;
+          ma   = &sol->met[iadr];
+          iadr = (ipb-1)*sol->offset + 1;
+          mb   = &sol->met[iadr];
+	  
+	  len = MMG_length(ca,cb,ma,mb);
+	  if(len > LLONG2)
+	    ier = MMG_spledg(mesh,sol,queue,&list,lon,crit,declic);
+      }
+      if ( ier > 0 ) {
+        nd++;
+        ds[ityp]++;
+        break;
+      }
+      else if ( ier < 0 ) {
+        *alert = 1;
+        break;
+      }        
+      break;
+    
+    case 7:
+      /*collapse d'une des 2 petites aretes*/
+      ier    = 0;
+      iarmin = item[0];
+      iarmax = item[1];
+      ia  = MMG_iare[iarmin][0];
+      ib  = MMG_iare[iarmin][1];
+      ipa = pt->v[ia];
+      ipb = pt->v[ib];
+      ca  = &mesh->point[ipa].c[0];
+      cb  = &mesh->point[ipb].c[0];
+      iadr = (ipa-1)*sol->offset + 1;
+      ma  = &sol->met[iadr];
+      iadr = (ipb-1)*sol->offset + 1;
+      mb  = &sol->met[iadr];
+
+      i = 6;
+      if(!mesh->info.noinsert && (MMG_length(ca,cb,ma,mb) < LSHORT)) {
+        pa  = &mesh->point[ipa];
+        pb  = &mesh->point[ipb];
+        if ( !(pb->tag & M_BDRY) && MMG_colpoi2(mesh,sol,k,ia,ib,QDEGRAD) ) {
+         ier = 2;
+	  nd++;
+          MMG_delPt(mesh,ipb);
+          ds[ityp]++;
+	  i = 0;
+          break;
+        } 
+        else if( !(pa->tag & M_BDRY) && MMG_colpoi2(mesh,sol,k,ib,ia,QDEGRAD) ) {
+          ier = 2;
+	  nd++;
+          MMG_delPt(mesh,ipa);
+          ds[ityp]++;
+	  i = 0;
+          break; 
+        }
+      }
+      if(ier) break;
+           
+      lon = MMG_coquil(mesh,k,iarmax,&list);
+      if ( lon <= 2 ) break;
+      if (!mesh->info.noinsert) {
+        lon = MMG_coquil(mesh,k,iarmax,&list);
+        if ( lon <= 2 ) break;
+        crit = pt->qual;
+        for (l=2; l<=lon; l++) {
+          iel = list.tetra[l] / 6;
+          pt1 = &mesh->tetra[iel];
+          if ( pt1->qual > crit )  crit = pt1->qual;
+        }  
+        crit *= OCRIT;
+
+        ia  = MMG_iare[iarmax][0];
+        ib  = MMG_iare[iarmax][1];
+        ipa = pt->v[ia];
+        ipb = pt->v[ib];
+        ca  = &mesh->point[ipa].c[0];
+        cb  = &mesh->point[ipb].c[0];
+        
+	iadr = (ipa-1)*sol->offset + 1;
+        ma   = &sol->met[iadr];
+        iadr = (ipb-1)*sol->offset + 1;
+        mb   = &sol->met[iadr];
+	
+	len = MMG_length(ca,cb,ma,mb);
+	if(len > LLONG2)
+	  ier = MMG_spledg(mesh,sol,queue,&list,lon,crit,declic);
+	
+	if ( ier > 0 ) {
+           nd++;
+          ds[ityp]++;
+          break;
+        }
+      }
+
+      /*boucle sur les autres aretes != de iarmax et de iarmin*/
+      for(i=0 ; i<6 ; i++) {
+        if(i==iarmin) continue;
+	if(i==iarmax) continue;
+        ia  = MMG_iare[i][0];
+        ib  = MMG_iare[i][1];
+        ipa = pt->v[ia];
+        ipb = pt->v[ib];
+	ca  = &mesh->point[ipa].c[0];
+        cb  = &mesh->point[ipb].c[0];
+        iadr = (ipa-1)*sol->offset + 1;
+        ma  = &sol->met[iadr];
+        iadr = (ipb-1)*sol->offset + 1;
+        mb  = &sol->met[iadr];
+
+        len = MMG_length(ca,cb,ma,mb);
+        if(!mesh->info.noinsert && (len < LSHORT)) {
+          pa  = &mesh->point[ipa];
+          pb  = &mesh->point[ipb];
+          if ( !(pb->tag & M_BDRY) && MMG_colpoi2(mesh,sol,k,ia,ib,QDEGRAD) ) {
+            ier = 3;
+	    nd++;
+            MMG_delPt(mesh,ipb);
+            ds[ityp]++;
+            break;
+          } 
+          else if( !(pa->tag & M_BDRY) && MMG_colpoi2(mesh,sol,k,ib,ia,QDEGRAD) ) {
+            ier = 3;
+	    nd++;
+            MMG_delPt(mesh,ipa);
+            ds[ityp]++;
+            break; 
+          }
+        } else if(!mesh->info.noinsert && (len > LLONG)) {
+          lon = MMG_coquil(mesh,k,i,&list);
+          if ( lon <= 2 ) break;
+          crit = pt->qual;
+          for (l=2; l<=lon; l++) {
+            iel = list.tetra[l] / 6;
+            pt1 = &mesh->tetra[iel];
+            if ( pt1->qual > crit )  crit = pt1->qual;
+          }  
+          crit *= OCRIT;
+          if (!mesh->info.noinsert)
+            ier = MMG_spledg(mesh,sol,queue,&list,lon,crit,declic);
+          if ( ier > 0 ) {
+             nd++;
+            ds[ityp]++;
+            break;
+          }
+	}
+      }
+      
+      if(i<6) break;
+      if(ier) break;
+      /*MMG_swap arete*/
+      for(i=0 ; i<6 ; i++) {
+	lon = MMG_coquil(mesh,k,i,&list);
+        if ( lon > 2 ) {
+	  crit = pt->qual;
+          for (l=2; l<=lon; l++) {
+            iel = list.tetra[l] / 6;
+            pt1 = &mesh->tetra[iel];
+            if ( pt1->qual > crit )  crit = pt1->qual;
+          }
+          crit *= OCRIT;
+          ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,declic);
+	  if(ier) {	    
+            nd++;
+            ds[ityp]++;
+	    break;
+	  }  
+        }
+      }
+            
+      break;
+    default:
+/*
+      for (i=0; i<4; i++) {
+        adj  = adja[i] >> 2;
+        if ( adj && pt->ref == mesh->tetra[adj].ref ) {
+          lon  = MMG_coquil(mesh,k,i,list);
+          if ( MMG_swapar(mesh,sol,queue,k,i,list,lon,declic) ) {
+            nsw++;
+ds[ityp]++;
+            break;
+          }
+        }
+      }
+*/
+      break;
+    }
+     
+  }
+  while ( k && *alert == 0 );
+
+  if(mesh->info.imprim < -10) { 
+	
+    for (k=0; k<=7; k++)
+      if ( cs[k] )
+        printf("  optim [%d]      = %5d   %5d  %6.2f %%\n",k,cs[k],ds[k],100.0*ds[k]/cs[k]);
+
+    sombdry = dsb[0]+dsb[1]+dsb[2];
+    for (k=1; k<=2; k++)
+      if ( dsb[k] )
+        printf("\n  optim bdry [%d] = %5d   %5d  %6.2f %%\n",k,dsb[k],sombdry,100.0*dsb[k]/sombdry);
+    
+    printf(" PROP %d  TREATED %d\n",np,ds[0]+ds[1]+ds[2]+ds[3]+ds[4]+ds[5]+ds[6]+ds[7]);
+
+  }
+
+   M_free(list.hedg.item);
+   MMG_kiufree(queue);
+   
+   if (!mesh->info.noswap) {
+     base   = -1;
+     *alert = 0;
+     ndd     = MMG_cendel(mesh,sol,declic,base);
+     if ( mesh->info.imprim < 0 && ndd > 0 )
+       fprintf(stdout,"      %7d SWAPPED\n",ndd);
+     else if ( ndd < 0 )
+       *alert = 1;     
+   }
+
+
+
+ /* base  = -1;
+  ier   = 0;
+  queue = MMG_kiuini(mesh,mesh->nemax,declic,base);
+  assert(queue);
+
+  do {
+    k = MMG_kiupop(queue);
+    if ( !k )  break;
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    else if ( pt->qual < declic )  continue;
+    
+    crit = M_MIN(2.*pt->qual,8e+8);
+    
+    for (i=0; i<4; i++) {
+      if(MMG_simu23(mesh,sol,k,i,crit)) {
+      printf("je swap\n");
+        MMG_swap23(mesh,sol,queue,k,i,1e+9);
+        break;
+      }
+    }
+
+  }
+  while ( k && *alert == 0 );
+  MMG_kiufree(queue);*/
+
+
+  return(nd);
+}
+
+
+/* insert point in bad tetra */
+int MMG_pretreat(pMesh mesh,pSol sol,double declic,int *alert) {
+  pTetra     pt,pt1;
+  List       list;
+  pQueue     queue;
+  double      crit;
+  double     len,*ca,*cb,*ma,*mb;
+  int        i,k,l,ia,ib,iel,ier,nd,base,wqual;
+  int        lon,iadr,ipa,ipb;
+
+  wqual = 30. / ALPHAD;
+
+  /*queue*/
+  base  = -1;
+  queue = MMG_kiuini(mesh,mesh->nemax,declic,base);
+  assert(queue);
+
+  nd   = 0;
+
+  if ( !MMG_zaldy4(&list.hedg,3*LONMAX) ) {
+    fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM MMG_OPTTYP.\n");
+    MMG_kiufree(queue);
+    return(0);
+  }
+
+  do {
+    k = MMG_kiupop(queue);
+    if ( !k )  break;
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    else if ( pt->qual < declic )  continue;
+
+    crit = pt->qual * OCRIT;
+
+    for(i=0 ; i<6 ; i++) {
+      ia   = MMG_iare[i][0];
+      ib   = MMG_iare[i][1];
+      ipa  = pt->v[ia];
+      ipb  = pt->v[ib];
+      ca   = &mesh->point[ipa].c[0];
+      cb   = &mesh->point[ipb].c[0];
+      iadr = (ipa-1)*sol->offset + 1;
+      ma   = &sol->met[iadr];
+      iadr = (ipb-1)*sol->offset + 1;
+      mb   = &sol->met[iadr];
+
+      len = MMG_length(ca,cb,ma,mb);
+      
+      if(!mesh->info.noinsert && (len > LLONG)) {
+        lon = MMG_coquil(mesh,k,i,&list);
+        if ( lon <= 2 ) break;
+        crit = pt->qual;
+        for (l=2; l<=lon; l++) {
+          iel = list.tetra[l] / 6;
+          pt1 = &mesh->tetra[iel];
+          if ( pt1->qual > crit )  crit = pt1->qual;
+        }
+	/*if(crit > wqual) {  
+          crit *= 2.;
+	} else {
+	  crit *= 1.05;
+	}                          */
+	crit = M_MIN(crit,8.e+8);  
+	ier = 0;
+        if (!mesh->info.noinsert)
+          ier = MMG_spledg(mesh,sol,queue,&list,lon,crit,declic);
+        if ( ier > 0 ) {
+          nd++;
+          break;
+        }
+      }
+    }
+  }  
+  while ( k && *alert == 0 );
+
+   M_free(list.hedg.item);
+   MMG_kiufree(queue);
+
+  return(nd);
+}
+
diff --git a/contrib/mmg3d/build/sources/outqua.c b/contrib/mmg3d/build/sources/outqua.c
new file mode 100644
index 0000000000000000000000000000000000000000..c1c35b27f46fd03e55413a5879d9ad043d1afba7
--- /dev/null
+++ b/contrib/mmg3d/build/sources/outqua.c
@@ -0,0 +1,322 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile,
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite,
+y compris les garanties de commercialisation ou
+d’adaptation dans un but spécifique.
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU
+en même temps que ce document.
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+spread under the terms and conditions of the license GNU General Public License
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.
+****************************************************************************/
+#include "mesh.h"
+
+/* print mesh quality histo */
+int MMG_outqua(pMesh mesh,pSol sol) {
+  pTetra    pt;
+  double    coef,rap4,rapl,rapmin,rapmax,rapavg,dcal;
+  int       his10[11],his01[33],rapnum,iout;
+  int       k,i,j,imax,iel,ir,nn,nex,ielreal,tmp;
+
+  iout = 0;
+
+  if(mesh->info.imprim < 0) MMG_priworst(mesh,sol);
+
+  rapmin  =  1.e20;
+  rapmax  = -1.e20;
+  rapavg  = 0.0;
+  rapnum  = 0;
+  iel     = 0;
+  ielreal = 0;
+  nn      = 0;
+
+  for (k=0; k<=32; k++)  his01[k] = 0;
+  for (k=0; k<=10; k++)  his10[k] = 0;
+
+  coef = ALPHAD;
+  nex  = 0;
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if( !pt->v[0] ) {
+      nex++;
+      continue;
+    }
+    nn++;
+    dcal = MMG_caltet(mesh,sol,k);//(sol->offset==6) ? MMG_caltet_ani(mesh,sol,k) : MMG_caltet_iso(mesh,sol,k);
+    rap4 = coef * dcal;
+    if(dcal == CALLIM) {
+      //printf("Wrong elt %d : %d %d %d %d (qual %e %e)\n",k,pt->v[0],pt->v[1],pt->v[2],pt->v[3],rap4,MMG_voltet(mesh,k));
+      tmp = pt->v[2];
+      pt->v[2] = pt->v[3];
+      pt->v[3] = tmp;
+      dcal = MMG_caltet(mesh,sol,k);//(sol->offset==6) ? MMG_caltet_ani(mesh,sol,k) : MMG_caltet_iso(mesh,sol,k);
+      rap4 = coef * dcal;
+      if(dcal == CALLIM) {
+        printf("Wrong elt %d : %d %d %d %d (qual %e (%e) %13.12f)\n",k,pt->v[0],pt->v[1],pt->v[2],pt->v[3],rap4,rap4/coef,MMG_voltet(mesh,k));
+        printf("vextex 0 : %e %e %e\n",mesh->point[pt->v[0]].c[0],mesh->point[pt->v[0]].c[1],mesh->point[pt->v[0]].c[2]);
+        printf("vextex 1 : %e %e %e\n",mesh->point[pt->v[1]].c[0],mesh->point[pt->v[1]].c[1],mesh->point[pt->v[1]].c[2]);
+        printf("vextex 2 : %e %e %e\n",mesh->point[pt->v[2]].c[0],mesh->point[pt->v[2]].c[1],mesh->point[pt->v[2]].c[2]);
+        printf("vextex 3 : %e %e %e\n",mesh->point[pt->v[3]].c[0],mesh->point[pt->v[3]].c[1],mesh->point[pt->v[3]].c[2]);
+        //MMG_saveMesh(mesh,"titi.mesh");
+        //exit(0);
+        iout += 1;
+      }
+			if(abs(mesh->info.imprim) > 5) printf("reorient tet %d\n",k);
+      //iout += 1;
+    }
+    //rap4 = M_MIN(rap4,1.0e9);
+    ir   = (int)rap4;
+    if ( rap4 > rapmax ) {
+      rapmax = rap4;
+      iel     = k;
+      ielreal = k - nex;
+    }
+    rapavg += rap4;
+    rapnum++;
+
+    if ( rap4 > 1.0 && rap4 < 17e10 ) {
+      rapmin = M_MIN(rapmin,rap4);
+      if ( rap4 < 10.0 ) {
+        his10[ir] += 1;
+	    his01[0]  += 1;
+      }
+      else if ( rap4 < 17e10 ) {
+        rapl = M_MIN(log10(rap4),32.0);
+        his01[(int)rapl] += 1;
+	    his01[0]  += 1;
+      }
+    }
+  }
+
+  /* print histo */
+  //EMI if(mesh->info.imprim < 0) {
+  fprintf(stdout,"\n  -- MESH QUALITY   %d \n",rapnum);
+  if ( (rapavg > 0) && (rapavg / rapnum < 100.0) )
+    fprintf(stdout,"     AVERAGE QUALITY        %12.4f\n",rapavg / rapnum);
+  fprintf(stdout,"     BEST  ELEMENT QUALITY  %12.4f\n",rapmin);
+  if ( rapmax < 1.e7 )
+    fprintf(stdout,"     WORST ELEMENT QUALITY  %12.4f\n",rapmax);
+  else
+    fprintf(stdout,"     WORST ELEMENT QUALITY  %12.4E\n",rapmax);
+  pt = &mesh->tetra[iel];
+  fprintf(stdout,"     WORST ELEMENT   %d (%d)   %d %d %d %d\n",
+	  iel,ielreal,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+
+  //EMI if ( abs(mesh->info.imprim) < 5 )  return(0);
+
+  fprintf(stdout,"\n     HISTOGRAMM\n");
+  j = 0;
+  for (i=1; i<32; i++)
+    j += his01[i];
+
+  if(rapmax > 1e+9)
+    imax = 9;
+  else
+    imax = M_MIN((int)rapmax,9);
+
+  for (i=M_MAX((int)rapmin,1); i<=imax; i++) {
+    fprintf(stdout,"     %5d < Q < %5d   %7d   %6.2f %%\n",
+	    i,i+1,his10[i],100.*(his10[i]/(float)his01[0]));
+  }
+
+  /* quality per interval */
+  if (j != 0) {
+    fprintf(stdout,"\n");
+    imax = (int)(M_MIN(3,log10(rapmax)));
+
+    for (i=1; i<=imax; i++) {
+      fprintf(stdout,"     %5.0f < Q < %5.0f   %7d   %6.2f %%\n",
+	      pow(10.,i),pow(10.,i+1),his01[i],100.*(his01[i]/(float)his01[0]));
+    }
+    for (i=4; i<=(int)log10(rapmax); i++) {
+      fprintf(stdout,"    10**%2d < Q < 10**%2d  %7d   %6.2f %%\n",
+	      i,i+1,his01[i],100.*(his01[i]/(float)his01[0]));
+    }
+    }
+
+  //}
+
+  return(iout);
+}
+
+double MMG_priworst(pMesh mesh, pSol sol) {
+  pTetra pt;
+  int    k,nbad,nreal,nex;
+  double bad;
+
+  /*worst quality*/
+  bad   = 1.;
+  nbad  = 0;
+  nex   = 0;
+  nreal = 0;
+  for(k=1 ; k<=mesh->ne ; k++) {
+    pt = &mesh->tetra[k];
+    if(!pt->v[0]) {
+      nex++;
+      continue;
+    }
+    if( pt->qual > bad) {
+      bad   = pt->qual;
+      nbad  = k - nex;
+      nreal = k;
+    }
+  }
+
+  if(nreal) printf("     worst quality %d (%d): %e %e\n",nreal,nbad,bad*ALPHAD,ALPHAC*MMG_calte1(mesh,sol,nreal));
+
+  return((&mesh->tetra[nreal])->qual);
+}
+/* print mesh quality histo */
+int MMG_outquacubic(pMesh mesh,pSol sol) {
+  pTetra    pt;
+  double    rapmin,rapmax,rapavg,dcal,som;
+  int       his10[11],his01[5],rapnum,iout;
+  int       k,i,j,iel,ir,nn,nex,ielreal,hismin;
+
+  iout = 0;
+
+  rapmin  = 0.;
+  rapmax  = 1.;
+  rapavg  = 0.0;
+  rapnum  = 0;
+  iel     = 0;
+  ielreal = 0;
+  nn      = 0;
+
+  for (k=0; k<=4; k++)  his01[k] = 0;
+  for (k=0; k<=10; k++)  his10[k] = 0;
+
+  nex  = 0;
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if( !pt->v[0] ) {
+      nex++;
+      continue;
+    }
+    nn++;
+    dcal = MMG_caltetcubic(mesh,sol,k);
+    if(dcal > 1) {
+      //printf("argggggg %d %e %e\n",k,dcal,MMG_caltet(mesh,sol,k)*ALPHAD);
+      dcal = 1.;
+    }
+    if(dcal == 0.) {
+      printf("Wrong elt %d (qual %e)\n",k,dcal);
+      iout += 1;
+    }
+    ir   = (int)(dcal*10);
+    if ( dcal < rapmax ) {
+      rapmax = dcal;
+      iel     = k;
+      ielreal = k - nex;
+    }
+    rapavg += dcal;
+    rapnum++;
+
+    if ( dcal <= 1.0 && dcal >= 1e-12 ) {
+      rapmin = M_MAX(rapmin,dcal);
+      if ( dcal > 0.1 ) {
+        his10[ir] += 1;
+	    his01[0]  += 1;
+      }
+      else if ( dcal > 1e-12 ) {
+        if(dcal > 0.01) {
+          his01[1] += 1;
+        } else if  (dcal > 0.001) {
+          his01[2] += 1;
+        } else if  (dcal > 0.0001) {
+          his01[3] += 1;
+        } else
+          his01[4] += 1;
+	    his01[0]  += 1;
+      }
+    }
+  }
+
+  /* print histo */
+  fprintf(stdout,"\n  -- MESH QUALITY (CUBIC)  %d\n",rapnum);
+  if ( rapavg / rapnum > 0.1 )
+    fprintf(stdout,"     AVERAGE QUALITY        %12.4f\n",rapavg / rapnum);
+  fprintf(stdout,"     BEST  ELEMENT QUALITY  %12.4f\n",rapmin);
+  if ( rapmin > 1.e-6 )
+    fprintf(stdout,"     WORST ELEMENT QUALITY  %12.4f\n",rapmax);
+  else
+    fprintf(stdout,"     WORST ELEMENT QUALITY  %12.4E\n",rapmax);
+  pt = &mesh->tetra[iel];
+  fprintf(stdout,"     WORST ELEMENT   %d (%d)   %d %d %d %d\n",
+	  iel,ielreal,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+
+  if ( abs(mesh->info.imprim) < 5 )  return(0);
+
+  fprintf(stdout,"\n     HISTOGRAMM\n");
+  j = 0;
+  for (i=1; i<4; i++)
+    j += his01[i];
+  hismin = M_MIN((int)(rapmin*10),1);
+  som = 0;
+  if( hismin == 1) {
+    fprintf(stdout,"     0.9   < Q <   1.0   %7d   %6.2f %%\n",
+        his10[9]+his10[10],100.*((his10[9]+his10[10])/(float)his01[0]));
+    hismin = 9;
+    som += 100.*((his10[9]+his10[10])/(float)his01[0]);
+  }
+  for (i=hismin; i>M_MAX((int)(rapmax*10),1); i--) {
+    fprintf(stdout,"     0.%d   < Q <   0.%d   %7d   %6.2f %%\n",
+	    i-1,i,his10[i-1],100.*(his10[i-1]/(float)his01[0]));
+	som += 100.*(his10[i-1]/(float)his01[0]);
+  }
+
+  /* quality per interval */
+  if (j != 0) {
+    fprintf(stdout,"\n");
+    j -= his01[1];
+    if(his01[1]!=0)
+      fprintf(stdout,"     0.01   < Q <  0.1   %7d   %6.2f %%\n",
+	      his01[1],100.*(his01[1]/(float)his01[0]));
+    if(j!=0)
+      fprintf(stdout,"     0.001  < Q <  0.01  %7d   %6.2f %%\n",
+  	    his01[2],100.*(his01[1]/(float)his01[0]));
+    j -= his01[2];
+    if(j!=0)
+      fprintf(stdout,"     0.0001 < Q <  0.001 %7d   %6.2f %%\n",
+  	    his01[3],100.*(his01[1]/(float)his01[0]));
+    j -= his01[3];
+    if(j!=0)
+        fprintf(stdout,"     0.     < Q    %7d   %6.2f %%\n",
+  	      his01[4],100.*(his01[1]/(float)his01[0]));
+   }
+  return(iout);
+}
diff --git a/contrib/mmg3d/build/sources/pattern.c b/contrib/mmg3d/build/sources/pattern.c
new file mode 100644
index 0000000000000000000000000000000000000000..d0994051651d8875d220a06592d8387d6fe396f2
--- /dev/null
+++ b/contrib/mmg3d/build/sources/pattern.c
@@ -0,0 +1,2440 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+
+unsigned char MMG_arfa[3][4] = { {2,0,1,3}, {1,2,0,3}, {0,1,2,3} }; 
+extern int MMG_permar[10][4];
+extern int MMG_pointar[64][2];
+extern int ddebug;
+//insert ip on ia-ib
+int MMG_pattern1(pMesh mesh,pSol sol,pHedge hash,int jel) {
+  pTetra     pt,pt1;
+  int        iel,ia,ib,ic,id,ip,tabref[4],i; 
+  if(ddebug) printf("on cut 1\n");
+
+  pt = &mesh->tetra[jel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]]; 
+  
+  if(pt->tabedg != 1) {    
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  ip = MMG_edgePoint(hash,ia,ib);
+  assert(ip);
+  if(ddebug) printf("ia %d %d %d %d\n",ia,ib,ic,id);
+  pt->v[0] = ia;
+  pt->v[1] = ip;
+  pt->v[2] = ic;
+  pt->v[3] = id;
+  pt->qual = MMG_caltet(mesh,sol,jel); 
+  pt->tabedg = 0;
+  pt->flag = mesh->flag;  
+  pt->bdryref[0] = -1;
+  pt->bdryref[1] = tabref[1];
+  pt->bdryref[2] = tabref[2];
+  pt->bdryref[3] = tabref[3];
+  if(ddebug) printf("creationi %d : %d %d %d %d\n",jel,ia,ip,ic,id); 
+  
+  iel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[iel];
+  pt1->v[0] = ip;
+  pt1->v[1] = ib;
+  pt1->v[2] = ic;
+  pt1->v[3] = id;
+  pt1->qual = MMG_caltet(mesh,sol,iel);
+  pt1->ref  = pt->ref;
+  pt1->flag = mesh->flag; 
+  if(ddebug) printf("tabref %d %d %d %d\n",tabref[0],tabref[1],tabref[2],tabref[3]);
+  pt1->bdryref[0] = tabref[0];
+  pt1->bdryref[1] = -1;
+  pt1->bdryref[2] = tabref[2];
+  pt1->bdryref[3] = tabref[3];
+  if(ddebug) printf("creationi %d : %d %d %d %d\n",iel,ip,ib,ic,id); 
+  //if(ddebug) exit(1); 
+  return(1);
+}
+
+int MMG_pattern2(pMesh mesh,pSol sol,pHedge hash, int iel) { 
+  pTetra     pt,pt1;
+  int        jel,ia,ib,ic,id,iad,ibc,i,tabref[4];
+  
+  pt = &mesh->tetra[iel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]]; 
+
+  if(pt->tabedg != 12) {
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  iad = MMG_edgePoint(hash,ia,id); 
+  assert(iad>0);
+  ibc = MMG_edgePoint(hash,ib,ic); 
+  assert(ibc>0);
+  
+  pt->v[0] = ibc;
+  pt->v[1] = ic;
+  pt->v[2] = iad;
+  pt->v[3] = id;
+  pt->qual = MMG_caltet(mesh,sol,iel); 
+  pt->tabedg = 0;
+  pt->flag = mesh->flag; 
+  pt->bdryref[0] = tabref[1];
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = tabref[0];
+  pt->bdryref[3] = -1;
+  
+  
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ib;
+  pt1->v[1] = ibc;
+  pt1->v[2] = iad;
+  pt1->v[3] = id;
+  pt1->qual = MMG_caltet(mesh,sol,jel);
+  pt1->ref  = pt->ref;
+  pt1->flag = mesh->flag;
+  pt1->bdryref[0] = -1;
+  pt1->bdryref[1] = tabref[2];
+  pt1->bdryref[2] = tabref[0];
+  pt1->bdryref[3] = -1;
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = ibc;
+  pt1->v[2] = ic;
+  pt1->v[3] = iad;
+  pt1->qual = MMG_caltet(mesh,sol,jel);
+  pt1->ref  = pt->ref;
+  pt1->flag = mesh->flag;
+  pt1->bdryref[0] = -1;
+  pt1->bdryref[1] = tabref[1];
+  pt1->bdryref[2] = -1;
+  pt1->bdryref[3] = tabref[3];
+   
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = ib;
+  pt1->v[2] = ibc;
+  pt1->v[3] = iad;
+  pt1->qual = MMG_caltet(mesh,sol,jel);
+  pt1->ref  = pt->ref;
+  pt1->flag = mesh->flag;
+  pt1->bdryref[0] = -1;
+  pt1->bdryref[1] = -1;
+  pt1->bdryref[2] = tabref[2];
+  pt1->bdryref[3] = tabref[3];
+  return(1); 
+}
+
+int MMG_pattern22(pMesh mesh,pSol sol,pHedge hash, int iel) { 
+  pTetra     pt,pt1;
+  int        jel,ia,ib,ic,id,iab,ibd,i,tabref[4];
+  
+  pt = &mesh->tetra[iel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]]; 
+
+  if(pt->tabedg != 17) {
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  iab = MMG_edgePoint(hash,ia,ib); 
+  assert(iab>0);
+  ibd = MMG_edgePoint(hash,ib,id); 
+  assert(ibd>0);
+  pt->v[0] = iab;
+  pt->v[1] = ib;
+  pt->v[2] = ic;
+  pt->v[3] = ibd;
+  pt->qual = MMG_caltet(mesh,sol,iel); 
+  //if(pt->qual==CALLIM) puts("ahhhhhhhhh1");
+  pt->tabedg = 0;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = tabref[0];
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = tabref[2];
+  pt->bdryref[3] = tabref[3];
+  
+  if ( ia > id ) {
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iab;
+    pt1->v[1] = ibd;
+    pt1->v[2] = ic;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("bbahhhhhhhhh1");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = iab;
+    pt1->v[2] = ic;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh11");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+  } else {
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = iab;
+    pt1->v[2] = ic;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) printf("ahhhvhhhhhh11 %d %d %d %d\n",ia,iab,ib,ibd);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ic;
+    pt1->v[2] = id;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahvvhhhhhhhh11");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[1];
+    
+  }
+   
+  return(1); 
+}
+
+int MMG_pattern3(pMesh mesh,pSol sol,pHedge hash,int iel) {
+  pTetra     pt,pt1;
+  int        ia,ib,ic,id,iad,iab,ibd,jel,i,tabref[4];
+  
+  pt = &mesh->tetra[iel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]];
+     
+  if(pt->tabedg != 21) {
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  
+  iad = MMG_edgePoint(hash,ia,id);
+  assert(iad>0);
+  iab = MMG_edgePoint(hash,ia,ib);
+  assert(iab>0);
+  ibd = MMG_edgePoint(hash,ib,id);
+  assert(ibd>0);
+  
+  pt->v[0] = iab;
+  pt->v[1] = ib;
+  pt->v[2] = ic;
+  pt->v[3] = ibd;
+  pt->qual = MMG_caltet(mesh,sol,iel); 
+  pt->tabedg = 0;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = tabref[0];
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = tabref[2];
+  pt->bdryref[3] = tabref[3];
+  
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = iab;
+  pt1->v[2] = ic;
+  pt1->v[3] = iad;
+  pt1->qual = MMG_caltet(mesh,sol,jel);
+  pt1->ref  = pt->ref;
+  pt1->flag = mesh->flag;
+  pt1->bdryref[0] = -1;
+  pt1->bdryref[1] = tabref[1];
+  pt1->bdryref[2] = tabref[2];
+  pt1->bdryref[3] = tabref[3];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = iad;
+  pt1->v[1] = ibd;
+  pt1->v[2] = ic;
+  pt1->v[3] = id;
+  pt1->qual = MMG_caltet(mesh,sol,jel);
+  pt1->ref  = pt->ref;   
+  pt1->flag = mesh->flag;
+  pt1->bdryref[0] = tabref[0];
+  pt1->bdryref[1] = tabref[1];
+  pt1->bdryref[2] = tabref[2];
+  pt1->bdryref[3] = -1;
+  
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = iab;
+  pt1->v[1] = iad;
+  pt1->v[2] = ibd;
+  pt1->v[3] = ic;
+  pt1->qual = MMG_caltet(mesh,sol,jel);
+  pt1->ref  = pt->ref;
+  pt1->flag = mesh->flag;
+  pt1->bdryref[0] = -1;
+  pt1->bdryref[1] = -1;
+  pt1->bdryref[2] = -1;
+  pt1->bdryref[3] = tabref[2];
+  
+  return(1);
+  
+}
+
+int MMG_pattern31(pMesh mesh,pSol sol,pHedge hash,int iel) {
+  pTetra     pt,pt1;
+  int        ia,ib,ic,id,iad,icd,ibd,jel,i,tabref[4];
+  
+  pt = &mesh->tetra[iel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]]; 
+  if(pt->tabedg != 52) {
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  
+  iad = MMG_edgePoint(hash,ia,id);
+  assert(iad>0);
+  icd = MMG_edgePoint(hash,ic,id);
+  assert(icd>0);
+  ibd = MMG_edgePoint(hash,ib,id);
+  assert(ibd>0);
+
+  pt->v[0] = iad;
+  pt->v[1] = ibd;
+  pt->v[2] = icd;
+  pt->v[3] = id;
+  pt->qual = MMG_caltet(mesh,sol,iel); 
+  pt->tabedg = 0;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = tabref[0];
+  pt->bdryref[1] = tabref[1];
+  pt->bdryref[2] = tabref[2];
+  pt->bdryref[3] = pt->ref;
+  
+  if ( (ia > ib) && (ib > ic) ) {
+    assert(ia > ic);  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ib;
+    pt1->v[2] = ic;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = ic;
+    pt1->v[2] = iad;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = pt->ref;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ic;
+    pt1->v[1] = iad;
+    pt1->v[2] = ibd;
+    pt1->v[3] = icd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = pt->ref;
+  } 
+  else if ( (ia > ib) && (ic > ib) && (ic > ia)) {  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = icd;
+    pt1->v[2] = iad;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = pt->ref;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ic;
+    pt1->v[1] = ia;
+    pt1->v[2] = ib;
+    pt1->v[3] = icd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = tabref[3];
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ib;
+    pt1->v[2] = icd;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = pt->ref;
+  }
+  else if ( (ia > ib) && (ic > ib) && (ia > ic)) {  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ib;
+    pt1->v[2] = ic;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = icd;
+    pt1->v[2] = iad;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = pt->ref;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = ic;
+    pt1->v[2] = iad;
+    pt1->v[3] = icd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = pt->ref;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = pt->ref;
+  }
+  else if ( (ib > ia) && (ib > ic) && (ic > ia)) {  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = ia;
+    pt1->v[2] = ibd;
+    pt1->v[3] = ic;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[3];
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ic;
+    pt1->v[1] = ia;
+    pt1->v[2] = ibd;
+    pt1->v[3] = icd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = pt->ref;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ibd;
+    pt1->v[2] = icd;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = pt->ref;
+  }
+  else if ( (ib > ia) && (ib > ic) && (ia > ic)) {  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = ia;
+    pt1->v[2] = ibd;
+    pt1->v[3] = ic;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[3];
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ic;
+    pt1->v[2] = ibd;
+    pt1->v[3] = ia;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = pt->ref;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ic;
+    pt1->v[2] = iad;
+    pt1->v[3] = icd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = pt->ref;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = pt->ref;
+  }
+  else if ( (ib > ia) && (ic > ib) ) { 
+    assert(ic > ia); 
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = ic;
+    pt1->v[2] = ia;
+    pt1->v[3] = icd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = pt->ref;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ibd;
+    pt1->v[2] = icd;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = pt->ref;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = pt->ref;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ib;
+    pt1->v[2] = icd;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = pt->ref;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = pt->ref;
+  }
+  
+  return(1);
+  
+}
+
+int MMG_pattern32(pMesh mesh,pSol sol,pHedge hash,int iel) {
+  pTetra     pt,pt1;
+  int        ia,ib,ic,id,iad,ibc,ibd,jel,i,tabref[4];
+  
+  pt = &mesh->tetra[iel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]]; 
+  if(pt->tabedg != 28) {
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  
+  iad = MMG_edgePoint(hash,ia,id);
+  assert(iad>0);
+  ibc = MMG_edgePoint(hash,ib,ic); 
+  assert(ibc>0);
+  ibd = MMG_edgePoint(hash,ib,id);
+  assert(ibd>0);
+
+  if ( (ia > ib) && (ic > id) ) {
+    pt->v[0] = iad;
+    pt->v[1] = ib;
+    pt->v[2] = ia;
+    pt->v[3] = ibc;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    //if(pt->qual==CALLIM) puts("ahhhhhhhhh1");
+    pt->tabedg = 0;
+    pt->flag = mesh->flag;
+    pt->bdryref[0] = tabref[3];
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = tabref[2];
+    pt->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = ibc;
+    pt1->v[2] = iad;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("bahhhhhhhhh2");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = ibc;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("bahhhhhhhhh3");
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = -1;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibc;
+    pt1->v[1] = iad;
+    pt1->v[2] = id;
+    pt1->v[3] = ic;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("bahhhhhhhhh4");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ic;
+    pt1->v[1] = ia;
+    pt1->v[2] = ibc;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("bahhhhhhhhh5");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = tabref[3];
+  } 
+  else if ( (ia > ib) && (id > ic) ) {  
+    pt->v[0] = iad;
+    pt->v[1] = ib;
+    pt->v[2] = ia;
+    pt->v[3] = ibc;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    //if(pt->qual==CALLIM) puts("ahhhhhhhhh1");
+    pt->tabedg = 0;
+    pt->flag = mesh->flag;
+    pt->bdryref[0] = tabref[3];
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = -1;
+    pt->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = ibc;
+    pt1->v[2] = iad;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh2");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ibc;
+    pt1->v[2] = iad;
+    pt1->v[3] = ic;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh3");
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = iad;
+    pt1->v[2] = id;
+    pt1->v[3] = ic;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh4");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ic;
+    pt1->v[1] = ia;
+    pt1->v[2] = ibc;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh5");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = tabref[3];
+  }
+  else if ( (ib > ia) && (ic > id) ) {  
+    pt->v[0] = ia;
+    pt->v[1] = iad;
+    pt->v[2] = ibc;
+    pt->v[3] = ic;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+   // if(pt->qual==CALLIM) puts("ahhhhhhhhh6");
+    pt->tabedg = 0;
+    pt->flag = mesh->flag;
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = tabref[3];
+    pt->bdryref[2] = tabref[1];
+    pt->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibc;
+    pt1->v[1] = ic;
+    pt1->v[2] = iad;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh7");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = ia;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh8");
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[2];
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ib;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh9");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibc;
+    pt1->v[1] = iad;
+    pt1->v[2] = ibd;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh10");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[2];
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+  }
+  else if ( (ib > ia) && (id > ic) ) {  
+    pt->v[0] = iad;
+    pt->v[1] = ibd;
+    pt->v[2] = ia;
+    pt->v[3] = ic;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    //if(pt->qual==CALLIM) puts("ahhhhhhhhh11");
+    pt->tabedg = 0;
+    pt->flag = mesh->flag;
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = tabref[1];
+    pt->bdryref[2] = -1;
+    pt->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ib;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh12");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ic;
+    pt1->v[1] = iad;
+    pt1->v[2] = ibd;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh13");
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[2];
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = -1;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibc;
+    pt1->v[1] = ic;
+    pt1->v[2] = ia;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    //if(pt1->qual==CALLIM) puts("ahhhhhhhhh14");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag; 
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = tabref[3];
+       
+  }
+  
+  return(1);
+  
+} 
+
+int MMG_pattern33(pMesh mesh,pSol sol,pHedge hash,int iel) {
+  pTetra     pt,pt1;
+  int        ia,ib,ic,id,iad,iac,ibd,jel,i,tabref[4];
+  
+  pt = &mesh->tetra[iel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]]; 
+  if(pt->tabedg != 22) {
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  
+  iad = MMG_edgePoint(hash,ia,id);
+  assert(iad>0);
+  iac = MMG_edgePoint(hash,ia,ic);
+  assert(iac>0);
+  ibd = MMG_edgePoint(hash,ib,id);
+  assert(ibd>0);
+
+  if ( (ia > ib) && (ic > id) ) { 
+    pt->v[0] = ia;
+    pt->v[1] = ib;
+    pt->v[2] = iac;
+    pt->v[3] = iad;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    if(pt->qual==CALLIM) printf("tet 1\n");
+    pt->tabedg = 0;
+    pt->flag = mesh->flag;
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = tabref[1];
+    pt->bdryref[2] = tabref[2];
+    pt->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = ic;
+    pt1->v[2] = id;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    if(pt1->qual==CALLIM) printf("tet 2\n");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[1];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ic;
+    pt1->v[2] = ib;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    if(pt1->qual==CALLIM) printf("tet 3\n");
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[3];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[0];
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = iac;
+    pt1->v[2] = ibd;
+    pt1->v[3] = ib;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    if(pt1->qual==CALLIM) printf("tet 4\n");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = iac;
+    pt1->v[2] = id;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    if(pt1->qual==CALLIM) printf("tet 5\n");
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[1];
+  } 
+  else if ( (ia > ib) && (id > ic) ) {  
+    pt->v[0] = ia;
+    pt->v[1] = ib;
+    pt->v[2] = iac;
+    pt->v[3] = iad;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag;
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = tabref[1];
+    pt->bdryref[2] = tabref[2];
+    pt->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ic;
+    pt1->v[2] = ib;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[3];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[0];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = iac;
+    pt1->v[2] = ibd;
+    pt1->v[3] = ib;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = iac;
+    pt1->v[2] = ic;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[1];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ic;
+    pt1->v[2] = id;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[1];
+  }
+  else if ( (ib > ia) && (ic > id) ) {  
+    pt->v[0] = iac;
+    pt->v[1] = ic;
+    pt->v[2] = id;
+    pt->v[3] = ibd;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag;
+    pt->bdryref[0] = tabref[0];
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = -1;
+    pt->bdryref[3] = tabref[1];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ic;
+    pt1->v[2] = ib;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[3];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[0];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = iac;
+    pt1->v[2] = id;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[1];
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = ia;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = ibd;
+    pt1->v[2] = ia;
+    pt1->v[3] = ib;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[2];
+    pt1->bdryref[1] = tabref[3];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+  }
+  else if ( (ib > ia) && (id > ic) ) {  
+    pt->v[0] = ibd;
+    pt->v[1] = ic;
+    pt->v[2] = ib;
+    pt->v[3] = iac;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag;
+    pt->bdryref[0] = tabref[3];
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = -1;
+    pt->bdryref[3] = tabref[0];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = ia;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = ibd;
+    pt1->v[2] = ia;
+    pt1->v[3] = ib;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;   
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[2];
+    pt1->bdryref[1] = tabref[3];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+  
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = iad;
+    pt1->v[2] = ic;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;    
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ic;
+    pt1->v[2] = iad;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;    
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+
+  }
+  
+  return(1);
+  
+}
+
+int MMG_pattern4(pMesh mesh,pSol sol,pHedge hash, int iel) { 
+  pTetra     pt,pt1;
+  int        jel,ia,ib,ic,id,iab,ibd,ibc,iad,i,tabref[4];
+  
+  pt = &mesh->tetra[iel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]]; 
+
+  if(pt->tabedg != 29) {
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  iab = MMG_edgePoint(hash,ia,ib); 
+  assert(iab>0);
+  ibd = MMG_edgePoint(hash,ib,id); 
+  assert(ibd>0);
+  iad = MMG_edgePoint(hash,ia,id); 
+  assert(iad>0);
+  ibc = MMG_edgePoint(hash,ib,ic); 
+  assert(ibc>0);
+  
+  if ( ( ic > id ) && ( ia > ic ) ) {  
+    pt->v[0] = iab;
+    pt->v[1] = ibc;
+    pt->v[2] = ic;
+    pt->v[3] = iad;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = -1;
+    pt->bdryref[3] = tabref[3];
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = id;
+    pt1->v[1] = ibd;
+    pt1->v[2] = iad;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = iab;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = iab;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ib;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = tabref[3];
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ia;
+    pt1->v[2] = ic;
+    pt1->v[3] = iab;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = tabref[3];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[1];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ic;
+    pt1->v[2] = id;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[1];
+  } 
+  else if ( ( ic > id ) && ( ic > ia ) ) { 
+    pt->v[0] = iad;
+    pt->v[1] = ia;
+    pt->v[2] = ibc;
+    pt->v[3] = iab;
+    pt->qual = MMG_caltet(mesh,sol,iel);  
+    if(pt->qual==CALLIM) printf("biel %d\n",iel);
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = tabref[3];
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = tabref[2];
+    pt->bdryref[3] = -1;
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = iab;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ib;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    if(pt1->qual==CALLIM) printf("ciel %d\n",iel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[2];
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = -1;
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = iab;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    if(pt1->qual==CALLIM) printf("diel %d\n",iel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[2];
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = iad;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ic;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    if(pt1->qual==CALLIM) printf("eiel %d\n",iel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[3];
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = -1;
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = id;
+    pt1->v[1] = ibd;
+    pt1->v[2] = iad;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    if(pt1->qual==CALLIM) printf("fiel %d\n",iel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = tabref[2];
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ic;
+    pt1->v[2] = id;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    if(pt1->qual==CALLIM) printf("giel %d\n",iel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[1];
+  }
+  else if ( ia > ic ) {
+    assert(id > ic); 
+    pt->v[0] = iab;
+    pt->v[1] = ibc;
+    pt->v[2] = ic;
+    pt->v[3] = iad;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = -1;
+    pt->bdryref[3] = tabref[3];
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ic;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = iab;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iab;
+    pt1->v[1] = ib;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = ic;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = iab;
+    pt1->v[2] = ic;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+  }
+  else {
+    assert(id > ic);
+    assert(ic > ia); 
+    pt->v[0] = ibd;
+    pt->v[1] = ic;
+    pt->v[2] = ibc;
+    pt->v[3] = iad;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = -1;
+    pt->bdryref[3] = tabref[0];
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ic;
+    pt1->v[2] = iad;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ibc;
+    pt1->v[2] = ic;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = iab;
+    pt1->v[2] = ibc;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iab;
+    pt1->v[1] = ib;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iad;
+    pt1->v[1] = ibd;
+    pt1->v[2] = iab;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[2];
+  }
+   
+  return(1); 
+}
+
+
+int MMG_pattern41(pMesh mesh,pSol sol,pHedge hash, int iel) { 
+  pTetra     pt,pt1;
+  int        jel,ia,ib,ic,id,iab,icd,iac,ibd,i,tabref[4];
+  
+  pt = &mesh->tetra[iel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]]; 
+  if(pt->tabedg != 51) {
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  iab = MMG_edgePoint(hash,ia,ib); 
+  assert(iab>0);
+  icd = MMG_edgePoint(hash,ic,id); 
+  assert(icd>0);
+  iac = MMG_edgePoint(hash,ia,ic); 
+  assert(iac>0);
+  ibd = MMG_edgePoint(hash,ib,id); 
+  assert(ibd>0);
+  
+  if ( ( ib > ic ) && ( ia > id ) ) { /*tetrap43.mesh*/
+    pt->v[0] = id;
+    pt->v[1] = iab;
+    pt->v[2] = iac;
+    pt->v[3] = ibd;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = tabref[2];
+    pt->bdryref[3] = -1;
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = id;
+    pt1->v[1] = ia;
+    pt1->v[2] = iac;
+    pt1->v[3] = iab;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[3];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[1];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = ic;
+    pt1->v[2] = ibd;
+    pt1->v[3] = iab;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[3];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = icd;
+    pt1->v[2] = iac;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ib;
+    pt1->v[2] = iab;
+    pt1->v[3] = ic;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[3];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ic;
+    pt1->v[2] = iac;
+    pt1->v[3] = icd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+  }
+  else if ( ( ib > ic ) && ( id > ia ) ) { /*tetra45.mesh*/ 
+    pt->v[0] = ia;
+    pt->v[1] = id;
+    pt->v[2] = ibd;
+    pt->v[3] = icd;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = tabref[0];
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = tabref[1];
+    pt->bdryref[3] = tabref[2];
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = icd;
+    pt1->v[1] = iac;
+    pt1->v[2] = ibd;
+    pt1->v[3] = ia;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = ic;
+    pt1->v[2] = ibd;
+    pt1->v[3] = iab;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[3];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = iab;
+    pt1->v[2] = iac;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ib;
+    pt1->v[2] = iab;
+    pt1->v[3] = ic;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[3];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = tabref[2];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = ic;
+    pt1->v[2] = iac;
+    pt1->v[3] = icd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+  }
+  else if ( ( ic > ib ) && ( ia > id ) ) { /*tetrap52.mesh*/ 
+    pt->v[0] = ib;
+    pt->v[1] = icd;
+    pt->v[2] = iab;
+    pt->v[3] = ibd;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = tabref[2];
+    pt->bdryref[2] = tabref[0];
+    pt->bdryref[3] = -1;
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = iab;
+    pt1->v[2] = icd;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = id;
+    pt1->v[1] = iab;
+    pt1->v[2] = icd;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = ia;
+    pt1->v[2] = iab;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[2];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[1];
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = icd;
+    pt1->v[2] = iab;
+    pt1->v[3] = ib;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[3];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ic;
+    pt1->v[1] = ib;
+    pt1->v[2] = icd;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = tabref[3];
+    pt1->bdryref[3] = tabref[0];
+  }
+  else { /*tetrap54.mesh*/ 
+    assert(( ic > ib ) && ( id > ia ) );
+    pt->v[0] = ib;
+    pt->v[1] = icd;
+    pt->v[2] = iab;
+    pt->v[3] = ibd;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = -1;
+    pt->bdryref[1] = tabref[2];
+    pt->bdryref[2] = tabref[0];
+    pt->bdryref[3] = -1;
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = icd;
+    pt1->v[1] = ia;
+    pt1->v[2] = iac;
+    pt1->v[3] = iab;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[3];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[1];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = icd;
+    pt1->v[2] = iab;
+    pt1->v[3] = ia;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = id;
+    pt1->v[1] = ia;
+    pt1->v[2] = icd;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[0];
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[1];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = iab;
+    pt1->v[2] = ib;
+    pt1->v[3] = icd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = tabref[3];
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ic;
+    pt1->v[1] = ib;
+    pt1->v[2] = icd;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = tabref[3];
+    pt1->bdryref[3] = tabref[0];
+  }
+   
+  return(1); 
+}
+
+int MMG_pattern5(pMesh mesh,pSol sol,pHedge hash, int iel) { 
+  pTetra     pt,pt1;
+  int        jel,ia,ib,ic,id,iac,icd,ibd,ibc,iad,i,tabref[4];
+  
+  pt = &mesh->tetra[iel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt->bdryref[MMG_permar[MMG_pointar[pt->tabedg][0]][i]];   
+
+  if(pt->tabedg != 62) {
+    ia = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][0]];
+    ib = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][1]];
+    ic = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][2]];
+    id = pt->v[MMG_permar[MMG_pointar[pt->tabedg][0]][3]];
+  } else {
+    ia = pt->v[0];
+    ib = pt->v[1];
+    ic = pt->v[2];
+    id = pt->v[3];
+  } 
+  iac = MMG_edgePoint(hash,ia,ic); 
+  assert(iac>0);
+  ibc = MMG_edgePoint(hash,ib,ic); 
+  assert(ibc>0);
+  ibd = MMG_edgePoint(hash,ib,id); 
+  assert(ibd>0);
+  icd = MMG_edgePoint(hash,ic,id); 
+  assert(icd>0);
+  iad = MMG_edgePoint(hash,ia,id); 
+  assert(iad>0); 
+	if(0 && (iel==8778 || iel==6508 )) ddebug = 1;   
+	if(iel==6512) ddebug=1;
+  if(ddebug) printf("tet %d : %d %d %d %d\n",iel,ia,ib,ic,id);    
+	if(ddebug) printf("cas %d : %d %d %d %d\n",pt->tabedg,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+  if(ddebug) printf("bdyref      %d %d %d %d\n",pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+  if(ddebug) printf("tabref      %d %d %d %d\n",tabref[0],tabref[1],tabref[2],tabref[3]);
+  if ( ( ia > ib ) ) { /*tetra_p51.mesh*/
+    pt->v[0] = ibc;
+    pt->v[1] = ic;
+    pt->v[2] = iac;
+    pt->v[3] = icd;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = tabref[1];
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = tabref[0];
+    pt->bdryref[3] = tabref[3];
+		if(ddebug) printf("1 : on change %d : %d %d %d %d\n",iel,pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibc;
+    pt1->v[1] = icd;
+    pt1->v[2] = iad;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = icd;
+    pt1->v[2] = iad;
+    pt1->v[3] = id;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibc;
+    pt1->v[1] = icd;
+    pt1->v[2] = iac;
+    pt1->v[3] = iad;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = ibc;
+    pt1->v[2] = iad;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ib;
+    pt1->v[1] = ia;
+    pt1->v[2] = iad;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+		pt1->bdryref[0] = tabref[1];
+		pt1->bdryref[1] = -1;//tabref[2];
+    pt1->bdryref[2] = tabref[3];//tabref[3];
+    pt1->bdryref[3] = tabref[2];//-1;
+		if(ddebug) printf("--on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibc;
+    pt1->v[1] = ib;
+    pt1->v[2] = iad;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[3];
+    pt1->bdryref[3] = -1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+  }
+  else { /*tetra_p54.mesh*/  
+    pt->v[0] = ibc;
+    pt->v[1] = ic;
+    pt->v[2] = iac;
+    pt->v[3] = icd;
+    pt->qual = MMG_caltet(mesh,sol,iel); 
+    pt->tabedg = 0;
+    pt->flag = mesh->flag; 
+    pt->bdryref[0] = tabref[1];
+    pt->bdryref[1] = -1;
+    pt->bdryref[2] = tabref[0];
+    pt->bdryref[3] = tabref[3];
+		if(ddebug) printf("2 : on change %d : %d %d %d %d\n",iel,pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+    
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = icd;
+    pt1->v[2] = iad;
+    pt1->v[3] = id;    
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = tabref[2];
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = ibd;
+    pt1->v[2] = iad;
+    pt1->v[3] = ia;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[2];
+    pt1->bdryref[1] = tabref[1];
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = iac;
+    pt1->v[1] = ia;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;//tabref[3];
+    pt1->bdryref[3] = tabref[3];//-1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ia;
+    pt1->v[1] = ib;
+    pt1->v[2] = ibc;
+    pt1->v[3] = ibd;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[0];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[2];
+    pt1->bdryref[3] = tabref[3];
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = ibd;
+    pt1->v[1] = iad;
+    pt1->v[2] = icd;
+    pt1->v[3] = iac;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;
+    pt1->bdryref[0] = tabref[1];
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = -1;
+    pt1->bdryref[3] = -1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+
+    jel = MMG_newElt(mesh);
+    pt1 = &mesh->tetra[jel];
+    pt1->v[0] = icd;
+    pt1->v[1] = ibd;
+    pt1->v[2] = iac;
+    pt1->v[3] = ibc;
+    pt1->qual = MMG_caltet(mesh,sol,jel);
+    pt1->ref  = pt->ref;
+    pt1->flag = mesh->flag;  
+    pt1->bdryref[0] = -1;
+    pt1->bdryref[1] = -1;
+    pt1->bdryref[2] = tabref[0];
+    pt1->bdryref[3] = -1;
+		if(ddebug) printf("on cree %d : %d %d %d %d\n",jel,pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+  }
+	ddebug = 0; 
+  return(1); 
+}
+
+//insert ip02 on ip0ip2 etc...
+
+/*create 8 tets
+ip02 ip0 ip01 ip03
+ip01 ip1 ip12 ip13
+ip12 ip2 ip02 ip23
+ip23 ip3 ip03 ip13
+ip02 i12 ip03 ip01
+ip01 ip03 ip13 ip12
+ip12 ip03 ip23 ip02
+ip23 ip12 ip13 ip03
+*/
+int MMG_pattern6(pMesh mesh,pSol sol,int jel,int *ipa) {
+  pTetra     pt,pt1;
+  int        iel,ip0,ip1,ip2,ip3,i,tabref[4]; 
+
+  pt1 = &mesh->tetra[jel];
+  for (i=0 ; i<4 ; i++) 
+    tabref[i] = pt1->bdryref[i]; 
+
+
+  ip0 = pt1->v[0];
+  ip1 = pt1->v[1];
+  ip2 = pt1->v[2];
+  ip3 = pt1->v[3];
+	ddebug = 0;
+  if(ddebug) printf("tet %d : %d %d %d %d\n",jel,ip0,ip1,ip2,ip3);
+  if(ddebug) printf("bdyref      %d %d %d %d\n",pt1->bdryref[0],pt1->bdryref[1],pt1->bdryref[2],pt1->bdryref[3]);
+  if(ddebug) printf("tabref      %d %d %d %d\n",tabref[0],tabref[1],tabref[2],tabref[3]);
+	ddebug = 0;
+  
+  pt1->v[0] = ipa[1];
+  pt1->v[1] = ip0;
+  pt1->v[2] = ipa[0];
+  pt1->v[3] = ipa[2];
+  pt1->qual = MMG_caltet(mesh,sol,jel);
+  pt1->tabedg = 0;
+  pt1->flag = mesh->flag;
+  pt1->bdryref[0] = tabref[2];
+  pt1->bdryref[1] = -1;
+  pt1->bdryref[2] = tabref[1];
+  pt1->bdryref[3] = tabref[3];
+
+  iel = MMG_newElt(mesh);
+  pt = &mesh->tetra[iel];
+  pt->v[0] = ipa[0];
+  pt->v[1] = ip1;
+  pt->v[2] = ipa[3];
+  pt->v[3] = ipa[4];
+  pt->qual = MMG_caltet(mesh,sol,iel);
+  pt->ref  = pt1->ref;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = tabref[0];
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = tabref[2];
+  pt->bdryref[3] = tabref[3];
+	// printf("on cree %d : %d %d %d %d\n",iel,pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+  iel = MMG_newElt(mesh);
+  pt = &mesh->tetra[iel];
+  pt->v[0] = ipa[3];
+  pt->v[1] = ip2;
+  pt->v[2] = ipa[1];
+  pt->v[3] = ipa[5];
+  pt->qual = MMG_caltet(mesh,sol,iel);
+  pt->ref  = pt1->ref;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = tabref[1];
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = tabref[0];
+  pt->bdryref[3] = tabref[3];
+	// printf("on cree %d : %d %d %d %d\n",iel,pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+
+  iel = MMG_newElt(mesh);
+  pt = &mesh->tetra[iel];
+  pt->v[0] = ipa[5];
+  pt->v[1] = ip3;
+  pt->v[2] = ipa[2];
+  pt->v[3] = ipa[4];
+  pt->qual = MMG_caltet(mesh,sol,iel);
+  pt->ref  = pt1->ref;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = tabref[2];
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = tabref[0];
+  pt->bdryref[3] = tabref[1];
+	// printf("on cree %d : %d %d %d %d\n",iel,pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+ 
+  iel = MMG_newElt(mesh);
+  pt = &mesh->tetra[iel];
+  pt->v[0] = ipa[1];
+  pt->v[1] = ipa[3];
+  pt->v[2] = ipa[2];
+  pt->v[3] = ipa[0];
+  pt->qual = MMG_caltet(mesh,sol,iel);
+  pt->ref  = pt1->ref;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = -1;
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = tabref[3];
+  pt->bdryref[3] = -1;
+	// printf("on cree %d : %d %d %d %d\n",iel,pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+
+  iel = MMG_newElt(mesh);
+  pt = &mesh->tetra[iel];
+  pt->v[0] = ipa[0];
+  pt->v[1] = ipa[2];
+  pt->v[2] = ipa[4];
+  pt->v[3] = ipa[3];
+  pt->qual = MMG_caltet(mesh,sol,iel);
+  pt->ref  = pt1->ref;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = -1;
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = -1;
+  pt->bdryref[3] = tabref[2];
+	// printf("on cree %d : %d %d %d %d\n",iel,pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+
+  iel = MMG_newElt(mesh);
+  pt = &mesh->tetra[iel];
+  pt->v[0] = ipa[3];
+  pt->v[1] = ipa[2];
+  pt->v[2] = ipa[5];
+  pt->v[3] = ipa[1];
+  pt->qual = MMG_caltet(mesh,sol,iel);
+  pt->ref  = pt1->ref;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = tabref[1];
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = -1;
+  pt->bdryref[3] = -1;
+	// printf("on cree %d : %d %d %d %d\n",iel,pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+
+  iel = MMG_newElt(mesh);
+  pt = &mesh->tetra[iel];
+  pt->v[0] = ipa[5];
+  pt->v[1] = ipa[3];
+  pt->v[2] = ipa[4];
+  pt->v[3] = ipa[2];
+  pt->qual = MMG_caltet(mesh,sol,iel);
+  pt->ref  = pt1->ref;
+  pt->flag = mesh->flag;
+  pt->bdryref[0] = -1;
+  pt->bdryref[1] = -1;
+  pt->bdryref[2] = -1;
+  pt->bdryref[3] = tabref[0];
+	// printf("on cree %d : %d %d %d %d\n",iel,pt->bdryref[0],pt->bdryref[1],pt->bdryref[2],pt->bdryref[3]);
+  //printf("on cut 6\n");
+  //exit(1);              
+  return(1);
+}
+
diff --git a/contrib/mmg3d/build/sources/quality.c b/contrib/mmg3d/build/sources/quality.c
new file mode 100644
index 0000000000000000000000000000000000000000..c8f93d7b09946e09680d4079f2850571bc8a8a91
--- /dev/null
+++ b/contrib/mmg3d/build/sources/quality.c
@@ -0,0 +1,1745 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"  
+
+
+double MMG_rao(pMesh mesh,int k,int inm);
+double MMG_caltetrao(pMesh mesh,pSol sol,int iel) {
+	return(MMG_rao(mesh,iel,0));
+}
+
+/* compute tetra volume */
+double MMG_voltet(pMesh mesh,int iel) {
+  pTetra   pt;
+  pPoint   p1,p2,p3,p4;
+  double   ax,ay,az,bx,by,bz,vol,len;
+  int      s1,s2,s3,s4;
+
+  pt = &mesh->tetra[iel];
+  s1 = pt->v[0];
+  s2 = pt->v[1];
+  s3 = pt->v[2];
+  s4 = pt->v[3];
+
+  /* sort tetra vertices */
+  if ( pt->v[0] < pt->v[1] ) {
+    if ( pt->v[0] < pt->v[2] ) {
+      s3 = pt->v[2];
+      if ( pt->v[0] < pt->v[3] ) {
+        s1 = pt->v[0];
+	      s2 = pt->v[1];
+	      s4 = pt->v[3];
+      }
+      else {
+        s1 = pt->v[3];
+	      s2 = pt->v[0];
+	      s4 = pt->v[1];
+      }
+    }
+    else {
+      s2 = pt->v[0];
+      if ( pt->v[2] < pt->v[3] ) {
+        s1 = pt->v[2];
+	      s3 = pt->v[1];
+	      s4 = pt->v[3];
+      }
+      else {
+        s1 = pt->v[3];
+	      s3 = pt->v[2];
+	      s4 = pt->v[1];
+      }
+    }
+  }
+  else {
+    if ( pt->v[1] < pt->v[2] ) {
+      if ( pt->v[1] < pt->v[3] ) {
+        s1 = pt->v[1];
+	      s2 = pt->v[2];
+        s3 = pt->v[0];
+	      s4 = pt->v[3];
+      }
+      else {
+        s1 = pt->v[3];
+	      s2 = pt->v[0];
+        s3 = pt->v[2];
+	      s4 = pt->v[1];
+      }
+    }
+    else {
+      s2 = pt->v[0];
+      if ( pt->v[2] < pt->v[3] ) {
+        s1 = pt->v[2];
+        s3 = pt->v[1];
+        s4 = pt->v[3];
+      }
+      else {
+        s1 = pt->v[3];
+        s3 = pt->v[2];
+        s4 = pt->v[1];
+      }
+    }
+  }
+
+  p1 = &mesh->point[s1];
+  p2 = &mesh->point[s4];
+  p3 = &mesh->point[s2];
+  p4 = &mesh->point[s3];
+  if ( s2 < s3 ) {
+    if ( s2 < s4 ) {
+      p2 = &mesh->point[s2];
+      p3 = &mesh->point[s3];
+      p4 = &mesh->point[s4];
+    }
+  }
+  else if ( s3 < s4 ) {
+    p2 = &mesh->point[s3];
+    p3 = &mesh->point[s4];
+    p4 = &mesh->point[s2];
+  }
+
+  /* compute volume */
+  ax = p3->c[0] - p1->c[0];
+  ay = p3->c[1] - p1->c[1];
+  az = p3->c[2] - p1->c[2];
+
+  bx = p4->c[0] - p1->c[0];
+  by = p4->c[1] - p1->c[1];
+  bz = p4->c[2] - p1->c[2];
+  //printf("a %e %e %e \n",(ay*bz - az*by),(az*bx - ax*bz),(ax*by - ay*bx));
+  vol = (p2->c[0]-p1->c[0]) * (ay*bz - az*by) \
+      + (p2->c[1]-p1->c[1]) * (az*bx - ax*bz) \
+      + (p2->c[2]-p1->c[2]) * (ax*by - ay*bx);
+  //printf("%e %e %e\n",(p2->c[0]-p1->c[0]),(p2->c[1]-p1->c[1]),(p2->c[2]-p1->c[2]));
+  //printf("vol1 %e %e %e\n",(p2->c[0]-p1->c[0]) * (ay*bz - az*by),(p2->c[1]-p1->c[1]) * (az*bx - ax*bz),
+  //  (p2->c[2]-p1->c[2]) * (ax*by - ay*bx));
+  len = ax*ax+ay*ay+az*az;
+  len += bx*bx+by*by+bz*bz;
+  len += (p2->c[0]-p1->c[0])*(p2->c[0]-p1->c[0])+(p2->c[1]-p1->c[1])*(p2->c[1]-p1->c[1])
+       + (p2->c[2]-p1->c[2])*(p2->c[2]-p1->c[2]);
+  len += (p2->c[0]-p3->c[0])*(p2->c[0]-p3->c[0])+(p2->c[1]-p3->c[1])*(p2->c[1]-p3->c[1])
+       + (p2->c[2]-p3->c[2])*(p2->c[2]-p3->c[2]);
+  len += (p2->c[0]-p4->c[0])*(p2->c[0]-p4->c[0])+(p2->c[1]-p4->c[1])*(p2->c[1]-p4->c[1])
+       + (p2->c[2]-p4->c[2])*(p2->c[2]-p4->c[2]);
+  len += (p3->c[0]-p4->c[0])*(p3->c[0]-p4->c[0])+(p3->c[1]-p4->c[1])*(p3->c[1]-p4->c[1])
+       + (p3->c[2]-p4->c[2])*(p3->c[2]-p4->c[2]);    
+  len /= 6.;
+  len = sqrt(len);
+  len = len*len*len; 
+  //printf("vol %e %e\n",vol,len);       
+  vol /= len;
+  return(vol);
+}
+
+
+/* quick volume check */
+double MMG_quickvol(double *c1,double *c2,double *c3,double *c4) {
+  double   ax,ay,az,bx,by,bz,vol;
+
+  ax = c3[0] - c1[0];
+  ay = c3[1] - c1[1];
+  az = c3[2] - c1[2];
+  
+  bx = c4[0] - c1[0];
+  by = c4[1] - c1[1];
+  bz = c4[2] - c1[2];
+  
+  vol = (c2[0]-c1[0]) * (ay*bz - az*by) \
+      + (c2[1]-c1[1]) * (az*bx - ax*bz) \
+      + (c2[2]-c1[2]) * (ax*by - ay*bx);
+
+  return(vol);
+}
+
+
+/* compute tetra quality aniso */
+double MMG_caltet_ani(pMesh mesh,pSol sol,int iel) {
+  pTetra     pt;
+  double     cal,abx,aby,abz,acx,acy,acz,adx,ady,adz;
+  double     bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz;
+  double     h1,h2,h3,h4,h5,h6,det,vol,rap,v1,v2,v3,num;
+  double    *a,*b,*c,*d;
+  double     *ma,*mb,*mc,*md,mm[6];
+  int        j,ia,ib,ic,id,iadr;
+
+  cal = CALLIM;
+  pt  = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(cal);
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+
+  /* average metric */
+  memset(mm,0,6*sizeof(double));
+  iadr = (ia-1)*sol->offset + 1;
+  ma   = &sol->met[iadr];
+  iadr = (ib-1)*sol->offset + 1;
+  mb   = &sol->met[iadr];
+  iadr = (ic-1)*sol->offset + 1;
+  mc   = &sol->met[iadr];
+  iadr = (id-1)*sol->offset + 1;
+  md   = &sol->met[iadr];
+  for (j=0; j<6; j++)
+    mm[j] = 0.25 * (ma[j]+mb[j]+mc[j]+md[j]);
+  a = mesh->point[ia].c;
+  b = mesh->point[ib].c;
+  c = mesh->point[ic].c;
+  d = mesh->point[id].c;
+
+  /* volume */
+  abx = b[0] - a[0];
+  aby = b[1] - a[1];
+  abz = b[2] - a[2];
+
+  acx = c[0] - a[0];
+  acy = c[1] - a[1];
+  acz = c[2] - a[2];
+  
+  adx = d[0] - a[0];
+  ady = d[1] - a[1];
+  adz = d[2] - a[2];
+
+  v1  = acy*adz - acz*ady;
+  v2  = acz*adx - acx*adz;
+  v3  = acx*ady - acy*adx;
+  vol = abx * v1 + aby * v2 + abz * v3;            
+  if ( vol <= 0. )  return(cal);
+  det = mm[0] * ( mm[3]*mm[5] - mm[4]*mm[4]) \
+      - mm[1] * ( mm[1]*mm[5] - mm[2]*mm[4]) \
+      + mm[2] * ( mm[1]*mm[4] - mm[2]*mm[3]);   
+  if ( det < EPSOK )   {
+    //printf("--- INVALID METRIC : DET (%d) %e\n",iel,det);
+    return(cal);
+  }
+  det = sqrt(det) * vol;
+  /* edge lengths */
+  h1 =      mm[0]*abx*abx + mm[3]*aby*aby + mm[5]*abz*abz \
+     + 2.0*(mm[1]*abx*aby + mm[2]*abx*abz + mm[4]*aby*abz);
+  h2 =      mm[0]*acx*acx + mm[3]*acy*acy + mm[5]*acz*acz \
+     + 2.0*(mm[1]*acx*acy + mm[2]*acx*acz + mm[4]*acy*acz);
+  h3 =      mm[0]*adx*adx + mm[3]*ady*ady + mm[5]*adz*adz \
+     + 2.0*(mm[1]*adx*ady + mm[2]*adx*adz + mm[4]*ady*adz);
+
+  bcx = c[0] - b[0];
+  bcy = c[1] - b[1];
+  bcz = c[2] - b[2];
+
+  bdx = d[0] - b[0];
+  bdy = d[1] - b[1];
+  bdz = d[2] - b[2];
+
+  cdx = d[0] - c[0];
+  cdy = d[1] - c[1];
+  cdz = d[2] - c[2];
+
+  h4 =      mm[0]*bdx*bdx + mm[3]*bdy*bdy + mm[5]*bdz*bdz \
+     + 2.0*(mm[1]*bdx*bdy + mm[2]*bdx*bdz + mm[4]*bdy*bdz);
+  h5 =      mm[0]*cdx*cdx + mm[3]*cdy*cdy + mm[5]*cdz*cdz \
+     + 2.0*(mm[1]*cdx*cdy + mm[2]*cdx*cdz + mm[4]*cdy*cdz);
+  h6 =      mm[0]*bcx*bcx + mm[3]*bcy*bcy + mm[5]*bcz*bcz \
+     + 2.0*(mm[1]*bcx*bcy + mm[2]*bcx*bcz + mm[4]*bcy*bcz);
+
+  /* quality */
+  rap = h1 + h2 + h3 + h4 + h5 + h6;
+  num = sqrt(rap) * rap;  
+  //if ( det > EPS1 * num )  completement arbitraire comme seuil!!!!
+  cal = num / det;  
+  if(cal >= CALLIM) printf(" %d %e %e %e %e\n",iel,cal,num,det,vol);  
+  
+  assert(cal < CALLIM);
+  return(cal);
+}
+
+/* compute tetra quality iso */
+double MMG_caltet5_iso(pMesh mesh,pSol sol,int iel) {
+  pTetra     pt;
+  double     cal,abx,aby,abz,acx,acy,acz,adx,ady,adz;
+  double     bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz;
+  double     h1,h2,h3,h4,h5,h6,vol,v1,v2,v3,rap,num;
+  double    *a,*b,*c,*d;
+  int        ia,ib,ic,id;
+double h;
+
+  cal = CALLIM;
+  pt = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(cal);
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+  a  = mesh->point[ia].c;
+  b  = mesh->point[ib].c;
+  c  = mesh->point[ic].c;
+  d  = mesh->point[id].c;
+  
+  h1 = sol->met[ia];
+  h2 = sol->met[ib];
+  h3 = sol->met[ic];
+  h4 = sol->met[id];
+  h  = 0.25*(h1 + h2 + h3 + h4);
+
+  /* volume */
+  abx = b[0] - a[0];
+  aby = b[1] - a[1];
+  abz = b[2] - a[2];
+
+  acx = c[0] - a[0];
+  acy = c[1] - a[1];
+  acz = c[2] - a[2];
+  
+  adx = d[0] - a[0];
+  ady = d[1] - a[1];
+  adz = d[2] - a[2];
+
+  v1  = acy*adz - acz*ady;
+  v2  = acz*adx - acx*adz;
+  v3  = acx*ady - acy*adx;
+  vol = abx * v1 + aby * v2 + abz * v3;
+  if ( vol <= 0. )  return(cal);
+
+  /* max edge */
+  h1 = abx*abx + aby*aby + abz*abz;
+  h2 = acx*acx + acy*acy + acz*acz;
+  h3 = adx*adx + ady*ady + adz*adz;
+
+  bcx = c[0] - b[0];
+  bcy = c[1] - b[1];
+  bcz = c[2] - b[2];
+
+  bdx = d[0] - b[0];
+  bdy = d[1] - b[1];
+  bdz = d[2] - b[2];
+
+  cdx = d[0] - c[0];
+  cdy = d[1] - c[1];
+  cdz = d[2] - c[2];
+
+  h4 = bdx*bdx + bdy*bdy + bdz*bdz;
+  h5 = cdx*cdx + cdy*cdy + cdz*cdz;
+  h6 = bcx*bcx + bcy*bcy + bcz*bcz;
+
+  /* quality */
+  rap = h1*h1 + h2*h2 + h3*h3 + h4*h4 + h5*h5 + h6*h6;
+  num = sqrt(rap) * rap;
+  cal = num / vol;
+  assert( cal < CALLIM );
+
+  return(cal);
+}          
+
+
+/* compute tetra quality iso */
+double MMG_caltet_iso(pMesh mesh,pSol sol,int iel) {
+  pTetra     pt;
+  double     cal,abx,aby,abz,acx,acy,acz,adx,ady,adz;
+  double     bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz;
+  double     h1,h2,h3,h4,h5,h6,vol,v1,v2,v3,rap,num;
+  double    *a,*b,*c,*d;
+  int        ia,ib,ic,id;
+
+  cal = CALLIM;
+  pt = &mesh->tetra[iel];  
+  if ( !pt->v[0] )  return(cal);
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+  a  = mesh->point[ia].c;
+  b  = mesh->point[ib].c;
+  c  = mesh->point[ic].c;
+  d  = mesh->point[id].c;
+  
+  /* volume */
+  abx = b[0] - a[0];
+  aby = b[1] - a[1];
+  abz = b[2] - a[2];
+
+  acx = c[0] - a[0];
+  acy = c[1] - a[1];
+  acz = c[2] - a[2];
+  
+  adx = d[0] - a[0];
+  ady = d[1] - a[1];
+  adz = d[2] - a[2];
+
+  v1  = acy*adz - acz*ady;
+  v2  = acz*adx - acx*adz;
+  v3  = acx*ady - acy*adx;
+  vol = abx * v1 + aby * v2 + abz * v3;   
+  if ( vol <= 0. )  {
+      /*tmp = pt->v[2];
+      pt->v[2] =pt->v[1];
+      pt->v[1] = tmp;*/  
+        //printf("arg on a un vol nul!!%8e \n ",vol);
+      return(cal);}
+
+  /* max edge */
+  h1 = abx*abx + aby*aby + abz*abz;
+  h2 = acx*acx + acy*acy + acz*acz;
+  h3 = adx*adx + ady*ady + adz*adz;
+
+  bcx = c[0] - b[0];
+  bcy = c[1] - b[1];
+  bcz = c[2] - b[2];
+
+  bdx = d[0] - b[0];
+  bdy = d[1] - b[1];
+  bdz = d[2] - b[2];
+
+  cdx = d[0] - c[0];
+  cdy = d[1] - c[1];
+  cdz = d[2] - c[2];
+
+  h4 = bdx*bdx + bdy*bdy + bdz*bdz;
+  h5 = cdx*cdx + cdy*cdy + cdz*cdz;
+  h6 = bcx*bcx + bcy*bcy + bcz*bcz;
+
+  /* quality */
+  rap = h1 + h2 + h3 + h4 + h5 + h6;
+  num = sqrt(rap) * rap;
+  //if ( vol > EPS1 * num ) completement arbitraire comme seuil!!!!    
+  cal = num / vol;
+  assert( cal < CALLIM );
+
+  return(cal);
+}
+
+
+static double determ(double ab[3],double ac[3],double *mm) {
+  double    a1,a2,a3,a4,a5,a6,m0,m1,m2,m3;
+
+  a1 = mm[0]*ab[0] + mm[1]*ab[1] + mm[2]*ab[2];
+  a2 = mm[1]*ab[0] + mm[3]*ab[1] + mm[4]*ab[2];
+  a3 = mm[2]*ab[0] + mm[4]*ab[1] + mm[5]*ab[2];
+
+  a4 = mm[0]*ac[0] + mm[1]*ac[1] + mm[2]*ac[2];
+  a5 = mm[1]*ac[0] + mm[3]*ac[1] + mm[4]*ac[2];
+  a6 = mm[2]*ac[0] + mm[4]*ac[1] + mm[5]*ac[2];
+
+  m0 = a1*ab[0] + a2*ab[1] + a3*ab[2];
+  m1 = a4*ab[0] + a5*ab[1] + a6*ab[2];
+  m2 = a1*ac[0] + a2*ac[1] + a3*ac[2];
+  m3 = a4*ac[0] + a5*ac[1] + a6*ac[2];
+
+  return(m0*m3 - m1*m2);
+}
+
+
+/* compute tetra quality for output (same as GHS3D)
+   note: must be normalized by ALPHAC  */
+double MMG_calte1_ani(pMesh mesh,pSol sol,int iel) {
+  pTetra     pt;
+  double     cal,ab[3],ac[3],ad[3],bc[3],bd[3],cd[3];
+  double     h1,h2,h3,h4,h5,h6,rapmax,vol,det,v1,v2,v3;
+  double     air,dd,num;
+  double    *a,*b,*c,*d;
+  double     *ma,*mb,*mc,*md,mm[6];
+  int        j,ia,ib,ic,id,iadr;
+
+  pt = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(0.0);
+  cal = CALLIM;
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+
+  /* average metric */
+  memset(mm,0,6*sizeof(double));
+  iadr = (ia-1)*sol->offset + 1;
+  ma   = &sol->met[iadr];
+  iadr = (ib-1)*sol->offset + 1;
+  mb   = &sol->met[iadr];
+  iadr = (ic-1)*sol->offset + 1;
+  mc   = &sol->met[iadr];
+  iadr = (id-1)*sol->offset + 1;
+  md   = &sol->met[iadr];
+  for (j=0; j<6; j++)
+    mm[j] = 0.25 * (ma[j]+mb[j]+mc[j]+md[j]);
+
+  a  = mesh->point[ia].c;
+  b  = mesh->point[ib].c;
+  c  = mesh->point[ic].c;
+  d  = mesh->point[id].c;
+
+  /* volume */
+  ab[0] = b[0] - a[0];
+  ab[1] = b[1] - a[1];
+  ab[2] = b[2] - a[2];
+
+  ac[0] = c[0] - a[0];
+  ac[1] = c[1] - a[1];
+  ac[2] = c[2] - a[2];
+  
+  ad[0] = d[0] - a[0];
+  ad[1] = d[1] - a[1];
+  ad[2] = d[2] - a[2];
+
+  v1  = ac[1]*ad[2] - ac[2]*ad[1];
+  v2  = ac[2]*ad[0] - ac[0]*ad[2];
+  v3  = ac[0]*ad[1] - ac[1]*ad[0];
+  vol = ab[0] * v1 + ab[1] * v2 + ab[2] * v3;
+  if ( vol <= 0. )  return(cal);
+
+  det = mm[0] * ( mm[3]*mm[5] - mm[4]*mm[4]) \
+      - mm[1] * ( mm[1]*mm[5] - mm[2]*mm[4]) \
+      + mm[2] * ( mm[1]*mm[4] - mm[2]*mm[3]);
+  if ( det < EPSOK )  {
+    if(iel) printf("INVALID METRIC : DET (%d) %e\n",iel,det);
+    return(cal);
+  }
+  det = sqrt(det) * vol;
+
+  /* edge lengths */
+  rapmax = 0.0;
+  h1 =      mm[0]*ab[0]*ab[0] + mm[3]*ab[1]*ab[1] + mm[5]*ab[2]*ab[2] \
+     + 2.0*(mm[1]*ab[0]*ab[1] + mm[2]*ab[0]*ab[2] + mm[4]*ab[1]*ab[2]);
+  rapmax = M_MAX(rapmax,h1); 
+  h2 =      mm[0]*ac[0]*ac[0] + mm[3]*ac[1]*ac[1] + mm[5]*ac[2]*ac[2] \
+     + 2.0*(mm[1]*ac[0]*ac[1] + mm[2]*ac[0]*ac[2] + mm[4]*ac[1]*ac[2]);
+  rapmax = M_MAX(rapmax,h2); 
+  h3 =      mm[0]*ad[0]*ad[0] + mm[3]*ad[1]*ad[1] + mm[5]*ad[2]*ad[2] \
+     + 2.0*(mm[1]*ad[0]*ad[1] + mm[2]*ad[0]*ad[2] + mm[4]*ad[1]*ad[2]);
+  rapmax = M_MAX(rapmax,h3); 
+
+  bc[0] = c[0] - b[0];
+  bc[1] = c[1] - b[1];
+  bc[2] = c[2] - b[2];
+
+  bd[0] = d[0] - b[0];
+  bd[1] = d[1] - b[1];
+  bd[2] = d[2] - b[2];
+
+  cd[0] = d[0] - c[0];
+  cd[1] = d[1] - c[1];
+  cd[2] = d[2] - c[2];
+
+  h4 =      mm[0]*bd[0]*bd[0] + mm[3]*bd[1]*bd[1] + mm[5]*bd[2]*bd[2] \
+     + 2.0*(mm[1]*bd[0]*bd[1] + mm[2]*bd[0]*bd[2] + mm[4]*bd[1]*bd[2]);
+  rapmax = M_MAX(rapmax,h4); 
+  h5 =      mm[0]*cd[0]*cd[0] + mm[3]*cd[1]*cd[1] + mm[5]*cd[2]*cd[2] \
+     + 2.0*(mm[1]*cd[0]*cd[1] + mm[2]*cd[0]*cd[2] + mm[4]*cd[1]*cd[2]);
+  rapmax = M_MAX(rapmax,h5); 
+  h6 =      mm[0]*bc[0]*bc[0] + mm[3]*bc[1]*bc[1] + mm[5]*bc[2]*bc[2] \
+     + 2.0*(mm[1]*bc[0]*bc[1] + mm[2]*bc[0]*bc[2] + mm[4]*bc[1]*bc[2]);
+  rapmax = M_MAX(rapmax,h6); 
+
+  /* surfaces */
+  dd  = determ(bd,bc,mm);
+  air = sqrt(dd);
+
+  dd   = determ(ac,ad,mm);
+  air += sqrt(dd);
+
+  dd   = determ(ad,ab,mm);
+  air += sqrt(dd);
+
+  dd   = determ(ab,ac,mm);
+  air += sqrt(dd);
+  
+  /* quality */
+  num = sqrt(rapmax) * air;
+  cal = num / det;
+  assert( cal < CALLIM );
+
+  return(cal);
+}
+
+
+double MMG_calte1_iso(pMesh mesh,pSol sol,int iel) {
+  pTetra     pt;
+  double     cal,abx,aby,abz,acx,acy,acz,adx,ady,adz;
+  double     bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz;
+  double     h1,h2,h3,h4,h5,h6,rapmax,vol,v1,v2,v3;
+  double     s1,s2,s3,s4,dd,d2,num;
+  double    *a,*b,*c,*d;
+  int        ia,ib,ic,id;
+
+  cal = CALLIM;
+  pt = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(cal);
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+  a  = mesh->point[ia].c;
+  b  = mesh->point[ib].c;
+  c  = mesh->point[ic].c;
+  d  = mesh->point[id].c;
+
+  /* volume */
+  abx = b[0] - a[0];
+  aby = b[1] - a[1];
+  abz = b[2] - a[2];
+
+  acx = c[0] - a[0];
+  acy = c[1] - a[1];
+  acz = c[2] - a[2];
+ 
+  adx = d[0] - a[0];
+  ady = d[1] - a[1];
+  adz = d[2] - a[2];
+
+  v1  = acy*adz - acz*ady;
+  v2  = acz*adx - acx*adz;
+  v3  = acx*ady - acy*adx;
+  vol = abx * v1 + aby * v2 + abz * v3;
+  if ( vol <= 0. )  return(cal);
+
+  /* max edge */
+  rapmax = 0.0;
+  h1 = abx*abx + aby*aby + abz*abz;
+  rapmax = M_MAX(rapmax,h1);
+  h2 = acx*acx + acy*acy + acz*acz;
+  rapmax = M_MAX(rapmax,h2);
+  h3 = adx*adx + ady*ady + adz*adz;
+  rapmax = M_MAX(rapmax,h3);
+
+  bcx = c[0] - b[0];
+  bcy = c[1] - b[1];
+  bcz = c[2] - b[2];
+
+  bdx = d[0] - b[0];
+  bdy = d[1] - b[1];
+  bdz = d[2] - b[2];
+
+  cdx = d[0] - c[0];
+  cdy = d[1] - c[1];
+  cdz = d[2] - c[2];
+
+  h4 = bdx*bdx + bdy*bdy + bdz*bdz;
+  rapmax = M_MAX(rapmax,h4);
+  h5 = cdx*cdx + cdy*cdy + cdz*cdz;
+  rapmax = M_MAX(rapmax,h5);
+  h6 = bcx*bcx + bcy*bcy + bcz*bcz;
+  rapmax = M_MAX(rapmax,h6);
+
+  /* surface */
+  dd = cdy*bdz - cdz*bdy;
+  d2 = dd * dd;
+  dd = cdz*bdx - cdx*bdz;
+  d2 = d2 + dd * dd;
+  dd = cdx*bdy - cdy*bdx;
+  d2 = d2 + dd * dd;
+  s1 = sqrt(d2);
+  
+  s2 = sqrt(v1*v1 + v2*v2 + v3*v3);
+  
+  dd = bdy*adz - bdz*ady;
+  d2 = dd * dd;
+  dd = bdz*adx - bdx*adz;
+  d2 = d2 + dd * dd;
+  dd = bdx*ady - bdy*adx;
+  d2 = d2 + dd * dd;
+  s3 = sqrt(d2);
+
+  dd = aby*acz - abz*acy;
+  d2 = dd * dd;
+  dd = abz*acx - abx*acz;
+  d2 = d2 + dd * dd;
+  dd = abx*acy - aby*acx;
+  d2 = d2 + dd * dd;
+  s4 = sqrt(d2);
+
+  /* quality */
+  num = sqrt(rapmax) * (s1+s2+s3+s4);
+  cal = num / vol;
+  assert( cal < CALLIM );
+
+  return(cal);
+}
+
+/* compute tetra quality aniso : (ia ib ic id) and (ie ib id ic) */
+int MMG_caltet2_ani(pMesh mesh,pSol sol,int iel,int ie,double crit, double * caltab) {
+  pTetra     pt;
+  double     cal,acx,acy,acz,adx,ady,adz;
+  double     bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz,bax,bay,baz;
+  double     bex,bey,bez,ecx,ecy,ecz,edx,edy,edz;
+  double     h1,h2,h3,h4,h5,h6,det,dete,vol,vole,rap,v1,v2,v3;
+  double     h1e,h2e,h3e,h4e,h5e,h6e,num;
+  double    *a,*b,*c,*d,*e;
+  double     *ma,*mb,*mc,*md,mm[6],mme[6],*me;
+  int        j,ia,ib,ic,id,iadr;
+
+  cal       = CALLIM;
+  caltab[0] = cal;
+  caltab[1] = cal;
+  pt  = &mesh->tetra[iel];
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+
+  /* average metric */
+  memset(mm,0,6*sizeof(double));
+  iadr = (ia-1)*sol->offset + 1;
+  ma   = &sol->met[iadr];
+  
+  iadr = (ie-1)*sol->offset + 1;
+  me   = &sol->met[iadr];
+  
+  iadr = (ib-1)*sol->offset + 1;
+  mb   = &sol->met[iadr];
+  
+  iadr = (ic-1)*sol->offset + 1;
+  mc   = &sol->met[iadr];
+  
+  iadr = (id-1)*sol->offset + 1;
+  md   = &sol->met[iadr];
+  for (j=0; j<6; j++) {
+    mm[j]  = 0.25 * (ma[j]+mb[j]+mc[j]+md[j]);
+    mme[j] = 0.25 * (me[j]+mb[j]+mc[j]+md[j]);
+  }
+
+  a = mesh->point[ia].c;
+  b = mesh->point[ib].c;
+  c = mesh->point[ic].c;
+  d = mesh->point[id].c;
+  e = mesh->point[ie].c;
+  
+  /* volume */
+  bcx = c[0] - b[0];
+  bcy = c[1] - b[1];
+  bcz = c[2] - b[2];
+
+  bax = a[0] - b[0];
+  bay = a[1] - b[1];
+  baz = a[2] - b[2];
+  
+  bex = e[0] - b[0];
+  bey = e[1] - b[1];
+  bez = e[2] - b[2];
+  
+  bdx = d[0] - b[0];
+  bdy = d[1] - b[1];
+  bdz = d[2] - b[2];
+
+
+  v1  = bdy*bcz - bdz*bcy;
+  v2  = bdz*bcx - bdx*bcz;
+  v3  = bdx*bcy - bdy*bcx;
+  
+  vol = bax * v1 + bay * v2 + baz * v3;
+  if ( vol <= 0. )  return(0);
+  
+  vole = -bex * v1 - bey * v2 - bez * v3;
+  if ( vole <= 0. ) return(0);
+  
+  det = mm[0] * ( mm[3]*mm[5] - mm[4]*mm[4]) \
+      - mm[1] * ( mm[1]*mm[5] - mm[2]*mm[4]) \
+      + mm[2] * ( mm[1]*mm[4] - mm[2]*mm[3]);
+  if ( det < EPSOK )  return(0);
+  
+  dete = mme[0] * ( mme[3]*mme[5] - mme[4]*mme[4]) \
+       - mme[1] * ( mme[1]*mme[5] - mme[2]*mme[4]) \
+       + mme[2] * ( mme[1]*mme[4] - mme[2]*mme[3]);  
+  if ( dete < EPSOK )  return(0);
+  
+  det = sqrt(det) * vol;
+
+  /* edge lengths */
+  acx = c[0] - a[0];
+  acy = c[1] - a[1];
+  acz = c[2] - a[2];
+ 
+  adx = d[0] - a[0];
+  ady = d[1] - a[1];
+  adz = d[2] - a[2];
+  
+  h1 =      mm[0]*bax*bax + mm[3]*bay*bay + mm[5]*baz*baz \
+     + 2.0*(mm[1]*bax*bay + mm[2]*bax*baz + mm[4]*bay*baz);
+  h2 =      mm[0]*acx*acx + mm[3]*acy*acy + mm[5]*acz*acz \
+     + 2.0*(mm[1]*acx*acy + mm[2]*acx*acz + mm[4]*acy*acz);
+  h3 =      mm[0]*adx*adx + mm[3]*ady*ady + mm[5]*adz*adz \
+     + 2.0*(mm[1]*adx*ady + mm[2]*adx*adz + mm[4]*ady*adz);
+     
+  cdx = d[0] - c[0];
+  cdy = d[1] - c[1];
+  cdz = d[2] - c[2];
+
+  h4 =      mm[0]*bdx*bdx + mm[3]*bdy*bdy + mm[5]*bdz*bdz \
+     + 2.0*(mm[1]*bdx*bdy + mm[2]*bdx*bdz + mm[4]*bdy*bdz);
+  h5 =      mm[0]*cdx*cdx + mm[3]*cdy*cdy + mm[5]*cdz*cdz \
+     + 2.0*(mm[1]*cdx*cdy + mm[2]*cdx*cdz + mm[4]*cdy*cdz);
+  h6 =      mm[0]*bcx*bcx + mm[3]*bcy*bcy + mm[5]*bcz*bcz \
+     + 2.0*(mm[1]*bcx*bcy + mm[2]*bcx*bcz + mm[4]*bcy*bcz);
+
+  /* quality */
+  rap = h1 + h2 + h3 + h4 + h5 + h6;
+  num = sqrt(rap) * rap;
+  caltab[0] = num / det;  
+  if ( caltab[0] > crit )  return(0);
+
+  dete = sqrt(dete) * vole;
+  ecx = c[0] - e[0];
+  ecy = c[1] - e[1];
+  ecz = c[2] - e[2];
+ 
+  edx = d[0] - e[0];
+  edy = d[1] - e[1];
+  edz = d[2] - e[2];
+ 
+  h1e =      mme[0]*bex*bex + mme[3]*bey*bey + mme[5]*bez*bez \
+      + 2.0*(mme[1]*bex*bey + mme[2]*bex*bez + mme[4]*bey*bez);
+  h2e =      mme[0]*ecx*ecx + mme[3]*ecy*ecy + mme[5]*ecz*ecz \
+      + 2.0*(mme[1]*ecx*ecy + mme[2]*ecx*ecz + mme[4]*ecy*ecz);
+  h3e =      mme[0]*edx*edx + mme[3]*edy*edy + mme[5]*edz*edz \
+      + 2.0*(mme[1]*edx*edy + mme[2]*edx*edz + mme[4]*edy*edz);   
+
+  h4e =      mme[0]*bdx*bdx + mme[3]*bdy*bdy + mme[5]*bdz*bdz \
+      + 2.0*(mme[1]*bdx*bdy + mme[2]*bdx*bdz + mme[4]*bdy*bdz);
+  h5e =      mme[0]*cdx*cdx + mme[3]*cdy*cdy + mme[5]*cdz*cdz \
+      + 2.0*(mme[1]*cdx*cdy + mme[2]*cdx*cdz + mme[4]*cdy*cdz);
+  h6e =      mme[0]*bcx*bcx + mme[3]*bcy*bcy + mme[5]*bcz*bcz \
+      + 2.0*(mme[1]*bcx*bcy + mme[2]*bcx*bcz + mme[4]*bcy*bcz);   
+
+  rap = h1e + h2e + h3e + h4e + h5e + h6e;
+  num = sqrt(rap) * rap;
+  caltab[1] = ( sqrt(rap) * rap ) / dete;
+  
+  if ( caltab[1] > crit )  return(0);
+  
+  return(1);
+}
+
+
+/* compute tetra quality iso : (ia ib ic id) and (ie ib id ic) */
+int MMG_caltet2_iso(pMesh mesh,pSol sol,int iel,int ie,double crit,double * caltab) {
+  pTetra     pt;
+  double     cal,acx,acy,acz,adx,ady,adz;
+  double     bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz,bax,bay,baz;
+  double     bex,bey,bez,ecx,ecy,ecz,edx,edy,edz;
+  double     h1,h2,h3,h4,h5,h6,vol,vole,rap,v1,v2,v3;
+  double     h1e,h2e,h3e,num;
+  double    *a,*b,*c,*d,*e;
+  int        ia,ib,ic,id;
+
+  cal       = CALLIM;
+  caltab[0] = cal;
+  caltab[1] = cal;
+  pt  = &mesh->tetra[iel];  
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+
+  a = mesh->point[ia].c;
+  b = mesh->point[ib].c;
+  c = mesh->point[ic].c;
+  d = mesh->point[id].c;
+  e = mesh->point[ie].c;
+  
+  /* volume */
+  bcx = c[0] - b[0];
+  bcy = c[1] - b[1];
+  bcz = c[2] - b[2];
+
+  bax = a[0] - b[0];
+  bay = a[1] - b[1];
+  baz = a[2] - b[2];
+  
+  bex = e[0] - b[0];
+  bey = e[1] - b[1];
+  bez = e[2] - b[2];
+  
+  bdx = d[0] - b[0];
+  bdy = d[1] - b[1];
+  bdz = d[2] - b[2];
+
+
+  v1  = bdy*bcz - bdz*bcy;
+  v2  = bdz*bcx - bdx*bcz;
+  v3  = bdx*bcy - bdy*bcx;
+  vol = bax * v1 + bay * v2 + baz * v3;
+  if ( vol <= 0. )  return(0);
+  
+  vole = -bex * v1 - bey * v2 - bez * v3;
+  if ( vole <= 0. ) return(0);
+/////////////////////    
+/*
+caltab[0] = MMG_caltetrao(mesh,sol,iel);   
+if ( caltab[0] > crit )  {
+	return(0);
+}
+pt = &mesh->tetra[0];
+pt->v[0] = ie;
+pt->v[1] = ib;
+pt->v[2] = id;
+pt->v[3] = ic;       
+caltab[1] = MMG_caltetrao(mesh,sol,0);   
+if ( caltab[1] > crit )  {
+	return(0);
+}
+return(1);      */
+////////////////////
+  
+  /* edge lengths */
+  acx = c[0] - a[0];
+  acy = c[1] - a[1];
+  acz = c[2] - a[2];
+ 
+  adx = d[0] - a[0];
+  ady = d[1] - a[1];
+  adz = d[2] - a[2];
+  
+  
+  h1 = bax*bax + bay*bay + baz*baz;
+  h2 = acx*acx + acy*acy + acz*acz;
+  h3 = adx*adx + ady*ady + adz*adz;
+     
+
+  cdx = d[0] - c[0];
+  cdy = d[1] - c[1];
+  cdz = d[2] - c[2];
+
+  h4 = bdx*bdx + bdy*bdy + bdz*bdz;
+  h5 = cdx*cdx + cdy*cdy + cdz*cdz;                                             
+  h6 = bcx*bcx + bcy*bcy + bcz*bcz;
+     
+  /* quality */
+  rap = h1 + h2 + h3 + h4 + h5 + h6;
+  num = sqrt(rap) * rap;
+  caltab[0] = num / vol;
+  
+  if ( caltab[0] > crit )  return(0);
+  
+  ecx = c[0] - e[0];
+  ecy = c[1] - e[1];
+  ecz = c[2] - e[2];
+ 
+  edx = d[0] - e[0];
+  edy = d[1] - e[1];
+  edz = d[2] - e[2];
+
+  h1e = bex*bex + bey*bey + bez*bez;
+  h2e = ecx*ecx + ecy*ecy + ecz*ecz;
+  h3e = edx*edx + edy*edy + edz*edz;   
+ 
+  rap = h1e + h2e + h3e + h4 + h5 + h6;
+  num = sqrt(rap) * rap;
+  caltab[1] = num / vole;
+  
+  if ( caltab[1] > crit )  return(0);
+
+  return(1);
+}
+
+/* compute tetra quality : 60*max(1/lmin,lmax) + iare : (ia ib ic id) and (ie ib id ic) */
+int MMG_caltet2long_ani(pMesh mesh,pSol sol,int iel,int ie,double crit, double * caltab) {
+  pTetra     pt;
+  double     cal,acx,acy,acz,adx,ady,adz;
+  double     bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz,bax,bay,baz;
+  double     bex,bey,bez,ecx,ecy,ecz,edx,edy,edz;
+  double     h1,h2,h3,h4,h5,h6,det,dete,vol,vole,v1,v2,v3;
+  double     h1e,h2e,h3e,h4e,h5e,h6e;
+  double    *a,*b,*c,*d,*e;
+  double     *ma,*mb,*mc,*md,mm[6],mme[6],*me; 
+  double     lmin,lmax;
+  int        j,ia,ib,ic,id,iadr,iarmin,iarmax;
+
+  cal       = CALLIM;
+  caltab[0] = cal;
+  caltab[1] = cal;
+  pt  = &mesh->tetra[iel];
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+
+  /* average metric */
+  memset(mm,0,6*sizeof(double));
+  iadr = (ia-1)*sol->offset + 1;
+  ma   = &sol->met[iadr];
+  
+  iadr = (ie-1)*sol->offset + 1;
+  me   = &sol->met[iadr];
+  
+  iadr = (ib-1)*sol->offset + 1;
+  mb   = &sol->met[iadr];
+  
+  iadr = (ic-1)*sol->offset + 1;
+  mc   = &sol->met[iadr];
+  
+  iadr = (id-1)*sol->offset + 1;
+  md   = &sol->met[iadr];
+  for (j=0; j<6; j++) {
+    mm[j]  = 0.25 * (ma[j]+mb[j]+mc[j]+md[j]);
+    mme[j] = 0.25 * (me[j]+mb[j]+mc[j]+md[j]);
+  }
+
+  a = mesh->point[ia].c;
+  b = mesh->point[ib].c;
+  c = mesh->point[ic].c;
+  d = mesh->point[id].c;
+  e = mesh->point[ie].c;
+  
+  /* volume */
+  bcx = c[0] - b[0];
+  bcy = c[1] - b[1];
+  bcz = c[2] - b[2];
+
+  bax = a[0] - b[0];
+  bay = a[1] - b[1];
+  baz = a[2] - b[2];
+  
+  bex = e[0] - b[0];
+  bey = e[1] - b[1];
+  bez = e[2] - b[2];
+  
+  bdx = d[0] - b[0];
+  bdy = d[1] - b[1];
+  bdz = d[2] - b[2];
+
+
+  v1  = bdy*bcz - bdz*bcy;
+  v2  = bdz*bcx - bdx*bcz;
+  v3  = bdx*bcy - bdy*bcx;
+  
+  vol = bax * v1 + bay * v2 + baz * v3;
+  if ( vol <= 0. )  return(0);
+  
+  vole = -bex * v1 - bey * v2 - bez * v3;
+  if ( vole <= 0. ) return(0);
+  
+  det = mm[0] * ( mm[3]*mm[5] - mm[4]*mm[4]) \
+      - mm[1] * ( mm[1]*mm[5] - mm[2]*mm[4]) \
+      + mm[2] * ( mm[1]*mm[4] - mm[2]*mm[3]);
+  if ( det < EPSOK )  return(0);
+  
+  dete = mme[0] * ( mme[3]*mme[5] - mme[4]*mme[4]) \
+       - mme[1] * ( mme[1]*mme[5] - mme[2]*mme[4]) \
+       + mme[2] * ( mme[1]*mme[4] - mme[2]*mme[3]);  
+  if ( dete < EPSOK )  return(0);
+  
+  det = sqrt(det) * vol;
+
+  /* edge lengths */
+  acx = c[0] - a[0];
+  acy = c[1] - a[1];
+  acz = c[2] - a[2];
+ 
+  adx = d[0] - a[0];
+  ady = d[1] - a[1];
+  adz = d[2] - a[2];
+  
+  h1 =      mm[0]*bax*bax + mm[3]*bay*bay + mm[5]*baz*baz \
+     + 2.0*(mm[1]*bax*bay + mm[2]*bax*baz + mm[4]*bay*baz);
+  h2 =      mm[0]*acx*acx + mm[3]*acy*acy + mm[5]*acz*acz \
+     + 2.0*(mm[1]*acx*acy + mm[2]*acx*acz + mm[4]*acy*acz);
+  h3 =      mm[0]*adx*adx + mm[3]*ady*ady + mm[5]*adz*adz \
+     + 2.0*(mm[1]*adx*ady + mm[2]*adx*adz + mm[4]*ady*adz);
+     
+  cdx = d[0] - c[0];
+  cdy = d[1] - c[1];
+  cdz = d[2] - c[2];
+
+  h4 =      mm[0]*bdx*bdx + mm[3]*bdy*bdy + mm[5]*bdz*bdz \
+     + 2.0*(mm[1]*bdx*bdy + mm[2]*bdx*bdz + mm[4]*bdy*bdz);
+  h5 =      mm[0]*cdx*cdx + mm[3]*cdy*cdy + mm[5]*cdz*cdz \
+     + 2.0*(mm[1]*cdx*cdy + mm[2]*cdx*cdz + mm[4]*cdy*cdz);
+  h6 =      mm[0]*bcx*bcx + mm[3]*bcy*bcy + mm[5]*bcz*bcz \
+     + 2.0*(mm[1]*bcx*bcy + mm[2]*bcx*bcz + mm[4]*bcy*bcz);
+
+  /* quality */ 
+  if( h1 < h2 ) {
+    lmin = h1;
+    iarmin = 0;
+    lmax = h2;
+    iarmax = 1;
+  } else {
+    lmin = h2;
+    iarmin = 1;    
+    lmax = h1;
+    iarmax = 0;
+  }
+  
+  if( h3 < lmin) {
+    lmin = h3;
+    iarmin = 2;
+  }
+  if( h3 > lmax) {
+    lmax = h3;
+    iarmax = 2;
+  }
+  if( h4 < lmin) {
+    lmin = h4;
+    iarmin = 3;
+  }
+  if( h4 > lmax) {
+    lmax = h4;
+    iarmax = 3;
+  }
+  if( h5 < lmin) {
+    lmin = h5;
+    iarmin = 4;
+  }
+  if( h5 > lmax) {
+    lmax = h5;
+    iarmax = 4;
+  }
+  if( h6 < lmin) {
+    lmin = h6;
+    iarmin = 5;
+  }
+  if( h6 > lmax) {
+    lmax = h6;
+    iarmax = 5;
+  }                 
+  
+  lmin = sqrt(lmin);
+  lmax = sqrt(lmax);     
+  lmin = (lmin > 1.) ? lmin : 1./lmin;
+  lmax = (lmax > 1.) ? lmax : 1./lmax;
+  if ( lmin > lmax) {
+     caltab[0] = 60*lmin + iarmin;
+  } else {
+     caltab[0] = 60*lmax + iarmax;
+  } 
+  
+  /*rap = h1 + h2 + h3 + h4 + h5 + h6;
+  num = sqrt(rap) * rap;
+  caltab[0] = num / det;  
+  */
+  if ( caltab[0] > crit )  return(0);
+
+  dete = sqrt(dete) * vole;
+  ecx = c[0] - e[0];
+  ecy = c[1] - e[1];
+  ecz = c[2] - e[2];
+ 
+  edx = d[0] - e[0];
+  edy = d[1] - e[1];
+  edz = d[2] - e[2];
+ 
+  h1e =      mme[0]*bex*bex + mme[3]*bey*bey + mme[5]*bez*bez \
+      + 2.0*(mme[1]*bex*bey + mme[2]*bex*bez + mme[4]*bey*bez);
+  h2e =      mme[0]*ecx*ecx + mme[3]*ecy*ecy + mme[5]*ecz*ecz \
+      + 2.0*(mme[1]*ecx*ecy + mme[2]*ecx*ecz + mme[4]*ecy*ecz);
+  h3e =      mme[0]*edx*edx + mme[3]*edy*edy + mme[5]*edz*edz \
+      + 2.0*(mme[1]*edx*edy + mme[2]*edx*edz + mme[4]*edy*edz);   
+
+  h4e =      mme[0]*bdx*bdx + mme[3]*bdy*bdy + mme[5]*bdz*bdz \
+      + 2.0*(mme[1]*bdx*bdy + mme[2]*bdx*bdz + mme[4]*bdy*bdz);
+  h5e =      mme[0]*cdx*cdx + mme[3]*cdy*cdy + mme[5]*cdz*cdz \
+      + 2.0*(mme[1]*cdx*cdy + mme[2]*cdx*cdz + mme[4]*cdy*cdz);
+  h6e =      mme[0]*bcx*bcx + mme[3]*bcy*bcy + mme[5]*bcz*bcz \
+      + 2.0*(mme[1]*bcx*bcy + mme[2]*bcx*bcz + mme[4]*bcy*bcz);   
+
+  if( h1e < h2e ) {
+    lmin = h1e;
+    iarmin = 0;
+    lmax = h2e;
+    iarmax = 1;
+  } else {
+    lmin = h2e;
+    iarmin = 1;    
+    lmax = h1e;
+    iarmax = 0;
+  }
+  
+  if( h3e < lmin) {
+    lmin = h3e;
+    iarmin = 2;
+  }
+  if( h3e > lmax) {
+    lmax = h3;
+    iarmax = 2;
+  }
+  if( h4 < lmin) {
+    lmin = h4;
+    iarmin = 3;
+  }
+  if( h4 > lmax) {
+    lmax = h4;
+    iarmax = 3;
+  }
+  if( h5 < lmin) {
+    lmin = h5;
+    iarmin = 4;
+  }
+  if( h5 > lmax) {
+    lmax = h5;
+    iarmax = 4;
+  }
+  if( h6 < lmin) {
+    lmin = h6;
+    iarmin = 5;
+  }
+  if( h6 > lmax) {
+    lmax = h6;
+    iarmax = 5;
+  }
+
+  
+  lmin = sqrt(lmin);
+  lmax = sqrt(lmax);
+  lmin = (lmin > 1.) ? lmin : 1./lmin;
+  lmax = (lmax > 1.) ? lmax : 1./lmax;
+  if ( lmin > lmax) {
+     caltab[1] = 60*lmin + iarmin;
+  } else {
+     caltab[1] = 60*lmax + iarmax;
+  } 
+  /*rap = h1e + h2e + h3e + h4e + h5e + h6e;
+  num = sqrt(rap) * rap;
+  caltab[1] = ( sqrt(rap) * rap ) / dete;
+  */
+  if ( caltab[1] > crit )  return(0);
+  
+  return(1);
+}
+
+
+/* compute tetra quality iso : (ia ib ic id) and (ie ib id ic) */
+int MMG_caltet2long_iso(pMesh mesh,pSol sol,int iel,int ie,double crit,double * caltab) {
+  pTetra     pt;
+  double     cal,acx,acy,acz,adx,ady,adz;
+  double     bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz,bax,bay,baz;
+  double     bex,bey,bez,ecx,ecy,ecz,edx,edy,edz;
+  double     h1,h2,h3,h4,h5,h6,vol,vole,rap,v1,v2,v3;
+  double     h1e,h2e,h3e,num;
+  double    *a,*b,*c,*d,*e; 
+  double     lmin,lmax;
+  int        ia,ib,ic,id,iarmin,iarmax;
+  cal       = CALLIM;
+  caltab[0] = cal;
+  caltab[1] = cal;
+  pt  = &mesh->tetra[iel];  
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+
+  a = mesh->point[ia].c;
+  b = mesh->point[ib].c;
+  c = mesh->point[ic].c;
+  d = mesh->point[id].c;
+  e = mesh->point[ie].c;
+  
+  /* volume */
+  bcx = c[0] - b[0];
+  bcy = c[1] - b[1];
+  bcz = c[2] - b[2];
+
+  bax = a[0] - b[0];
+  bay = a[1] - b[1];
+  baz = a[2] - b[2];
+  
+  bex = e[0] - b[0];
+  bey = e[1] - b[1];
+  bez = e[2] - b[2];
+  
+  bdx = d[0] - b[0];
+  bdy = d[1] - b[1];
+  bdz = d[2] - b[2];
+
+
+  v1  = bdy*bcz - bdz*bcy;
+  v2  = bdz*bcx - bdx*bcz;
+  v3  = bdx*bcy - bdy*bcx;
+  vol = bax * v1 + bay * v2 + baz * v3;
+  if ( vol <= 0. )  return(0);
+  
+  vole = -bex * v1 - bey * v2 - bez * v3;
+  if ( vole <= 0. ) return(0);
+/////////////////////    
+/*
+caltab[0] = MMG_caltetrao(mesh,sol,iel);   
+if ( caltab[0] > crit )  {
+	return(0);
+}
+pt = &mesh->tetra[0];
+pt->v[0] = ie;
+pt->v[1] = ib;
+pt->v[2] = id;
+pt->v[3] = ic;       
+caltab[1] = MMG_caltetrao(mesh,sol,0);   
+if ( caltab[1] > crit )  {
+	return(0);
+}
+return(1);      */
+////////////////////
+  
+  /* edge lengths */
+  acx = c[0] - a[0];
+  acy = c[1] - a[1];
+  acz = c[2] - a[2];
+ 
+  adx = d[0] - a[0];
+  ady = d[1] - a[1];
+  adz = d[2] - a[2];
+  
+  
+  h1 = bax*bax + bay*bay + baz*baz;
+  h2 = acx*acx + acy*acy + acz*acz;
+  h3 = adx*adx + ady*ady + adz*adz;
+     
+
+  cdx = d[0] - c[0];
+  cdy = d[1] - c[1];
+  cdz = d[2] - c[2];
+
+  h4 = bdx*bdx + bdy*bdy + bdz*bdz;
+  h5 = cdx*cdx + cdy*cdy + cdz*cdz;                                             
+  h6 = bcx*bcx + bcy*bcy + bcz*bcz;
+     
+  /* quality */
+  if( h1 < h2 ) {
+    lmin = h1;
+    iarmin = 0;
+    lmax = h2;
+    iarmax = 1;
+  } else {
+    lmin = h2;
+    iarmin = 1;    
+    lmax = h1;
+    iarmax = 0;
+  }
+  
+  if( h3 < lmin) {
+    lmin = h3;
+    iarmin = 2;
+  }
+  if( h3 > lmax) {
+    lmax = h3;
+    iarmax = 2;
+  }
+  if( h4 < lmin) {
+    lmin = h4;
+    iarmin = 3;
+  }
+  if( h4 > lmax) {
+    lmax = h4;
+    iarmax = 3;
+  }
+  if( h5 < lmin) {
+    lmin = h5;
+    iarmin = 4;
+  }
+  if( h5 > lmax) {
+    lmax = h5;
+    iarmax = 4;
+  }
+  if( h6 < lmin) {
+    lmin = h6;
+    iarmin = 5;
+  }
+  if( h6 > lmax) {
+    lmax = h6;
+    iarmax = 5;
+  }
+  lmin = sqrt(lmin);
+  lmax = sqrt(lmax);   
+  lmin = (lmin > 1.) ? lmin : 1./lmin;
+  lmax = (lmax > 1.) ? lmax : 1./lmax; 
+  //printf("long %e %e %e %e %e %e\n",sqrt(h1),sqrt(h2),sqrt(h3),sqrt(h4),sqrt(h5),sqrt(h6));
+  //printf("--lmin %e %e\n",lmin,lmax);
+  if ( lmin > lmax) {
+     caltab[0] = 60*lmin + iarmin;
+  } else {
+     caltab[0] = 60*lmax + iarmax;
+  }
+  rap = h1 + h2 + h3 + h4 + h5 + h6;
+  num = sqrt(rap) * rap;
+  cal = num / vol;
+              
+  //printf("cal %e ? %e\n",caltab[0],crit);
+  if ( cal > crit )  return(0);   
+  
+  ecx = c[0] - e[0];
+  ecy = c[1] - e[1];
+  ecz = c[2] - e[2];
+ 
+  edx = d[0] - e[0];
+  edy = d[1] - e[1];
+  edz = d[2] - e[2];
+
+  h1e = bex*bex + bey*bey + bez*bez;
+  h2e = ecx*ecx + ecy*ecy + ecz*ecz;
+  h3e = edx*edx + edy*edy + edz*edz;   
+ 
+  if( h1e < h2e ) {
+    lmin = h1e;
+    iarmin = 0;
+    lmax = h2e;
+    iarmax = 1;
+  } else {
+    lmin = h2e;
+    iarmin = 1;    
+    lmax = h1e;
+    iarmax = 0;
+  }
+  
+  if( h3e < lmin) {
+    lmin = h3e;
+    iarmin = 2;
+  }
+  if( h3e > lmax) {
+    lmax = h3;
+    iarmax = 2;
+  }
+  if( h4 < lmin) {
+    lmin = h4;
+    iarmin = 3;
+  }
+  if( h4 > lmax) {
+    lmax = h4;
+    iarmax = 3;
+  }
+  if( h5 < lmin) {
+    lmin = h5;
+    iarmin = 4;
+  }
+  if( h5 > lmax) {
+    lmax = h5;
+    iarmax = 4;
+  }
+  if( h6 < lmin) {
+    lmin = h6;
+    iarmin = 5;
+  }
+  if( h6 > lmax) {
+    lmax = h6;
+    iarmax = 5;
+  }
+  
+  lmin = sqrt(lmin);
+  lmax = sqrt(lmax);     
+  lmin = (lmin > 1.) ? lmin : 1./lmin;
+  lmax = (lmax > 1.) ? lmax : 1./lmax;   
+  //printf("lmin %e %e\n",lmin,lmax);
+  if ( lmin > lmax) {
+     caltab[1] = 60*lmin + iarmin;
+  } else {
+     caltab[1] = 60*lmax + iarmax;
+  }
+  rap = h1e + h2e + h3e + h4 + h5 + h6;
+  num = sqrt(rap) * rap;
+  cal = num / vole;
+  
+  //printf("cal %e ? %e\n",caltab[1],crit);
+  if ( cal > crit )  return(0);
+//  puts("je passe");exit(0);
+
+  return(1);
+}
+
+double MMG_calte3_ani(pMesh mesh,pSol sol,int iel) {
+  pTetra     pt;
+  double     cal,ab[3],ac[3],ad[3],bc[3],bd[3],cd[3];
+  double     vol,det,v1,v2,v3,air,dd;
+  double    *a,*b,*c,*d;
+  double     *ma,*mb,*mc,*md,mm[6];
+  int        j,ia,ib,ic,id,iadr;
+  static double id3[6] ={1.0,0.,0.,1.,0.,1.};
+
+  pt = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(0.0);
+  cal = CALLIM;
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+
+  /* average metric */
+  memset(mm,0,6*sizeof(double));
+  iadr = (ia-1)*sol->offset + 1;
+  ma   = &sol->met[iadr];
+  iadr = (ib-1)*sol->offset + 1;
+  mb   = &sol->met[iadr];
+  iadr = (ic-1)*sol->offset + 1;
+  mc   = &sol->met[iadr];
+  iadr = (id-1)*sol->offset + 1;
+  md   = &sol->met[iadr];
+  for (j=0; j<6; j++)
+    mm[j] = 0.25 * (ma[j]+mb[j]+mc[j]+md[j]);
+
+  a  = mesh->point[ia].c;
+  b  = mesh->point[ib].c;
+  c  = mesh->point[ic].c;
+  d  = mesh->point[id].c;
+
+  /* volume */
+  ab[0] = b[0] - a[0];
+  ab[1] = b[1] - a[1];
+  ab[2] = b[2] - a[2];
+
+  ac[0] = c[0] - a[0];
+  ac[1] = c[1] - a[1];
+  ac[2] = c[2] - a[2];
+  
+  ad[0] = d[0] - a[0];
+  ad[1] = d[1] - a[1];
+  ad[2] = d[2] - a[2];
+
+  v1  = ac[1]*ad[2] - ac[2]*ad[1];
+  v2  = ac[2]*ad[0] - ac[0]*ad[2];
+  v3  = ac[0]*ad[1] - ac[1]*ad[0];
+  vol = ab[0] * v1 + ab[1] * v2 + ab[2] * v3;
+  if ( vol <= 0. )  return(cal);
+
+  det = mm[0] * ( mm[3]*mm[5] - mm[4]*mm[4]) \
+      - mm[1] * ( mm[1]*mm[5] - mm[2]*mm[4]) \
+      + mm[2] * ( mm[1]*mm[4] - mm[2]*mm[3]);
+  if ( det < EPSOK )  return(cal);
+  det = sqrt(det) * vol;
+
+  bc[0] = c[0] - b[0];
+  bc[1] = c[1] - b[1];
+  bc[2] = c[2] - b[2];
+
+  bd[0] = d[0] - b[0];
+  bd[1] = d[1] - b[1];
+  bd[2] = d[2] - b[2];
+
+  cd[0] = d[0] - c[0];
+  cd[1] = d[1] - c[1];
+  cd[2] = d[2] - c[2];
+
+  /* surfaces */
+  memcpy(mm,id3,6*sizeof(double));
+  dd  = determ(bd,bc,mm);
+  air = dd;
+if ( iel ==17460 )  printf("aire1 %E\n",sqrt(dd));
+
+  dd   = determ(ac,ad,mm);
+if ( iel ==17460 )  printf("aire2 %E\n",sqrt(dd));
+  air  = M_MAX(air,dd);
+
+  dd   = determ(ad,ab,mm);
+if ( iel ==17460 )  printf("aire3 %E\n",sqrt(dd));
+  air  = M_MAX(air,dd);
+
+  dd   = determ(ab,ac,mm);
+if ( iel ==17460 )  printf("aire4 %E\n",sqrt(dd));
+  air  = M_MAX(air,dd);
+  
+  /* quality */
+  if ( iel == 20496 ) {
+    printf("vol %E  \n",vol);
+    printf("a %d: %f %f %f\n",ia,a[0],a[1],a[2]);
+    printf("b %d: %f %f %f\n",ib,b[0],b[1],b[2]);
+    printf("c %d: %f %f %f\n",ic,c[0],c[1],c[2]);
+    printf("d %d: %f %f %f\n",id,d[0],d[1],d[2]);
+  }
+  cal = 3.0*vol / sqrt(air);
+
+  return(cal);
+}
+
+/* compute tetra quality to be the cubic mean ration : 15552 * V^2/(somme l^2)^3 */
+double MMG_caltetcubic(pMesh mesh,pSol sol,int iel) {
+  pTetra     pt;
+  double     cal,abx,aby,abz,acx,acy,acz,adx,ady,adz;
+  double     bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz;
+  double     h1,h2,h3,h4,h5,h6,vol,rap,v1,v2,v3;
+  double    *a,*b,*c,*d;
+  int        ia,ib,ic,id;
+
+  cal = 0.;
+  pt  = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(cal);
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+
+  a = mesh->point[ia].c;
+  b = mesh->point[ib].c;
+  c = mesh->point[ic].c;
+  d = mesh->point[id].c;
+
+  /* volume */
+  abx = b[0] - a[0];
+  aby = b[1] - a[1];
+  abz = b[2] - a[2];
+
+  acx = c[0] - a[0];
+  acy = c[1] - a[1];
+  acz = c[2] - a[2];
+  
+  adx = d[0] - a[0];
+  ady = d[1] - a[1];
+  adz = d[2] - a[2];
+
+  v1  = acy*adz - acz*ady;
+  v2  = acz*adx - acx*adz;
+  v3  = acx*ady - acy*adx;
+  vol = abx * v1 + aby * v2 + abz * v3;
+  if ( vol <= 0. )  return(cal); 
+  vol /= 6.;
+
+  /*  edge */
+  h1 = abx*abx + aby*aby + abz*abz;
+  h2 = acx*acx + acy*acy + acz*acz;
+  h3 = adx*adx + ady*ady + adz*adz;
+
+  bcx = c[0] - b[0];
+  bcy = c[1] - b[1];
+  bcz = c[2] - b[2];
+
+  bdx = d[0] - b[0];
+  bdy = d[1] - b[1];
+  bdz = d[2] - b[2];
+
+  cdx = d[0] - c[0];
+  cdy = d[1] - c[1];
+  cdz = d[2] - c[2];
+
+  h4 = bdx*bdx + bdy*bdy + bdz*bdz;
+  h5 = cdx*cdx + cdy*cdy + cdz*cdz;
+  h6 = bcx*bcx + bcy*bcy + bcz*bcz;
+
+  /* quality */
+  rap = h1 + h2 + h3 + h4 + h5 + h6;
+  cal = vol*vol;
+  cal *= 15552.; 
+  cal = exp(0.3333333333*log(cal));
+  //cal = pow(cal,0.33333333333);
+  cal = cal / rap; 
+
+  return(cal);
+}
+
+/* compute tetra quality : 60*max(1/lmin,lmax) + iare*/
+double MMG_callong(pMesh mesh,pSol sol,int iel) {
+  pTetra     pt;
+  double    *ca,*cb,len;
+  double     *ma,*mb,lmin,lmax,cal; 
+  double     ux,uy,uz,dd1,sa,dd2,sb,dd,rap;
+  int        i,ia,ib,iadr,iarmin,iarmax,ipa,ipb;
+
+  cal = CALLIM;
+  pt  = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(cal);
+  
+  lmax   = 0.;
+  lmin   = CALLIM;
+  iarmin = 0;
+  iarmax = 0;
+  for (i=0; i<6; i++) {
+  
+    /* edge length */
+    ia  = MMG_iare[i][0];
+    ib  = MMG_iare[i][1];
+    ipa = pt->v[ia];
+    ipb = pt->v[ib];
+
+    ca  = &mesh->point[ipa].c[0];
+    cb  = &mesh->point[ipb].c[0];
+     
+    iadr = (ipa-1)*sol->offset + 1;
+    ma  = &sol->met[iadr];
+
+    iadr = (ipb-1)*sol->offset + 1;
+    mb  = &sol->met[iadr];        
+    
+    if(sol->offset==6) {
+      ux = cb[0] - ca[0];
+      uy = cb[1] - ca[1];
+      uz = cb[2] - ca[2];
+
+      dd1 =      ma[0]*ux*ux + ma[3]*uy*uy + ma[5]*uz*uz \
+          + 2.0*(ma[1]*ux*uy + ma[2]*ux*uz + ma[4]*uy*uz);
+      if ( dd1 <= 0.0 )  dd1 = 0.0;
+
+      dd2 =     mb[0]*ux*ux + mb[3]*uy*uy + mb[5]*uz*uz \
+          + 2.0*(mb[1]*ux*uy +mb[2]*ux*uz + mb[4]*uy*uz);
+      if ( dd2 <= 0.0 )  dd2 = 0.0;
+
+      /*longueur approchee*/   
+      /*precision a 3.5 10e-3 pres*/
+      if(fabs(dd1-dd2) < 0.05 ) {
+        //printf("bonne precision %e \n",sqrt(0.5*(dd1+dd2)) - (sqrt(dd1)+sqrt(dd2)+4.0*sqrt(0.5*(dd1+dd2))) / 6.0 );
+        len = sqrt(0.5*(dd1+dd2));
+      } else{
+        len = (sqrt(dd1)+sqrt(dd2)+4.0*sqrt(0.5*(dd1+dd2))) / 6.0;
+      }
+    } else {
+      sa   = *ma;
+      sb   = *mb;
+
+      ux = cb[0] - ca[0];
+      uy = cb[1] - ca[1];
+      uz = cb[2] - ca[2];
+      dd = sqrt(ux*ux + uy*uy + uz*uz);
+
+      rap = (sb - sa) / sa;
+      if ( fabs(rap) < EPS1 )
+        /*len = dd * (2.0-EPS1) / (2.0*sa);*/
+        len = dd / sa;
+      else
+        /*len = max(dd/sa,dd/sb);*/
+        len = dd * (1.0/sa + 1.0/sb + 8.0 / (sa+sb)) / 6.0;
+      
+    }    
+    if ( len < lmin ) {
+      lmin = len;
+      iarmin = i;
+    } 
+    if ( len > lmax ) {
+      lmax = len;
+      iarmax = i;
+    }
+  }  
+  lmin = (lmin > 1.) ? lmin : 1./lmin;
+  lmax = (lmax > 1.) ? lmax : 1./lmax;
+  if ( lmin > lmax) {
+    cal = 60*lmin + iarmin;
+  } else {
+    cal = 60*lmax + iarmax;
+  } 
+  return(cal);
+}
diff --git a/contrib/mmg3d/build/sources/queue.c b/contrib/mmg3d/build/sources/queue.c
new file mode 100644
index 0000000000000000000000000000000000000000..2756bf3bb8d744fca88a3822f5aada85f2edaa9c
--- /dev/null
+++ b/contrib/mmg3d/build/sources/queue.c
@@ -0,0 +1,167 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+ 
+pQueue MMG_kiuini(pMesh mesh,int nbel,double declic,int base) {
+  pQueue   q;
+  pTetra   pt;
+  int      k;
+
+  q = (Queue*)M_malloc(sizeof(Queue),"kiuini");
+  assert(q);
+  q->stack = (int*)M_calloc((1+nbel),sizeof(int),"kiuini.stack");
+  assert(q->stack);
+
+  q->cur = 0;
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] || pt->qual < declic )    continue;
+    else if ( base > 0 && pt->flag < base )  continue;
+
+    q->stack[q->cur] = k;
+    q->cur = k;
+  }
+
+  return(q);
+}
+
+
+void MMG_kiufree(pQueue q) {
+  M_free(q->stack);
+  M_free(q);
+}
+
+
+int MMG_kiudel(pQueue q,int iel) {
+  int   k;
+
+  if ( !q->stack[0] )
+    return(0);
+
+  else if ( q->cur != iel && !q->stack[iel] )
+    return(0);
+
+  else if ( iel == q->stack[0] ) {
+    if ( iel == q->cur ) {
+      q->cur = 0;
+      q->stack[0] = 0;
+      return(1);
+    }
+    else {
+      q->stack[0]   = q->stack[iel];
+      q->stack[iel] = 0; 
+      return(1);
+    }
+  }
+
+  else {
+    for (k=iel-1; k>0; k--)
+      if ( q->stack[k] == iel )  break;
+assert(k>0);
+    if ( iel == q->cur ) {
+      q->cur        = k;
+      q->stack[k]   = 0;
+      q->stack[iel] = 0;
+      return(1);
+    }
+    else {
+      q->stack[k]   = q->stack[iel];
+      q->stack[iel] = 0;
+      return(1);
+    }
+  }
+
+  return(0);
+}
+
+
+int MMG_kiuput(pQueue q,int iel) {
+  int    k;
+
+  if ( !q->stack[0] )
+    return(0);
+
+  else if ( iel == q->cur || q->stack[iel] )
+    return(0);
+
+  else if ( iel > q->cur ) {
+    q->stack[q->cur] = iel;
+    q->stack[iel]    = 0;
+    q->cur = iel;
+    return(1);
+  }
+
+  else if ( iel < q->stack[0] ) {
+    q->stack[iel] = q->stack[0];
+    q->stack[0]   = iel;
+    return(1);
+  }
+
+  else {
+    for (k=iel-1; k>=0; k--)
+      if ( q->stack[k] )  break;
+assert(k>-1);
+    q->stack[iel] = q->stack[k];
+    q->stack[k]   = iel;
+    return(1);
+  }
+
+  return(0);
+}
+
+
+int MMG_kiupop(pQueue q) {
+  int  cur;
+
+  cur = q->stack[0];
+  q->stack[0]   = q->stack[cur];
+  q->stack[cur] = 0;
+  if ( q->cur == cur )  q->cur = 0;
+
+  return(cur);
+}
+
+
diff --git a/contrib/mmg3d/build/sources/ratio.c b/contrib/mmg3d/build/sources/ratio.c
new file mode 100644
index 0000000000000000000000000000000000000000..0f3cfaa4c32825514ae599ab8b7f6ade2dbf3982
--- /dev/null
+++ b/contrib/mmg3d/build/sources/ratio.c
@@ -0,0 +1,442 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile,
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite,
+y compris les garanties de commercialisation ou
+d’adaptation dans un but spécifique.
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU
+en même temps que ce document.
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+spread under the terms and conditions of the license GNU General Public License
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.
+****************************************************************************/
+/*
+ratio =
+tet equi : 1.
+tet rect (1 1 sqrt(2)) : 3.73 vs 1.93 (avec sqrt(lmax/lmin))
+tet rect (1 1/sqrt(2) 1/sqrt(2)) : 2.2413 vs 1.49
+*/
+
+#include "mesh.h"
+
+int MMG_gauss(double mat[6][6],double rhs[6],double* met);
+
+
+/*compute the aniso ratio for an element k*/
+double MMG_rao(pMesh mesh,int k,FILE* inm) {
+  pTetra		pt;
+  pPoint		ppa,ppb;
+  double		edg[6][3],mat[6][6],met[6],rhs[6];
+  double		lambda[3],v[3][3],lmin,lmax,rao;
+//  double		bufd[GmfMaxTyp];
+  int			i,j;
+
+  pt = &mesh->tetra[k];
+
+   /*compute the ellipsoide*/
+  for (i=0 ; i<6 ; i++)
+    rhs[i] = 1;
+
+  for (i=0 ; i<6 ; i++) {
+    ppa = &mesh->point[pt->v[MMG_iare[i][0]]];
+    ppb = &mesh->point[pt->v[MMG_iare[i][1]]];
+    for (j=0 ; j<3 ; j++) {
+  	  edg[i][j] = ppb->c[j] - ppa->c[j];
+    }
+    mat[i][0] = edg[i][0]*edg[i][0];
+    mat[i][1] = 2*edg[i][0]*edg[i][1];
+    mat[i][2] = 2*edg[i][0]*edg[i][2];
+    mat[i][3] = edg[i][1]*edg[i][1];
+    mat[i][4] = 2*edg[i][1]*edg[i][2];
+    mat[i][5] = edg[i][2]*edg[i][2];
+  }
+  MMG_gauss(mat,rhs,met);
+
+  /*find the eigenvalues of the metric*/
+  if ( !eigenv(1,met,lambda,v) ) {
+    for (j=0 ; j<6 ; j++)
+  	  printf("%e %e %e %e %e %e\n",mat[j][0],mat[j][1],mat[j][2],mat[j][3],mat[j][4],mat[j][5]);
+  	  printf("\n met %e %e %e %e %e %e\n",met[0],met[1],met[2],met[3],met[4],met[5]);
+  puts("pbs eigen");
+     return(0);
+  }
+  /*calculate the aniso ratio obtained*/
+  lmin = M_MIN(lambda[0],lambda[1]);
+  lmin = M_MIN(lmin,lambda[2]);
+  lmax = M_MAX(lambda[0],lambda[1]);
+  lmax = M_MAX(lmax,lambda[2]);
+  rao  = sqrt(lmax / lmin);
+  if(inm) {
+	  /*for (i=0; i<6; i++)
+      bufd[i] = met[i];
+
+      bufd[2] = met[3];
+	    bufd[3] = met[2];*/
+		  fprintf(inm,"%.15lg \n",rao);
+  }
+
+  return(rao);
+}
+
+
+int MMG_avgmet(pSol sol,pTetra pt,double* mm) {
+  double	*ma,*mb,*mc,*md,h1,h2,h3,h4,h;
+  int		ia,ib,ic,id,j,iadr;
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+
+  if (sol->offset==1) {
+	h1 = sol->met[ia];
+	h2 = sol->met[ib];
+	h3 = sol->met[ic];
+	h4 = sol->met[id];
+	h  = 0.25*(h1 + h2 + h3 + h4);
+	mm[0] = h;
+	mm[1] = 0;
+	mm[2] = 0;
+	mm[3] = h;
+	mm[4] = 0;
+	mm[5] = h;
+  } else {
+
+	/* average metric */
+	memset(mm,0,6*sizeof(double));
+	iadr = (ia-1)*sol->offset + 1;
+	ma   = &sol->met[iadr];
+	iadr = (ib-1)*sol->offset + 1;
+	mb   = &sol->met[iadr];
+	iadr = (ic-1)*sol->offset + 1;
+	mc   = &sol->met[iadr];
+	iadr = (id-1)*sol->offset + 1;
+	md   = &sol->met[iadr];
+	for (j=0; j<6; j++)
+	  mm[j] = 0.25 * (ma[j]+mb[j]+mc[j]+md[j]);
+
+  }
+
+  return(1);
+}
+
+/* compute the prescribed and obtained anisotropic ratio
+   print histo + save the ratio in a file
+*/
+int MMG_ratio(pMesh mesh, pSol sol,char* firaoame) {
+  FILE     *inm;
+  pTetra		pt;
+  double		met[6];
+  double		lambda[3],v[3][3],lmin,lmax,rao;
+  int			  k,ne,typ;
+  char			*ptr,data[128],chaine[128];
+  double    rapmin,rapmax,rapavg;
+  int       his[10],rapnum;
+  int       iel,ir,nn,nex,ielreal;
+  static double bd[9] = {1.0, 2., 10.0, 50., 100., 200., 500., 1000., 5000. };
+
+  /*save ratio obtained ?*/
+  inm = 0;
+  if(firaoame) {
+    strcpy(data,firaoame);
+    ptr = strstr(data,".meshb");
+    if ( ptr )  *ptr = '\0';
+    else {
+      ptr = strstr(data,".mesh");
+      if ( ptr ) {
+        *ptr = '\0';
+      }
+    }
+    strcat(data,".sol");
+    if( !(inm = fopen(data,"w")) ) {
+      fprintf(stderr,"  ** UNABLE TO OPEN %s.\n",data);
+      return(0);
+    }
+    else
+      fprintf(stdout,"  %%%% %s OPENED\n",data);
+
+    /*entete fichier*/
+    strcpy(&chaine[0],"MeshVersionFormatted 2\n");
+    fprintf(inm,"%s",chaine);
+    strcpy(&chaine[0],"\n\nDimension 3\n");
+    fprintf(inm,"%s ",chaine);
+
+	  typ = 1;
+
+	  ne = 0;
+    for(k=1 ; k<=mesh->ne ; k++) {
+	    pt = &mesh->tetra[k];
+	    if(!pt->v[0]) continue;
+	    ne++;
+    }
+    strcpy(&chaine[0],"\n\nSolAtTetrahedra\n");
+    fprintf(inm,"%s",chaine);
+    fprintf(inm,"%d\n",ne);
+    fprintf(inm,"%d %d\n",1,typ);
+
+  }
+
+  if ( mesh->info.imprim  < 0 ) {
+
+    /*ratio prescribed*/
+
+    rapmin  =  1.e20;
+    rapmax  = -1.e20;
+    rapavg  = 0.0;
+    rapnum  = 0;
+    iel     = 0;
+    ielreal = 0;
+    nn      = 0;
+    nex     = 0;
+
+    for (k=0; k<10; k++)  his[k] = 0;
+
+    for(k=1 ; k<=mesh->ne ; k++) {
+	  pt = &mesh->tetra[k];
+	  if(!pt->v[0]) {
+        nex++;
+        continue;
+      }
+      nn++;
+
+	  /*mean metric*/
+	  MMG_avgmet(sol,pt,met);
+
+      /*find the eigenvalues of the prescribed metric*/
+	  if ( !eigenv(1,met,lambda,v) ) {
+   	     puts("pbs eigen");
+             if(inm) fclose(inm);
+	     return(0);
+      }
+
+      /*calculate the aniso ratio */
+	  lmin = M_MIN(lambda[0],lambda[1]);
+	  lmin = M_MIN(lmin,lambda[2]);
+ 	  lmax = M_MAX(lambda[0],lambda[1]);
+	  lmax = M_MAX(lmax,lambda[2]);
+	  rao  = sqrt(lmax / lmin);
+
+	  /*histo prescribed*/
+	  ir   = (int)rao;
+	  if ( rao > rapmax ) {
+        rapmax = rao;
+        iel     = k;
+        ielreal = k - nex;
+      }
+      rapavg += rao;
+      rapnum++;
+      if ( rao < 17e10 ) {
+        rapmin = M_MIN(rapmin,rao);
+        if (rao < bd[3]) {
+	      if (rao > bd[2])       his[3]++;
+	      else if (rao > bd[1])  his[2]++;
+	      else                   his[1]++;
+        }
+        else if (rao < bd[5]) {
+	      if (rao > bd[4])       his[5]++;
+	      else if (rao > bd[3])  his[4]++;
+        }
+        else if (rao < bd[6])    his[6]++;
+        else if (rao < bd[7])    his[7]++;
+        else if (rao < bd[8])    his[8]++;
+        else                     his[9]++;
+      }
+
+    }
+    /* print histo ratio obtained*/
+    fprintf(stdout,"\n  -- ANISOTROPIC RATIO PRESCRIBED   %d\n",rapnum);
+    fprintf(stdout,"      AVERAGE RATIO       %12.4f\n",rapavg / rapnum);
+    fprintf(stdout,"     SMALLEST RATIO       %12.4f\n",rapmin);
+    if (rapmax < 1.e4) {
+      fprintf(stdout,"      LARGEST RATIO       %12.4f\n",rapmax);
+    } else {
+	  fprintf(stdout,"      LARGEST RATIO       %12.4e\n",rapmax);
+    }
+    pt = &mesh->tetra[iel];
+    fprintf(stdout,"           ELEMENT   %d (%d)   %d %d %d %d\n",
+        iel,ielreal,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+
+    fprintf(stdout,"\n     HISTOGRAMM\n");
+    for (k=1; k<9; k++) {
+      if ( his[k] > 0 )
+        fprintf(stdout,"   %8.2f < R <%8.2f  %8d   %5.2f %%  \n",
+            	bd[k-1],bd[k],his[k],100.*(his[k]/(float)rapnum));
+    }
+    if ( his[9] )
+      fprintf(stdout,"    5000.00 < R            %8d   %5.2f %%  \n",
+        his[9],100.*(his[9]/(float)rapnum));
+
+  }
+
+  rapmin  =  1.e20;
+  rapmax  = -1.e20;
+  rapavg  = 0.0;
+  rapnum  = 0;
+  iel     = 0;
+  ielreal = 0;
+  nn      = 0;
+  nex     = 0;
+
+  for (k=0; k<10; k++)  his[k] = 0;
+
+  for(k=1 ; k<=mesh->ne ; k++) {
+	pt = &mesh->tetra[k];
+	if(!pt->v[0]) {
+      nex++;
+      continue;
+    }
+    nn++;
+
+	rao = MMG_rao(mesh,k,inm);
+
+    /*histo obtained*/
+	ir   = (int)rao;
+	if ( rao > rapmax ) {
+      rapmax = rao;
+      iel     = k;
+      ielreal = k - nex;
+    }
+    rapavg += rao;
+    rapnum++;
+
+    if ( rao < 17e10 ) {
+      rapmin = M_MIN(rapmin,rao);
+      if (rao < bd[3]) {
+	    if (rao > bd[2])       his[3]++;
+	    else if (rao > bd[1])  his[2]++;
+	    else                   his[1]++;
+      }
+      else if (rao < bd[5]) {
+	    if (rao > bd[4])       his[5]++;
+	    else if (rao > bd[3])  his[4]++;
+      }
+      else if (rao < bd[6])    his[6]++;
+      else if (rao < bd[7])    his[7]++;
+      else if (rao < bd[8])    his[8]++;
+      else                     his[9]++;
+    }
+
+  }
+
+  if(inm) fclose(inm);
+
+  /* print histo ratio obtained*/
+  if (mesh->info.imprim < 0){
+    fprintf(stdout,"        ANISOTROPIC RATIO (MEAN = %6.2g, MAX = %6.2g, MIN = %6.2f)\n",rapavg / rapnum, rapmax, rapmin);
+  }
+  else if (mesh->info.imprim < 0){
+    fprintf(stdout,"\n  -- ANISOTROPIC RATIO OBTAINED   %d\n",rapnum);
+    fprintf(stdout,"      AVERAGE RATIO       %12.4f\n",rapavg / rapnum);
+    fprintf(stdout,"     SMALLEST RATIO       %12.4f\n",rapmin);
+    if (rapmax < 1.e4) {
+      fprintf(stdout,"      LARGEST RATIO       %12.4f\n",rapmax);
+    } else {
+      fprintf(stdout,"      LARGEST RATIO       %12.4e\n",rapmax);
+    }
+    pt = &mesh->tetra[iel];
+
+    fprintf(stdout,"           ELEMENT   %d (%d)   %d %d %d %d\n",
+	    iel,ielreal,pt->v[0],pt->v[1],pt->v[2],pt->v[3]);
+  }
+
+  if ( abs(mesh->info.imprim) < 5 )  return 1;
+
+  fprintf(stdout,"\n     HISTOGRAMM\n");
+  for (k=1; k<9; k++) {
+    if ( his[k] > 0 )
+      fprintf(stdout,"   %8.2f < R <%8.2f  %8d   %5.2f %%  \n",
+      		bd[k-1],bd[k],his[k],100.*(his[k]/(float)rapnum));
+  }
+  if ( his[9] )
+    fprintf(stdout,"    5000.00 < R            %8d   %5.2f %%  \n",
+        his[9],100.*(his[9]/(float)rapnum));
+
+  return(1);
+}
+
+/* solve mat*met = rhs */
+int MMG_gauss(double mat[6][6],double rhs[6],double* met) {
+  int i,j,l;
+  double tmp,piv;
+  /*printf("begin : \n");
+  for (j=0 ; j<6 ; j++)
+	  printf("%e %e %e %e %e %e\n",mat[j][0],mat[j][1],mat[j][2],mat[j][3],mat[j][4],mat[j][5]);
+  */
+  /* triangularisation*/
+  for (i=0 ; i<5 ; i++) {
+	l = i+1;
+    while ( (fabs(mat[i][i]) < 1e-8) && (l<6)){
+	  for (j=0 ; j<6 ; j++) {
+		tmp = mat[i][j];
+		mat[i][j] = mat[l][j];
+		mat[l][j] = tmp;
+	  }
+	  tmp    = rhs[i];
+	  rhs[i] = rhs[l];
+	  rhs[l] = tmp;
+
+	  l++;
+    }
+	if ((fabs(mat[i][i]) < 1e-8)) {
+	   //puts("WARNING PIV");
+		met[0] = 1;
+		met[1] = 0;
+		met[2] = 0;
+		met[3] = 1e7;
+		met[4] = 10;
+		met[5] = 1e7;
+		return(1);
+	}
+    for (j=i+1 ; j<6 ; j++) {
+	  piv = mat[j][i];
+	  for (l=0 ; l<6 ; l++) {
+ 		mat[j][l] -= piv*mat[i][l] / mat[i][i];
+	  }
+	  rhs[j] -= piv*rhs[i]/ mat[i][i];
+    }
+  }
+  /*remontee*/
+  met[5] = rhs[5] / mat[5][5];
+  for(j=4 ; j>=0 ; j--){
+	met[j] = rhs[j];
+	for(l=j+1 ; l<6 ; l++) {
+	  met[j] += -mat[j][l]*met[l];
+	}
+	met[j] /= mat[j][j];
+  }
+
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/scalem.c b/contrib/mmg3d/build/sources/scalem.c
new file mode 100644
index 0000000000000000000000000000000000000000..5865dd041566c54135986ee8261f6809f3d579c2
--- /dev/null
+++ b/contrib/mmg3d/build/sources/scalem.c
@@ -0,0 +1,230 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_scaleMesh(pMesh mesh,pSol sol) {
+  pTetra    pt;
+  pPoint    ppt;
+  pDispl    pd;
+  Info     *info;
+  double    dd,d1;
+  double    *m,*mold;
+  int       i,k,iadr,alloc,ii,jj,kk;
+  double	lambda[3],v[3][3];  
+
+  /* mark used vertices */
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+    for (i=0; i<4; i++) {
+      ppt = &mesh->point[ pt->v[i] ];
+      ppt->tag &= ~M_UNUSED;
+    }
+  }
+  
+  if (abs(mesh->info.option)==10) {
+    return(1);
+  }           
+  
+  /* compute bounding box */
+  info = &mesh->info;
+  for (i=0; i<3; i++) {
+    info->min[i] =  DBL_MAX;
+    info->max[i] = -DBL_MAX;
+  }
+
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED ) continue;
+    for (i=0; i<3; i++) {
+      if ( ppt->c[i] > info->max[i] )  info->max[i] = ppt->c[i];
+      if ( ppt->c[i] < info->min[i] )  info->min[i] = ppt->c[i];
+    }
+  }
+  info->delta = info->max[0]-info->min[0];
+  dd = info->max[1]-info->min[1];
+  if ( dd > info->delta )
+    info->delta = dd;
+  dd = info->max[2]-info->min[2];
+  if ( dd > info->delta )
+    info->delta = dd;
+  if ( info->delta < EPS30 ) {
+    fprintf(stdout,"  ## Unable to scale mesh.\n");
+    return(0);
+  }
+  /* normalize coordinates */
+  alloc = mesh->disp != NULL;
+  dd = (double)PRECI / info->delta;
+  if ( !alloc ) {
+    for (k=1; k<=mesh->np; k++) {
+      ppt = &mesh->point[k];
+      if ( ppt->tag & M_UNUSED )  continue;
+      ppt->c[0] = dd * (ppt->c[0] - info->min[0]);
+      ppt->c[1] = dd * (ppt->c[1] - info->min[1]);
+      ppt->c[2] = dd * (ppt->c[2] - info->min[2]);
+    }
+  }
+  else {
+    pd  = mesh->disp;
+    for (k=1; k<=mesh->np; k++) {
+      ppt = &mesh->point[k];
+      if ( ppt->tag & M_UNUSED )  continue;
+      ppt->c[0] = dd * (ppt->c[0] - info->min[0]);
+      ppt->c[1] = dd * (ppt->c[1] - info->min[1]);
+      ppt->c[2] = dd * (ppt->c[2] - info->min[2]);
+
+      pd->mv[3*(k-1) + 1 + 0] *= dd;
+      pd->mv[3*(k-1) + 1 + 1] *= dd;
+      pd->mv[3*(k-1) + 1 + 2] *= dd;
+
+      d1 = pd->mv[3*(k-1) + 1 + 0]*pd->mv[3*(k-1) + 1 + 0]
+ 		 + pd->mv[3*(k-1) + 1 + 1]*pd->mv[3*(k-1) + 1 + 1] \
+         + pd->mv[3*(k-1) + 1 + 2]*pd->mv[3*(k-1) + 1 + 2];
+      if ( d1 > EPS2 )  ppt->tag  |= M_MOVE;
+    }
+  }
+
+  /* normalize metric */
+  if ( !sol->np ) return(1);
+  
+  switch (sol->offset) {
+  case 1:
+    for (k=1; k<=sol->np; k++)  {
+      sol->met[k] *= dd;
+     }
+    break;
+
+  case 6:
+    dd = 1.0 / (dd*dd);
+    for (k=1; k<=mesh->np; k++) {
+      iadr = (k-1)*sol->offset + 1;
+      m    = &sol->met[iadr];
+      for (i=0; i<sol->offset; i++)  m[i]   *= dd; 
+      
+      /*calcul du log de M*/                 
+      if ( !eigenv(1,m,lambda,v) ) {
+         printf("WRONG METRIC AT POINT %d -- \n",k);
+	     return(0);
+      }
+      for (i=0; i<3; i++) { 
+        if(lambda[i]<=0) {    
+            printf("WRONG METRIC AT POINT %d -- eigenvalue : %e %e %e -- det %e\n",k,lambda[0],lambda[1],lambda[2],
+                                                        m[0]*(m[3]*m[5]-m[4]*m[4])-m[1]*(m[1]*m[5]-m[2]*m[4])+
+                                                        m[2]*(m[1]*m[4]-m[2]*m[3]));
+            printf("WRONG METRIC AT POINT %d -- metric %e %e %e %e %e %e\n",k,m[0],m[1],m[2],m[3],m[4],m[5]);
+            return(0);  
+        }  
+        lambda[i] = log(lambda[i]);
+      }
+      mold    = &sol->metold[iadr]; 
+      kk = 0;
+      for (ii=0; ii<3; ii++) {
+	for (jj=ii; jj<3; jj++) {
+	  mold[kk] = lambda[0]*v[0][ii]*v[0][jj] + 
+	    lambda[1]*v[1][ii]*v[1][jj] +
+	    lambda[2]*v[2][ii]*v[2][jj]; 
+	  kk = kk+1;
+	}                     
+      }
+    }
+    break;
+  default:
+    fprintf(stderr,"  ## SPECIFIC DATA NOT USED.\n");
+    exit(2);
+  }
+  
+  /* compute quality */       
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k]; 
+    if ( pt->v[0] ) {
+      pt->qual = MMG_caltet(mesh,sol,k);   
+    }
+    else
+      pt->qual = 0.0;
+  }
+
+  return(1);
+}
+
+
+int MMG_unscaleMesh(pMesh mesh,pSol sol) {
+  pPoint     ppt;
+  Info      *info;
+  double     dd;
+  double     *m;
+  int        i,k,iadr;
+
+  info = &mesh->info;
+
+  /* de-normalize coordinates */
+  dd = info->delta / (double)PRECI;
+  for (k=1; k<=mesh->np; k++) {
+    ppt = &mesh->point[k];
+    if ( ppt->tag & M_UNUSED )  continue;
+    ppt->c[0] = ppt->c[0] * dd + info->min[0];
+    ppt->c[1] = ppt->c[1] * dd + info->min[1];
+    ppt->c[2] = ppt->c[2] * dd + info->min[2];
+  }
+
+  /* de-normalize metric */
+  sol->np = mesh->np;
+  if ( sol->offset == 1 ) {
+    for (k=1; k<=sol->np; k++)  sol->met[k] *= dd;
+  }
+  else {
+    dd = 1.0 / (dd*dd);
+    for (k=1; k<=sol->np; k++) {
+      iadr = (k-1)*sol->offset + 1;
+      m    = &sol->met[iadr];
+      for (i=0; i<6; i++)  m[i] *= dd;
+    }
+  }
+
+  return(1);
+}
+
+
+
diff --git a/contrib/mmg3d/build/sources/simu23.c b/contrib/mmg3d/build/sources/simu23.c
new file mode 100644
index 0000000000000000000000000000000000000000..e9d91c9398bbb7e914f7a00399133b271c43c761
--- /dev/null
+++ b/contrib/mmg3d/build/sources/simu23.c
@@ -0,0 +1,144 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_simu23(pMesh mesh,pSol sol,int iel,int i,double crit) {
+  pTetra     pt,pt1;
+  int       *adja,iadr,jel,j,ia,ib,s1,s2,s3;
+  if ( !MMG_getnElt(mesh,3) )  return(-1);
+
+  pt = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(0);
+
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if ( !adja[i] || (pt->bdryref[i]!=-1))  return(0);
+  jel  = adja[i] / 4;
+  j    = adja[i] % 4;
+  pt1  = &mesh->tetra[jel];
+
+  ia = pt->v[i];
+  ib = pt1->v[j];
+  s1 = pt->v[ MMG_idir[i][0] ];
+  s2 = pt->v[ MMG_idir[i][1] ];
+  s3 = pt->v[ MMG_idir[i][2] ];
+
+  /* quality of new tetras */
+  pt1 = &mesh->tetra[0];
+  pt1->v[0] = ia;
+  pt1->v[1] = ib;
+  pt1->v[2] = s1;
+  pt1->v[3] = s2;
+  if ( MMG_caltet(mesh,sol,0) > crit ) {
+    memset(pt1,0,sizeof(Tetra));
+    return(0);
+  }
+
+  pt1->v[2] = s2;
+  pt1->v[3] = s3;
+  if ( MMG_caltet(mesh,sol,0) > crit ) {
+    memset(pt1,0,sizeof(Tetra));
+    return(0);
+  }
+
+  pt1->v[2] = s3;
+  pt1->v[3] = s1;
+  if ( MMG_caltet(mesh,sol,0) > crit ) {
+    memset(pt1,0,sizeof(Tetra));
+    return(0);
+  }
+
+  memset(pt1,0,sizeof(Tetra));
+  
+  /* set function ptr */
+//  MMG_swpptr = MMG_swap23;
+  return(1);
+}
+
+
+int MMG_simu32(pMesh mesh,pSol sol,pList list,double crit) {
+  pTetra    pt,pt1;
+  double    caltab[2];
+  int      *adja,k,adj,ia,ib,s1,s2,s3,iadr,iel,iar;
+  short     voy;
+
+  /* initial qual */
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ];
+
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s3   = pt1->v[voy];
+
+  /* qual 2 elts */
+  pt1 = &mesh->tetra[0];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s2;
+  pt1->v[3] = s3;
+  if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+    memset(pt1,0,sizeof(Tetra));
+    return(0);
+  }
+  list->qual[1] = caltab[0];
+  list->qual[2] = caltab[1];
+
+  memset(pt1,0,sizeof(Tetra));
+
+  /* set function ptr */
+  MMG_swpptr = MMG_swap32;
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/simu44.c b/contrib/mmg3d/build/sources/simu44.c
new file mode 100644
index 0000000000000000000000000000000000000000..fd459a25c53b3e1d896b723e0d11b675699b0496
--- /dev/null
+++ b/contrib/mmg3d/build/sources/simu44.c
@@ -0,0 +1,140 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/* return 41-42 for config */
+int MMG_simu44(pMesh mesh,pSol sol,pList list,double crit) {
+  pTetra	pt,pt1;
+  double	caltab[2];
+  int		ia,ib,s1,s2,s3,s4,iadr,*adja,k,adj,iel,iar;
+  short		voy;
+  
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ];
+
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s3   = pt1->v[voy];
+
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s4   = pt1->v[voy];
+
+  do {
+     /*config 1 tetra 1*/
+     pt1 = &mesh->tetra[0];
+     pt1->v[0] = ia;
+     pt1->v[1] = s1;
+     pt1->v[2] = s2;
+     pt1->v[3] = s3;
+     if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+       memset(pt1,0,sizeof(Tetra));
+       break;
+     }
+     list->qual[1] = caltab[0];
+     list->qual[2] = caltab[1];
+  
+     pt1 = &mesh->tetra[0];
+     pt1->v[0] = ia;
+     pt1->v[1] = s1;
+     pt1->v[2] = s3;
+     pt1->v[3] = s4;
+     if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+       memset(pt1,0,sizeof(Tetra));
+       break;
+     }
+     list->qual[3] = caltab[0];
+     list->qual[4] = caltab[1];
+ 
+  
+     /* set function ptr */
+     MMG_swpptr = MMG_swap44_1;
+     return(41);
+  
+  } while(0);
+    
+  /* alternate config */
+  pt1 = &mesh->tetra[0];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s2;
+  pt1->v[3] = s4;
+  if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+    memset(pt1,0,sizeof(Tetra));
+    return(0);
+  }
+  list->qual[1] = caltab[0];
+  list->qual[2] = caltab[1];
+
+  pt1 = &mesh->tetra[0];
+  pt1->v[0] = ia;
+  pt1->v[1] = s2;
+  pt1->v[2] = s3;
+  pt1->v[3] = s4;
+  if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+    memset(pt1,0,sizeof(Tetra));
+    return(0);  
+  }
+  list->qual[3] = caltab[0];
+  list->qual[4] = caltab[1];
+
+  /* set function ptr */
+  MMG_swpptr = MMG_swap44_2;
+  return(42);
+}
+
diff --git a/contrib/mmg3d/build/sources/simu56.c b/contrib/mmg3d/build/sources/simu56.c
new file mode 100644
index 0000000000000000000000000000000000000000..06671c0b2f140cb653400b4771b196ec7beee424
--- /dev/null
+++ b/contrib/mmg3d/build/sources/simu56.c
@@ -0,0 +1,364 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_simu56(pMesh mesh,pSol sol,pList list,double crit) {
+  pTetra	pt,pt1;
+  double	qual[21],caltab[2];
+  int		j,ia,ib,s1,s2,s3,s4,s5;
+  int		iadr,*adja,k,adj,iel,iar;
+  short		voy;
+  
+  for(j = 0 ; j<21 ; j++) {
+    qual[j] = -1;
+  }
+  
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ]; 
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s3   = pt1->v[voy];
+
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s2) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+     k = MMG_idir[voy][2];
+  } else {
+    puts("MMG_simu56_ani: point non existant");
+    exit(0);
+  }  
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s4   = pt1->v[voy];
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s5   = pt1->v[voy];
+  
+  do {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s3;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        memset(pt1,0,sizeof(Tetra));
+				qual[1] = 0;
+        break;
+      }
+      qual[1] = caltab[0];
+      qual[2] = caltab[1];
+      list->qual[1] = qual[1];
+      list->qual[2] = qual[2];
+      
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[3] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[3] = caltab[0];
+      qual[4] = caltab[1];
+      list->qual[3] = qual[3];
+      list->qual[4] = qual[4];
+ 
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[5] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[5] = caltab[0];
+      qual[6] = caltab[1];
+      list->qual[5] = qual[5];
+      list->qual[6] = qual[6];
+     
+     /* set function ptr */
+     memset(pt1,0,sizeof(Tetra));
+     MMG_swpptr = MMG_swap56_1;
+     return(51);
+  } while(0);
+  
+  do {
+      if(!qual[1]) break;      
+      list->qual[1] = qual[1];
+      list->qual[2] = qual[2];
+      
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[7] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+      list->qual[3] = qual[7];
+      list->qual[4] = qual[8];
+ 
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        qual[9] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[9] = caltab[0];
+      qual[10] = caltab[1];
+      list->qual[5] = qual[9];
+      list->qual[6] = qual[10];
+     
+     /* set function ptr */
+     memset(pt1,0,sizeof(Tetra));
+     MMG_swpptr = MMG_swap56_3;
+     return(53);
+  } while(0);
+  
+  do {
+    if(!qual[5]) break;
+    else if(qual[5] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[5] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[5] = caltab[0];
+      qual[6] = caltab[1];
+    }
+    list->qual[1] = qual[5];
+    list->qual[2] = qual[6];
+        
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s3;
+    pt1->v[3] = s4;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[11] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[11] = caltab[0];
+    qual[12] = caltab[1];
+    list->qual[3] = qual[11];
+    list->qual[4] = qual[12];
+ 
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s4;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[13] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[13] = caltab[0];
+    qual[14] = caltab[1];
+    list->qual[5] = qual[13];
+    list->qual[6] = qual[14];
+     
+     /* set function ptr */
+     memset(pt1,0,sizeof(Tetra));
+     MMG_swpptr = MMG_swap56_4;
+     return(54);
+  } while(0);
+  
+  do {
+    if(!qual[11]) break;
+    else if(qual[11] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[11] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[11] = caltab[0];
+      qual[12] = caltab[1];
+    }  
+    list->qual[3] = qual[11];
+    list->qual[4] = qual[12];
+
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[15] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[15] = caltab[0];
+    qual[16] = caltab[1];
+    list->qual[1] = qual[15];
+    list->qual[2] = qual[16];
+      
+ 
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s4;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[17] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[17] = caltab[0];
+    qual[18] = caltab[1];
+    list->qual[5] = qual[17];
+    list->qual[6] = qual[18];
+     
+     /* set function ptr */
+     memset(pt1,0,sizeof(Tetra));
+     MMG_swpptr = MMG_swap56_2;
+     
+     return(52);
+  } while(0);
+  
+  if(!qual[15]) return(0);
+  else if(qual[15] == -1) {
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[15] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      return(0);
+    }
+    qual[15] = caltab[0];
+    qual[16] = caltab[1];
+  }
+  list->qual[1] = qual[15];
+  list->qual[2] = qual[16];
+  
+  if(!qual[9]) return(0);
+  else if(qual[9] == -1) {
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s3;
+    pt1->v[2] = s4;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[9] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      return(0);
+    }
+    qual[9] = caltab[0];
+    qual[10] = caltab[1];
+  }  
+  list->qual[5] = qual[9];
+  list->qual[6] = qual[10];
+  
+  pt1 = &mesh->tetra[0];
+  pt1->v[0] = ia;
+  pt1->v[1] = s2;
+  pt1->v[2] = s3;
+  pt1->v[3] = s5;
+  if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+    qual[19] = 0;
+    memset(pt1,0,sizeof(Tetra));
+    return(0);
+  }
+  qual[19] = caltab[0];
+  qual[20] = caltab[1];
+  list->qual[3] = qual[19];
+  list->qual[4] = qual[20];
+ 
+
+
+  /* set function ptr */
+  memset(pt1,0,sizeof(Tetra));
+  MMG_swpptr = MMG_swap56_5;
+  return(55);
+}
diff --git a/contrib/mmg3d/build/sources/simu68.c b/contrib/mmg3d/build/sources/simu68.c
new file mode 100644
index 0000000000000000000000000000000000000000..f1114219a1eafb576ed965cd65653301c95c3ef4
--- /dev/null
+++ b/contrib/mmg3d/build/sources/simu68.c
@@ -0,0 +1,1130 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/* return 1-14 for config */
+int MMG_simu68(pMesh mesh,pSol sol,pList list,double crit) {
+  pTetra pt,pt1;
+  int ia,ib,s1,s2,s3,s4,s5,s6;
+  int iadr,*adja,k,adj,iel,iar;
+  short  voy;
+  double caltab[2],qual[41];
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+
+  for(k=0 ; k<41 ; k++)
+    qual[k] = -1;
+    
+  /*find points of polygone*/
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ]; 
+  /*printf("s1 %d s2 %d\n",s1,s2);
+  */iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+ /* printf("tetra %d : %d %d %d %d\n",adj,pt1->v[0],pt1->v[1],pt1->v[2],pt1->v[3]);
+  */s3  = pt1->v[voy];
+  /*printf("s3 %d \n",s3);
+  */iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s2) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+     k = MMG_idir[voy][2];
+  } else {
+    puts("MMG_simu56: point s2 non existant");
+    exit(0);
+  }  
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  /*printf("tetra %d : %d %d %d %d\n",adj,pt1->v[0],pt1->v[1],pt1->v[2],pt1->v[3]);
+  */s4   = pt1->v[voy];
+  /*printf("s4 %d\n",s4);
+  */iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s3) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s3) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s3) {
+     k = MMG_idir[voy][2];
+  } else {
+    puts("MMG_simu56_ani: point s4 non existant");
+    exit(0);
+  }  
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s5   = pt1->v[voy]; 
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s6   = pt1->v[voy];
+  
+  /*cas 1*/
+  do {
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s3;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[1] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[1] = caltab[0];
+    qual[2] = caltab[1];
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+   
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s3;
+    pt1->v[3] = s4;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[3] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[3] = caltab[0];
+    qual[4] = caltab[1];
+    list->qual[3] = qual[3];
+    list->qual[4] = qual[4];
+ 
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s4;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[5] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[5] = caltab[0];
+    qual[6] = caltab[1];
+    list->qual[5] = qual[5];
+    list->qual[6] = qual[6];
+     
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s5;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[7] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[7] = caltab[0];
+    qual[8] = caltab[1];
+    list->qual[7] = qual[7];
+    list->qual[8] = qual[8];
+    
+    /* set function ptr */
+    memset(pt1,0,sizeof(Tetra));
+    MMG_swpptr = MMG_swap68_1;
+     
+    return(1);
+  } while(0);
+  
+  /*cas 2*/
+  do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[3]) break;
+    else if(qual[3] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[3] = caltab[0];
+      qual[4] = caltab[1];
+    }  
+    list->qual[3] = qual[3];
+    list->qual[4] = qual[4];
+ 
+    
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s4;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[9] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[9]  = caltab[0];
+    qual[10] = caltab[1];
+    list->qual[5] = qual[9];
+    list->qual[6] = qual[10];
+   
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s4;
+    pt1->v[2] = s5;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[11] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[11] = caltab[0];
+    qual[12] = caltab[1];
+    list->qual[7] = qual[11];
+    list->qual[8] = qual[12];
+
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_2;
+   return(2);
+  } while(0);
+  
+  /*cas 3*/
+  do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s3;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[13] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[13] = caltab[0];
+    qual[14] = caltab[1];
+    list->qual[3] = qual[13];
+    list->qual[4] = qual[14];
+ 
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s3;
+    pt1->v[2] = s5;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[15] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[15] = caltab[0];
+    qual[16] = caltab[1];
+    list->qual[5] = qual[15];
+    list->qual[6] = qual[16];
+   
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s3;
+    pt1->v[2] = s4;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[17] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[17] = caltab[0];
+    qual[18] = caltab[1];
+    list->qual[7] = qual[17];
+    list->qual[8] = qual[18];
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_3;
+   return(3);
+  } while(0);
+  
+  /*cas 4*/
+  do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+    
+    if(!qual[11]) break;
+    else if(qual[11] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[11] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[11] = caltab[0];
+      qual[12] = caltab[1];
+    }
+    list->qual[7] = qual[11];
+    list->qual[8] = qual[12];
+    
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[13] = 0;
+	memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[3] = qual[13];
+    list->qual[4] = qual[14];
+    
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s3;
+    pt1->v[2] = s4;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[19] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[19] = caltab[0];
+    qual[20] = caltab[1];
+    list->qual[5] = qual[19];
+    list->qual[6] = qual[20];
+   
+    
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_4;
+   return(4);
+  } while(0);
+  
+  /*cas 5*/
+  do {
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[9] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1]; 
+    }
+    list->qual[5] = qual[9];
+    list->qual[6] = qual[10];
+
+    if(!qual[11]) break;
+    else if(qual[11] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[11] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[11] = caltab[0];
+      qual[12] = caltab[1]; 
+    }
+    list->qual[7] = qual[11];
+    list->qual[8] = qual[12];
+    
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s3;
+    pt1->v[3] = s4;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[21] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[21] = caltab[0];
+    qual[22] = caltab[1];
+    list->qual[1] = qual[21];
+    list->qual[2] = qual[22];
+    
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s4;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[23] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[23] = caltab[0];
+    qual[24] = caltab[1];
+    list->qual[3] = qual[23];
+    list->qual[4] = qual[24];
+ 
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_5;
+   return(5);
+  } while(0);
+  
+  
+  /*cas 6*/
+  do {
+    if(!qual[5]) break;
+    else if(qual[5] == -1) { 
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[5] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[5] = caltab[0];
+      qual[6] = caltab[1];
+    } 
+    list->qual[5] = qual[5];
+    list->qual[6] = qual[6];
+
+    if(!qual[7]) break;
+    else if(qual[7] == -1) {   
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[7] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+    }  
+    list->qual[7] = qual[7];
+    list->qual[8] = qual[8];
+    
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[21] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }  
+    list->qual[1] = qual[21];
+    list->qual[2] = qual[22];
+    
+    if(!qual[23]) break;
+    else if(qual[23] == -1) {    
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[23] = 0;  
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[23] = caltab[0];
+      qual[24] = caltab[1];
+    }  
+    list->qual[3] = qual[23];
+    list->qual[4] = qual[24];
+ 
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_6;
+   return(6);
+  } while(0);
+  
+  /*cas 7*/
+  do {
+    if(!qual[7]) break;
+    else if(qual[7] == -1) {   
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[7] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+    }  
+    list->qual[1] = qual[7];
+    list->qual[2] = qual[8];
+    
+    if(!qual[17]) break;
+    else if(qual[17] == -1) { 
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[17] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[17] = caltab[0];
+      qual[18] = caltab[1];
+    }  
+    list->qual[7] = qual[17];
+    list->qual[8] = qual[18];
+     
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[25] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[25] = caltab[0];
+    qual[26] = caltab[1];
+    list->qual[3] = qual[25];
+    list->qual[4] = qual[26];
+ 
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s3;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[27] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[27] = caltab[0];
+    qual[28] = caltab[1];
+    list->qual[5] = qual[27];
+    list->qual[6] = qual[28];
+   
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_7;
+   return(7);
+  } while(0);
+  
+ /*cas 8*/
+ do {
+    if(!qual[11]) break;
+    else if(qual[11] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        qual[11] = 0;
+	memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[11] = caltab[0];
+      qual[12] = caltab[1];
+    }  
+    list->qual[7] = qual[11];
+    list->qual[8] = qual[12];
+    
+    if(!qual[19]) break;
+    else if(qual[19] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        qual[19] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[19] = caltab[0];
+      qual[20] = caltab[1];
+    }      
+    list->qual[5] = qual[19];
+    list->qual[6] = qual[20];
+    
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+      qual[29] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[29] = caltab[0];
+    qual[30] = caltab[1];
+    list->qual[1] = qual[29];
+    list->qual[2] = qual[30];
+    
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s3;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+      qual[31] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[31] = caltab[0];
+    qual[32] = caltab[1];
+    list->qual[3] = qual[31];
+    list->qual[4] = qual[32];
+   
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_8;
+   return(8);
+  } while(0);
+  
+  /*cas 9*/
+  do {  
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[7]) break;
+    else if(qual[7] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[7] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+    } 
+    list->qual[5] = qual[7];
+    list->qual[6] = qual[8];
+    
+    if(!qual[17]) break;
+    else if(qual[17] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[17] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[17] = caltab[0];
+      qual[18] = caltab[1];
+    }  
+    list->qual[3] = qual[17];
+    list->qual[4] = qual[18];
+   
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s3;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[33] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[33] = caltab[0];
+    qual[34] = caltab[1];
+    list->qual[7] = qual[33];
+    list->qual[8] = qual[34];
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_9;
+   return(9);
+  } while(0);
+  
+  /*cas 10*/
+  do {
+    if(!qual[7]) break;
+    else if(qual[7] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[7] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+    }
+    list->qual[7] = qual[7];
+    list->qual[8] = qual[8];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[21] = 0;
+	memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[1] = qual[21];
+    list->qual[2] = qual[22];
+    
+    if(!qual[25]) break;
+    else if(qual[25] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[25] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[5] = qual[25];
+    list->qual[6] = qual[26];
+ 
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s4;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[35] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[35] = caltab[0];
+    qual[36] = caltab[1];
+    list->qual[3] = qual[35];
+    list->qual[4] = qual[36];
+ 
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_10;
+   return(10);
+  } while(0);
+  
+  /*cas 11*/
+  do {
+    if(!qual[21]) break;
+    else if(qual[21] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[21] = 0;
+	memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[1] = qual[21];
+    list->qual[2] = qual[22];
+    
+    if(!qual[29]) break;
+    else if(qual[29] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[29] = 0;
+	memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[7] = qual[29];
+    list->qual[8] = qual[30];
+
+    if(!qual[35]) break;
+    else if(qual[35] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        qual[35] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[35] = caltab[0];
+      qual[36] = caltab[1];
+    }
+    list->qual[3] = qual[35];
+    list->qual[4] = qual[36];
+ 
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s5;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[37] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[37] = caltab[0];
+    qual[38] = caltab[1];
+    list->qual[5] = qual[37];
+    list->qual[6] = qual[38];
+   
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_11;
+   return(11);
+  } while(0);
+  
+  /*cas 12*/
+  do {
+    if(!qual[17]) break;
+    else if(qual[17] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[17] = 0;
+	memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[17] = caltab[0];
+      qual[18] = caltab[1];
+    }
+    list->qual[7] = qual[17];
+    list->qual[8] = qual[18];
+    
+    if(!qual[27]) break;
+    else if(qual[27] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[27] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[27] = caltab[0];
+      qual[28] = caltab[1];
+    }
+    list->qual[5] = qual[27];
+    list->qual[6] = qual[28];
+
+   if(!qual[29]) break;
+    else if(qual[29] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[29] = 0;
+	memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+
+    list->qual[1] = qual[29];
+    list->qual[2] = qual[30];
+    
+    if(!qual[37]) break;
+    else if(qual[37] == -1){
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[37] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[3] = qual[37];
+    list->qual[4] = qual[38];
+    
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_12;
+   return(12);
+  } while(0);
+  
+  
+  /*cas 13*/
+  do {
+    if(!qual[15]) break;
+    else if(qual[15] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[15] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[15] = caltab[0];
+      qual[16] = caltab[1];
+    }
+    list->qual[5] = qual[15];
+    list->qual[6] = qual[16];
+   
+    if(!qual[17]) break;
+    else if(qual[17] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[17] = 0;
+	memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[17] = caltab[0];
+      qual[18] = caltab[1];
+    }
+    list->qual[7] = qual[17];
+    list->qual[8] = qual[18];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[29] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[1] = qual[29];
+    list->qual[2] = qual[30];
+    
+    if(!qual[31]) break;
+    else if(qual[31] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[31] = 0;
+	memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[31] = caltab[0];
+      qual[32] = caltab[1];
+    }
+    list->qual[3] = qual[31];
+    list->qual[4] = qual[32];
+ 
+ 
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_13;
+   return(13);
+  } while(0);
+  
+  /*cas 14*/
+  do {
+    if(!qual[11]) break;
+    else if(qual[11] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[11] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[11] = caltab[0];
+      qual[12] = caltab[1];
+    }
+    list->qual[5] = qual[11];
+    list->qual[6] = qual[12];
+    
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[21] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[3] = qual[21];
+    list->qual[4] = qual[22];
+    
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        qual[29] = 0;
+        memset(pt1,0,sizeof(Tetra));
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[1] = qual[29];
+    list->qual[2] = qual[30];
+        
+   
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s4;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      qual[39] = 0;
+      memset(pt1,0,sizeof(Tetra));
+      break;
+    }
+    qual[39] = caltab[0];
+    qual[40] = caltab[1];
+    list->qual[7] = qual[39];
+    list->qual[8] = qual[40];
+   /* set function ptr */
+   memset(pt1,0,sizeof(Tetra));
+   MMG_swpptr = MMG_swap68_14;
+   return(14);
+  } while(0);
+  
+   return(0);
+
+}
diff --git a/contrib/mmg3d/build/sources/simu710.c b/contrib/mmg3d/build/sources/simu710.c
new file mode 100644
index 0000000000000000000000000000000000000000..9dfb31659a65c74fbcec6c462c245538dbcc40c0
--- /dev/null
+++ b/contrib/mmg3d/build/sources/simu710.c
@@ -0,0 +1,4021 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile,
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite,
+y compris les garanties de commercialisation ou
+d’adaptation dans un but spécifique.
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU
+en même temps que ce document.
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011,
+spread under the terms and conditions of the license GNU General Public License
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.
+****************************************************************************/
+#include "mesh.h"
+
+/* return 1-49 for config */
+int MMG_simu710(pMesh mesh,pSol sol,pList list,double crit) {
+  pTetra	pt,pt1;
+  double	qual[71],caltab[2];
+  int		ia,ib,s1,s2,s3,s4,s5,s6,s7;
+  int		iadr,*adja,k,adj,iel,iar;
+  short  voy;
+
+  for(k=0 ; k<71 ; k++) qual[k] = -1;
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+
+  /*find points of polygone*/
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ];
+
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+
+  s3  = pt1->v[voy];
+
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s2) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+     k = MMG_idir[voy][2];
+  } else {
+    puts("MMG_simu710: point s2 non existant");
+    exit(0);
+  }
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+
+  s4   = pt1->v[voy];
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s3) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s3) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s3) {
+     k = MMG_idir[voy][2];
+  } else {
+    printf("MMG_simu710: point s3 non existant %d \n",s3);
+    exit(0);
+  }
+
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+
+  s5   = pt1->v[voy];
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s4) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s4) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s4) {
+     k = MMG_idir[voy][2];
+  } else {
+    puts("MMG_simu710: point s4 non existant");
+    exit(0);
+  }
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s6   = pt1->v[voy];
+
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s7   = pt1->v[voy];
+
+ /* printf("polygone : %d %d %d %d %d %d %d\n",s1,s2,s3,s4,s5,s6,s7);
+  */
+  /*for(k=1 ; k<=7 ; k++) {
+    jel = list->tetra[k]/6;
+    pt1 =&mesh->tetra[jel];
+    printf("tetra %d : %d %d %d %d\n",jel,pt1->v[0],pt1->v[1],pt1->v[2],pt1->v[3]);
+  }*/
+
+  /*cas 1*/
+  do {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s3;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("cal 0 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[1] = 0;
+        break;
+      }
+      qual[1] = caltab[0];
+      qual[2] = caltab[1];
+      list->qual[1] = qual[1];
+      list->qual[2] = qual[2];
+
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("caltab[0] 2 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[3] = 0;
+        break;
+      }
+      qual[3] = caltab[0];
+      qual[4] = caltab[1];
+      list->qual[3] = qual[3];
+      list->qual[4] = qual[4];
+
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("cal 4 %e %e \n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[5] = 0;
+        break;
+      }
+      qual[5] = caltab[0];
+      qual[6] = caltab[1];
+      list->qual[5] = qual[5];
+      list->qual[6] = qual[6];
+
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        //printf("cal 6 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[7] = 0;
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+      list->qual[7] = qual[7];
+      list->qual[8] = qual[8];
+
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("cal 8 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+      list->qual[9]  = qual[9];
+      list->qual[10] = qual[10];
+
+     MMG_swpptr = MMG_swap710_1;
+
+     return(71);
+  } while(0);
+
+  /*cas 2*/
+  do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[3]) break;
+    else if(qual[3] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 3 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[3] = 0;
+        break;
+      }
+      qual[3] = caltab[0];
+      qual[4] = caltab[1];
+    }
+    list->qual[3] = qual[3];
+    list->qual[4] = qual[4];
+
+    if(!qual[5]) break;
+    else if(qual[5] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 5 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[5] = 0;
+        break;
+      }
+      qual[5] = caltab[0];
+      qual[6] = caltab[1];
+    }
+    list->qual[5] = qual[5];
+    list->qual[6] = qual[6];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s5;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+      //printf("2:cal 11 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[11] = 0;
+      break;
+    }
+    qual[11] = caltab[0];
+    qual[12] = caltab[1];
+    list->qual[7] = qual[11];
+    list->qual[8] = qual[12];
+
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s5;
+    pt1->v[2] = s6;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[13] = 0;
+      break;
+    }
+    qual[13] = caltab[0];
+    qual[14] = caltab[1];
+    list->qual[9] = qual[13];
+    list->qual[10] = qual[14];
+
+    MMG_swpptr = MMG_swap710_2;
+    return(72);
+  } while(0);
+
+  /*cas 3*/
+  do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[3]) break;
+    else if(qual[3] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 3 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[3] = 0;
+        break;
+      }
+      qual[3] = caltab[0];
+      qual[4] = caltab[1];
+    }
+    list->qual[3] = qual[3];
+    list->qual[4] = qual[4];
+
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[5] = qual[13];
+    list->qual[6] = qual[14];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s4;
+    pt1->v[2] = s5;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+      //printf("2:cal 15 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[15] = 0;
+      break;
+    }
+    qual[15] = caltab[0];
+    qual[16] = caltab[1];
+    list->qual[7] = qual[15];
+    list->qual[8] = qual[16];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s4;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+      //printf("2:cal 11 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[17] = 0;
+      break;
+    }
+    qual[17] = caltab[0];
+    qual[18] = caltab[1];
+    list->qual[9]  = qual[17];
+    list->qual[10] = qual[18];
+
+    MMG_swpptr = MMG_swap710_3;
+    return(73);
+  } while(0);
+
+  /*cas 4*/
+  do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[3]) break;
+    else if(qual[3] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 3 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[3] = 0;
+        break;
+      }
+      qual[3] = caltab[0];
+      qual[4] = caltab[1];
+    }
+    list->qual[3] = qual[3];
+    list->qual[4] = qual[4];
+
+    if(!qual[17]) break;
+    else if(qual[17] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 17 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[17] = 0;
+        break;
+      }
+      qual[17] = caltab[0];
+      qual[18] = caltab[1];
+    }
+    list->qual[5] = qual[17];
+    list->qual[6] = qual[18];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s4;
+    pt1->v[2] = s6;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+      //printf("2:cal 19 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[19] = 0;
+      break;
+    }
+    qual[19] = caltab[0];
+    qual[20] = caltab[1];
+    list->qual[7] = qual[19];
+    list->qual[8] = qual[20];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s4;
+    pt1->v[2] = s5;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+      //printf("2:cal 11 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[21] = 0;
+      break;
+    }
+    qual[21] = caltab[0];
+    qual[22] = caltab[1];
+    list->qual[9]  = qual[21];
+    list->qual[10] = qual[22];
+
+    MMG_swpptr = MMG_swap710_4;
+    return(74);
+  } while(0);
+
+  /*cas 5*/
+  do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[3]) break;
+    else if(qual[3] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 3 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[3] = 0;
+        break;
+      }
+      qual[3] = caltab[0];
+      qual[4] = caltab[1];
+    }
+    list->qual[3] = qual[3];
+    list->qual[4] = qual[4];
+
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 9 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[5] = qual[9];
+    list->qual[6] = qual[10];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[7] = qual[21];
+    list->qual[8] = qual[22];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s4;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+      //printf("2:cal 11 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[23] = 0;
+      break;
+    }
+    qual[23] = caltab[0];
+    qual[24] = caltab[1];
+    list->qual[9]  = qual[23];
+    list->qual[10] = qual[24];
+
+    MMG_swpptr = MMG_swap710_5;
+    return(75);
+  } while(0);
+
+  /*cas 6*/
+  do {
+    if(!qual[5]) break;
+    else if(qual[5] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("cal 4 %e %e \n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[5] = 0;
+        break;
+      }
+      qual[5] = caltab[0];
+      qual[6] = caltab[1];
+    }
+    list->qual[1] = qual[5];
+    list->qual[2] = qual[6];
+
+    if(!qual[7]) break;
+    else if(qual[7] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        //printf("cal 6 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[7] = 0;
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+    }
+    list->qual[3] = qual[7];
+    list->qual[4] = qual[8];
+
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("cal 8 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[5] = qual[9];
+    list->qual[6] = qual[10];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s3;
+    pt1->v[3] = s4;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[25] = 0;
+      break;
+    }
+    qual[25] = caltab[0];
+    qual[26] = caltab[1];
+    list->qual[7] = qual[25];
+    list->qual[8] = qual[26];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s4;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 12 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[27] = 0;
+      break;
+    }
+    qual[27] = caltab[0];
+    qual[28] = caltab[1];
+    list->qual[9]  = qual[27];
+    list->qual[10] = qual[28];
+    MMG_swpptr = MMG_swap710_6;
+    return(76);
+  } while(0);
+
+ /*cas 7*/
+ do {
+   if(!qual[5]) break;
+   else if(qual[5] == -1) {
+     pt1 = &mesh->tetra[0];
+     pt1->v[0] = ia;
+     pt1->v[1] = s1;
+     pt1->v[2] = s4;
+     pt1->v[3] = s5;
+     if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+       //printf("2:cal 5 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+       memset(pt1,0,sizeof(Tetra));
+       qual[5] = 0;
+       break;
+     }
+     qual[5] = caltab[0];
+     qual[6] = caltab[1];
+    }
+    list->qual[1] = qual[5];
+    list->qual[2] = qual[6];
+
+    if(!qual[11]) break;
+    else if (qual[11] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        //printf("2:cal 11 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[11] = 0;
+        break;
+      }
+      qual[11] = caltab[0];
+      qual[12] = caltab[1];
+    }
+    list->qual[3] = qual[11];
+    list->qual[4] = qual[12];
+
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[5] = qual[13];
+    list->qual[6] = qual[14];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[7] = qual[25];
+    list->qual[8] = qual[26];
+
+    if(!qual[27]) break;
+    else if(qual[27] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 12 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[27] = 0;
+        break;
+      }
+      qual[27] = caltab[0];
+      qual[28] = caltab[1];
+    }
+    list->qual[9]  = qual[27];
+    list->qual[10] = qual[28];
+    MMG_swpptr = MMG_swap710_7;
+    return(77);
+ } while(0);
+
+ /*cas 8*/
+ do {
+   if(!qual[13]) break;
+   else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[1] = qual[13];
+    list->qual[2] = qual[14];
+
+    if(!qual[15]) break;
+    else if(qual[15] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 15 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[15] = 0;
+        break;
+      }
+      qual[15] = caltab[0];
+      qual[16] = caltab[1];
+    }
+    list->qual[3] = qual[15];
+    list->qual[4] = qual[16];
+
+    if(!qual[17]) break;
+    else if(qual[17] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 17 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[17] = 0;
+        break;
+      }
+      qual[17] = caltab[0];
+      qual[18] = caltab[1];
+    }
+    list->qual[5] = qual[17];
+    list->qual[6] = qual[18];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[7] = qual[25];
+    list->qual[8] = qual[26];
+
+    if(!qual[27]) break;
+    else if(qual[27] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 12 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[27] = 0;
+        break;
+      }
+      qual[27] = caltab[0];
+      qual[28] = caltab[1];
+    }
+    list->qual[9]  = qual[27];
+    list->qual[10] = qual[28];
+    MMG_swpptr = MMG_swap710_8;
+    return(78);
+ } while(0);
+
+ /*cas 9*/
+ do {
+    if(!qual[17]) break;
+    else if(qual[17] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 17 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[17] = 0;
+        break;
+      }
+      qual[17] = caltab[0];
+      qual[18] = caltab[1];
+    }
+    list->qual[1] = qual[17];
+    list->qual[2] = qual[18];
+
+    if(!qual[19]) break;
+    else if(qual[19] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 19 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[19] = 0;
+        break;
+      }
+      qual[19] = caltab[0];
+      qual[20] = caltab[1];
+    }
+    list->qual[3] = qual[19];
+    list->qual[4] = qual[20];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[5] = qual[21];
+    list->qual[6] = qual[22];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[7] = qual[25];
+    list->qual[8] = qual[26];
+
+    if(!qual[27]) break;
+    else if(qual[27] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 12 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[27] = 0;
+        break;
+      }
+      qual[27] = caltab[0];
+      qual[28] = caltab[1];
+    }
+    list->qual[9]  = qual[27];
+    list->qual[10] = qual[28];
+    MMG_swpptr = MMG_swap710_9;
+    return(79);
+ } while(0);
+
+ /*cas 10*/
+ do {
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 9 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[1] = qual[9];
+    list->qual[2] = qual[10];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[3] = qual[21];
+    list->qual[4] = qual[22];
+
+    if(!qual[23]) break;
+    else if(qual[23] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s4;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 11 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[23] = 0;
+        break;
+      }
+      qual[23] = caltab[0];
+      qual[24] = caltab[1];
+    }
+    list->qual[5]  = qual[23];
+    list->qual[6] = qual[24];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[7] = qual[25];
+    list->qual[8] = qual[26];
+
+    if(!qual[27]) break;
+    else if(qual[27] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 12 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[27] = 0;
+        break;
+      }
+      qual[27] = caltab[0];
+      qual[28] = caltab[1];
+    }
+    list->qual[9]  = qual[27];
+    list->qual[10] = qual[28];
+
+    MMG_swpptr = MMG_swap710_10;
+    memset(pt1,0,sizeof(Tetra));
+    return(80);
+ } while(0);
+
+ /*cas 11*/
+ do {
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[1] = qual[25];
+    list->qual[2] = qual[26];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[29] = 0;
+      break;
+    }
+    qual[29] = caltab[0];
+    qual[30] = caltab[1];
+    list->qual[3] = qual[29];
+    list->qual[4] = qual[30];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s6;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[31] = 0;
+      break;
+    }
+    qual[31] = caltab[0];
+    qual[32] = caltab[1];
+    list->qual[5] = qual[31];
+    list->qual[6] = qual[32];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s5;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[33] = 0;
+      break;
+    }
+    qual[33] = caltab[0];
+    qual[34] = caltab[1];
+    list->qual[7] = qual[33];
+    list->qual[8] = qual[34];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s4;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[35] = 0;
+      break;
+    }
+    qual[35] = caltab[0];
+    qual[36] = caltab[1];
+    list->qual[9]  = qual[35];
+    list->qual[10] = qual[36];
+
+    memset(pt1,0,sizeof(Tetra));
+    MMG_swpptr = MMG_swap710_11;
+    return(81);
+ } while(0);
+
+ /*cas 12*/
+ do {
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[1] = qual[29];
+    list->qual[2] = qual[30];
+
+    if(!qual[31]) break;
+    else if(qual[31] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[31] = 0;
+        break;
+      }
+      qual[31] = caltab[0];
+      qual[32] = caltab[1];
+    }
+    list->qual[3] = qual[31];
+    list->qual[4] = qual[32];
+
+    if(!qual[33]) break;
+    else if(qual[33] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[33] = 0;
+        break;
+      }
+      qual[33] = caltab[0];
+      qual[34] = caltab[1];
+    }
+    list->qual[5] = qual[33];
+    list->qual[6] = qual[34];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s3;
+    pt1->v[2] = s4;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[37] = 0;
+      break;
+    }
+    qual[37] = caltab[0];
+    qual[38] = caltab[1];
+    list->qual[7] = qual[37];
+    list->qual[8] = qual[38];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s3;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[39] = 0;
+      break;
+    }
+    qual[39] = caltab[0];
+    qual[40] = caltab[1];
+    list->qual[9] = qual[39];
+    list->qual[10] = qual[40];
+
+    MMG_swpptr = MMG_swap710_12;
+    memset(pt1,0,sizeof(Tetra));
+    return(82);
+
+ } while(0);
+
+ /*cas 13*/
+ do {
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[1] = qual[29];
+    list->qual[2] = qual[30];
+
+    if(!qual[31]) break;
+    else if(qual[31] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[31] = 0;
+        break;
+      }
+      qual[31] = caltab[0];
+      qual[32] = caltab[1];
+    }
+    list->qual[3] = qual[31];
+    list->qual[4] = qual[32];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[5] = qual[37];
+    list->qual[6] = qual[38];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s3;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[41] = 0;
+      break;
+    }
+    qual[41] = caltab[0];
+    qual[42] = caltab[1];
+    list->qual[7] = qual[41];
+    list->qual[8] = qual[42];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s3;
+    pt1->v[2] = s5;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[43] = 0;
+      break;
+    }
+    qual[43] = caltab[0];
+    qual[44] = caltab[1];
+    list->qual[9]  = qual[43];
+    list->qual[10] = qual[44];
+    MMG_swpptr = MMG_swap710_13;
+    memset(pt1,0,sizeof(Tetra));
+    return(84);
+ } while(0);
+
+ /*cas 15*/
+ do {
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[1] = qual[21];
+    list->qual[2] = qual[22];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[3] = qual[25];
+    list->qual[4] = qual[26];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[5] = qual[29];
+    list->qual[6] = qual[30];
+
+    if(!qual[31]) break;
+    else if(qual[31] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[31] = 0;
+        break;
+      }
+      qual[31] = caltab[0];
+      qual[32] = caltab[1];
+    }
+    list->qual[7] = qual[31];
+    list->qual[8] = qual[32];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s4;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[47] = 0;
+      break;
+    }
+    qual[47] = caltab[0];
+    qual[48] = caltab[1];
+    list->qual[9]  = qual[47];
+    list->qual[10] = qual[48];
+
+    MMG_swpptr = MMG_swap710_15;
+    memset(pt1,0,sizeof(Tetra));
+    return(85);
+ } while(0);
+
+ /*cas 16*/
+ do {
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 9 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[1] = qual[9];
+    list->qual[2] = qual[10];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[3] = qual[25];
+    list->qual[4] = qual[26];
+
+    if(!qual[33]) break;
+    else if(qual[33] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[33] = 0;
+        break;
+      }
+      qual[33] = caltab[0];
+      qual[34] = caltab[1];
+    }
+    list->qual[5] = qual[33];
+    list->qual[6] = qual[34];
+
+    if(!qual[35]) break;
+    else if(qual[35] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[35] = 0;
+        break;
+      }
+      qual[35] = caltab[0];
+      qual[36] = caltab[1];
+    }
+    list->qual[7] = qual[35];
+    list->qual[8] = qual[36];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[49] = 0;
+      break;
+    }
+    qual[49] = caltab[0];
+    qual[50] = caltab[1];
+    list->qual[9]  = qual[49];
+    list->qual[10] = qual[50];
+
+    MMG_swpptr = MMG_swap710_16;
+    memset(pt1,0,sizeof(Tetra));
+    return(86);
+ } while(0);
+
+ /*cas 17*/
+ do {
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 9 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[1] = qual[9];
+    list->qual[2] = qual[10];
+
+    if(!qual[33]) break;
+    else if(qual[33] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        ////printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[33] = 0;
+        break;
+      }
+      qual[33] = caltab[0];
+      qual[34] = caltab[1];
+    }
+    list->qual[3] = qual[33];
+    list->qual[4] = qual[34];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[5] = qual[37];
+    list->qual[6] = qual[38];
+
+    if(!qual[39]) break;
+    else if(qual[39] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[39] = 0;
+        break;
+      }
+      qual[39] = caltab[0];
+      qual[40] = caltab[1];
+    }
+    list->qual[7] = qual[39];
+    list->qual[8] = qual[40];
+
+    if(!qual[49]) break;
+    else if(qual[49] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[49] = 0;
+        break;
+      }
+      qual[49] = caltab[0];
+      qual[50] = caltab[1];
+    }
+    list->qual[9]  = qual[49];
+    list->qual[10] = qual[50];
+
+    MMG_swpptr = MMG_swap710_17;
+    memset(pt1,0,sizeof(Tetra));
+    return(87);
+ } while(0);
+
+ /*cas 18*/
+ do {
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 9 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[1] = qual[9];
+    list->qual[2] = qual[10];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[3] = qual[37];
+    list->qual[4] = qual[38];
+
+     if(!qual[41]) break;
+    else if(qual[41] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[41] = 0;
+        break;
+      }
+      qual[41] = caltab[0];
+      qual[42] = caltab[1];
+    }
+    list->qual[5] = qual[41];
+    list->qual[6] = qual[42];
+
+    if(!qual[43]) break;
+    else if(qual[43] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[43] = 0;
+        break;
+      }
+      qual[43] = caltab[0];
+      qual[44] = caltab[1];
+    }
+    list->qual[7] = qual[43];
+    list->qual[8] = qual[44];
+
+    if(!qual[49]) break;
+    else if(qual[49] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[49] = 0;
+        break;
+      }
+      qual[49] = caltab[0];
+      qual[50] = caltab[1];
+    }
+    list->qual[9]  = qual[49];
+    list->qual[10] = qual[50];
+
+    MMG_swpptr = MMG_swap710_18;
+    memset(pt1,0,sizeof(Tetra));
+    return(88);
+ } while(0);
+
+ /*cas 19*/
+ do {
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 9 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[1] = qual[9];
+    list->qual[2] = qual[10];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[3] = qual[21];
+    list->qual[4] = qual[22];
+
+    if(!qual[41]) break;
+    else if(qual[41] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[41] = 0;
+        break;
+      }
+      qual[41] = caltab[0];
+      qual[42] = caltab[1];
+    }
+    list->qual[5] = qual[41];
+    list->qual[6] = qual[42];
+
+    if(!qual[45]) break;
+    else if(qual[45] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[45] = 0;
+        break;
+      }
+      qual[45] = caltab[0];
+      qual[46] = caltab[1];
+    }
+    list->qual[7] = qual[45];
+    list->qual[8] = qual[46];
+
+    if(!qual[49]) break;
+    else if(qual[49] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[49] = 0;
+        break;
+      }
+      qual[49] = caltab[0];
+      qual[50] = caltab[1];
+    }
+    list->qual[9]  = qual[49];
+    list->qual[10] = qual[50];
+
+
+    MMG_swpptr = MMG_swap710_19;
+    memset(pt1,0,sizeof(Tetra));
+    return(89);
+ } while(0);
+
+ /*cas 20*/
+ do {
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 9 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[1] = qual[9];
+    list->qual[2] = qual[10];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[3] = qual[21];
+    list->qual[4] = qual[22];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[5] = qual[25];
+    list->qual[6] = qual[26];
+
+    if(!qual[47]) break;
+    else if(qual[47] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s4;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[47] = 0;
+        break;
+      }
+      qual[47] = caltab[0];
+      qual[48] = caltab[1];
+    }
+    list->qual[7] = qual[47];
+    list->qual[8] = qual[48];
+    if(!qual[49]) break;
+    else if(qual[49] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[49] = 0;
+        break;
+      }
+      qual[49] = caltab[0];
+      qual[50] = caltab[1];
+    }
+    list->qual[9]  = qual[49];
+    list->qual[10] = qual[50];
+
+    MMG_swpptr = MMG_swap710_20;
+    memset(pt1,0,sizeof(Tetra));
+    return(90);
+ } while(0);
+
+/*cas 21*/
+do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[3] = qual[37];
+    list->qual[4] = qual[38];
+
+    if(!qual[43]) break;
+    else if(qual[43] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[43] = 0;
+        break;
+      }
+      qual[43] = caltab[0];
+      qual[44] = caltab[1];
+    }
+    list->qual[5] = qual[43];
+    list->qual[6] = qual[44];
+
+    if(!qual[69]) break;
+    else if(qual[69] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[69] = 0;
+        break;
+      }
+      qual[69] = caltab[0];
+      qual[70] = caltab[1];
+    }
+    list->qual[7] = qual[69];
+    list->qual[8] = qual[70];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s3;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[51] = 0;
+      break;
+    }
+    qual[51] = caltab[0];
+    qual[52] = caltab[1];
+    list->qual[9]  = qual[51];
+    list->qual[10] = qual[52];
+
+    MMG_swpptr = MMG_swap710_21;
+    memset(pt1,0,sizeof(Tetra));
+    return(91);
+} while(0);
+
+/*cas 22*/
+do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[3] = qual[21];
+    list->qual[4] = qual[22];
+
+    if(!qual[45]) break;
+    else if(qual[45] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[45] = 0;
+        break;
+      }
+      qual[45] = caltab[0];
+      qual[46] = caltab[1];
+    }
+    list->qual[5] = qual[45];
+    list->qual[6] = qual[46];
+
+    if(!qual[51]) break;
+    else if(qual[51] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[51] = 0;
+        break;
+      }
+      qual[51] = caltab[0];
+      qual[52] = caltab[1];
+    }
+    list->qual[7]  = qual[51];
+    list->qual[8] = qual[52];
+
+    if(!qual[69]) break;
+    else if(qual[69] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[69] = 0;
+        break;
+      }
+      qual[69] = caltab[0];
+      qual[70] = caltab[1];
+    }
+    list->qual[9] = qual[69];
+    list->qual[10] = qual[70];
+
+    MMG_swpptr = MMG_swap710_22;
+    memset(pt1,0,sizeof(Tetra));
+    return(92);
+} while(0);
+
+/*cas 23*/
+do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[19]) break;
+    else if(qual[19] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 19 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[19] = 0;
+        break;
+      }
+      qual[19] = caltab[0];
+      qual[20] = caltab[1];
+    }
+    list->qual[3] = qual[19];
+    list->qual[4] = qual[20];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[5] = qual[21];
+    list->qual[6] = qual[22];
+
+    if(!qual[51]) break;
+    else if(qual[51] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[51] = 0;
+        break;
+      }
+      qual[51] = caltab[0];
+      qual[52] = caltab[1];
+    }
+    list->qual[7] = qual[51];
+    list->qual[8] = qual[52];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s3;
+    pt1->v[2] = s4;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[53] = 0;
+      break;
+    }
+    qual[53] = caltab[0];
+    qual[54] = caltab[1];
+
+    list->qual[9]  = qual[53];
+    list->qual[10] = qual[54];
+
+    MMG_swpptr = MMG_swap710_23;
+    memset(pt1,0,sizeof(Tetra));
+    return(93);
+} while(0);
+
+/*cas 24*/
+do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[3] = qual[13];
+    list->qual[4] = qual[14];
+
+    if(!qual[15]) break;
+    else if(qual[15] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 15 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[15] = 0;
+        break;
+      }
+      qual[15] = caltab[0];
+      qual[16] = caltab[1];
+    }
+    list->qual[5] = qual[15];
+    list->qual[6] = qual[16];
+
+    if(!qual[51]) break;
+    else if(qual[51] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[51] = 0;
+        break;
+      }
+      qual[51] = caltab[0];
+      qual[52] = caltab[1];
+    }
+    list->qual[7] = qual[51];
+    list->qual[8] = qual[52];
+
+    if(!qual[53]) break;
+    else if(qual[53] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[53] = 0;
+        break;
+      }
+      qual[53] = caltab[0];
+      qual[54] = caltab[1];
+    }
+    list->qual[9]  = qual[53];
+    list->qual[10] = qual[54];
+
+    MMG_swpptr = MMG_swap710_24;
+    memset(pt1,0,sizeof(Tetra));
+    return(94);
+} while(0);
+
+/*cas 25*/
+do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[3] = qual[13];
+    list->qual[4] = qual[14];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[5] = qual[37];
+    list->qual[6] = qual[38];
+
+    if(!qual[51]) break;
+    else if(qual[51] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[51] = 0;
+        break;
+      }
+      qual[51] = caltab[0];
+      qual[52] = caltab[1];
+    }
+    list->qual[7] = qual[51];
+    list->qual[8] = qual[52];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s3;
+    pt1->v[2] = s5;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[55] = 0;
+      break;
+    }
+    qual[55] = caltab[0];
+    qual[56] = caltab[1];
+
+    list->qual[9]  = qual[55];
+    list->qual[10] = qual[56];
+
+    MMG_swpptr = MMG_swap710_25;
+    memset(pt1,0,sizeof(Tetra));
+    return(95);
+} while(0);
+
+/*cas 26*/
+do {
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[1] = qual[29];
+    list->qual[2] = qual[30];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[3] = qual[37];
+    list->qual[4] = qual[38];
+
+    if(!qual[43]) break;
+    else if(qual[43] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[43] = 0;
+        break;
+      }
+      qual[43] = caltab[0];
+      qual[44] = caltab[1];
+    }
+    list->qual[5] = qual[43];
+    list->qual[6] = qual[44];
+
+    if(!qual[69]) break;
+    else if(qual[69] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[69] = 0;
+        break;
+      }
+      qual[69] = caltab[0];
+      qual[70] = caltab[1];
+    }
+    list->qual[7] = qual[69];
+    list->qual[8] = qual[70];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s3;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[57] = 0;
+      break;
+    }
+    qual[57] = caltab[0];
+    qual[58] = caltab[1];
+    list->qual[9]  = qual[57];
+    list->qual[10] = qual[58];
+
+    MMG_swpptr = MMG_swap710_26;
+    memset(pt1,0,sizeof(Tetra));
+    return(96);
+} while(0);
+
+/*cas 27*/
+do {
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[1] = qual[21];
+    list->qual[2] = qual[22];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[3] = qual[29];
+    list->qual[4] = qual[30];
+
+    if(!qual[45]) break;
+    else if(qual[45] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[45] = 0;
+        break;
+      }
+      qual[45] = caltab[0];
+      qual[46] = caltab[1];
+    }
+    list->qual[5] = qual[45];
+    list->qual[6] = qual[46];
+
+    if(!qual[57]) break;
+    else if(qual[57] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[57] = 0;
+        break;
+      }
+      qual[57] = caltab[0];
+      qual[58] = caltab[1];
+    }
+    list->qual[7] = qual[57];
+    list->qual[8] = qual[58];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s3;
+    pt1->v[2] = s6;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[69] = 0;
+      break;
+    }
+    qual[69] = caltab[0];
+    qual[70] = caltab[1];
+    list->qual[9]  = qual[69];
+    list->qual[10] = qual[70];
+    MMG_swpptr = MMG_swap710_27;
+    memset(pt1,0,sizeof(Tetra));
+    return(97);
+} while(0);
+
+/*cas 28*/
+do {
+    if(!qual[19]) break;
+    else if(qual[19] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 19 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[19] = 0;
+        break;
+      }
+      qual[19] = caltab[0];
+      qual[20] = caltab[1];
+    }
+    list->qual[1] = qual[19];
+    list->qual[2] = qual[20];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[3] = qual[21];
+    list->qual[4] = qual[22];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[5] = qual[29];
+    list->qual[6] = qual[30];
+
+    if(!qual[53]) break;
+    else if(qual[53] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[53] = 0;
+        break;
+      }
+      qual[53] = caltab[0];
+      qual[54] = caltab[1];
+    }
+    list->qual[7] = qual[53];
+    list->qual[8] = qual[54];
+
+    if(!qual[57]) break;
+    else if(qual[57] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[57] = 0;
+        break;
+      }
+      qual[57] = caltab[0];
+      qual[58] = caltab[1];
+    }
+    list->qual[9] = qual[57];
+    list->qual[10] = qual[58];
+
+    MMG_swpptr = MMG_swap710_28;
+    memset(pt1,0,sizeof(Tetra));
+    return(98);
+} while(0);
+
+/*cas 29*/
+do {
+   if(!qual[13]) break;
+   else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[1] = qual[13];
+    list->qual[2] = qual[14];
+
+    if(!qual[15]) break;
+    else if(qual[15] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 15 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[15] = 0;
+        break;
+      }
+      qual[15] = caltab[0];
+      qual[16] = caltab[1];
+    }
+    list->qual[3] = qual[15];
+    list->qual[4] = qual[16];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[5] = qual[29];
+    list->qual[6] = qual[30];
+
+    if(!qual[53]) break;
+    else if(qual[53] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[53] = 0;
+        break;
+      }
+      qual[53] = caltab[0];
+      qual[54] = caltab[1];
+    }
+    list->qual[7] = qual[53];
+    list->qual[8] = qual[54];
+
+    if(!qual[57]) break;
+    else if(qual[57] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[57] = 0;
+        break;
+      }
+      qual[57] = caltab[0];
+      qual[58] = caltab[1];
+    }
+    list->qual[9] = qual[57];
+    list->qual[10] = qual[58];
+
+    MMG_swpptr = MMG_swap710_29;
+    memset(pt1,0,sizeof(Tetra));
+    return(99);
+} while(0);
+
+/*cas 30*/
+do {
+   if(!qual[13]) break;
+   else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[1] = qual[13];
+    list->qual[2] = qual[14];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[3] = qual[29];
+    list->qual[4] = qual[30];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[5] = qual[37];
+    list->qual[6] = qual[38];
+
+    if(!qual[55]) break;
+    else if(qual[55] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[55] = 0;
+        break;
+      }
+      qual[55] = caltab[0];
+      qual[56] = caltab[1];
+    }
+    list->qual[8] = qual[55];
+    list->qual[9] = qual[56];
+
+    if(!qual[57]) break;
+    else if(qual[57] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[57] = 0;
+        break;
+      }
+      qual[57] = caltab[0];
+      qual[58] = caltab[1];
+    }
+    list->qual[9] = qual[57];
+    list->qual[10] = qual[58];
+
+    MMG_swpptr = MMG_swap710_30;
+    memset(pt1,0,sizeof(Tetra));
+    return(100);
+} while(0);
+
+/*cas 31*/
+do {
+    if(!qual[19]) break;
+    else if(qual[19] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 19 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[19] = 0;
+        break;
+      }
+      qual[19] = caltab[0];
+      qual[20] = caltab[1];
+    }
+    list->qual[1] = qual[19];
+    list->qual[2] = qual[20];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[3] = qual[21];
+    list->qual[4] = qual[22];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[5] = qual[25];
+    list->qual[6] = qual[26];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[7] = qual[29];
+    list->qual[8] = qual[30];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s4;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[59] = 0;
+      break;
+    }
+    qual[59] = caltab[0];
+    qual[60] = caltab[1];
+    list->qual[9]  = qual[59];
+    list->qual[10] = qual[60];
+
+    MMG_swpptr = MMG_swap710_31;
+    memset(pt1,0,sizeof(Tetra));
+    return(101);
+} while(0);
+
+/*cas 32*/
+do {
+   if(!qual[13]) break;
+   else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[1] = qual[13];
+    list->qual[2] = qual[14];
+
+    if(!qual[15]) break;
+    else if(qual[15] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 15 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[15] = 0;
+        break;
+      }
+      qual[15] = caltab[0];
+      qual[16] = caltab[1];
+    }
+    list->qual[3] = qual[15];
+    list->qual[4] = qual[16];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[5] = qual[25];
+    list->qual[6] = qual[26];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[7] = qual[29];
+    list->qual[8] = qual[30];
+
+    if(!qual[59]) break;
+    else if(qual[59] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s4;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[59] = 0;
+        break;
+      }
+      qual[59] = caltab[0];
+      qual[60] = caltab[1];
+    }
+    list->qual[9]  = qual[59];
+    list->qual[10] = qual[60];
+
+    MMG_swpptr = MMG_swap710_32;
+    memset(pt1,0,sizeof(Tetra));
+    return(102);
+} while(0);
+
+/*cas 33*/
+do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[7]) break;
+    else if(qual[7] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        //printf("cal 6 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[7] = 0;
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+    }
+    list->qual[3] = qual[7];
+    list->qual[4] = qual[8];
+
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("cal 8 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[5] = qual[9];
+    list->qual[6] = qual[10];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[7] = qual[37];
+    list->qual[8] = qual[38];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s3;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[61] = 0;
+      break;
+    }
+    qual[61] = caltab[0];
+    qual[62] = caltab[1];
+    list->qual[9]  = qual[61];
+    list->qual[10] = qual[62];
+
+    MMG_swpptr = MMG_swap710_33;
+    memset(pt1,0,sizeof(Tetra));
+    return(103);
+} while(0);
+
+/*cas 34*/
+do {
+    if(!qual[7]) break;
+    else if(qual[7] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        //printf("cal 6 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[7] = 0;
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+    }
+    list->qual[1] = qual[7];
+    list->qual[2] = qual[8];
+
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("cal 8 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[3] = qual[9];
+    list->qual[4] = qual[10];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[5] = qual[37];
+    list->qual[6] = qual[38];
+
+    if(!qual[39]) break;
+    else if(qual[39] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[39] = 0;
+        break;
+      }
+      qual[39] = caltab[0];
+      qual[40] = caltab[1];
+    }
+    list->qual[7] = qual[39];
+    list->qual[8] = qual[40];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s2;
+    pt1->v[3] = s5;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[63] = 0;
+      break;
+    }
+    qual[63] = caltab[0];
+    qual[64] = caltab[1];
+    list->qual[9]  = qual[63];
+    list->qual[10] = qual[64];
+
+    MMG_swpptr = MMG_swap710_34;
+    memset(pt1,0,sizeof(Tetra));
+    return(104);
+} while(0);
+
+/*cas 35*/
+do {
+    if(!qual[7]) break;
+    else if(qual[7] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        //printf("cal 6 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[7] = 0;
+        break;
+      }
+      qual[7] = caltab[0];
+      qual[8] = caltab[1];
+    }
+    list->qual[1] = qual[7];
+    list->qual[2] = qual[8];
+
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("cal 8 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[3] = qual[9];
+    list->qual[4] = qual[10];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[5] = qual[25];
+    list->qual[6] = qual[26];
+
+    if(!qual[35]) break;
+    else if(qual[35] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[35] = 0;
+        break;
+      }
+      qual[35] = caltab[0];
+      qual[36] = caltab[1];
+    }
+    list->qual[7] = qual[35];
+    list->qual[8] = qual[36];
+
+    if(!qual[63]) break;
+    else if(qual[63] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[63] = 0;
+        break;
+      }
+      qual[63] = caltab[0];
+      qual[64] = caltab[1];
+    }
+    list->qual[9]  = qual[63];
+    list->qual[10] = qual[64];
+
+    MMG_swpptr = MMG_swap710_35;
+    memset(pt1,0,sizeof(Tetra));
+    return(105);
+} while(0);
+
+/*cas 36*/
+do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[11]) break;
+    else if (qual[11] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        //printf("2:cal 11 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[11] = 0;
+        break;
+      }
+      qual[11] = caltab[0];
+      qual[12] = caltab[1];
+    }
+    list->qual[3] = qual[11];
+    list->qual[4] = qual[12];
+
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[5] = qual[13];
+    list->qual[6] = qual[14];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[7] = qual[37];
+    list->qual[8] = qual[38];
+
+    if(!qual[61]) break;
+    else if(qual[61] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[61] = 0;
+        break;
+      }
+      qual[61] = caltab[0];
+      qual[62] = caltab[1];
+    }
+    list->qual[9]  = qual[61];
+    list->qual[10] = qual[62];
+
+    MMG_swpptr = MMG_swap710_36;
+    memset(pt1,0,sizeof(Tetra));
+    return(106);
+} while(0);
+
+/*cas 37*/
+do {
+    if(!qual[11]) break;
+    else if (qual[11] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        //printf("2:cal 11 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[11] = 0;
+        break;
+      }
+      qual[11] = caltab[0];
+      qual[12] = caltab[1];
+    }
+    list->qual[1] = qual[11];
+    list->qual[2] = qual[12];
+
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[3] = qual[13];
+    list->qual[4] = qual[14];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[5] = qual[37];
+    list->qual[6] = qual[38];
+
+    if(!qual[39]) break;
+    else if(qual[39] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[39] = 0;
+        break;
+      }
+      qual[39] = caltab[0];
+      qual[40] = caltab[1];
+    }
+    list->qual[7] = qual[39];
+    list->qual[8] = qual[40];
+
+    if(!qual[63]) break;
+    else if(qual[63] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[63] = 0;
+        break;
+      }
+      qual[63] = caltab[0];
+      qual[64] = caltab[1];
+    }
+    list->qual[9]  = qual[63];
+    list->qual[10] = qual[64];
+
+    MMG_swpptr = MMG_swap710_37;
+    memset(pt1,0,sizeof(Tetra));
+    return(107);
+} while(0);
+
+/*cas 38*/
+do {
+    if(!qual[11]) break;
+    else if (qual[11] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab))  {
+        //printf("2:cal 11 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[11] = 0;
+        break;
+      }
+      qual[11] = caltab[0];
+      qual[12] = caltab[1];
+    }
+    list->qual[1] = qual[11];
+    list->qual[2] = qual[12];
+
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[3] = qual[13];
+    list->qual[4] = qual[14];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[5] = qual[25];
+    list->qual[6] = qual[26];
+
+    if(!qual[35]) break;
+    else if(qual[35] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[35] = 0;
+        break;
+      }
+      qual[35] = caltab[0];
+      qual[36] = caltab[1];
+    }
+    list->qual[7] = qual[35];
+    list->qual[8] = qual[36];
+
+    if(!qual[63]) break;
+    else if(qual[63] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[63] = 0;
+        break;
+      }
+      qual[63] = caltab[0];
+      qual[64] = caltab[1];
+    }
+    list->qual[9]  = qual[63];
+    list->qual[10] = qual[64];
+
+    MMG_swpptr = MMG_swap710_38;
+    memset(pt1,0,sizeof(Tetra));
+    return(108);
+} while(0);
+
+/*cas 39*/
+do {
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[1] = qual[13];
+    list->qual[2] = qual[14];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[3] = qual[29];
+    list->qual[4] = qual[30];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[5] = qual[37];
+    list->qual[6] = qual[38];
+
+    if(!qual[39]) break;
+    else if(qual[39] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[39] = 0;
+        break;
+      }
+      qual[39] = caltab[0];
+      qual[40] = caltab[1];
+    }
+    list->qual[7] = qual[39];
+    list->qual[8] = qual[40];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s2;
+    pt1->v[2] = s5;
+    pt1->v[3] = s7;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[65] = 0;
+      break;
+    }
+    qual[65] = caltab[0];
+    qual[66] = caltab[1];
+    list->qual[9]  = qual[65];
+    list->qual[10] = qual[66];
+
+    MMG_swpptr = MMG_swap710_39;
+    memset(pt1,0,sizeof(Tetra));
+    return(109);
+} while(0);
+
+/*cas 40*/
+do {
+    if(!qual[13]) break;
+    else if(qual[13] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s5;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 13 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[13] = 0;
+        break;
+      }
+      qual[13] = caltab[0];
+      qual[14] = caltab[1];
+    }
+    list->qual[1] = qual[13];
+    list->qual[2] = qual[14];
+
+    if(!qual[25]) break;
+    else if(qual[25] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s3;
+      pt1->v[3] = s4;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[25] = 0;
+        break;
+      }
+      qual[25] = caltab[0];
+      qual[26] = caltab[1];
+    }
+    list->qual[3] = qual[25];
+    list->qual[4] = qual[26];
+
+    if(!qual[29]) break;
+    else if(qual[29] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s2;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[29] = 0;
+        break;
+      }
+      qual[29] = caltab[0];
+      qual[30] = caltab[1];
+    }
+    list->qual[5] = qual[29];
+    list->qual[6] = qual[30];
+
+    if(!qual[35]) break;
+    else if(qual[35] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[35] = 0;
+        break;
+      }
+      qual[35] = caltab[0];
+      qual[36] = caltab[1];
+    }
+    list->qual[7] = qual[35];
+    list->qual[8] = qual[36];
+
+    if(!qual[65]) break;
+    else if(qual[65] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s2;
+      pt1->v[2] = s5;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[65] = 0;
+        break;
+      }
+      qual[65] = caltab[0];
+      qual[66] = caltab[1];
+    }
+    list->qual[9]  = qual[65];
+    list->qual[10] = qual[66];
+
+    MMG_swpptr = MMG_swap710_40;
+    memset(pt1,0,sizeof(Tetra));
+    return(110);
+} while(0);
+
+/*cas 41*/
+do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 9 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[3] = qual[9];
+    list->qual[4] = qual[10];
+
+    if(!qual[21]) break;
+    else if(qual[21] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s4;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 21 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[21] = 0;
+        break;
+      }
+      qual[21] = caltab[0];
+      qual[22] = caltab[1];
+    }
+    list->qual[5] = qual[21];
+    list->qual[6] = qual[22];
+
+    if(!qual[45]) break;
+    else if(qual[45] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[45] = 0;
+        break;
+      }
+      qual[45] = caltab[0];
+      qual[46] = caltab[1];
+    }
+    list->qual[7] = qual[45];
+    list->qual[8] = qual[46];
+
+    pt1 = &mesh->tetra[0];
+    pt1->v[0] = ia;
+    pt1->v[1] = s1;
+    pt1->v[2] = s3;
+    pt1->v[3] = s6;
+    if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+      //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+      memset(pt1,0,sizeof(Tetra));
+      qual[67] = 0;
+      break;
+    }
+    qual[67] = caltab[0];
+    qual[68] = caltab[1];
+    list->qual[9]  = qual[67];
+    list->qual[10] = qual[68];
+
+    MMG_swpptr = MMG_swap710_41;
+    memset(pt1,0,sizeof(Tetra));
+    return(111);
+} while(0);
+
+/*cas 42*/
+do {
+    if(!qual[1]) break;
+    list->qual[1] = qual[1];
+    list->qual[2] = qual[2];
+
+    if(!qual[9]) break;
+    else if(qual[9] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s6;
+      pt1->v[3] = s7;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)){
+        //printf("2:cal 9 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+	qual[9] = 0;
+        break;
+      }
+      qual[9]  = caltab[0];
+      qual[10] = caltab[1];
+    }
+    list->qual[3] = qual[9];
+    list->qual[4] = qual[10];
+
+    if(!qual[37]) break;
+    else if(qual[37] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s4;
+      pt1->v[3] = s5;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[37] = 0;
+        break;
+      }
+      qual[37] = caltab[0];
+      qual[38] = caltab[1];
+    }
+    list->qual[5] = qual[37];
+    list->qual[6] = qual[38];
+
+    if(!qual[43]) break;
+    else if(qual[43] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s3;
+      pt1->v[2] = s5;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[43] = 0;
+        break;
+      }
+      qual[43] = caltab[0];
+      qual[44] = caltab[1];
+    }
+    list->qual[7] = qual[43];
+    list->qual[8] = qual[44];
+
+    if(!qual[67]) break;
+    else if(qual[67] == -1) {
+      pt1 = &mesh->tetra[0];
+      pt1->v[0] = ia;
+      pt1->v[1] = s1;
+      pt1->v[2] = s3;
+      pt1->v[3] = s6;
+      if(!MMG_caltet2(mesh,sol,0,ib,crit,caltab)) {
+        //printf("2:cal 25 %e %e\n",caltab[0],MMG_voltet(mesh,0));
+        memset(pt1,0,sizeof(Tetra));
+        qual[67] = 0;
+        break;
+      }
+      qual[67] = caltab[0];
+      qual[68] = caltab[1];
+    }
+    list->qual[9]  = qual[67];
+    list->qual[10] = qual[68];
+
+    MMG_swpptr = MMG_swap710_42;
+    memset(pt1,0,sizeof(Tetra));
+    return(112);
+} while(0);
+
+ return(0);
+}
diff --git a/contrib/mmg3d/build/sources/solmap.c b/contrib/mmg3d/build/sources/solmap.c
new file mode 100644
index 0000000000000000000000000000000000000000..16e05cefc3b070eaa28b420f104d1824ffbd4a84
--- /dev/null
+++ b/contrib/mmg3d/build/sources/solmap.c
@@ -0,0 +1,208 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+static int MMG_inxtt[5] = {0,1,2,0,1};
+
+
+/* compute iso size map */
+int MMG_doSol(pMesh mesh,pSol sol) {
+  pTetra     pt;
+  pTria      ptt;
+  pPoint     p1,p2;
+  double     ux,uy,uz,dd;
+  int        i,k,ia,ib,ipa,ipb;
+
+  /* memory alloc */
+  sol->np     = mesh->np;
+  sol->npfixe = mesh->npfixe;
+  sol->npmax  = mesh->npmax;
+  sol->offset = 1;
+
+  if ( !MMG_zaldy3(sol) )  return(0);
+  /* boundary edges */
+  for (k=1; k<=mesh->nt; k++) {
+    ptt = &mesh->tria[k];
+    if ( !ptt->v[0] )  continue;
+
+    for (i=0; i<3; i++) {
+      ib  = MMG_inxtt[i+1];
+      ipa = ptt->v[i];
+      ipb = ptt->v[ib];
+      p1  = &mesh->point[ipa];
+      p2  = &mesh->point[ipb];
+
+      ux  = p1->c[0] - p2->c[0];
+      uy  = p1->c[1] - p2->c[1];
+      uz  = p1->c[2] - p2->c[2];
+      dd  = sqrt(ux*ux + uy*uy + uz*uz);
+
+      sol->met[ipa] += dd;
+      p1->mark++;
+      sol->met[ipb] += dd;
+      p2->mark++;
+    }
+  }
+
+  /* internal edges */
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k];
+    if ( !pt->v[0] )  continue;
+
+    /* internal edges */
+    for (i=0; i<6; i++) {
+      ia  = MMG_iare[i][0];
+      ib  = MMG_iare[i][1];
+      ipa = pt->v[ia];
+      ipb = pt->v[ib];
+      p1  = &mesh->point[ipa];
+      p2  = &mesh->point[ipb];
+
+      ux  = p1->c[0] - p2->c[0];
+      uy  = p1->c[1] - p2->c[1];
+      uz  = p1->c[2] - p2->c[2];
+      dd  = sqrt(ux*ux + uy*uy + uz*uz);
+
+      //if ( !(p1->tag & M_BDRY) ) {
+        sol->met[ipa] += dd;
+        p1->mark++;
+      //}
+      //if ( !(p2->tag & M_BDRY) ) {
+        sol->met[ipb] += dd;
+        p2->mark++;
+      //}
+    }
+  }
+
+  /* vertex size */
+  sol->hmin = 1.e20;
+  sol->hmax = 0.0;
+  for (k=1; k<=mesh->np; k++) {
+    p1 = &mesh->point[k];
+    if ( !p1->mark )  continue;
+
+    sol->met[k] = sol->met[k] / (double)p1->mark;
+ 
+    if ( sol->met[k] < sol->hmin )
+      sol->hmin = sol->met[k];
+    else if ( sol->met[k] > sol->hmax )
+      sol->hmax = sol->met[k];
+
+    p1->mark = 0;
+  }
+
+  if ( mesh->info.imprim < -4 )
+    fprintf(stdout,"     HMIN %f   HMAX %f\n",sol->hmin,sol->hmax);   
+    
+  /*compute quality*/
+  for (k=1; k<=mesh->ne; k++) {
+    pt = &mesh->tetra[k]; 
+    if ( pt->v[0] ) 
+      pt->qual = MMG_caltet(mesh,sol,k);
+    else
+      pt->qual = 0.0;
+  }
+    
+  return(1);
+}
+
+/* interpol iso/aniso size map */
+int MMG_computeMetric(pMesh mesh,pSol sol,int ip,double * coor) {
+  pTetra 	pt;
+  pPoint 	ppt;
+  double 	cb[4];
+  double 	dma[6],mai[6],mi[6];
+  double 	*mp,*ma;
+  int    	ktet,i,k,base,iadr,ia;
+  
+  ppt = &mesh->point[ip];
+  base = ++mesh->mark;
+  ktet = MMG_loctet(mesh,ppt->tmp,base,coor,cb);
+  if(!ktet) return(-1);
+  assert(ktet < mesh->ne + 1);
+  pt = &mesh->tetra[ktet];
+  
+  if ( sol->offset == 1 ) {
+    sol->met[ip] = cb[0] * sol->metold[pt->v[0]];
+  
+    for(k = 1 ; k < 4 ; k++) {
+      sol->met[ip] += cb[k] * sol->metold[pt->v[k]];
+    }
+  } else {
+    iadr = (ip-1)*sol->offset + 1;
+    mp   = &sol->met[iadr];
+      
+    for (i=0; i<6; i++) mi[i] = 0;
+    
+    for(k = 0 ; k < 4 ; k++) {
+      ia   = pt->v[k];
+      iadr = (ia-1)*sol->offset + 1;
+      ma   = &sol->met[iadr];
+      for (i=0; i<6; i++) {
+        dma[i] = ma[i];
+      }
+      
+      if ( !MMG_invmat(dma,mai) ) {
+        fprintf(stderr,"  ## INVALID METRIC.\n");
+        return(0);
+      }
+  
+      for (i=0; i<6; i++)
+        mi[i] += cb[k]*mai[i];
+    }
+    
+    if ( !MMG_invmat(mi,mai) ) {
+      fprintf(stderr,"  ## INVALID METRIC.\n");
+      return(0);
+    }
+  
+    for (i=0; i<6; i++)  mp[i] = mai[i];
+
+  }  
+  
+  return(1);
+}
+
diff --git a/contrib/mmg3d/build/sources/spledg.c b/contrib/mmg3d/build/sources/spledg.c
new file mode 100644
index 0000000000000000000000000000000000000000..de9965df9f9cced2525b7dcaedfee865ce13e804
--- /dev/null
+++ b/contrib/mmg3d/build/sources/spledg.c
@@ -0,0 +1,132 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define QCOEF   0.995
+
+
+int MMG_spledg(pMesh mesh,pSol sol,pQueue queue,pList list,int lon,double crit,double declic) {
+  pTetra    pt,pt0,pt1;
+  pPoint    pa,pb;
+  double     cal;
+  double    *ca,*cb,c[3],*ma,*mb,*mip,mp[6];
+  int       l,ipa,ipb,ip,jel,na,nbt,iel,iar,ret,iadr;
+  short     ia,ib;
+    
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  ia  = MMG_iare[iar][0];
+  ib  = MMG_iare[iar][1];
+
+  pt  = &mesh->tetra[iel];
+  ipa = pt->v[ia];
+  ipb = pt->v[ib];
+  pa  = &mesh->point[ipa];
+  pb  = &mesh->point[ipb];
+  
+  ca  = &mesh->point[ipa].c[0];
+  cb  = &mesh->point[ipb].c[0];
+       
+  iadr = (ipa-1)*sol->offset + 1;
+  ma  = &sol->met[iadr];
+
+  iadr = (ipb-1)*sol->offset + 1;
+  mb  = &sol->met[iadr];
+    
+  /* metric interpolation */
+  if ( !MMG_interp(ma,mb,mp,0.5) ) return(0);
+	
+  c[0] = 0.5*(ca[0] + cb[0]);
+  c[1] = 0.5*(ca[1] + cb[1]);
+  c[2] = 0.5*(ca[2] + cb[2]);
+  ip   = MMG_newPt(mesh,c);
+  if ( ip < 1 ) return(0);
+  iadr = (ip-1)*sol->offset + 1;
+  mip  = &sol->met[iadr];	  
+  memcpy(mip,mp,sol->offset*sizeof(double));
+    
+  /* eval coquille */
+  pt0  = &mesh->tetra[0];
+  nbt  = 0;
+  for (l=1; l<=lon; l++) {
+    jel = list->tetra[l] / 6;
+    na  = list->tetra[l] % 6;
+    pt1 = &mesh->tetra[jel];
+
+    memcpy(pt0->v,pt1->v,4*sizeof(int));
+    ipb = MMG_iare[na][0];
+    pt0->v[ipb] = ip;
+    cal = MMG_caltet(mesh,sol,0);
+    if ( cal > crit ) {
+      MMG_delPt(mesh,ip);
+      return(0);
+    }
+
+    memcpy(pt0->v,pt1->v,4*sizeof(int));
+    ipb = MMG_iare[na][1];
+    pt0->v[ipb] = ip;
+    cal = MMG_caltet(mesh,sol,0);
+    if ( cal > crit ) {
+      MMG_delPt(mesh,ip);
+      return(0);
+    }
+  }
+
+  /* update */
+  for (l=1; l<=lon; l++) {
+    list->tetra[l] = list->tetra[l] / 6;
+    mesh->tetra[list->tetra[l]].mark = mesh->mark;
+  }
+  ret = MMG_delons(mesh,sol,queue,ip,list,lon,declic);
+  if ( ret <= 0 ) {
+    MMG_delPt(mesh,ip);
+    return(0);
+  }
+  else {  
+    return(ip);  
+  }
+}
+
diff --git a/contrib/mmg3d/build/sources/sproto.h b/contrib/mmg3d/build/sources/sproto.h
new file mode 100644
index 0000000000000000000000000000000000000000..178623047f10fc79039824bed172d372282a1f0e
--- /dev/null
+++ b/contrib/mmg3d/build/sources/sproto.h
@@ -0,0 +1,240 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+
+int MMG_cassar(pMesh mesh,pSol sol,int ipa,int ipb,float t);
+int MMG_analar(pMesh ,pSol ,pBucket ,int *,int *,int *,int *);
+int MMG_boulep(pMesh ,int ,int ,pList );
+int MMG_bouleg(pMesh ,int ,int ,pList );
+int MMG_coquil(pMesh ,int ,int ,pList );
+int MMG_cendel(pMesh ,pSol ,float ,int );
+int MMG_spledg(pMesh ,pSol ,pQueue ,pList ,int ,float ,float );
+
+/* delaunay */
+int MMG_correction(pMesh ,int ,pList ,int ,int ,char );
+int MMG_delone(pMesh ,pSol ,int ,pList ,int );
+int MMG_delons(pMesh ,pSol ,pQueue ,int ,pList ,int ,float );
+int MMG_cenrad_ani(pMesh ,pSol ,int ,double *,double *,double *); 
+int MMG_cenrad_iso(pMesh ,pSol ,int ,double *,double *); 
+
+int MMG_colpoi(pMesh ,pSol ,int ,int ,int ,float );
+
+/* hash */
+int  MMG_hashTetra(pMesh );
+int  MMG_hashEdge(pMesh ,pHedge ,int ,int ,int *);
+int  MMG_inEdge(pHedge ,int *,int *,int *);
+int  MMG_markBdry(pMesh );
+
+/* inout */
+int  MMG_loadMesh(pMesh ,char *);
+int  MMG_loadSol(pSol ,char *,int );
+int  MMG_loadVect(pMesh ,char *,int );
+int  MMG_saveMesh(pMesh ,char *);
+int  MMG_saveSol(pMesh ,pSol ,char *);
+int  MMG_saveVect(pMesh ,char *);
+
+int  MMG_loctet(pMesh ,int ,int ,double *,double *);
+int  MMG_computeMetric(pMesh ,pSol ,int ,double * );
+
+/* scale */
+int  MMG_doSol(pMesh ,pSol );
+int  MMG_scaleMesh(pMesh ,pSol );
+int  MMG_unscaleMesh(pMesh ,pSol );
+
+int  MMG_mmg3d1(pMesh ,pSol ,int *);
+int  MMG_mmg3d9(pMesh ,pSol ,int *);
+
+
+/* zaldy */
+int  MMG_newPt(pMesh ,double *c);
+int  MMG_newElt(pMesh );
+int  MMG_getnElt(pMesh ,int );
+int  MMG_newTria(pMesh );
+void MMG_delPt(pMesh ,int );
+void MMG_delElt(pMesh ,int );
+void MMG_delTria(pMesh ,int );
+int  MMG_zaldy(pMesh );
+int  MMG_zaldy3(pSol );
+int  MMG_zaldy4(pHedge ,int );
+
+int  MMG_optra4(pMesh ,pSol );
+int  MMG_optcoq(pMesh ,pSol);
+int  MMG_opttet(pMesh ,pSol);
+int  MMG_opttyp(pMesh ,pSol ,float ,int *);
+int  MMG_optbdry(pMesh ,pSol ,int );
+int  MMG_opt2peau(pMesh ,pSol ,pQueue ,int ,float );
+int  MMG_optlap(pMesh ,pSol );
+
+/* swapar */
+typedef int (*MMG_Swap)(pMesh ,pSol ,pList );
+//MMG_Swap MMG_swpptr;
+
+int  MMG_swapar(pMesh ,pSol ,pQueue ,List *,int ,float ,float );
+
+int  MMG_simu23(pMesh ,pSol ,int ,int ,float );
+int  MMG_simu32(pMesh ,pSol ,pList ,float );
+int  MMG_swap32(pMesh ,pSol ,pList );
+int  MMG_swap23(pMesh ,pSol ,pQueue ,int ,int ,float );
+
+int  MMG_simu44(pMesh ,pSol ,pList ,float );
+int  MMG_swap44_1(pMesh ,pSol ,pList );
+int  MMG_swap44_2(pMesh ,pSol ,pList );
+
+int  MMG_simu56(pMesh ,pSol ,pList ,float );
+int  MMG_swap56_1(pMesh ,pSol ,pList );
+int  MMG_swap56_2(pMesh ,pSol ,pList );
+int  MMG_swap56_3(pMesh ,pSol ,pList );
+int  MMG_swap56_4(pMesh ,pSol ,pList );
+int  MMG_swap56_5(pMesh ,pSol ,pList );
+
+int MMG_simu68(pMesh ,pSol ,pList ,float );
+int MMG_swap68_1(pMesh ,pSol ,pList );
+int MMG_swap68_2(pMesh ,pSol ,pList );
+int MMG_swap68_3(pMesh ,pSol ,pList );
+int MMG_swap68_4(pMesh ,pSol ,pList );
+int MMG_swap68_5(pMesh ,pSol ,pList );
+int MMG_swap68_6(pMesh ,pSol ,pList );
+int MMG_swap68_7(pMesh ,pSol ,pList );
+int MMG_swap68_8(pMesh ,pSol ,pList );
+int MMG_swap68_9(pMesh ,pSol ,pList );
+int MMG_swap68_10(pMesh ,pSol ,pList );
+int MMG_swap68_11(pMesh ,pSol ,pList );
+int MMG_swap68_12(pMesh ,pSol ,pList );
+int MMG_swap68_13(pMesh ,pSol ,pList );
+int MMG_swap68_14(pMesh ,pSol ,pList );
+
+int MMG_simu710(pMesh ,pSol ,pList ,float );
+int MMG_swap710_1(pMesh ,pSol ,pList );
+int MMG_swap710_2(pMesh ,pSol ,pList );
+int MMG_swap710_3(pMesh ,pSol ,pList );
+int MMG_swap710_4(pMesh ,pSol ,pList );
+int MMG_swap710_5(pMesh ,pSol ,pList );
+int MMG_swap710_6(pMesh ,pSol ,pList );
+int MMG_swap710_7(pMesh ,pSol ,pList );
+int MMG_swap710_8(pMesh ,pSol ,pList );
+int MMG_swap710_9(pMesh ,pSol ,pList );
+int MMG_swap710_10(pMesh ,pSol sol,pList list);
+int MMG_swap710_11(pMesh ,pSol sol,pList list);
+int MMG_swap710_12(pMesh ,pSol sol,pList list);
+int MMG_swap710_13(pMesh ,pSol ,pList );
+int MMG_swap710_14(pMesh ,pSol ,pList );
+int MMG_swap710_15(pMesh ,pSol ,pList );
+int MMG_swap710_16(pMesh ,pSol ,pList );
+int MMG_swap710_17(pMesh ,pSol ,pList );
+int MMG_swap710_18(pMesh ,pSol ,pList );
+int MMG_swap710_19(pMesh ,pSol ,pList );
+int MMG_swap710_20(pMesh ,pSol ,pList );
+int MMG_swap710_21(pMesh ,pSol ,pList );
+int MMG_swap710_22(pMesh ,pSol ,pList );
+int MMG_swap710_23(pMesh ,pSol ,pList );
+int MMG_swap710_24(pMesh ,pSol ,pList );
+int MMG_swap710_25(pMesh ,pSol ,pList );
+int MMG_swap710_26(pMesh ,pSol ,pList );
+int MMG_swap710_27(pMesh ,pSol ,pList );
+int MMG_swap710_28(pMesh ,pSol ,pList );
+int MMG_swap710_29(pMesh ,pSol ,pList );
+int MMG_swap710_30(pMesh ,pSol ,pList );
+int MMG_swap710_31(pMesh ,pSol ,pList );
+int MMG_swap710_32(pMesh ,pSol ,pList );
+int MMG_swap710_33(pMesh ,pSol ,pList );
+int MMG_swap710_34(pMesh ,pSol ,pList );
+int MMG_swap710_35(pMesh ,pSol ,pList );
+int MMG_swap710_36(pMesh ,pSol ,pList );
+int MMG_swap710_37(pMesh ,pSol ,pList );
+int MMG_swap710_38(pMesh ,pSol ,pList );
+int MMG_swap710_39(pMesh ,pSol ,pList );
+int MMG_swap710_40(pMesh ,pSol ,pList );
+int MMG_swap710_41(pMesh ,pSol ,pList );
+int MMG_swap710_42(pMesh ,pSol ,pList );
+
+int  MMG_typelt(pMesh ,int ,int *);
+
+/* function pointers */
+extern double (*MMG_length)(pMesh ,pSol ,int ,int );
+extern double (*MMG_caltet)(pMesh ,pSol ,int );
+extern double (*MMG_calte1)(pMesh ,pSol ,int );
+extern int    (*MMG_caltet2)(pMesh ,pSol ,int ,int ,double ,double *);
+extern int    (*MMG_cavity)(pMesh ,pSol ,int ,int ,pList ,int );
+extern int    (*MMG_buckin)(pMesh ,pSol ,pBucket ,int );
+extern int    (*MMG_optlen)(pMesh ,pSol ,float ,int );
+extern int    (*MMG_interp)(double *,double *,double *,float );
+extern int    (*MMG_optlentet)(pMesh ,pSol ,pQueue ,float ,int ,int );
+extern int    (*MMG_movevertex)(pMesh ,pSol ,int ,int );
+
+/* quality */
+double MMG_voltet(pMesh ,int );
+double MMG_quickvol(double *,double *,double *,double *);
+int    MMG_prilen(pMesh ,pSol );
+void   MMG_outqua(pMesh ,pSol );
+double MMG_priworst(pMesh , pSol );
+
+int  MMG_chkmsh(pMesh ,int ,int );
+
+/* bucket */
+pBucket MMG_newBucket(pMesh ,int );
+int     MMG_addBucket(pMesh ,pBucket ,int );
+int     MMG_delBucket(pMesh ,pBucket ,int );
+void    MMG_freeBucket(pBucket );
+
+/* heap */
+Heap *MMG_hipini(pMesh ,int ,short ,float ,int );
+void  MMG_hipfree(Heap *);
+int   MMG_hipput(pMesh ,Heap *,int );
+int   MMG_hippop(pMesh ,Heap *);
+void  MMG_hiprep(pMesh ,Heap *,int );
+void  MMG_hipdel(pMesh ,Heap *,int );
+
+/* queue */
+pQueue MMG_kiuini(pMesh ,int ,float ,int );
+int    MMG_kiupop(pQueue );
+int    MMG_kiudel(pQueue ,int );
+int    MMG_kiuput(pQueue ,int );
+void   MMG_kiufree(pQueue );
+
+/* matrices */
+int MMG_invmat(double *,double *);
+
+double MMG_calte3_ani(pMesh mesh,pSol sol,int iel);
+
diff --git a/contrib/mmg3d/build/sources/swap23.c b/contrib/mmg3d/build/sources/swap23.c
new file mode 100644
index 0000000000000000000000000000000000000000..f51bfef10400b03f5a40e4fccf072720492f2fb8
--- /dev/null
+++ b/contrib/mmg3d/build/sources/swap23.c
@@ -0,0 +1,678 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_swap23(pMesh mesh,pSol sol,pQueue q, int iel,int iar, double declic) {
+  pTetra pt,pt_a,pt1,pt2,pt3;
+  int jel,*adja,*adja_a,i_a,v,v_a,voy_a,a0,a1,a2;
+  int iadr,kel,lel,*adja1,adj0,adj1,adj2;
+  int s[3],n0,n1,n2,ref;
+
+  pt   = &mesh->tetra[iel];
+  ref  = pt->ref;
+  iadr = 4*(iel-1) + 1;
+  adja = &mesh->adja[iadr];
+  v    = pt->v[iar];
+  
+  i_a = adja[iar] >> 2;
+  assert(i_a);
+  voy_a = adja[iar] % 4;
+  pt_a = &mesh->tetra[i_a];
+  v_a = pt_a->v[voy_a];
+  iadr = 4*(i_a-1) + 1;
+  adja_a = &mesh->adja[iadr];
+  
+  /*check iel and i_a in the same SD*/
+	if(pt->ref != pt_a->ref) return(0);
+	
+  n0 = MMG_idir[iar][0];
+  n1 = MMG_idir[iar][1];
+  n2 = MMG_idir[iar][2];
+
+  s[0] = pt->v[n0];
+  s[1] = pt->v[n1];
+  s[2] = pt->v[n2];
+    
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[1] = v_a;
+  pt1->v[0] = v;
+  pt1->v[2] = s[0];
+  pt1->v[3] = s[1];
+  pt1->qual = MMG_caltet(mesh,sol,jel);
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  if ( pt1->qual >= declic )  MMG_kiuput(q,jel);
+  
+  kel = MMG_newElt(mesh);
+  pt2 = &mesh->tetra[kel];
+  pt2->v[1] = v_a;
+  pt2->v[0] = v;
+  pt2->v[2] = s[1];
+  pt2->v[3] = s[2]; 
+  pt2->qual = MMG_caltet(mesh,sol,kel);
+  pt2->flag = mesh->flag;
+  pt2->ref  = ref;
+  if ( pt2->qual >= declic )  MMG_kiuput(q,kel);
+
+  lel = MMG_newElt(mesh);
+  pt3 = &mesh->tetra[lel];
+  pt3->v[1] = v_a;
+  pt3->v[0] = v;
+  pt3->v[2] = s[2];
+  pt3->v[3] = s[0];   
+  pt3->qual = MMG_caltet(mesh,sol,lel);
+  pt3->flag = mesh->flag;
+  pt3->ref  = ref;
+  if ( pt3->qual >= declic )  MMG_kiuput(q,lel);
+
+//printf("qual %e %e %e\n",pt1->qual,pt2->qual,pt3->qual);
+  /*neighboors*/
+  a0 = n0;
+  a1 = n1;
+  a2 = n2;
+  
+  if(pt->v[MMG_idir[n0][0]] == v) {
+    if(pt->v[MMG_idir[n0][1]] == s[0]
+       || pt->v[MMG_idir[n0][2]] == s[0]) {
+       if(pt->v[MMG_idir[n0][1]] == s[1]
+       || pt->v[MMG_idir[n0][2]] == s[1])
+         a0 = n0;
+       else {
+         assert(pt->v[MMG_idir[n0][1]] == s[2]
+        	|| pt->v[MMG_idir[n0][2]] == s[2]);
+         a2 = n0;
+       }	   
+     } else {
+       a1 = n0;
+     }   
+  } else if(pt->v[MMG_idir[n0][1]] == v){
+    if(pt->v[MMG_idir[n0][0]] == s[0]
+       || pt->v[MMG_idir[n0][2]] == s[0]) {
+       if(pt->v[MMG_idir[n0][0]] == s[1]
+       || pt->v[MMG_idir[n0][2]] == s[1])
+         a0 = n0;
+       else {
+         assert(pt->v[MMG_idir[MMG_idir[iar][0]][0]] == s[2]
+        	|| pt->v[MMG_idir[MMG_idir[iar][0]][2]] == s[2]);
+         a2 = n0;
+       }	   
+     } else {
+       a1 = n0;
+     }   
+  } else {
+    assert(pt->v[MMG_idir[n0][2]] == v);
+    if(pt->v[MMG_idir[n0][1]] == s[0]
+       || pt->v[MMG_idir[n0][0]] == s[0]) {
+       if(pt->v[MMG_idir[n0][1]] == s[1]
+       || pt->v[MMG_idir[n0][0]] == s[1])
+         a0 = n0;
+       else {
+         assert(pt->v[MMG_idir[n0][1]] == s[2]
+        	|| pt->v[MMG_idir[n0][0]] == s[2]);
+         a2 = n0;
+       }	   
+     } else {
+       a1 = n0;
+     }     
+  }
+
+  if(pt->v[MMG_idir[MMG_idir[iar][1]][0]] == v) {
+    if(pt->v[MMG_idir[MMG_idir[iar][1]][1]] == s[0]
+       || pt->v[MMG_idir[MMG_idir[iar][1]][2]] == s[0]) {
+       if(pt->v[MMG_idir[MMG_idir[iar][1]][1]] == s[1]
+       || pt->v[MMG_idir[MMG_idir[iar][1]][2]] == s[1])
+         a0 = MMG_idir[iar][1];
+       else {
+         assert(pt->v[MMG_idir[MMG_idir[iar][1]][1]] == s[2]
+        	|| pt->v[MMG_idir[MMG_idir[iar][1]][2]] == s[2]);
+         a2 = MMG_idir[iar][1];
+       }	   
+     } else {
+       a1 = MMG_idir[iar][1];
+     }   
+  } else if(pt->v[MMG_idir[MMG_idir[iar][1]][1]] == v){
+    if(pt->v[MMG_idir[MMG_idir[iar][1]][0]] == s[0]
+       || pt->v[MMG_idir[MMG_idir[iar][1]][2]] == s[0]) {
+       if(pt->v[MMG_idir[MMG_idir[iar][1]][0]] == s[1]
+       || pt->v[MMG_idir[MMG_idir[iar][1]][2]] == s[1])
+         a0 = MMG_idir[iar][1];
+       else {
+         assert(pt->v[MMG_idir[MMG_idir[iar][1]][0]] == s[2]
+        	|| pt->v[MMG_idir[MMG_idir[iar][1]][2]] == s[2]);
+         a2 = MMG_idir[iar][1];
+       }	   
+     } else {
+       a1 = MMG_idir[iar][1];
+     }   
+  } else {
+    assert(pt->v[MMG_idir[MMG_idir[iar][1]][2]] == v);
+    if(pt->v[MMG_idir[MMG_idir[iar][1]][1]] == s[0]
+       || pt->v[MMG_idir[MMG_idir[iar][1]][0]] == s[0]) {
+       if(pt->v[MMG_idir[MMG_idir[iar][1]][1]] == s[1]
+       || pt->v[MMG_idir[MMG_idir[iar][1]][0]] == s[1])
+         a0 = MMG_idir[iar][1];
+       else {
+         assert(pt->v[MMG_idir[MMG_idir[iar][1]][1]] == s[2]
+        	|| pt->v[MMG_idir[MMG_idir[iar][1]][0]] == s[2]);
+         a2 = MMG_idir[iar][1];
+       }	   
+     } else {
+       a1 = MMG_idir[iar][1];
+     }     
+  }
+
+ if(pt->v[MMG_idir[n2][0]] == v) {
+    if(pt->v[MMG_idir[n2][1]] == s[0]
+       || pt->v[MMG_idir[n2][2]] == s[0]) {
+       if(pt->v[MMG_idir[n2][1]] == s[1]
+       || pt->v[MMG_idir[n2][2]] == s[1])
+         a0 = n2;
+       else {
+         assert(pt->v[MMG_idir[n2][1]] == s[2]
+        	|| pt->v[MMG_idir[n2][2]] == s[2]);
+         a2 = n2;
+       }	   
+     } else {
+       a1 = n2;
+     }   
+  } else if(pt->v[MMG_idir[n2][1]] == v){
+    if(pt->v[MMG_idir[n2][0]] == s[0]
+       || pt->v[MMG_idir[n2][2]] == s[0]) {
+       if(pt->v[MMG_idir[n2][0]] == s[1]
+       || pt->v[MMG_idir[n2][2]] == s[1])
+         a0 = n2;
+       else {
+         assert(pt->v[MMG_idir[n2][0]] == s[2]
+        	|| pt->v[MMG_idir[n2][2]] == s[2]);
+         a2 = n2;
+       }	   
+     } else {
+       a1 = n2;
+     }   
+  } else {
+    assert(pt->v[MMG_idir[n2][2]] == v);
+    if(pt->v[MMG_idir[MMG_idir[iar][2]][1]] == s[0]
+       || pt->v[MMG_idir[MMG_idir[iar][2]][0]] == s[0]) {
+       if(pt->v[MMG_idir[MMG_idir[iar][2]][1]] == s[1]
+       || pt->v[MMG_idir[MMG_idir[iar][2]][0]] == s[1])
+         a0 = MMG_idir[iar][2];
+       else {
+         assert(pt->v[MMG_idir[MMG_idir[iar][2]][1]] == s[2]
+        	|| pt->v[MMG_idir[MMG_idir[iar][2]][0]] == s[2]);
+         a2 = MMG_idir[iar][2];
+       }	   
+     } else {
+       a1 = MMG_idir[iar][2];
+     }     
+  }
+
+  adj0 = MMG_idir[voy_a][0];
+  adj1 = MMG_idir[voy_a][1];
+  adj2 = MMG_idir[voy_a][2];
+
+  if(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][0]] == v_a) {
+    if(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][1]] == s[0]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][0]][2]] == s[0]) {
+       if(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][1]] == s[1]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][0]][2]] == s[1])
+         adj0 = MMG_idir[voy_a][0];
+       else {
+         assert(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][1]] == s[2]
+        	|| pt_a->v[MMG_idir[MMG_idir[voy_a][0]][2]] == s[2]);
+         adj2 = MMG_idir[voy_a][0];
+       }	   
+     } else {
+       adj1 = MMG_idir[voy_a][0];
+     }   
+  } else if(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][1]] == v_a){
+    if(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][0]] == s[0]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][0]][2]] == s[0]) {
+       if(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][0]] == s[1]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][0]][2]] == s[1])
+         adj0 = MMG_idir[voy_a][0];
+       else {
+         assert(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][0]] == s[2]
+        	|| pt_a->v[MMG_idir[MMG_idir[voy_a][0]][2]] == s[2]);
+         adj2 = MMG_idir[voy_a][0];
+       }	   
+     } else {
+       adj1 = MMG_idir[voy_a][0];
+     }   
+  } else {
+    assert(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][2]] == v_a);
+    if(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][1]] == s[0]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][0]][0]] == s[0]) {
+       if(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][1]] == s[1]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][0]][0]] == s[1])
+         adj0 = MMG_idir[voy_a][0];
+       else {
+         assert(pt_a->v[MMG_idir[MMG_idir[voy_a][0]][1]] == s[2]
+        	|| pt_a->v[MMG_idir[MMG_idir[voy_a][0]][0]] == s[2]);
+         adj2 = MMG_idir[voy_a][0];
+       }	   
+     } else {
+       adj1 = MMG_idir[voy_a][0];
+     }     
+  }
+
+  if(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][0]] == v_a) {
+    if(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][1]] == s[0]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][1]][2]] == s[0]) {
+       if(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][1]] == s[1]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][1]][2]] == s[1])
+         adj0 = MMG_idir[voy_a][1];
+       else {
+         assert(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][1]] == s[2]
+        	|| pt_a->v[MMG_idir[MMG_idir[voy_a][1]][2]] == s[2]);
+         adj2 = MMG_idir[voy_a][1];
+       }	   
+     } else {
+       adj1 = MMG_idir[voy_a][1];
+     }   
+  } else if(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][1]] == v_a){
+    if(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][0]] == s[0]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][1]][2]] == s[0]) {
+       if(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][0]] == s[1]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][1]][2]] == s[1])
+         adj0 = MMG_idir[voy_a][1];
+       else {
+         assert(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][0]] == s[2]
+        	|| pt_a->v[MMG_idir[MMG_idir[voy_a][1]][2]] == s[2]);
+         adj2 = MMG_idir[voy_a][1];
+       }	   
+     } else {
+       adj1 = MMG_idir[voy_a][1];
+     }   
+  } else {
+    assert(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][2]] == v_a);
+    if(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][1]] == s[0]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][1]][0]] == s[0]) {
+       if(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][1]] == s[1]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][1]][0]] == s[1])
+         adj0 = MMG_idir[voy_a][1];
+       else {
+         assert(pt_a->v[MMG_idir[MMG_idir[voy_a][1]][1]] == s[2]
+        	|| pt_a->v[MMG_idir[MMG_idir[voy_a][1]][0]] == s[2]);
+         adj2 = MMG_idir[voy_a][1];
+       }	   
+     } else {
+       adj1 = MMG_idir[voy_a][1];
+     }     
+  }
+
+ if(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][0]] == v_a) {
+    if(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][1]] == s[0]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][2]][2]] == s[0]) {
+       if(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][1]] == s[1]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][2]][2]] == s[1])
+         adj0 = MMG_idir[voy_a][2];
+       else {
+         assert(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][1]] == s[2]
+        	|| pt_a->v[MMG_idir[MMG_idir[voy_a][2]][2]] == s[2]);
+         adj2 = MMG_idir[voy_a][2];
+       }	   
+     } else {
+       adj1 = MMG_idir[voy_a][2];
+     }   
+  } else if(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][1]] == v_a){
+    if(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][0]] == s[0]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][2]][2]] == s[0]) {
+       if(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][0]] == s[1]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][2]][2]] == s[1])
+         adj0 = MMG_idir[voy_a][2];
+       else {
+         assert(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][0]] == s[2]
+        	|| pt_a->v[MMG_idir[MMG_idir[voy_a][2]][2]] == s[2]);
+         adj2 = MMG_idir[voy_a][2];
+       }	   
+     } else {
+       adj1 = MMG_idir[voy_a][2];
+     }   
+  } else {
+    assert(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][2]] == v_a);
+    if(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][1]] == s[0]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][2]][0]] == s[0]) {
+       if(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][1]] == s[1]
+       || pt_a->v[MMG_idir[MMG_idir[voy_a][2]][0]] == s[1])
+         adj0 = MMG_idir[voy_a][2];
+       else {
+         assert(pt_a->v[MMG_idir[MMG_idir[voy_a][2]][1]] == s[2]
+        	|| pt_a->v[MMG_idir[MMG_idir[voy_a][2]][0]] == s[2]);
+         adj2 = MMG_idir[voy_a][2];
+       }	   
+     } else {
+       adj1 = MMG_idir[voy_a][2];
+     }     
+  }
+  
+  iadr = 4*(jel-1) + 1;
+  adja1 = &mesh->adja[iadr];
+  adja1[1] = adja[a0];
+  adja1[0] = adja_a[adj0];
+  adja1[2] = 4*kel + 3;
+  adja1[3] = 4*lel + 2;   
+	pt1->bdryref[0] = pt_a->bdryref[adj0];
+	pt1->bdryref[1] = pt->bdryref[a0];
+	pt1->bdryref[2] = -1;
+	pt1->bdryref[3] = -1;
+	
+  
+  
+  if ((adja[a0]>>2)) {
+    iadr = 4*((adja[a0]>>2)-1) + 1;
+    adja1 = &mesh->adja[iadr];
+    adja1[adja[a0]%4] = 4*jel + 1;
+  }
+  
+  if (adja_a[adj0]>>2) {
+    iadr = 4*((adja_a[adj0]>>2)-1) + 1;
+    adja1 = &mesh->adja[iadr];
+    adja1[adja_a[adj0]%4] = 4*jel ;
+  }
+  
+  iadr = 4*(kel-1) + 1;
+  adja1 = &mesh->adja[iadr];
+  adja1[1] = adja[a1];
+  adja1[0] = adja_a[adj1];
+  adja1[2] = 4*lel + 3;
+  adja1[3] = 4*jel + 2;
+	pt2->bdryref[0] = pt_a->bdryref[adj1];
+	pt2->bdryref[1] = pt->bdryref[a1];
+	pt2->bdryref[2] = -1;
+	pt2->bdryref[3] = -1;
+  
+  if ((adja[a1]>>2)) {
+    iadr = 4*((adja[a1]>>2)-1) + 1;
+    adja1 = &mesh->adja[iadr];
+    adja1[adja[a1]%4] = 4*kel + 1; 
+  }
+    
+  if ((adja_a[adj1]>>2)) {
+    iadr = 4*((adja_a[adj1]>>2)-1) + 1;
+    adja1 = &mesh->adja[iadr];
+    adja1[adja_a[adj1]%4] = 4*kel ; 
+  }
+    
+  iadr = 4*(lel-1) + 1;
+  adja1 = &mesh->adja[iadr];
+  adja1[1] = adja[a2];
+  adja1[0] = adja_a[adj2];
+  adja1[2] = 4*jel + 3;
+  adja1[3] = 4*kel + 2;
+	pt3->bdryref[0] = pt_a->bdryref[adj2];
+	pt3->bdryref[1] = pt->bdryref[a2];
+	pt3->bdryref[2] = -1;
+	pt3->bdryref[3] = -1;
+  
+  if ((adja[a2]>>2)) {
+    iadr = 4*((adja[a2]>>2)-1) + 1;
+    adja1 = &mesh->adja[iadr];
+    adja1[adja[a2]%4] = 4*lel + 1; 
+  }
+   
+  if ((adja_a[adj2]>>2)) {
+    iadr = 4*((adja_a[adj2]>>2)-1) + 1;
+    adja1 = &mesh->adja[iadr];
+    adja1[adja_a[adj2]%4] = 4*lel; 
+  }
+    
+  /*del*/
+  MMG_delElt(mesh,iel);
+  MMG_delElt(mesh,i_a);
+  MMG_kiudel(q,iel);
+  MMG_kiudel(q,i_a);
+
+
+  return(1);
+}
+
+
+/* remove edge AB */
+int MMG_swap32(pMesh mesh,pSol sol,pList list) {
+  pTetra    pt,pt1,pt2,pt0;   
+	Hedge			hed;
+  int      *adja,k,i,jel,kel,adj,iadr,ia,ib,s1,s2,s3;
+  int       old,iarold,kk,adj_a,adj_b,iel,iar,ref,ref_a,ref_b;   
+  short     voy,voy_a,voy_b;
+  
+  if ( !MMG_zaldy4(&hed,10) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP32 IGNORED\n"); 
+  }   
+
+  /* face s1 s2 s3 */
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  pt   = &mesh->tetra[iel];
+  ref  = pt->ref;
+  
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ];
+	for(i=0 ; i<6 ; i++) {
+		//printf("1. insere %d %d -- %d\n",pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] >> 2;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s3   = pt1->v[voy];
+
+  /* create 2 new tetra */ 
+  /*edge : ias1 -- ias2 -- ias3 -- s1s2 -- s1s3 -- s2s3*/
+  jel = MMG_newElt(mesh);  
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s2;
+  pt1->v[3] = s3;
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  /* 2nd elt */
+  /*edge : ibs2 -- ibs1 -- ibs3 -- s2s1 -- s2s3 -- s2s3*/
+  kel = MMG_newElt(mesh);
+  pt2 = &mesh->tetra[kel];
+  pt2->v[0] = ib;
+  pt2->v[1] = s2;
+  pt2->v[2] = s1;
+  pt2->v[3] = s3;
+  pt2->qual = list->qual[2];
+  pt2->flag = mesh->flag;
+  pt2->ref  = ref;
+
+  /* external faces */
+  for (k=2; k<=3; k++) {
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0    = &mesh->tetra[old]; 
+		for(i=0 ; i<6 ; i++) {    
+			//printf("2. insere %d %d -- %d\n",pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    iarold = list->tetra[k] % 6;
+    kk     = MMG_iare[iarold][1];
+    if( pt0->v[kk] == ib ) {
+      adj_a  = adja[kk] >> 2;
+      voy_a = adja[kk] % 4;
+      ref_a = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] >> 2;
+      voy_b  = adja[kk] % 4;
+      ref_b = pt0->bdryref[kk]; 
+    } 
+    else /*if ( pt0->v[MMG_iare[iarold][0]] == ib ) */{
+      adj_b  = adja[kk] >> 2;
+      voy_b  = adja[kk] % 4;   
+      ref_b  = pt0->bdryref[kk];  
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] >> 2;
+      voy_a  = adja[kk] % 4;  
+      ref_a  = pt0->bdryref[kk];  
+    }
+
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    if ( old == (adja[MMG_isar[iar][0]]>>2) ) {  
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      pt1->bdryref[1] = ref_a;   
+			
+      if (adj_a) {        
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 1;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      pt2->bdryref[2] = ref_b;
+      if (adj_b) {        
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 2; 
+      }
+    } 
+    else if ( old == (adja[MMG_isar[iar][1]]>>2) ) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      pt1->bdryref[2] = ref_a;  
+        
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 2;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      pt2->bdryref[1] = ref_b; 
+      
+      if (adj_b) {        
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 1;
+      }
+    }
+ 
+  }
+
+  /* du tetra 1 de la liste pour ia */
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] >> 2;
+  voy  = adja[k] % 4;
+   
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  pt1->bdryref[3] = pt->bdryref[k];
+    
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;  
+  }
+
+  /* du tetra 1 de la liste pour ib */
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] >> 2;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  pt2->bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3; 
+  }
+
+  /* internal face */
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  
+	for(i=0 ; i<6 ; i++) {
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1); 
+		//printf("pas trouve %d %d\n",pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]]);
+		assert(pt1->bdryinfo[i]!=1);
+		pt2->bdryinfo[i] = MMG_edgePut(&hed,pt2->v[MMG_iare[i][0]],pt2->v[MMG_iare[i][1]],1);
+		assert(pt2->bdryinfo[i]!=1);
+	}
+
+  /* remove 3 old tetra */
+  for (k=1; k<=3; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = 0;
+  
+	M_free(hed.item);
+  return(2);
+}
+
diff --git a/contrib/mmg3d/build/sources/swap44.c b/contrib/mmg3d/build/sources/swap44.c
new file mode 100644
index 0000000000000000000000000000000000000000..b8d645576d6e15b0f007f976bd52fe1a8644304e
--- /dev/null
+++ b/contrib/mmg3d/build/sources/swap44.c
@@ -0,0 +1,608 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/* remove edge iar of iel */
+int MMG_swap44_1(pMesh mesh,pSol sol,pList list) {
+  pTetra    pt,pt0,pt1;
+	Hedge			hed;
+  int      *adja,i,k,jel,kel,lel,mel,adj,base,iadr,ia,ib,s1,s2,s3,s4;
+  int       old,iarold,kk,adj_a,adj_b,iel,iar,ref,ref_a,ref_b;
+  short     voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,13) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP441 IGNORED\n"); 
+  }   
+
+  /* face s1 s2 s3 */
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  base = mesh->mark;
+  pt   = &mesh->tetra[iel];
+  ref  = pt->ref;
+  
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ];
+  for(i=0 ; i<6 ; i++) {
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s3   = pt1->v[voy];
+
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s4   = pt1->v[voy];
+ 
+  /* create 4 new tetra */
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s2;
+  pt1->v[3] = s3;
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  /* 2nd elt */
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s1;
+  pt1->v[2] = s3;
+  pt1->v[3] = s2;
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s3;
+  pt1->v[3] = s4;
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  /* 2nd elt */
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s1;
+  pt1->v[2] = s4;
+  pt1->v[3] = s3;
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  /* external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+
+  if (adj) {    
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+    
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    if(iadr<0) {puts("aaaaaaaaaaaa");exit(0);}
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;  
+  }
+  
+  for(k=2 ; k<=4 ; k++) {
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk]; 
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+  
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    if(old==(adja[MMG_isar[iar][0]]/4)) { 
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[1] = ref_a;
+        
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 1;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[1] = ref_b;
+        
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 1; 
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){ 
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[2] = ref_a;
+        
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 2; 
+      }
+      
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[3] = ref_b;
+        
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 3;
+      }
+    } else {
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[1] = ref_a;
+        
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 1;
+      }
+      
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[1] = ref_b;
+        
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 1; 
+      }
+    }
+  
+  }
+  
+	/*bdryinfo*/           
+	pt1 = &mesh->tetra[jel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1); 
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[kel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[lel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[mel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	
+  /* internal face */
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  
+  /* remove 4 old tetra */
+  for (k=1; k<=4; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+    
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = 0;
+  
+	M_free(hed.item);
+  return(4);
+}
+
+
+/* remove edge iar of iel */
+int MMG_swap44_2(pMesh mesh,pSol sol,pList list) {
+  pTetra    pt,pt0,pt1;
+	Hedge			hed;
+  int      *adja,i,k,jel,kel,lel,mel,adj,base,iadr,ia,ib,s1,s2,s3,s4;
+  int       old,iarold,kk,adj_a,adj_b,iel,iar,ref,ref_a,ref_b;
+  short     voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,13) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP442 IGNORED\n"); 
+  }   
+
+  /* face s1 s2 s3 */
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  base = mesh->mark;
+  pt   = &mesh->tetra[iel];
+  ref  = pt->ref;
+  
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ];
+	for(i=0 ; i<6 ; i++) { 
+		//printf("on insere %d %d -- %d\n",pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s3   = pt1->v[voy];
+
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s4   = pt1->v[voy];
+ 
+  /* create 4 new tetra */
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s2;
+  pt1->v[3] = s4;
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  /* 2nd elt */
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s1;
+  pt1->v[2] = s4;
+  pt1->v[3] = s2;
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s2;
+  pt1->v[2] = s3;
+  pt1->v[3] = s4;
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  /* 2nd elt */
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s2;
+  pt1->v[2] = s4;
+  pt1->v[3] = s3;
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+ 
+  /* external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+    
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;  
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+    
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  
+  for(k=2 ; k<=4 ; k++) {
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+		for(i=0 ; i<6 ; i++) {
+			//printf("2. on insere %d %d -- %d\n",pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4; 
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk]; 
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+  
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    if(old==(adja[MMG_isar[iar][0]]/4)) {  
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[3]  = ref_a;
+        
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 3; 
+      }
+       
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[2]  = ref_b;
+        
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 2; 
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[2]  = ref_a;
+        
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 2; 
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[3]  = ref_b;
+        
+      if (adj_b){
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 3;
+      }
+    } else {
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[1]  = ref_a;
+        
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 1; 
+      }
+      
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[1]  = ref_b;
+        
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 1;
+      }
+    }
+  
+  }
+  /*bdryinfo*/           
+	pt1 = &mesh->tetra[jel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);   
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[kel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[lel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[mel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	
+  /* internal face */
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = lel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 1;
+  
+  /* remove 4 old tetra */
+  for (k=1; k<=4; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+    
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = 0;
+  
+	M_free(hed.item);
+	
+  return(4);
+}
diff --git a/contrib/mmg3d/build/sources/swap56.c b/contrib/mmg3d/build/sources/swap56.c
new file mode 100644
index 0000000000000000000000000000000000000000..a116080e05e1a1c597cfd9db7b44ba9ce5288be8
--- /dev/null
+++ b/contrib/mmg3d/build/sources/swap56.c
@@ -0,0 +1,1956 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_swap56_1(pMesh mesh,pSol sol,pList list) {
+  pTetra  pt,pt1,pt0;
+	Hedge			hed;
+  int     i,ia,ib,s1,s2,s3,s4,s5;
+  int     jel,kel,lel,mel,nel,pel;
+  int     iadr,iarold,*adja,k,adj,old,kk,adj_a,adj_b,j,iel,iar,ref,ref_a,ref_b;
+  short   voy,voy_a,voy_b; 
+
+  if ( !MMG_zaldy4(&hed,17) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP442 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref;
+  
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ]; 
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s3   = pt1->v[voy];
+
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s2) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+     k = MMG_idir[voy][2];
+  } else {
+    puts("point non existant");
+    exit(0);
+  }   
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s4   = pt1->v[voy];
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s5   = pt1->v[voy];
+  
+  /* create 6 new tetra */
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s2;
+  pt1->v[3] = s3;
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s1;
+  pt1->v[2] = s3;
+  pt1->v[3] = s2;
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+     
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s3;
+  pt1->v[3] = s4;
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s1;
+  pt1->v[2] = s4;
+  pt1->v[3] = s3;
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+ 
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s4;
+  pt1->v[3] = s5;
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+    
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s1;
+  pt1->v[2] = s5;
+  pt1->v[3] = s4;
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3]  = pt->bdryref[k];
+  
+  if (adj)  {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3; 
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2]  = pt->bdryref[k];
+
+  if (adj)  {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  
+  for(k=2 ; k<=5 ; k++) {
+    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4; 
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk]; 
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[1] = ref_a;
+      
+      if (adj_a) { 
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 1; 
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[1] = ref_b;
+      
+      if (adj_b){
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 1; 
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      iadr = (nel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[nel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = nel*4 + 2;
+      }
+      
+      iadr = (pel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[pel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = pel*4 + 3;
+      }
+    } else {
+       /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s2) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      if(old==adja[j] / 4) {
+         
+         iadr = (lel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[lel].bdryref[1] = ref_a;
+         
+         if (adj_a){
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = lel*4 + 1;
+         }
+      
+         iadr = (mel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[mel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = mel*4 + 1;
+         }
+      } else {
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 1;
+         }
+      
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[pel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = pel*4 + 1;
+         }
+      }
+    }
+  
+  }
+  
+   /*internal faces*/
+   iadr = (jel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = kel*4 + 0;
+   adja[2] = lel*4 + 3;
+   iadr = (kel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = jel*4 + 0;
+   adja[3] = mel*4 + 2;
+  
+   iadr = (lel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = mel*4 + 0;
+   adja[3] = jel*4 + 2;
+   adja[2] = nel*4 + 3;
+  
+   iadr = (mel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = lel*4 + 0;
+   adja[2] = kel*4 + 3;
+   adja[3] = pel*4 + 2;
+  
+   iadr = (nel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = pel*4 + 0;
+   adja[3] = lel*4 + 2;
+   
+   iadr = (pel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = nel*4 + 0;
+   adja[2] = mel*4 + 3;
+  
+  /*bdryinfo*/           
+	pt1 = &mesh->tetra[jel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);   
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[kel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[lel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[mel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[nel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[pel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+
+
+  /* remove 5 old tetra */
+  for (k=1; k<=5; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = pel;
+  list->tetra[7] = 0;
+	M_free(hed.item);
+  return(6);
+}
+
+
+int MMG_swap56_2(pMesh mesh,pSol sol,pList list) {
+   pTetra pt,pt1,pt0;
+	 Hedge			hed;
+   int i,ia,ib,s1,s2,s3,s4,s5;
+   int jel,kel,lel,mel,nel,pel;
+   int iadr,iarold,*adja,k,adj,old,kk,adj_a,adj_b,j,iel,iar,ref,ref_a,ref_b;
+   short voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,17) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP442 IGNORED\n"); 
+  }   
+   
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref;
+  
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ]; 
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_isar[iar][0];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+   pt1  = &mesh->tetra[adj];
+   s3   = pt1->v[voy];
+
+   iadr = (adj-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   if(pt1->v[MMG_idir[voy][0]]==s2) {
+      k = MMG_idir[voy][0];
+   } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+      k = MMG_idir[voy][1];
+   } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+      k = MMG_idir[voy][2];
+   } else {
+     puts("point non existant");
+     exit(0);
+   }   
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+   pt1  = &mesh->tetra[adj];
+   s4   = pt1->v[voy];
+  
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_isar[iar][1];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+   pt1  = &mesh->tetra[adj];
+   s5   = pt1->v[voy];
+
+   /* create 6 new tetra */
+   jel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[jel];
+   pt1->v[0] = ia;
+   pt1->v[1] = s1;
+   pt1->v[2] = s2;
+   pt1->v[3] = s5;
+   pt1->qual = list->qual[1];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+  
+   kel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[kel];
+   pt1->v[0] = ib;
+   pt1->v[1] = s1;
+   pt1->v[2] = s5;
+   pt1->v[3] = s2;
+   pt1->qual = list->qual[2];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+      
+   lel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[lel];
+   pt1->v[0] = ia;
+   pt1->v[1] = s2;
+   pt1->v[2] = s3;
+   pt1->v[3] = s4;
+   pt1->qual = list->qual[3];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+
+   mel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[mel];
+   pt1->v[0] = ib;
+   pt1->v[1] = s2;
+   pt1->v[2] = s4;
+   pt1->v[3] = s3;
+   pt1->qual = list->qual[4];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+
+   nel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[nel];
+   pt1->v[0] = ia;
+   pt1->v[1] = s2;
+   pt1->v[2] = s4;
+   pt1->v[3] = s5;
+   pt1->qual = list->qual[5];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+
+   pel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[pel];
+   pt1->v[0] = ib;
+   pt1->v[1] = s2;
+   pt1->v[2] = s5;
+   pt1->v[3] = s4;
+   pt1->qual = list->qual[6];
+   pt1->flag = mesh->flag; 
+   pt1->ref  = ref;
+
+  /*external faces*/
+     /*tetra iel*/
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_iare[iar][1];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+  
+   iadr = (jel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[3] = adj*4 + voy;
+   mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+   
+   if (adj) {
+     iadr = (adj-1)*4 + 1;
+     adja = &mesh->adja[iadr];
+     adja[voy] = jel*4 + 3; 
+   }
+  
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_iare[iar][0];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+  
+   iadr = (kel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[2] = adj*4 + voy;
+   mesh->tetra[kel].bdryref[2] = pt->bdryref[k];  
+   
+   if (adj) {
+     iadr = (adj-1)*4 + 1;
+     adja = &mesh->adja[iadr];
+     adja[voy] = kel*4 + 2;  
+   }
+  
+   for(k=2 ; k<=5 ; k++) {
+    
+     old  = list->tetra[k] / 6;
+     iadr = (old-1)*4 + 1;
+     adja = &mesh->adja[iadr];
+
+     pt0 = &mesh->tetra[old];
+		  for(i=0 ; i<6 ; i++) {
+		  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		  }
+     iarold = list->tetra[k] % 6;
+     kk    = MMG_iare[iarold][1];
+     if(pt0->v[kk]==ib) {
+       adj_a  = adja[kk] / 4;
+       voy_a  = adja[kk] % 4; 
+       ref_a  = pt0->bdryref[kk];
+       kk    = MMG_iare[iarold][0];
+       adj_b  = adja[kk] / 4;
+       voy_b  = adja[kk] % 4;
+       ref_b  = pt0->bdryref[kk];
+     } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+       adj_b  = adja[kk] / 4;
+       voy_b  = adja[kk] % 4;
+       ref_b  = pt0->bdryref[kk]; 
+       kk    = MMG_iare[iarold][0];
+       adj_a  = adja[kk] / 4;
+       voy_a  = adja[kk] % 4;
+       ref_a  = pt0->bdryref[kk];
+     } 
+   
+     iadr = (iel-1)*4 + 1;
+     adja = &mesh->adja[iadr];
+     if(old==(adja[MMG_isar[iar][0]]/4)) {
+       iadr = (lel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[3] = adj_a*4 + voy_a;
+       mesh->tetra[lel].bdryref[3] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = lel*4 + 3; 
+       }
+      
+       iadr = (mel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[2] = adj_b*4 + voy_b;
+       mesh->tetra[mel].bdryref[2] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = mel*4 + 2;
+       }
+      
+     } else if(old==(adja[MMG_isar[iar][1]]/4)){
+       iadr = (jel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[2] = adj_a*4 + voy_a; 
+       mesh->tetra[jel].bdryref[2] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = jel*4 + 2;
+       }
+      
+       iadr = (kel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[3] = adj_b*4 + voy_b;
+       mesh->tetra[kel].bdryref[3] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = kel*4 + 3;
+       }
+     } else {
+
+     /*old n'est pas un voisin de iel*/
+       iadr = (iel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       j    = MMG_isar[iar][0];
+       adj  = adja[j] / 4;
+       voy  = adja[j] % 4;
+       pt1  = &mesh->tetra[adj];
+    
+      if(pt1->v[MMG_idir[voy][0]]==s2) {
+          j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+          j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+          j = MMG_idir[voy][2];
+      }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       if(old==adja[j] / 4) {
+         
+         iadr = (lel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a; 
+         mesh->tetra[lel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = lel*4 + 1;
+         }
+      
+         iadr = (mel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[mel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = mel*4 + 1;
+         }
+       } else {
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 1; 
+         }
+      
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[pel].bdryref[1] = ref_b;
+        
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = pel*4 + 1;
+         }
+       }
+    }
+  
+  }
+  
+   /*internal faces*/
+   iadr = (jel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = kel*4 + 0;
+   adja[1] = nel*4 + 2;
+
+   iadr = (kel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = jel*4 + 0;
+   adja[1] = pel*4 + 3;
+ 
+   iadr = (lel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = mel*4 + 0;
+   adja[2] = nel*4 + 3;
+   
+   
+   iadr = (mel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = lel*4 + 0;
+   adja[3] = pel*4 + 2;
+  
+   iadr = (nel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = pel*4 + 0;
+   adja[2] = jel*4 + 1;
+   adja[3] = lel*4 + 2;
+  
+   iadr = (pel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = nel*4 + 0;
+   adja[3] = kel*4 + 1;
+   adja[2] = mel*4 + 3;
+ 
+  /*bdryinfo*/           
+	pt1 = &mesh->tetra[jel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);   
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[kel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[lel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[mel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[nel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[pel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+  /* remove 5 old tetra */
+  for (k=1; k<=5; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = pel;
+  list->tetra[7] = 0;
+	M_free(hed.item);
+
+  return(6);
+}
+
+
+int MMG_swap56_3(pMesh mesh,pSol sol,pList list) {
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int i,ia,ib,s1,s2,s3,s4,s5;
+  int jel,kel,lel,mel,nel,pel;
+  int iadr,iarold,*adja,k,adj,old,kk,adj_a,adj_b,j,iel,iar,ref,ref_a,ref_b;
+  short voy,voy_a,voy_b;
+  
+  if ( !MMG_zaldy4(&hed,17) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP442 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref;
+
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ]; 
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s3   = pt1->v[voy];
+  
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s2) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+     k = MMG_idir[voy][2];
+  } else {
+    puts("point non existant");
+    exit(0);
+  }   
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s4   = pt1->v[voy];
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s5   = pt1->v[voy];
+  
+  /* create 6 new tetra */
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s2;
+  pt1->v[3] = s3;
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+	 
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s1;
+  pt1->v[2] = s3;
+  pt1->v[3] = s2;
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s1;
+  pt1->v[2] = s3;
+  pt1->v[3] = s5;
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s1;
+  pt1->v[2] = s5;
+  pt1->v[3] = s3;
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s3;
+  pt1->v[2] = s4;
+  pt1->v[3] = s5;
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s3;
+  pt1->v[2] = s5;
+  pt1->v[3] = s4;
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3; 
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  
+  for(k=2 ; k<=5 ; k++) {
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+  
+    pt0 = &mesh->tetra[old];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk]; 
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+  
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[1] = ref_a;
+      
+      if (adj_a){
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 1; 
+      }
+     
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[1] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 1; 
+      }
+     
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 2; 
+      }
+     
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 3; 
+      }
+    } else {
+  
+    /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+   
+     if(pt1->v[MMG_idir[voy][0]]==s2) {
+         j = MMG_idir[voy][0];
+     } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+         j = MMG_idir[voy][1];
+     } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+         j = MMG_idir[voy][2];
+     }
+  
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      if(old==adja[j] / 4) {
+        
+        iadr = (nel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[3] = adj_a*4 + voy_a;
+        mesh->tetra[nel].bdryref[3] = ref_a;
+        
+        if (adj_a) {
+          iadr = (adj_a-1)*4 + 1;
+          adja = &mesh->adja[iadr];
+          adja[voy_a] = nel*4 + 3;
+        }
+     
+        iadr = (pel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[2] = adj_b*4 + voy_b;
+        mesh->tetra[pel].bdryref[2] = ref_b;
+        
+        if (adj_b) {
+          iadr = (adj_b-1)*4 + 1;
+          adja = &mesh->adja[iadr];
+          adja[voy_b] = pel*4 + 2;
+        }
+      } else {
+        iadr = (nel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[1] = adj_a*4 + voy_a;
+        mesh->tetra[nel].bdryref[1] = ref_a;
+        
+        if (adj_a) {
+          iadr = (adj_a-1)*4 + 1;
+          adja = &mesh->adja[iadr];
+          adja[voy_a] = nel*4 + 1; 
+        }
+     
+        iadr = (pel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[1] = adj_b*4 + voy_b;
+        mesh->tetra[pel].bdryref[1] = ref_b;
+        
+        if (adj_b) {
+          iadr = (adj_b-1)*4 + 1;
+          adja = &mesh->adja[iadr];
+          adja[voy_b] = pel*4 + 1;  
+        }
+      }
+   }
+  
+  }
+  
+   /*internal faces*/
+   iadr = (jel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = kel*4 + 0;
+   adja[2] = lel*4 + 3;
+
+   iadr = (kel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = jel*4 + 0;
+   adja[3] = mel*4 + 2;
+  
+   iadr = (lel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = mel*4 + 0;
+   adja[3] = jel*4 + 2;
+   adja[1] = nel*4 + 2;
+   
+   iadr = (mel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = lel*4 + 0;
+   adja[2] = kel*4 + 3;
+   adja[1] = pel*4 + 3;
+   
+   iadr = (nel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = pel*4 + 0;
+   adja[2] = lel*4 + 1;
+   
+   iadr = (pel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = nel*4 + 0;
+   adja[3] = mel*4 + 1;
+
+  /*bdryinfo*/           
+	pt1 = &mesh->tetra[jel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);   
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[kel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[lel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[mel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[nel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[pel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+  
+  /* remove 5 old tetra */
+  for (k=1; k<=5; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = pel;
+  list->tetra[7] = 0;
+ 	M_free(hed.item);
+
+  return(6);
+}
+
+
+int MMG_swap56_4(pMesh mesh,pSol sol,pList list) {
+   pTetra pt,pt1,pt0;
+ 	 Hedge			hed;
+   int i,ia,ib,s1,s2,s3,s4,s5;
+   int jel,kel,lel,mel,nel,pel;
+   int iadr,iarold,*adja,k,adj,old,kk,adj_a,adj_b,j,iel,iar,ref,ref_a,ref_b;
+   short voy,voy_a,voy_b;
+  if ( !MMG_zaldy4(&hed,17) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP442 IGNORED\n"); 
+  }   
+  
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref;
+
+   ia  = pt->v[ MMG_iare[iar][0] ];
+   ib  = pt->v[ MMG_iare[iar][1] ];
+   s1  = pt->v[ MMG_isar[iar][0] ];
+   s2  = pt->v[ MMG_isar[iar][1] ]; 
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_isar[iar][0];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+   pt1  = &mesh->tetra[adj];
+   s3   = pt1->v[voy];
+
+   iadr = (adj-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   if(pt1->v[MMG_idir[voy][0]]==s2) {
+      k = MMG_idir[voy][0];
+   } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+      k = MMG_idir[voy][1];
+   } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+      k = MMG_idir[voy][2];
+   } else {
+     puts("point non existant");
+     exit(0);
+   }   
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+   pt1  = &mesh->tetra[adj];
+   s4   = pt1->v[voy];
+  
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_isar[iar][1];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+   pt1  = &mesh->tetra[adj];
+   s5   = pt1->v[voy];
+
+   /* create 6 new tetra */
+   jel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[jel];
+   pt1->v[0] = ia;
+   pt1->v[1] = s1;
+   pt1->v[2] = s4;
+   pt1->v[3] = s5;
+   pt1->qual = list->qual[1];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+
+   kel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[kel];
+   pt1->v[0] = ib;
+   pt1->v[1] = s1;
+   pt1->v[2] = s5;
+   pt1->v[3] = s4;
+   pt1->qual = list->qual[2];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+    
+   lel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[lel];
+   pt1->v[0] = ia;
+   pt1->v[1] = s2;
+   pt1->v[2] = s3;
+   pt1->v[3] = s4;
+   pt1->qual = list->qual[3];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+     
+   mel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[mel];
+   pt1->v[0] = ib;
+   pt1->v[1] = s2;
+   pt1->v[2] = s4;
+   pt1->v[3] = s3;
+   pt1->qual = list->qual[4];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+  
+   nel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[nel];
+   pt1->v[0] = ia;
+   pt1->v[1] = s1;
+   pt1->v[2] = s2;
+   pt1->v[3] = s4;
+   pt1->qual = list->qual[5];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+     
+   pel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[pel];
+   pt1->v[0] = ib;
+   pt1->v[1] = s1;
+   pt1->v[2] = s4;
+   pt1->v[3] = s2;
+   pt1->qual = list->qual[6];
+   pt1->flag = mesh->flag; 
+   pt1->ref  = ref;
+
+  /*external faces*/
+     /*tetra iel*/
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_iare[iar][1];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+  
+   iadr = (nel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[3] = adj*4 + voy;
+   mesh->tetra[nel].bdryref[3] = pt->bdryref[k];
+   
+   if (adj) {
+     iadr = (adj-1)*4 + 1;
+     adja = &mesh->adja[iadr];
+     adja[voy] = nel*4 + 3; 
+   }
+  
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_iare[iar][0];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+  
+   iadr = (pel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[2] = adj*4 + voy; 
+   mesh->tetra[pel].bdryref[2] = pt->bdryref[k];
+   
+   if (adj) {
+     iadr = (adj-1)*4 + 1;
+     adja = &mesh->adja[iadr];
+     adja[voy] = pel*4 + 2;
+   }
+  
+   for(k=2 ; k<=5 ; k++) {
+    
+     old  = list->tetra[k] / 6;
+     iadr = (old-1)*4 + 1;
+     adja = &mesh->adja[iadr];
+
+     pt0 = &mesh->tetra[old];
+		  for(i=0 ; i<6 ; i++) {
+		  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		  }
+     iarold = list->tetra[k] % 6;
+     kk    = MMG_iare[iarold][1];
+     if(pt0->v[kk]==ib) {
+       adj_a  = adja[kk] / 4;
+       voy_a  = adja[kk] % 4;
+       ref_a  = pt0->bdryref[kk];
+       kk    = MMG_iare[iarold][0];
+       adj_b  = adja[kk] / 4;
+       voy_b  = adja[kk] % 4;
+       ref_b  = pt0->bdryref[kk]; 
+     } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+       adj_b  = adja[kk] / 4;
+       voy_b  = adja[kk] % 4;
+       ref_b  = pt0->bdryref[kk];  
+       kk    = MMG_iare[iarold][0];
+       adj_a  = adja[kk] / 4;
+       voy_a  = adja[kk] % 4;
+       ref_a  = pt0->bdryref[kk]; 
+     } 
+   
+     iadr = (iel-1)*4 + 1;
+     adja = &mesh->adja[iadr];
+     if(old==(adja[MMG_isar[iar][0]]/4)) {
+       iadr = (lel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[3] = adj_a*4 + voy_a;
+       mesh->tetra[lel].bdryref[3] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = lel*4 + 3; 
+       }
+      
+       iadr = (mel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[2] = adj_b*4 + voy_b; 
+       mesh->tetra[mel].bdryref[2] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = mel*4 + 2;
+       }
+      
+     } else if(old==(adja[MMG_isar[iar][1]]/4)){
+       iadr = (jel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[2] = adj_a*4 + voy_a; 
+       mesh->tetra[jel].bdryref[2] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = jel*4 + 2;
+       }
+      
+       iadr = (kel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[3] = adj_b*4 + voy_b;
+       mesh->tetra[kel].bdryref[3] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = kel*4 + 3; 
+       }
+     } else {
+
+     /*old n'est pas un voisin de iel*/
+       iadr = (iel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       j    = MMG_isar[iar][0];
+       adj  = adja[j] / 4;
+       voy  = adja[j] % 4;
+       pt1  = &mesh->tetra[adj];
+    
+      if(pt1->v[MMG_idir[voy][0]]==s2) {
+          j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+          j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+          j = MMG_idir[voy][2];
+      }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       if(old==adja[j] / 4) {
+         
+         iadr = (lel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[lel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = lel*4 + 1;
+         }
+      
+         iadr = (mel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b; 
+         mesh->tetra[mel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = mel*4 + 1; 
+         }
+       } else {
+         iadr = (jel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[jel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = jel*4 + 1;
+         }
+      
+         iadr = (kel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b; 
+         mesh->tetra[kel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = kel*4 + 1; 
+         }
+       }
+    }
+  
+  }
+  
+   /*internal faces*/
+   iadr = (jel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = kel*4 + 0;
+   adja[3] = nel*4 + 2;
+
+   iadr = (kel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = jel*4 + 0;
+   adja[2] = pel*4 + 3;
+  
+   iadr = (lel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = mel*4 + 0;
+   adja[2] = nel*4 + 1;
+   
+   iadr = (mel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = lel*4 + 0;
+   adja[3] = pel*4 + 1;
+  
+   iadr = (nel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = pel*4 + 0;
+   adja[2] = jel*4 + 3;
+   adja[1] = lel*4 + 2;
+   
+   iadr = (pel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = nel*4 + 0;
+   adja[3] = kel*4 + 2;
+   adja[1] = mel*4 + 3;
+  
+  /*bdryinfo*/           
+	pt1 = &mesh->tetra[jel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);   
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[kel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[lel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[mel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[nel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[pel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+  /* remove 5 old tetra */
+  for (k=1; k<=5; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = pel;
+  list->tetra[7] = 0;
+  
+  M_free(hed.item);
+
+  return(6);
+}
+
+
+int MMG_swap56_5(pMesh mesh,pSol sol,pList list) {
+   pTetra pt,pt1,pt0;
+	 Hedge			hed;
+   int i,ia,ib,s1,s2,s3,s4,s5;
+   int jel,kel,lel,mel,nel,pel;
+   int iadr,iarold,*adja,k,adj,old,kk,adj_a,adj_b,j,iel,iar,ref,ref_a,ref_b;
+   short voy,voy_a,voy_b;
+  if ( !MMG_zaldy4(&hed,17) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP442 IGNORED\n"); 
+  }   
+
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref;
+
+   ia  = pt->v[ MMG_iare[iar][0] ];
+   ib  = pt->v[ MMG_iare[iar][1] ];
+   s1  = pt->v[ MMG_isar[iar][0] ];
+   s2  = pt->v[ MMG_isar[iar][1] ]; 
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_isar[iar][0];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+   pt1  = &mesh->tetra[adj];
+   s3   = pt1->v[voy];
+
+   iadr = (adj-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   if(pt1->v[MMG_idir[voy][0]]==s2) {
+      k = MMG_idir[voy][0];
+   } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+      k = MMG_idir[voy][1];
+   } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+      k = MMG_idir[voy][2];
+   } else {
+     puts("point non existant");
+     exit(0);
+   }   
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+   pt1  = &mesh->tetra[adj];
+   s4   = pt1->v[voy];
+  
+   iadr = (iel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   k    = MMG_isar[iar][1];
+   adj  = adja[k] / 4;
+   voy  = adja[k] % 4;
+   pt1  = &mesh->tetra[adj];
+   s5   = pt1->v[voy];
+
+   /* create 6 new tetra */
+   jel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[jel];
+   pt1->v[0] = ia;
+   pt1->v[1] = s1;
+   pt1->v[2] = s2;
+   pt1->v[3] = s5;
+   pt1->qual = list->qual[1];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+   
+   kel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[kel];
+   pt1->v[0] = ib;
+   pt1->v[1] = s1;
+   pt1->v[2] = s5;
+   pt1->v[3] = s2;
+   pt1->qual = list->qual[2];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+   
+   lel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[lel];
+   pt1->v[0] = ia;
+   pt1->v[1] = s2;
+   pt1->v[2] = s3;
+   pt1->v[3] = s5;
+   pt1->qual = list->qual[3];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+   
+   mel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[mel];
+   pt1->v[0] = ib;
+   pt1->v[1] = s2;
+   pt1->v[2] = s5;
+   pt1->v[3] = s3;
+   pt1->qual = list->qual[4];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+
+   nel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[nel];
+   pt1->v[0] = ia;
+   pt1->v[1] = s3;
+   pt1->v[2] = s4;
+   pt1->v[3] = s5;
+   pt1->qual = list->qual[5];
+   pt1->flag = mesh->flag;
+   pt1->ref  = ref;
+   
+   pel = MMG_newElt(mesh);
+   pt1 = &mesh->tetra[pel];
+   pt1->v[0] = ib;
+   pt1->v[1] = s3;
+   pt1->v[2] = s5;
+   pt1->v[3] = s4;
+   pt1->qual = list->qual[6];
+   pt1->flag = mesh->flag; 
+   pt1->ref  = ref;
+   
+   /*external faces*/
+     /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2; 
+  }
+  
+  for(k=2 ; k<=5 ; k++) {
+    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4; 
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[3] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 3;
+      }
+      
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[2] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 2; 
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 2;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s2) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      if(old==adja[j] / 4) {
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[3] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[3] = ref_a;
+          
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 3;
+         }
+      
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[2] = adj_b*4 + voy_b;
+         mesh->tetra[pel].bdryref[2] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = pel*4 + 2;
+         }
+      } else {
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 1;
+         }
+      
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[pel].bdryref[1] = ref_b;
+          
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = pel*4 + 1;
+         }
+      }
+    }
+  
+  }
+  
+   /*internal faces*/
+   iadr = (jel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = kel*4 + 0;
+   adja[1] = lel*4 + 2;
+   
+   iadr = (kel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = jel*4 + 0;
+   adja[1] = mel*4 + 3;
+  
+   iadr = (lel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = mel*4 + 0;
+   adja[1] = nel*4 + 2;
+   adja[2] = jel*4 + 1;
+   
+   iadr = (mel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = lel*4 + 0;
+   adja[1] = pel*4 + 3;
+   adja[3] = kel*4 + 1;
+   
+   iadr = (nel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = pel*4 + 0;
+   adja[2] = lel*4 + 1;
+   
+   iadr = (pel-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[0] = nel*4 + 0;
+   adja[3] = mel*4 + 1;
+  
+  /*bdryinfo*/           
+	pt1 = &mesh->tetra[jel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);   
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[kel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[lel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[mel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[nel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+	pt1 = &mesh->tetra[pel];
+	for(i=0 ; i<6 ; i++) { 
+		pt1->bdryinfo[i] = MMG_edgePut(&hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+		if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	}
+  /* remove 5 old tetra */
+  for (k=1; k<=5; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = pel;
+  list->tetra[7] = 0;
+ 	M_free(hed.item);
+
+  return(6);
+}
diff --git a/contrib/mmg3d/build/sources/swap68.c b/contrib/mmg3d/build/sources/swap68.c
new file mode 100644
index 0000000000000000000000000000000000000000..ec7c45dc7e936b3796d84f4a85f458aa41c3b2b4
--- /dev/null
+++ b/contrib/mmg3d/build/sources/swap68.c
@@ -0,0 +1,5674 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/*find points of polygone*/
+static int MMG_findpolygone(pMesh mesh,int iel,int iar,int *s) {
+  pTetra pt,pt1;
+  int ia,ib;
+  int iadr,*adja,k,adj;
+  int s1,s2,s3,s4,s5,s6;
+  short voy;
+
+  pt  = &mesh->tetra[iel];
+
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  s1  = pt->v[ MMG_isar[iar][0] ];
+  s2  = pt->v[ MMG_isar[iar][1] ]; 
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  
+  s3  = pt1->v[voy];
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s2) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s2) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s2) {
+     k = MMG_idir[voy][2];
+  } else {
+    puts("MMG_simu56_ani: point s2 non existant");
+    exit(0);
+  }  
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s4   = pt1->v[voy];
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_idir[voy][0]]==s3) {
+     k = MMG_idir[voy][0];
+  } else if(pt1->v[MMG_idir[voy][1]]==s3) {
+     k = MMG_idir[voy][1];
+  } else if(pt1->v[MMG_idir[voy][2]]==s3) {
+     k = MMG_idir[voy][2];
+  } else {
+    puts("MMG_simu56_ani: point s4 non existant");
+    exit(0);
+  }  
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s5   = pt1->v[voy]; 
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  s6   = pt1->v[voy];
+  
+  /*printf("polygone : %d %d %d %d %d %d\n",s1,s2,s3,s4,s5,s6);*/
+  s[0]=s1;
+  s[1]=s2;
+  s[2]=s3;
+  s[3]=s4;
+  s[4]=s5;
+  s[5]=s6;
+  return(1);
+}
+
+static int MMG_updatebdryinfo(pMesh mesh,pHedge hed,pList list) { 
+	pTetra  pt1;
+	int     iel,i,k;
+	
+	for(k=1 ; k<=8 ; k++) { 
+		iel = list->tetra[k];
+	  pt1 = &mesh->tetra[iel];
+	  for(i=0 ; i<6 ; i++) { 
+	  	pt1->bdryinfo[i] = MMG_edgePut(hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+	  	if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	  }
+	}
+	
+	return(1);
+}
+
+int MMG_swap68_1(pMesh mesh,pSol sol,pList list) { 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short   voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP661 IGNORED\n"); 
+  }   
+
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  //printf("nx : %d %d %d %d %d %d %d %d\n",jel,kel,lel,mel,nel,oel,pel,qel);
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3; 
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4; 
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[1] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 1;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[1] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 1;
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      iadr = (pel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[pel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = pel*4 + 2;
+      }
+      
+      iadr = (qel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[qel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = qel*4 + 3;
+      }
+    } else {
+       /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      if(old == adja[j] / 4) {        
+         iadr = (lel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[lel].bdryref[1] = ref_a;
+          
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = lel*4 + 1; 
+         }
+      
+         iadr = (mel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[mel].bdryref[1] = ref_b;         
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = mel*4 + 1;
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       if(old == adja[j] / 4) {   
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 1;  
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 1; 
+         }
+      }
+      else {
+       iadr = (nel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_a*4 + voy_a;
+       mesh->tetra[nel].bdryref[1] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = nel*4 + 1;
+       }
+      
+       iadr = (oel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_b*4 + voy_b;
+       mesh->tetra[oel].bdryref[1] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = oel*4 + 1; 
+       }    
+     }
+    }
+   }
+ }
+ 
+  /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = lel*4 + 2;
+  adja[2] = pel*4 + 3;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = mel*4 + 3;
+  adja[3] = qel*4 + 2; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[3] = nel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[2] = oel*4 + 3;
+
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_2(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP662 IGNORED\n"); 
+  }   
+
+
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;   
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[1] = ref_a;
+        
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 1; 
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[1] = ref_b;
+       
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 1; 
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (nel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[nel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = nel*4 + 2;
+      }
+      
+      iadr = (oel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[oel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = oel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (lel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[lel].bdryref[1] = ref_a;
+          
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = lel*4 + 1; 
+         }
+      
+         iadr = (mel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[mel].bdryref[1] = ref_b;
+          
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = mel*4 + 1; 
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 1; 
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 1;
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (pel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[3] = adj_a*4 + voy_a;
+       mesh->tetra[pel].bdryref[3] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = pel*4 + 3;
+       }
+      
+       iadr = (qel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[2] = adj_b*4 + voy_b;
+       mesh->tetra[qel].bdryref[2] = ref_b;
+        
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = qel*4 + 2;
+       }     
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = lel*4 + 2;
+  adja[1] = pel*4 + 2;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = mel*4 + 3;
+  adja[1] = qel*4 + 3; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 1;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_3(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;    
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+    
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3; 
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2; 
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[1] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 1;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[1] = ref_b;
+
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 1;
+      }
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 2; 
+      }
+      
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[3] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[3] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 3;
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[2] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[2] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 2;
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 1; 
+         }
+      
+         iadr = (oel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[oel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = oel*4 + 1;
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (pel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_a*4 + voy_a;
+       mesh->tetra[pel].bdryref[1] = ref_a;
+        
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = pel*4 + 1;
+       }
+      
+       iadr = (qel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_b*4 + voy_b;
+       mesh->tetra[qel].bdryref[1] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = qel*4 + 1;
+       }     
+     }
+    }
+   }
+  }  
+
+  /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[1] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[1] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 1;
+  adja[3] = pel*4 + 2;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 1;
+  adja[2] = qel*4 + 3; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 3;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 2;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_4(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP684 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3; 
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;  
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[1] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 1;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[1] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 1;
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[2] = ref_a;
+       
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 2;
+      }
+      
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[3] = ref_b;
+       
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[3] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[3] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 3;
+         }
+      
+         iadr = (oel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[2] = adj_b*4 + voy_b;
+         mesh->tetra[oel].bdryref[2] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = oel*4 + 2; 
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[1] = ref_a;
+          
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 1;
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 1;
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (pel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[3] = adj_a*4 + voy_a;
+       mesh->tetra[pel].bdryref[3] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = pel*4 + 3;
+       }
+      
+       iadr = (qel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[2] = adj_b*4 + voy_b;
+       mesh->tetra[qel].bdryref[2] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = qel*4 + 2;
+       }     
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[1] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[1] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 1;
+  adja[1] = pel*4 + 2;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 1;
+  adja[1] = qel*4 + 3; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 1;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_5(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP685 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[3] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 3;
+      }  
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[2] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 2;
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (nel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[nel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = nel*4 + 2;
+      }
+      
+      iadr = (oel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[oel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = oel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (jel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[jel].bdryref[1] = ref_a;
+         
+         if (adj_a){
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = jel*4 + 1;
+         }
+         
+         iadr = (kel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[kel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = kel*4 + 1; 
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 1;
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[1] = ref_b;
+          
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 1;
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (pel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[3] = adj_a*4 + voy_a;
+       mesh->tetra[pel].bdryref[3] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = pel*4 + 3;
+       }
+      
+       iadr = (qel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[2] = adj_b*4 + voy_b;
+       mesh->tetra[qel].bdryref[2] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = qel*4 + 2;
+       }     
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = kel*4 + 3;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = lel*4 + 2;
+  adja[1] = pel*4 + 2;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = mel*4 + 3;
+  adja[1] = qel*4 + 3; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 1;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_6(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP686 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3; 
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;  
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[3] = ref_a;
+     
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 3;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[2] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 2;
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (pel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[pel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = pel*4 + 2;
+      }
+      
+      iadr = (qel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[qel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = qel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (jel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[jel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = jel*4 + 1;
+         }
+      
+         iadr = (kel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[kel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = kel*4 + 1;
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 1;
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[1] = ref_b;
+          
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 1; 
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (nel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_a*4 + voy_a;
+       mesh->tetra[nel].bdryref[1] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = nel*4 + 1; 
+       }
+      
+       iadr = (oel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_b*4 + voy_b;
+       mesh->tetra[oel].bdryref[1] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = oel*4 + 1;
+       }     
+     }
+    }
+   }
+  }  
+ 
+  /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = kel*4 + 3;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = lel*4 + 2;
+  adja[2] = pel*4 + 3;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = mel*4 + 3;
+  adja[3] = qel*4 + 2; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[3] = nel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[2] = oel*4 + 3;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_7(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP687 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;  
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (nel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[nel].bdryref[3] = ref_a; 
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = nel*4 + 3;
+      }
+      
+      iadr = (oel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[oel].bdryref[2] = ref_b; 
+       
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = oel*4 + 2; 
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[2] = ref_a; 
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 2;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[3] = ref_b; 
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[3] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[3] = ref_a; 
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 3;
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[2] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[2] = ref_b; 
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 2; 
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (jel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[jel].bdryref[1] = ref_a; 
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = jel*4 + 1; 
+         }
+      
+         iadr = (kel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[kel].bdryref[1] = ref_b; 
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = kel*4 + 1;
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (pel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_a*4 + voy_a;
+       mesh->tetra[pel].bdryref[1] = ref_a; 
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = pel*4 + 1; 
+       }
+      
+       iadr = (qel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_b*4 + voy_b;
+       mesh->tetra[qel].bdryref[1] = ref_b; 
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = qel*4 + 1;
+       }     
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[3] = lel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[2] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 3;
+  adja[1] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 2;
+  adja[1] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 1;
+  adja[1] = pel*4 + 2;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 1;
+  adja[1] = qel*4 + 3; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 1;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_8(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP688 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;  
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[3] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 3;    
+      }
+      
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[2] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 2;  
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 2;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[3] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[3] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 3;
+         }
+      
+         iadr = (oel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[2] = adj_b*4 + voy_b;
+         mesh->tetra[oel].bdryref[2] = ref_b;
+          
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = oel*4 + 2;
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 1; 
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 1;
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (pel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[3] = adj_a*4 + voy_a;
+       mesh->tetra[pel].bdryref[3] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = pel*4 + 3;
+       }
+      
+       iadr = (qel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[2] = adj_b*4 + voy_b;
+       mesh->tetra[qel].bdryref[2] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = qel*4 + 2;
+       }     
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = lel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 1;
+  adja[1] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 1;
+  adja[1] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 1;
+  adja[1] = pel*4 + 2;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 1;
+  adja[1] = qel*4 + 3; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 1;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_9(pMesh mesh,pSol sol,pList list){
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP689 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2; 
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_a*4 + voy_a; 
+      mesh->tetra[jel].bdryref[1] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 1;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[1] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[1] = ref_b;
+       
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 1;
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (nel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[nel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = nel*4 + 2; 
+      }
+      
+      iadr = (oel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[oel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = oel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (lel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[3] = adj_a*4 + voy_a;
+         mesh->tetra[lel].bdryref[3] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = lel*4 + 3;
+         }
+      
+         iadr = (mel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[2] = adj_b*4 + voy_b;
+         mesh->tetra[mel].bdryref[2] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = mel*4 + 2;
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 1;
+         }
+      
+         iadr = (oel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[oel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = oel*4 + 1; 
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (lel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_a*4 + voy_a;
+       mesh->tetra[lel].bdryref[1] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = lel*4 + 1;
+       }
+      
+       iadr = (mel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_b*4 + voy_b;
+       mesh->tetra[mel].bdryref[1] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = mel*4 + 1; 
+       }    
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = qel*4 + 1;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = pel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = qel*4 + 3;
+
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = nel*4 + 3;
+  adja[3] = jel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[3] = oel*4 + 2;
+  adja[2] = kel*4 + 3;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_10(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP6810 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3; 
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;  
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4; 
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[3] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 3;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[2] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 2;
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (pel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[pel].bdryref[2] = ref_a;
+        
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = pel*4 + 2;
+      }
+      
+      iadr = (qel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[qel].bdryref[3] = ref_b;
+       
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = qel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (jel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[jel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = jel*4 + 1;
+         }
+      
+         iadr = (kel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[kel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = kel*4 + 1;
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 1;
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 1; 
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (lel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_a*4 + voy_a;
+       mesh->tetra[lel].bdryref[1] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = lel*4 + 1;
+       }
+      
+       iadr = (mel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_b*4 + voy_b;
+       mesh->tetra[mel].bdryref[1] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = mel*4 + 1; 
+       }    
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[2] = nel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[3] = oel*4 + 1;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = pel*4 + 3;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[3] = qel*4 + 2; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[3] = nel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[2] = oel*4 + 3;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_11(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP6811 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3; 
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a; 
+      mesh->tetra[jel].bdryref[3] = ref_a; 
+       
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 3;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[2] = ref_b; 
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 2; 
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (pel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[pel].bdryref[2] = ref_a; 
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = pel*4 + 2;
+      }
+      
+      iadr = (qel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[qel].bdryref[3] = ref_b; 
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = qel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (jel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[jel].bdryref[1] = ref_a; 
+        
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = jel*4 + 1;
+         }
+      
+         iadr = (kel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[kel].bdryref[1] = ref_b; 
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = kel*4 + 1; 
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[1] = ref_a; 
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 1;
+         }
+      
+         iadr = (oel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[oel].bdryref[1] = ref_b; 
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = oel*4 + 1;
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (lel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_a*4 + voy_a;
+       mesh->tetra[lel].bdryref[1] = ref_a; 
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = lel*4 + 1;
+       }
+      
+       iadr = (mel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_b*4 + voy_b;
+       mesh->tetra[mel].bdryref[1] = ref_b; 
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = mel*4 + 1;
+       }     
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = lel*4 + 2;
+  adja[2] = pel*4 + 1;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = mel*4 + 3;
+  adja[3] = qel*4 + 1; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = nel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = oel*4 + 3;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_12(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP6812 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3; 
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;  
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (nel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[nel].bdryref[3] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = nel*4 + 3;
+      }
+      
+      iadr = (oel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[oel].bdryref[2] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = oel*4 + 2;  
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[2] = ref_a;
+       
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 2; 
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 3;
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[3] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[3] = ref_a;
+          
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 3;
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[2] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[2] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 2; 
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (lel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[lel].bdryref[1] = ref_a;
+          
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = lel*4 + 1;
+         }
+      
+         iadr = (mel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[mel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = mel*4 + 1;
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (pel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_a*4 + voy_a;
+       mesh->tetra[pel].bdryref[1] = ref_a;
+       
+       if (adj_a){
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = pel*4 + 1;   
+       }
+      
+       iadr = (qel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_b*4 + voy_b;
+       mesh->tetra[qel].bdryref[1] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = qel*4 + 1;
+       }     
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = lel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 1;
+  adja[3] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 1;
+  adja[2] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 3;
+  adja[1] = pel*4 + 2;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 2;
+  adja[1] = qel*4 + 3; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 1;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_13(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP6813 IGNORED\n"); 
+  }   
+
+
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4; 
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[3] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 3;
+      }
+      
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[2] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 2;
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 2;
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 3; 
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (pel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[3] = adj_a*4 + voy_a;
+         mesh->tetra[pel].bdryref[3] = ref_a;
+          
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = pel*4 + 3;
+         }
+      
+         iadr = (qel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[2] = adj_b*4 + voy_b;
+         mesh->tetra[qel].bdryref[2] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = qel*4 + 2;  
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 1;
+         }
+      
+         iadr = (oel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[oel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = oel*4 + 1; 
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (pel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_a*4 + voy_a;
+       mesh->tetra[pel].bdryref[1] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = pel*4 + 1;
+       }
+      
+       iadr = (qel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[1] = adj_b*4 + voy_b;
+       mesh->tetra[qel].bdryref[1] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = qel*4 + 1;
+       }     
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = lel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 1;
+  adja[1] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 1;
+  adja[1] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 1;
+  adja[3] = pel*4 + 2;	
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 1;
+  adja[2] = qel*4 + 3; 
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 3;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 2;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
+
+int MMG_swap68_14(pMesh mesh,pSol sol,pList list){ 
+  pTetra pt1,pt,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[6],j;
+  int    jel,kel,lel,mel,nel,oel,pel,qel;
+  int    iarold,iadr,*adja,k,adj,adj_a,adj_b,old,kk,iel,iar,ref,ref_a,ref_b;
+  short  voy,voy_a,voy_b;
+
+  if ( !MMG_zaldy4(&hed,21) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP6814 IGNORED\n"); 
+  }   
+
+  
+  /*find points of polygone*/
+  iel  = list->tetra[1] / 6;
+  iar  = list->tetra[1] % 6;
+  if(!MMG_findpolygone(mesh,iel,iar,s)) return(0);
+  
+  pt  = &mesh->tetra[iel];  
+	for(i=0 ; i<6 ; i++) { 
+		MMG_edgePut(&hed,pt->v[MMG_iare[i][0]],pt->v[MMG_iare[i][1]],pt->bdryinfo[i]);
+	}
+  ref = pt->ref;
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+ 
+  /*create 8 tetras*/
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[3];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[4];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->flag = mesh->flag;
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->flag = mesh->flag; 
+  pt1->ref  = ref;
+  
+  /*external faces*/
+  /*tetra iel*/
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_iare[iar][0];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k];
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;  
+  }
+  
+  for(k=2 ; k<=6 ; k++) {    
+    old  = list->tetra[k] / 6;
+    iadr = (old-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+
+    pt0 = &mesh->tetra[old];
+	  for(i=0 ; i<6 ; i++) {
+	  	MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+	  }
+    iarold = list->tetra[k] % 6;
+    kk    = MMG_iare[iarold][1];
+    if(pt0->v[kk]==ib) {
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;  
+      ref_a  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4;
+      ref_b  = pt0->bdryref[kk];
+    } else /*if(pt0->v[MMG_iare[iarold][0]]==ib)*/{
+      adj_b  = adja[kk] / 4;
+      voy_b  = adja[kk] % 4; 
+      ref_b  = pt0->bdryref[kk];
+      kk    = MMG_iare[iarold][0];
+      adj_a  = adja[kk] / 4;
+      voy_a  = adja[kk] % 4;
+      ref_a  = pt0->bdryref[kk];
+    } 
+   
+    iadr = (iel-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    /*s[1] appartient a old */
+    if(old==(adja[MMG_isar[iar][0]]/4)) {
+      iadr = (lel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_a*4 + voy_a;
+      mesh->tetra[lel].bdryref[3] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = lel*4 + 3;  
+      }
+      
+      iadr = (mel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_b*4 + voy_b;
+      mesh->tetra[mel].bdryref[2] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = mel*4 + 2;
+      }
+      
+    } else if(old==(adja[MMG_isar[iar][1]]/4)){
+      /*s[0] appartient a old*/
+      iadr = (jel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[2] = adj_a*4 + voy_a;
+      mesh->tetra[jel].bdryref[2] = ref_a;
+      
+      if (adj_a) {
+        iadr = (adj_a-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_a] = jel*4 + 2; 
+      }
+      
+      iadr = (kel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      adja[3] = adj_b*4 + voy_b;
+      mesh->tetra[kel].bdryref[3] = ref_b;
+      
+      if (adj_b) {
+        iadr = (adj_b-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        adja[voy_b] = kel*4 + 3; 
+      }
+    } else {
+      /*old n'est pas un voisin de iel*/
+      iadr = (iel-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      j    = MMG_isar[iar][0];
+      adj  = adja[j] / 4;
+      voy  = adja[j] % 4;
+      pt1  = &mesh->tetra[adj];
+    
+     if(pt1->v[MMG_idir[voy][0]]==s[1]) {
+         j = MMG_idir[voy][0];
+      } else if(pt1->v[MMG_idir[voy][1]]==s[1]) {
+         j = MMG_idir[voy][1];
+      } else if(pt1->v[MMG_idir[voy][2]]==s[1]) {
+         j = MMG_idir[voy][2];
+      }
+
+      iadr = (adj-1)*4 + 1;
+      adja = &mesh->adja[iadr];
+      /*s[2] s[3] appartient a old*/
+      if(old == adja[j] / 4) {        
+         iadr = (lel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[lel].bdryref[1] = ref_a;
+         
+         if (adj_a) {
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = lel*4 + 1;
+         }
+      
+         iadr = (mel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[mel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = mel*4 + 1;
+         }
+      } else {
+        iadr = (iel-1)*4 + 1;
+        adja = &mesh->adja[iadr];
+        j    = MMG_isar[iar][1];
+        adj  = adja[j] / 4;
+        voy  = adja[j] % 4;
+        pt1  = &mesh->tetra[adj];
+    
+       if(pt1->v[MMG_idir[voy][0]]==s[0]) {
+           j = MMG_idir[voy][0];
+       } else if(pt1->v[MMG_idir[voy][1]]==s[0]) {
+           j = MMG_idir[voy][1];
+       } else if(pt1->v[MMG_idir[voy][2]]==s[0]) {
+          j = MMG_idir[voy][2];
+       }
+
+       iadr = (adj-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       /*s[4] s[5] appartient a old*/
+       if(old == adja[j] / 4) {   
+         iadr = (nel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_a*4 + voy_a;
+         mesh->tetra[nel].bdryref[1] = ref_a;
+         
+         if (adj_a) { 
+           iadr = (adj_a-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_a] = nel*4 + 1;
+         }
+      
+         iadr = (oel-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[1] = adj_b*4 + voy_b;
+         mesh->tetra[oel].bdryref[1] = ref_b;
+         
+         if (adj_b) {
+           iadr = (adj_b-1)*4 + 1;
+           adja = &mesh->adja[iadr];
+           adja[voy_b] = oel*4 + 1;   
+         }
+      }
+      else {
+      /*s[3] s[4] appartient a old*/
+       iadr = (nel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[3] = adj_a*4 + voy_a;
+       mesh->tetra[nel].bdryref[3] = ref_a;
+       
+       if (adj_a) {
+         iadr = (adj_a-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_a] = nel*4 + 3;
+       }
+      
+       iadr = (oel-1)*4 + 1;
+       adja = &mesh->adja[iadr];
+       adja[2] = adj_b*4 + voy_b;
+       mesh->tetra[oel].bdryref[2] = ref_b;
+       
+       if (adj_b) {
+         iadr = (adj_b-1)*4 + 1;
+         adja = &mesh->adja[iadr];
+         adja[voy_b] = oel*4 + 2;
+       }     
+     }
+    }
+   }
+ }  
+ 
+   /*internal faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = pel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = qel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 1;
+
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 1;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = jel*4 + 1;
+  adja[3] = lel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = oel*4 + 3;
+  adja[3] = kel*4 + 1;
+  adja[2] = mel*4 + 3;
+  /* remove 6 old tetra */
+  for (k=1; k<=6; k++)
+    MMG_delElt(mesh,list->tetra[k]/6);
+
+  list->tetra[1] = jel;
+  list->tetra[2] = kel;
+  list->tetra[3] = lel;
+  list->tetra[4] = mel;
+  list->tetra[5] = nel;
+  list->tetra[6] = oel;
+  list->tetra[7] = pel;
+  list->tetra[8] = qel;
+  list->tetra[9] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(8);
+}
diff --git a/contrib/mmg3d/build/sources/swap710.c b/contrib/mmg3d/build/sources/swap710.c
new file mode 100644
index 0000000000000000000000000000000000000000..b58b84adf2794eb3310cea6a95e017afbf29d4aa
--- /dev/null
+++ b/contrib/mmg3d/build/sources/swap710.c
@@ -0,0 +1,19044 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/*find points of polygone*/
+static int MMG_findpolygone(pMesh mesh,pList list,int *s) {
+  pTetra pt,pt1;
+  int ia,ib;
+  int j,iadr,*adja,k,adj,iar,iel;
+  short voy;
+ 
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+
+  /*find points of polygone*/
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+//  printf("MMG_swap edge %d %d\n",ia,ib);
+  s[0]  = pt->v[ MMG_isar[iar][0] ];
+  s[1]  = pt->v[ MMG_isar[iar][1] ]; 
+
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][0];
+  
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  for(j=0 ; j<6 ; j++)
+    if((ia == pt1->v[MMG_iare[j][0]] && ib ==pt1->v[MMG_iare[j][1]]) ||
+       (ia == pt1->v[MMG_iare[j][1]] && ib ==pt1->v[MMG_iare[j][0]])) break;
+  assert(j<6);
+  iar = j;
+  list->tetra[2] = 6*adj + iar;
+       
+  s[2]  = pt1->v[voy];
+
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_isar[iar][0]] == s[1]) {
+    k = MMG_isar[iar][0];
+  } else {
+    assert(pt1->v[MMG_isar[iar][1]] == s[1]);
+    k = MMG_isar[iar][1];
+  }
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  for(j=0 ; j<6 ; j++)
+    if((ia == pt1->v[MMG_iare[j][0]] && ib ==pt1->v[MMG_iare[j][1]]) ||
+       (ia == pt1->v[MMG_iare[j][1]] && ib ==pt1->v[MMG_iare[j][0]])) break;
+  assert(j<6);
+  iar = j;
+  list->tetra[3] = 6*adj + iar;
+  
+  s[3]   = pt1->v[voy];
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_isar[iar][0]] == s[2]) {
+    k = MMG_isar[iar][0];
+  } else {
+    assert(pt1->v[MMG_isar[iar][1]] == s[2]);
+    k = MMG_isar[iar][1];
+  }
+     
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  for(j=0 ; j<6 ; j++)
+    if((ia == pt1->v[MMG_iare[j][0]] && ib ==pt1->v[MMG_iare[j][1]]) ||
+       (ia == pt1->v[MMG_iare[j][1]] && ib ==pt1->v[MMG_iare[j][0]])) break;
+  assert(j<6);
+  iar = j;
+  list->tetra[4] = 6*adj + iar;
+  
+  s[4]   = pt1->v[voy];
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_isar[iar][0]] == s[3]) {
+    k = MMG_isar[iar][0];
+  } else {
+    assert(pt1->v[MMG_isar[iar][1]] == s[3]);
+    k = MMG_isar[iar][1];
+  }
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  for(j=0 ; j<6 ; j++)
+    if((ia == pt1->v[MMG_iare[j][0]] && ib ==pt1->v[MMG_iare[j][1]]) ||
+       (ia == pt1->v[MMG_iare[j][1]] && ib ==pt1->v[MMG_iare[j][0]])) break;
+  assert(j<6);
+  iar = j;
+  list->tetra[5] = 6*adj + iar;
+  
+  s[5]   = pt1->v[voy]; 
+  
+  iadr = (adj-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  if(pt1->v[MMG_isar[iar][0]] == s[4]) {
+    k = MMG_isar[iar][0];
+  } else {
+    assert(pt1->v[MMG_isar[iar][1]] == s[4]);
+    k = MMG_isar[iar][1];
+  }
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  for(j=0 ; j<6 ; j++)
+    if((ia == pt1->v[MMG_iare[j][0]] && ib ==pt1->v[MMG_iare[j][1]]) ||
+       (ia == pt1->v[MMG_iare[j][1]] && ib ==pt1->v[MMG_iare[j][0]])) break;
+  assert(j<6);
+  iar = j;
+  list->tetra[6] = 6*adj + j;
+    
+  iar  = list->tetra[1] % 6;  
+  iadr = (iel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  k    = MMG_isar[iar][1];
+  adj  = adja[k] / 4;
+  voy  = adja[k] % 4;
+  pt1  = &mesh->tetra[adj];
+  for(j=0 ; j<6 ; j++)
+    if((ia == pt1->v[MMG_iare[j][0]] && ib ==pt1->v[MMG_iare[j][1]]) ||
+       (ia == pt1->v[MMG_iare[j][1]] && ib ==pt1->v[MMG_iare[j][0]])) break;
+  assert(j<6);
+  list->tetra[7] = 6*adj + j;
+  
+  s[6]   = pt1->v[voy];
+  
+  return(1);
+}
+
+static int MMG_updatebdryinfo(pMesh mesh,pHedge hed,pList list) { 
+	pTetra  pt1;
+	int     iel,i,k;
+	
+	for(k=1 ; k<=10 ; k++) { 
+		iel = list->tetra[k];
+	  pt1 = &mesh->tetra[iel];
+	  for(i=0 ; i<6 ; i++) { 
+	  	pt1->bdryinfo[i] = MMG_edgePut(hed,pt1->v[MMG_iare[i][0]],pt1->v[MMG_iare[i][1]],1);
+	  	if(pt1->bdryinfo[i]<2) pt1->bdryinfo[i]=0;
+	  }
+	}
+	
+	return(1);
+}
+
+int MMG_swap710_1(pMesh mesh,pSol sol,pList list){
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+  
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+	ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+     
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy; 
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;  
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy; 
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;  
+  mesh->tetra[rel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;  
+  mesh->tetra[sel].bdryref[1] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;  
+  mesh->tetra[rel].bdryref[2] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[3] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = lel*4 + 2;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = mel*4 + 3;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[3] = nel*4 + 2;
+  adja[2] = rel*4 + 3;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[2] = oel*4 + 3;
+  adja[3] = sel*4 + 2;
+ 
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[3] = pel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[2] = qel*4 + 3; 
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+
+}
+int MMG_swap710_2(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+	ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+ 
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy; 
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy; 
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy; 
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy; 
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;  
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy; 
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;   
+  mesh->tetra[sel].bdryref[3] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;  
+  mesh->tetra[rel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[1] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;   
+  mesh->tetra[pel].bdryref[2] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy; 
+  mesh->tetra[qel].bdryref[3] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = lel*4 + 2;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = mel*4 + 3;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[3] = nel*4 + 2;
+  adja[1] = rel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[2] = oel*4 + 3;
+  adja[1] = sel*4 + 3;
+ 
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[2] = pel*4 + 1;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[3] = qel*4 + 1; 
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+
+}
+int MMG_swap710_3(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+ 
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 1;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = oel*4 + 3;
+  adja[3] = sel*4 + 1;
+ 
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[3] = lel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[2] = mel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+  
+return(1);
+}
+int MMG_swap710_4(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+ 
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[3] = lel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = qel*4 + 3;
+  adja[2] = mel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 1;
+  adja[3] = rel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 1;
+  adja[2] = sel*4 + 3;
+ 
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[2] = pel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[3] = qel*4 + 2; 
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+ 
+return(1);
+}
+int MMG_swap710_5(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+ 
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+   iadr = (adj-1)*4 + 1;
+   adja = &mesh->adja[iadr];
+   adja[voy] = oel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+ 
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = nel*4 + 3;
+  adja[3] = lel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = oel*4 + 2;
+  adja[2] = mel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+return(1);
+}
+int MMG_swap710_6(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+ 
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  } 
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = lel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = nel*4 + 3;
+  adja[3] = jel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = oel*4 + 2;
+  adja[2] = kel*4 + 3;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+ 
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = kel*4 + 2;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+return(1);
+}
+int MMG_swap710_7(pMesh mesh,pSol sol,pList list){  
+ pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  adja[1] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+  adja[1] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 1;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 1;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+ 
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = kel*4 + 2;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+  
+return(1);
+}
+int MMG_swap710_8(pMesh mesh,pSol sol,pList list){  
+ pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = nel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = kel*4 + 3;
+  adja[3] = oel*4 + 1;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+ 
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = nel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = oel*4 + 2;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+return(1);
+}
+int MMG_swap710_9(pMesh mesh,pSol sol,pList list){
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+  
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+  
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+ 
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+  
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 1;
+  adja[3] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 1;
+  adja[2] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[2] = jel*4 + 3;
+  adja[1] = pel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[3] = kel*4 + 2; 
+  adja[1] = qel*4 + 3;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+/*printf("new tets %d %d %d %d %d %d %d %d %d %d\n",jel,kel,lel,mel,nel,oel,pel,qel,rel,sel);
+  */list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+        
+  return(1);
+  
+}
+int MMG_swap710_10(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1; 
+  }  
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[3] = nel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[2] = oel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = nel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = oel*4 + 1;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = jel*4 + 3;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[3] = kel*4 + 2;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+ 
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = nel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = oel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+
+}
+int MMG_swap710_11(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3; 
+  }
+  
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2; 
+  }
+  
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  } 
+
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1; 
+  }
+  
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+    
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 1;
+  adja[3] = pel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 1;
+  adja[2] = qel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 3;
+  adja[3] = rel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 2;
+  adja[2] = sel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[2] = pel*4 + 3;
+  adja[3] = jel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[3] = qel*4 + 2; 
+  adja[2] = kel*4 + 3;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+
+}
+int MMG_swap710_12(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = lel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 1;
+  adja[3] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 1;
+  adja[2] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 3;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 2;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = nel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = oel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+
+}
+int MMG_swap710_13(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = lel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 1;
+  adja[3] = pel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 1;
+  adja[2] = qel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = rel*4 + 2;
+  adja[2] = lel*4 + 3;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = sel*4 + 3;
+  adja[3] = mel*4 + 2;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[2] = pel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[3] = qel*4 + 1; 
+  adja[2] = oel*4 + 3;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+
+}
+int MMG_swap710_14(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = oel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 1;
+  adja[3] = pel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 1;
+  adja[2] = qel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = rel*4 + 2;
+  adja[2] = nel*4 + 3;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = sel*4 + 3;
+  adja[3] = oel*4 + 2;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = pel*4 + 1;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = kel*4 + 3; 
+  adja[3] = qel*4 + 1;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+}
+int MMG_swap710_15(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = pel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = qel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 1;
+  adja[3] = rel*4 + 2;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 1;
+  adja[2] = sel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = pel*4 + 3;
+  adja[3] = lel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = kel*4 + 3; 
+  adja[3] = qel*4 + 2;  
+  adja[2] = mel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+}
+int MMG_swap710_16(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+                                            
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 3;
+  adja[3] = lel*4 + 2;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 2;
+  adja[2] = mel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = rel*4 + 1;
+  adja[3] = pel*4 + 2;
+ 
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = sel*4 + 1;
+  adja[2] = qel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = oel*4 + 3; 
+  adja[3] = kel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+}
+int MMG_swap710_17(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = rel*4 + 1;
+  adja[3] = pel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = sel*4 + 1;
+  adja[2] = qel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 1;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = lel*4 + 3;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = oel*4 + 3;
+  adja[3] = mel*4 + 2;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = mel*4 + 3; 
+  adja[3] = kel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1); 
+}
+int MMG_swap710_18(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = qel*4 + 2;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = rel*4 + 1;
+  adja[1] = pel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = sel*4 + 1;
+  adja[1] = qel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = nel*4 + 1;
+  adja[3] = lel*4 + 2;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = oel*4 + 1;
+  adja[2] = mel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = oel*4 + 3; 
+  adja[3] = kel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1); 
+}
+int MMG_swap710_19(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = qel*4 + 1;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = rel*4 + 1;
+  adja[1] = pel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = sel*4 + 1;
+  adja[1] = qel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = nel*4 + 1;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[3] = oel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = oel*4 + 3; 
+  adja[3] = kel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1); 
+}
+
+int MMG_swap710_20(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = qel*4 + 1;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = rel*4 + 1;
+  adja[3] = nel*4 + 2;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[3] = sel*4 + 1;
+  adja[2] = oel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = kel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1); 
+}
+int MMG_swap710_21(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt = &mesh->tetra[iel];
+  assert(  pt->v[k] == ib);
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 3;
+  adja[3] = lel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 2;
+  adja[2] = mel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+  adja[2] = oel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[3] = jel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[2] = kel*4 + 3;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_22(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = nel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = oel*4 + 1;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = rel*4 + 2;
+  adja[3] = jel*4 + 2;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = sel*4 + 3;
+  adja[2] = kel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[2] = pel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[3] = qel*4 + 1; 
+  adja[2] = oel*4 + 3;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1); 
+}
+int MMG_swap710_23(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = rel*4 + 1;
+  adja[3] = nel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = sel*4 + 1;
+  adja[2] = oel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = rel*4 + 2;
+  adja[3] = jel*4 + 2;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = sel*4 + 3;
+  adja[2] = kel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = pel*4 + 1;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = mel*4 + 3; 
+  adja[3] = qel*4 + 1;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_24(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = nel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = oel*4 + 1;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = rel*4 + 1;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[3] = sel*4 + 1;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = rel*4 + 2;
+  adja[3] = jel*4 + 2;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = sel*4 + 3;
+  adja[2] = kel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = pel*4 + 1;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = oel*4 + 3; 
+  adja[3] = qel*4 + 1;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_25(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = rel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = sel*4 + 1;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = rel*4 + 2;
+  adja[3] = jel*4 + 2;
+ 
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = sel*4 + 3;
+  adja[2] = kel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = pel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = mel*4 + 3; 
+  adja[3] = qel*4 + 1;  
+  adja[2] = oel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_26(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt = &mesh->tetra[iel];
+  assert(  pt->v[k] == ib);
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = nel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = oel*4 + 2;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 3;
+  adja[3] = lel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 2;
+  adja[2] = mel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+  adja[2] = oel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = jel*4 + 1;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = kel*4 + 1;  
+  
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+}
+int MMG_swap710_27(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+     
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+  
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = nel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = oel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = pel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = qel*4 + 3;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = kel*4 + 3;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = rel*4 + 2;
+  adja[2] = lel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = sel*4 + 3;
+  adja[3] = mel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[2] = pel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[3] = qel*4 + 1; 
+  adja[2] = oel*4 + 3;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+ 
+  return(1);
+}
+int MMG_swap710_28(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = pel*4 + 1;
+  adja[3] = lel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = qel*4 + 1;
+  adja[2] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 2;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = rel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = sel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = kel*4 + 3;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = nel*4 + 1;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = oel*4 + 1;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_29(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = kel*4 + 3;
+  adja[3] = qel*4 + 1;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = rel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = sel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = nel*4 + 1;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = oel*4 + 1;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_30(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = qel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = rel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = sel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = rel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = kel*4 + 3;
+  adja[3] = sel*4 + 1;
+  adja[2] = oel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = lel*4 + 1;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = mel*4 + 1;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_31(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[2] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[3] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 1;
+  adja[3] = lel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 1;
+  adja[2] = mel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 2;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = rel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = sel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = pel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = kel*4 + 3; 
+  adja[3] = qel*4 + 1;  
+  adja[2] = oel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_32(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[3];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[2] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[3] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = rel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = kel*4 + 3;
+  adja[3] = sel*4 + 1;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = rel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = sel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[2] = pel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = mel*4 + 3; 
+  adja[3] = qel*4 + 1;  
+  adja[2] = oel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_33(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = nel*4 + 3;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = oel*4 + 2;
+  adja[2] = sel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[3] = lel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[2] = mel*4 + 3;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = lel*4 + 3;
+  adja[3] = jel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = mel*4 + 2;  
+  adja[2] = kel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_34(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 1;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = oel*4 + 3;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = kel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_35(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+  int    ia,ib,s[7];
+	Hedge			hed;
+  int    i,iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = lel*4 + 3;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = mel*4 + 2;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = jel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = kel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 2;
+ 
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[3] = nel*4 + 2;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[2] = oel*4 + 3;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = kel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);
+}
+int MMG_swap710_36(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = oel*4 + 3;
+  adja[2] = sel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = lel*4 + 1;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = mel*4 + 1;
+ 
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = lel*4 + 3;
+  adja[3] = jel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = mel*4 + 2;  
+  adja[2] = kel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+return(1);
+}
+int MMG_swap710_37(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if (adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 1;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 1;
+ 
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = oel*4 + 3;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = kel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+return(1);
+}
+int MMG_swap710_38(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[1];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[rel].bdryref[3] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = rel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[sel].bdryref[2] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = sel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[2] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[3] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[1] = lel*4 + 2;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[1] = mel*4 + 3;
+  adja[2] = sel*4 + 3;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = jel*4 + 1;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = kel*4 + 1;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 2;
+ 
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 1;
+  adja[3] = nel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 1;
+  adja[2] = oel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = jel*4 + 3;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = kel*4 + 2;  
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+return(1);  
+}
+int MMG_swap710_39(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[1] = rel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[1] = sel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 1;
+ 
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = rel*4 + 3;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = oel*4 + 3;
+  adja[3] = sel*4 + 2;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = lel*4 + 1;
+  adja[3] = pel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = kel*4 + 3; 
+  adja[3] = mel*4 + 1;  
+  adja[2] = qel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+return(1);   
+}
+int MMG_swap710_40(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[4];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[3]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[2];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[6];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[1];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[4];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 2; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[2] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[3] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 1;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 1;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[3] = qel*4 + 2;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[1] = rel*4 + 2;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[1] = sel*4 + 3;
+ 
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[2] = rel*4 + 3;
+  adja[3] = lel*4 + 2;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[3] = sel*4 + 2;
+  adja[2] = mel*4 + 3;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = jel*4 + 2;
+  adja[2] = nel*4 + 1;
+  adja[3] = pel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = kel*4 + 3; 
+  adja[3] = oel*4 + 1;  
+  adja[2] = qel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+//MMG_chkmsh(mesh,1,0);
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+  return(1); 
+}
+int MMG_swap710_41(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[3];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = sel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 1;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 1;
+ 
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[1] = nel*4 + 2;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[1] = oel*4 + 3;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = lel*4 + 3;
+  adja[3] = jel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = mel*4 + 2;  
+  adja[2] = kel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+  return(1);  
+}
+int MMG_swap710_42(pMesh mesh,pSol sol,pList list){  
+  pTetra pt,pt1,pt0;
+	Hedge			hed;
+  int    i,ia,ib,s[7];
+  int    iadr,iadr2,*adja,*adja2,k,k1,adj,iel,iar;
+  int    jel,kel,lel,mel,nel,oel,pel,qel,rel,sel,ref;
+  short  voy;
+
+  if ( !MMG_zaldy4(&hed,25) ) {
+    if ( mesh->info.ddebug )  fprintf(stdout,"  ## MEMORY ALLOCATION PROBLEM : EDGES UPDATE SWAP663 IGNORED\n"); 
+  }   
+
+  iel = list->tetra[1] / 6;
+  iar = list->tetra[1] % 6;
+  pt  = &mesh->tetra[iel];
+  ref = pt->ref; 
+  MMG_findpolygone(mesh,list,s);
+    
+  ia  = pt->v[ MMG_iare[iar][0] ];
+  ib  = pt->v[ MMG_iare[iar][1] ];
+
+  jel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[jel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[1];
+  pt1->v[3] = s[2];
+  pt1->qual = list->qual[1];
+  pt1->ref  = ref;
+
+  kel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[kel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[1];
+  pt1->qual = list->qual[2];
+  pt1->ref  = ref;
+
+  lel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[lel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[6]; 
+  pt1->qual = list->qual[3];
+  pt1->ref  = ref;
+  
+  mel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[mel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[6];
+  pt1->v[3] = s[5];    
+  pt1->qual = list->qual[4];
+  pt1->ref  = ref;
+  
+  nel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[nel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[3];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[5];
+  pt1->ref  = ref;
+  
+  oel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[oel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[3];
+  pt1->qual = list->qual[6];
+  pt1->ref  = ref;
+  
+  pel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[pel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[4];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[7];
+  pt1->ref  = ref;
+  
+  qel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[qel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[2];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[4];
+  pt1->qual = list->qual[8];
+  pt1->ref  = ref;
+  
+  rel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[rel];
+  pt1->v[0] = ia;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[2];
+  pt1->v[3] = s[5];
+  pt1->qual = list->qual[9];
+  pt1->ref  = ref;
+
+  sel = MMG_newElt(mesh);
+  pt1 = &mesh->tetra[sel];
+  pt1->v[0] = ib;
+  pt1->v[1] = s[0];
+  pt1->v[2] = s[5];
+  pt1->v[3] = s[2];	    
+  pt1->qual = list->qual[10];
+  pt1->ref  = ref;
+
+  /*adj of iel*/
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[3] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 3;
+  }
+  k    = MMG_iare[iar][0];
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[2] = pt->bdryref[k]; 
+
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 2;
+  }
+  /*adj list->tetra[2]*/
+  iel = list->tetra[2] / 6;
+  iar = list->tetra[2] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[jel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = jel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[kel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = kel*4 + 1; 
+  }
+  /*adj list->tetra[3]*/
+  iel = list->tetra[3] / 6;
+  iar = list->tetra[3] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  pt1 = &mesh->tetra[adj];
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[3] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 3;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  pt1 = &mesh->tetra[adj];
+   
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[2] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 2;
+  }  
+  /*adj list->tetra[4]*/
+  iel = list->tetra[4] / 6;
+  iar = list->tetra[4] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[nel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = nel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[oel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = oel*4 + 1;
+  }
+  /*adj list->tetra[5]*/
+  iel = list->tetra[5] / 6;
+  iar = list->tetra[5] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[pel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = pel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[qel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = qel*4 + 1; 
+  }
+  /*adj list->tetra[6]*/
+  iel = list->tetra[6] / 6;
+  pt = &mesh->tetra[iel];
+  iar = list->tetra[6] % 6;
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[1] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 1;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[1] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[1] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 1;
+  }
+  /*adj list->tetra[7]*/
+  iel = list->tetra[7] / 6;
+  iar = list->tetra[7] % 6;
+  pt = &mesh->tetra[iel];
+  iadr2 = (iel-1)*4 + 1;
+  adja2 = &mesh->adja[iadr2];
+  k    = MMG_iare[iar][1];
+  if(pt->v[k] == ib) 
+    k1 = MMG_iare[iar][0];
+  else {
+    k  = MMG_iare[iar][0];
+    k1 = MMG_iare[iar][1];
+  }
+  adj  = adja2[k] / 4;
+  voy  = adja2[k] % 4;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[2] = adj*4 + voy;
+  mesh->tetra[lel].bdryref[2] = pt->bdryref[k]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = lel*4 + 2;
+  }
+  adj  = adja2[k1] / 4;
+  voy  = adja2[k1] % 4;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[3] = adj*4 + voy;
+  mesh->tetra[mel].bdryref[3] = pt->bdryref[k1]; 
+  if(adj) {
+    iadr = (adj-1)*4 + 1;
+    adja = &mesh->adja[iadr];
+    adja[voy] = mel*4 + 3;
+  }
+  /*internals faces*/
+  iadr = (jel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = kel*4 + 0;
+  adja[2] = rel*4 + 3;
+  
+  iadr = (kel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = jel*4 + 0;
+  adja[3] = sel*4 + 2;
+  
+  iadr = (lel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = mel*4 + 0;
+  adja[3] = rel*4 + 2;
+  
+  iadr = (mel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = lel*4 + 0;
+  adja[2] = sel*4 + 3;
+    
+  iadr = (nel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = oel*4 + 0;
+  adja[2] = pel*4 + 3;
+  
+  iadr = (oel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = nel*4 + 0;
+  adja[3] = qel*4 + 2;
+ 
+  iadr = (pel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = qel*4 + 0;
+  adja[3] = nel*4 + 2;
+  adja[2] = rel*4 + 1;
+
+  iadr = (qel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = pel*4 + 0;
+  adja[2] = oel*4 + 3;
+  adja[3] = sel*4 + 1;
+
+  iadr = (rel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = sel*4 + 0;
+  adja[1] = pel*4 + 2;
+  adja[2] = lel*4 + 3;
+  adja[3] = jel*4 + 2;
+
+  iadr = (sel-1)*4 + 1;
+  adja = &mesh->adja[iadr];
+  adja[0] = rel*4 + 0;
+  adja[1] = qel*4 + 3; 
+  adja[3] = mel*4 + 2;  
+  adja[2] = kel*4 + 3;
+  
+  /* remove 7 old tetra */
+  for (k=1; k<=7; k++) { 
+		pt0 = &mesh->tetra[list->tetra[k]/6];
+		for(i=0 ; i<6 ; i++) {
+			MMG_edgePut(&hed,pt0->v[MMG_iare[i][0]],pt0->v[MMG_iare[i][1]],pt0->bdryinfo[i]);
+		}
+    MMG_delElt(mesh,list->tetra[k]/6); 
+  }
+
+  list->tetra[1]  = jel;
+  list->tetra[2]  = kel;
+  list->tetra[3]  = lel;
+  list->tetra[4]  = mel;
+  list->tetra[5]  = nel;
+  list->tetra[6]  = oel;
+  list->tetra[7]  = pel;
+  list->tetra[8]  = qel;
+  list->tetra[9]  = rel;
+  list->tetra[10] = sel;
+  list->tetra[11] = 0;
+	MMG_updatebdryinfo(mesh,&hed,list);
+	M_free(hed.item);
+
+return(1);
+}
diff --git a/contrib/mmg3d/build/sources/swapar.c b/contrib/mmg3d/build/sources/swapar.c
new file mode 100644
index 0000000000000000000000000000000000000000..043f858142ece48b71c06f5cb72f5a635bfb1d5d
--- /dev/null
+++ b/contrib/mmg3d/build/sources/swapar.c
@@ -0,0 +1,106 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+int MMG_swapar(pMesh mesh,pSol sol,pQueue q,List *list,int lon,double crit,double declic) {
+  pTetra   pt;
+  int      i,l,jel,ncas,ddebug;
+
+  MMG_swpptr = 0;
+  ncas   = 0;
+  if ( !MMG_getnElt(mesh,10) )  return(-1);
+	if(0 && list->tetra[1]/6==144988) ddebug=1;
+	else ddebug=0;
+	
+  switch(lon) {
+  case 3:
+  	  ncas = MMG_simu32(mesh,sol,list,crit);
+    break;
+  case 4:
+  	  ncas = MMG_simu44(mesh,sol,list,crit); 
+    break;
+  case 5:
+  	  ncas = MMG_simu56(mesh,sol,list,crit);
+    break;
+  case 6:
+  	  ncas = MMG_simu68(mesh,sol,list,crit); 
+			if(ddebug) printf("on vient avec %d\n",list->tetra[1]/6);
+    break;
+  case 7:
+    ncas = MMG_simu710(mesh,sol,list,crit);
+    break;  
+  default:
+    return(0);
+  }
+
+  if ( ncas && MMG_swpptr ) {
+		if(ddebug) MMG_saveMesh(mesh,"avt.mesh");
+    for (l=1; l<=lon; l++) {
+      jel = list->tetra[l]/6;
+      pt  = &mesh->tetra[jel]; 
+			if(ddebug) {
+				printf("tet %d : %d %d %d %d -- %d %d %d %d %d %d\n",jel,pt->v[0],pt->v[1],pt->v[2],pt->v[3],
+					pt->bdryinfo[0],pt->bdryinfo[1],pt->bdryinfo[2],pt->bdryinfo[3],pt->bdryinfo[4],pt->bdryinfo[5]);
+			}     
+			MMG_kiudel(q,jel);
+    }
+    lon = MMG_swpptr(mesh,sol,list);
+    assert(lon);
+    if(!lon) return(0); 
+    
+    for (l=1; l<=lon; l++) {
+      jel = list->tetra[l];
+      pt  = &mesh->tetra[jel]; 
+      if ( pt->qual >= declic )  MMG_kiuput(q,jel);
+      for (i=0; i<4; i++)  mesh->point[pt->v[i]].flag = mesh->flag; 
+    }
+		if(ddebug) MMG_saveMesh(mesh,"sw.mesh");
+    return(1);
+  }
+
+  return(0);
+}
+
diff --git a/contrib/mmg3d/build/sources/swaptet.c b/contrib/mmg3d/build/sources/swaptet.c
new file mode 100644
index 0000000000000000000000000000000000000000..c9d75b00edd935bcc06ddb1d9949d194b6951db6
--- /dev/null
+++ b/contrib/mmg3d/build/sources/swaptet.c
@@ -0,0 +1,105 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define SCRIT    0.95
+
+int MMG_swaptet(pMesh mesh,pSol sol,pQueue queue,double declic,int iel) {
+  pTetra pt,pt1;
+  List   list;
+  double crit;
+  int    i,*adja,iadr,lon,ier,adj,j,jel;
+  char   tabar,done;
+  
+  pt = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(0);
+
+  /* mark internal edges */
+  tabar = 0;
+  iadr  = 4*(iel-1) + 1;
+  adja  = &mesh->adja[iadr];
+  for (i=0; i<4; i++) {
+    adj = adja[i] >> 2;
+    if ( !adj || pt->ref != mesh->tetra[adj].ref ) {
+      tabar |= 1 << MMG_iarf[i][0];
+      tabar |= 1 << MMG_iarf[i][1];
+      tabar |= 1 << MMG_iarf[i][2];
+    }
+  }
+  if ( tabar == ALL_BDRY )  return(0);
+  
+  /* MMG_swap for anisotropy */
+  done = 0;
+  for (i=0; i<6; i++) {
+    if ( tabar & 1<<i )  continue;
+
+    lon  = MMG_coquil(mesh,iel,i,&list);
+    if ( lon < 3 || lon > 7 )  continue;
+
+    /* qual crit */
+    crit = pt->qual;
+    for (j=2; j<=lon; j++) {
+      jel  = list.tetra[j] / 6;
+      pt1  = &mesh->tetra[jel];
+      crit = M_MAX(crit,pt1->qual);
+    }
+    crit *= SCRIT;
+
+    ier = MMG_swapar(mesh,sol,queue,&list,lon,crit,declic);
+    if ( ier > 0 ) {
+      return(1);
+      break;
+    }
+    else if ( ier < 0 ) {
+      fprintf(stdout,"  ## UNABLE TO MMG_swap.\n");
+      return(-1);
+    }
+    
+  }
+  
+  return(0);
+ 
+}
diff --git a/contrib/mmg3d/build/sources/typelt.c b/contrib/mmg3d/build/sources/typelt.c
new file mode 100644
index 0000000000000000000000000000000000000000..659c8eb0337249bc138b09f277a98cd7a2a56b6a
--- /dev/null
+++ b/contrib/mmg3d/build/sources/typelt.c
@@ -0,0 +1,378 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+#define EPSVOL    0.001
+#define RAPMAX    0.25
+
+
+/* identify type of element :
+  ityp= 0: 4 faces bonnes          (elt ok)
+        1: 4 faces bonnes, vol nul (sliver)
+        2: 4 faces ok, vol nul+sommet proche face   (chapeau)
+        3: 3 faces bonnes, 1 obtuse    (aileron)
+        4: 2 faces bonnes, 2 faces aigu => 1 petite arete
+        5: 1 face bonne, 3 petites aretes
+        6: 2 faces grandes aretes, 2 faces petites iaretes
+        7: 4 faces grandes aretes 
+   item: bad entity
+*/
+
+
+/* nb face obtuse :    nb faces aigu : 
+ityp :  0: 0		0
+	1: 0		0
+	2: 0		0
+	3: 1		0
+	4: 0		2
+	5: 0		3
+	6: 2		2
+	7: 0		4
+*/
+/* nb gde arete :    nb petite arete : 
+ityp :  0: 0		0
+	1: 0		0
+	2: 0		0
+	3: 1		0
+	4: 0		1
+	5: 0		3
+	6: 1		1
+	7: 0		2
+*/
+
+int MMG_typelt(pMesh mesh,int iel,int *item) {
+  pTetra    pt;
+  pPoint    pa,pb,pc,pd;
+  double    abx,aby,abz,acx,acy,acz,adx,ady,adz,v1,v2,v3,vol;
+  double    bcx,bcy,bcz,bdx,bdy,bdz,cdx,cdy,cdz,h[6],volchk,ssmall;
+  double    s[4],dd,rapmin,rapmax,surmin,surmax;
+  int       i,k,ia,ib,ic,id,ityp,isur,isurmax,isurmin,iarmax,iarmin;
+  int       nobtus,naigu;
+  short     i0,i1,i2;
+
+  ityp = 0;
+  pt = &mesh->tetra[iel];
+  if ( !pt->v[0] )  return(-1);
+
+  ia = pt->v[0];
+  ib = pt->v[1];
+  ic = pt->v[2];
+  id = pt->v[3];
+  pa = &mesh->point[ia];
+  pb = &mesh->point[ib];
+  pc = &mesh->point[ic];
+  pd = &mesh->point[id];
+
+  /* volume */
+  abx = pb->c[0] - pa->c[0]; 
+  aby = pb->c[1] - pa->c[1]; 
+  abz = pb->c[2] - pa->c[2]; 
+
+  acx = pc->c[0] - pa->c[0]; 
+  acy = pc->c[1] - pa->c[1]; 
+  acz = pc->c[2] - pa->c[2]; 
+
+  adx = pd->c[0] - pa->c[0]; 
+  ady = pd->c[1] - pa->c[1]; 
+  adz = pd->c[2] - pa->c[2]; 
+
+  v1  = acy*adz - acz*ady;
+  v2  = acz*adx - acx*adz;
+  v3  = acx*ady - acy*adx;
+  vol = abx * v1 + aby * v2 + abz * v3;
+
+  /* max edge */
+  h[0] = abx*abx + aby*aby + abz*abz;
+  h[1] = acx*acx + acy*acy + acz*acz;
+  h[2] = adx*adx + ady*ady + adz*adz;
+
+  bcx = pc->c[0] - pb->c[0];
+  bcy = pc->c[1] - pb->c[1];
+  bcz = pc->c[2] - pb->c[2];
+
+  bdx = pd->c[0] - pb->c[0];
+  bdy = pd->c[1] - pb->c[1];
+  bdz = pd->c[2] - pb->c[2];
+
+  cdx = pd->c[0] - pc->c[0];
+  cdy = pd->c[1] - pc->c[1];
+  cdz = pd->c[2] - pc->c[2];
+
+  h[3] = bcx*bcx + bcy*bcy + bcz*bcz;
+  h[4] = bdx*bdx + bdy*bdy + bdz*bdz;
+  h[5] = cdx*cdx + cdy*cdy + cdz*cdz;
+
+  /* face areas */
+  dd = cdy*bdz - cdz*bdy; 
+  s[0] = dd * dd;
+  dd = cdz*bdx - cdx*bdz;
+  s[0] = s[0] + dd * dd;
+  dd = cdx*bdy - cdy*bdx;
+  s[0] = s[0] + dd * dd;
+  s[0] = sqrt(s[0]);
+
+  s[1] = sqrt(v1*v1 + v2*v2 + v3*v3);
+
+  dd = bdy*adz - bdz*ady;
+  s[2] = dd * dd;
+  dd = bdz*adx - bdx*adz;
+  s[2] = s[2] + dd * dd;
+  dd = bdx*ady - bdy*adx;
+  s[2] = s[2] + dd * dd;
+  s[2] = sqrt(s[2]);
+
+  dd = aby*acz - abz*acy;
+  s[3] = dd * dd;
+  dd = abz*acx - abx*acz;
+  s[3] = s[3] + dd * dd;
+  dd = abx*acy - aby*acx;
+  s[3] = s[3] + dd * dd;
+  s[3] = sqrt(s[3]);
+
+  /* classification */
+  rapmin = h[0];
+  rapmax = h[0];
+  iarmin = 0;
+  iarmax = 0;
+  for (i=1; i<6; i++) {
+    if ( h[i] < rapmin ) {
+      rapmin = h[i];
+      iarmin = i;
+    }
+    else if ( h[i] > rapmax ) {
+      rapmax = h[i];
+      iarmax = i;
+    }
+  }
+  rapmin = sqrt(rapmin);
+  rapmax = sqrt(rapmax);
+  volchk = EPSVOL * rapmin*rapmin*rapmin;
+
+  /* small volume: types 1,2,3,4 */
+  if ( vol < volchk ) {
+//puts("volume nul : type 1,2,3,4");
+    
+    ssmall = 0.4 * (s[0]+s[1]+s[2]+s[3]);
+    isur   = 0;
+    for (i=0; i<4; i++)
+      isur += s[i] > ssmall;
+
+    /* types 2,3 */
+    item[0] = iarmax;
+    item[1] = MMG_isar[iarmax][0];    
+    if ( isur == 1 ) {
+      surmin   = s[0];
+      isurmin = 0;
+      surmax   = s[0];
+      isurmax = 0;
+      for (i=1; i<4; i++) {
+        if ( s[i] < surmin ) {
+          surmin  = s[i];
+	  isurmin = i;
+	}  
+        else if ( s[i] > surmax ) {
+	  surmax  = s[i];
+	  isurmax = i;
+	}  
+      }
+      dd = surmin / surmax;
+      if ( dd < RAPMAX ) {
+        item[1] = MMG_isar[iarmax][0];
+        return(3);
+      }
+      else {
+        item[0] = isurmax;
+	item[1] = isurmin;
+        return(2);
+      }	
+    }
+
+    /* types 1 */
+    isur = 0;
+    if ( s[0]+s[1] > ssmall )  isur = 1;
+    if ( s[0]+s[2] > ssmall )  isur++;
+    if ( s[0]+s[3] > ssmall )  isur++;
+
+    if ( isur > 2 ) {
+      dd = rapmin / rapmax;
+      item[0] = iarmin;
+      item[1] = MMG_idir[iarmin][0];
+      if ( dd < 0.01 )  return(4);
+      if ( s[0]+s[1] > ssmall ) {
+        item[0] = 0;
+        return(1);
+      }
+      if ( s[0]+s[2] > ssmall ) {
+        item[0] = 1;
+        return(1);
+      }
+      if ( s[0]+s[3] > ssmall ) {
+        item[0] = 2;
+        return(1);
+      }
+    }
+    
+//puts("default");
+    item[0] = 0;
+    return(1);
+  }/*end chkvol*/
+
+ dd = rapmin / rapmax;
+  /* types 3,6,7 */
+ if ( dd < RAPMAX ) { /*ie une arete 4 fois plus gde qu'une autre*/
+    
+    for (i=0; i<6; i++)  h[i] = sqrt(h[i]);
+
+    nobtus = 0;
+    for (k=0; k<4; k++) {
+      for (i=0; i<3; i++) {
+        i0 = MMG_idir[k][i];
+        i1 = MMG_idir[k][MMG_inxt[i]];
+        i2 = MMG_idir[k][MMG_inxt[i+1]];
+        if ( h[i0]+h[i1] < 1.2*h[i2] ) {/*1.4 ie une face obtus*/
+	  nobtus++;
+          item[0] = i2;
+	  item[1] = MMG_idir[k][MMG_inxt[i+1]];
+        }
+      }
+    }
+    
+    switch(nobtus){
+      case 0 : 
+       break;
+      case 1: 
+       item[0] = iarmax;
+       item[1] = MMG_isar[iarmax][0];
+       return(3);
+      case 2:  
+       item[0] = iarmin;
+       item[1] = iarmax;
+       return(6);
+      default:
+       item[0] = iarmin;
+       item[1] = iarmax;
+//printf("default obtus %d\n",nobtus);
+       return(7);
+    }
+  }
+
+  /* type 4,5,7 */
+  else if ( dd < 0.7*RAPMAX ) {
+    naigu = 0;
+    for (k=0; k<4; k++) {
+      for (i=0; i<3; i++) {
+        i0 = MMG_idir[k][i];
+        i1 = MMG_idir[k][MMG_inxt[i]];
+        i2 = MMG_idir[k][MMG_inxt[i+1]];
+    	if ( h[i0]+h[i1] > 1.5*h[i2] )  naigu++;/*1.5*/
+      }
+    }
+    switch(naigu){
+      case 0 : 
+       break;
+      case 1: 
+       break;
+      case 2:  
+       item[0] = iarmin;
+       return(4);
+      case 3:
+/*#warning definir item*/
+       return(5);  
+      default:
+       item[0] = iarmin;
+       item[1] = iarmax;
+//printf("default aigu\n");
+       return(7);
+    }
+  }
+
+//   /* types 3,4,5,6,7 */
+//   else {
+//     isur = 0;
+//     for (i=0; i<6; i++) {
+//       if ( h[i] < 2.0*rapmin )  isur++;
+//     }
+// 
+//     switch(isur){
+//       case 2: 
+//        puts("2 aretes tres grande retourne 7");
+//        return(7);
+//       case 3:  return(5);
+//       case 1:
+//         for (k=0; k<4; k++) {
+//           for (i=0; i<3; i++) {
+//             i0 = MMG_idir[k][i];
+//             i1 = MMG_idir[k][i+1];
+//             i2 = MMG_idir[k][i+2];
+//             if ( h[i0]+h[i1] < 1.25*h[i2] )  return(6);
+//           }
+//         }
+// 	puts("on retourne 4 la");
+//         return(4);
+//     }
+//   }
+      
+//   surmin   = s[0];
+//   isurmin = 0;
+//   surmax   = s[0];
+//   isurmax = 0;
+//   for (i=1; i<4; i++) {
+//     if ( s[i] < surmin ) {
+//       surmin  = s[i];
+//       isurmin = i;
+//     }  
+//     else if ( s[i] > surmax ) {
+//       surmax  = s[i];
+//       isurmax = i;
+//     }  
+//   }
+// 
+//   item[0] = isurmax;
+//   item[1] = isurmin;
+  item[0] = 0;
+  //puts("default");
+  return(1);
+}
diff --git a/contrib/mmg3d/build/sources/zaldy.c b/contrib/mmg3d/build/sources/zaldy.c
new file mode 100644
index 0000000000000000000000000000000000000000..6bc7cd271c8688be93248d11cf1a177389e6d599
--- /dev/null
+++ b/contrib/mmg3d/build/sources/zaldy.c
@@ -0,0 +1,256 @@
+/****************************************************************************
+Logiciel initial: MMG3D Version 4.0
+Co-auteurs : Cecile Dobrzynski et Pascal Frey.
+Propriétaires :IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+diffusé sous les termes et conditions de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.  
+
+Ce fichier est une partie de MMG3D.
+MMG3D est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
+suivant les termes de la licence publique générale de GNU
+Version 3 ou toute version ultérieure.
+MMG3D est distribué dans l'espoir qu'il sera utile, mais SANS 
+AUCUNE GARANTIE ; sans même garantie de valeur marchande.  
+Voir la licence publique générale de GNU pour plus de détails.
+MMG3D est diffusé en espérant qu’il sera utile, 
+mais SANS AUCUNE GARANTIE, ni explicite ni implicite, 
+y compris les garanties de commercialisation ou 
+d’adaptation dans un but spécifique. 
+Reportez-vous à la licence publique générale de GNU pour plus de détails.
+Vous devez avoir reçu une copie de la licence publique générale de GNU 
+en même temps que ce document. 
+Si ce n’est pas le cas, aller voir <http://www.gnu.org/licenses/>.
+/****************************************************************************
+Initial software: MMG3D Version 4.0
+Co-authors: Cecile Dobrzynski et Pascal Frey.
+Owners: IPB - UPMC -INRIA.
+
+Copyright © 2004-2005-2006-2007-2008-2009-2010-2011, 
+spread under the terms and conditions of the license GNU General Public License 
+as published Version 3, or (at your option) any later version.
+
+This file is part of MMG3D
+MMG3D is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+MMG3D 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 MMG3D. If not, see <http://www.gnu.org/licenses/>.  
+****************************************************************************/
+#include "mesh.h"
+
+/* get new point address */
+int MMG_newPt(pMesh mesh,double c[3]) {
+  pPoint  ppt;
+  int     curpt;
+
+  if ( !mesh->npnil )  return(0);
+
+  curpt = mesh->npnil;
+  if ( mesh->npnil > mesh->np )  mesh->np = mesh->npnil;
+  ppt   = &mesh->point[curpt];
+  memcpy(ppt->c,c,3*sizeof(double));
+  ppt->tag   &= ~M_UNUSED;
+  mesh->npnil = ppt->tmp;
+  ppt->tmp    = 0;
+  ppt->flag   = mesh->flag;
+
+  return(curpt);
+}
+
+
+void MMG_delPt(pMesh mesh,int ip) {
+  pPoint   ppt;
+  ppt = &mesh->point[ip];
+  memset(ppt,0,sizeof(Point));
+  ppt->tag    = M_UNUSED;
+  ppt->tmp    = mesh->npnil;
+  mesh->npnil = ip;
+  if ( ip == mesh->np )  mesh->np--;
+}
+
+
+/* get new elt address */
+int MMG_newElt(pMesh mesh) {
+  int     curiel;
+
+  if ( !mesh->nenil ) {
+    fprintf(stdout,"  ## UNABLE TO ALLOCATE NEW ELEMENT.\n");
+    return(0);
+  }
+  curiel      = mesh->nenil;
+  if ( mesh->nenil > mesh->ne )  mesh->ne = mesh->nenil;
+  mesh->nenil = mesh->tetra[curiel].v[3];
+  mesh->tetra[curiel].v[3] = 0;
+
+  return(curiel);
+}
+
+
+void MMG_delElt(pMesh mesh,int iel) {
+  pTetra   pt;
+  int      iadr,i;
+
+  pt = &mesh->tetra[iel];
+  if ( !pt->v[0] ) {
+    fprintf(stdout,"  ## INVALID TETRA.\n");
+    return;
+  }
+  memset(pt,0,sizeof(Tetra));
+  pt->v[3] = mesh->nenil;
+  pt->qual = 0.0;
+  pt->edge = 0;
+	iadr = (iel-1)*4 + 1;
+  memset(&mesh->adja[iadr],0,4*sizeof(int));
+
+  mesh->nenil = iel;
+  if ( iel == mesh->ne )  mesh->ne--;
+}
+
+
+/* check if n elets available */
+int MMG_getnElt(pMesh mesh,int n) {
+  int     curiel;
+
+  if ( !mesh->nenil )  return(0);
+  curiel = mesh->nenil;
+  do {
+    curiel = mesh->tetra[curiel].v[3];
+  }
+  while (--n);
+
+  return(n == 0);
+}
+
+
+/* get new elt address */
+int MMG_newTria(pMesh mesh) {
+  int     curiel;
+
+  if ( !mesh->ntnil ) {
+    fprintf(stdout,"  ## UNABLE TO ALLOCATE NEW TRIANGLE.\n");
+    return(0);
+  }
+  curiel      = mesh->ntnil;
+  if ( mesh->ntnil > mesh->nt )  mesh->nt = mesh->ntnil;
+  mesh->ntnil = mesh->tria[curiel].v[2];
+  mesh->tria[curiel].v[2] = 0;
+
+  return(curiel);
+}
+
+
+void MMG_delTria(pMesh mesh,int iel) {
+  pTria    pt;
+
+  pt = &mesh->tria[iel];
+  if ( !pt->v[0] ) {
+    fprintf(stdout,"  ## INVALID TRIANGLE.\n");
+    return;
+  }
+  memset(pt,0,sizeof(Tria));
+  pt->v[2]    = mesh->ntnil;
+  mesh->ntnil = iel;
+  if ( iel == mesh->nt )  mesh->nt--;
+}
+
+
+/* allocate main structure */
+int MMG_zaldy(pMesh mesh) {
+  int     million = 1048576L;
+  int     k,npask;
+
+  if ( mesh->info.memory < 0 ) {
+    mesh->npmax = M_MAX(1.5*mesh->np,NPMAX);
+    mesh->nemax = M_MAX(1.5*mesh->ne,NEMAX);
+    mesh->ntmax = M_MAX(1.5*mesh->nt,NTMAX);
+  }
+  else {
+    /* point+tria+tets+adja+sol+bucket+queue */
+    int bytes = sizeof(Point)   + 0.2*sizeof(Tria) \
+              + 6*sizeof(Tetra) + 4*sizeof(int) \
+	      + sizeof(Sol) + sizeof(Displ) \
+	      + sizeof(int) + 5*sizeof(int);
+
+    npask = (double)mesh->info.memory / bytes * million;
+    mesh->npmax = M_MAX(1.5*mesh->np,npask);
+    mesh->nemax = M_MAX(1.5*mesh->ne,6*npask);
+    mesh->ntmax = M_MAX(1.5*mesh->nt,(int)(0.3*npask));
+  }
+
+  mesh->point = (pPoint)M_calloc(mesh->npmax+1,sizeof(Point),"MMG_zaldy.point");
+  assert(mesh->point);
+  mesh->tetra = (pTetra)M_calloc(mesh->nemax+1,sizeof(Tetra),"MMG_zaldy.tetra");
+  assert(mesh->tetra);
+  mesh->tria  = (pTria)M_calloc(mesh->ntmax+1,sizeof(Tria),"MMG_zaldy.tria");
+  assert(mesh->tria);
+  mesh->adja = (int*)M_calloc(4*mesh->nemax+5,sizeof(int),"MMG_zaldy.adja");
+  assert(mesh->adja);
+  mesh->disp = (pDispl)M_calloc(1,sizeof(Displ),"MMG_zaldy.displ");
+  assert(mesh->disp);
+  mesh->disp->mv = (double*)M_calloc(3*(mesh->npmax + 1),sizeof(double),"MMG_zaldy.displ");
+  assert(mesh->disp->mv);
+  mesh->disp->alpha = (short*)M_calloc(mesh->npmax+1,sizeof(short),"MMG_zaldy.displ");
+  assert(mesh->disp->alpha);
+
+  /* keep track of empty links */
+  mesh->npnil = mesh->np + 1;
+  mesh->nenil = mesh->ne + 1;
+
+  for (k=mesh->npnil; k<mesh->npmax-1; k++)
+    mesh->point[k].tmp  = k+1;
+
+  for (k=mesh->nenil; k<mesh->nemax-1; k++)
+    mesh->tetra[k].v[3] = k+1;
+
+  if ( mesh->nt ) {
+    mesh->ntnil = mesh->nt + 1;
+    for (k=mesh->ntnil; k<mesh->ntmax-1; k++)
+      mesh->tria[k].v[2] = k+1;
+  }
+
+  return(1);
+}
+
+
+/* sol structure */
+int MMG_zaldy3(pSol sol) {
+  if ( sol->npmax ) {
+    sol->met = (double*)M_calloc(sol->npmax+1,sol->offset*sizeof(double),"MMG_zaldy3");
+    assert(sol->met);
+    sol->metold = (double*)M_calloc(sol->npmax+1,sol->offset*sizeof(double),"MMG_zaldy3");
+    assert(sol->metold);
+  }
+
+  return(1);
+}
+
+
+/* edge structure for cavity */
+int MMG_zaldy4(pHedge hedg,int size) { 
+  int   k;
+
+  hedg->size  = size;
+  hedg->hnxt  = size;
+  hedg->nhmax = (int)(16*size);
+  hedg->item  = (hedge*)M_calloc(hedg->nhmax+1,sizeof(hedge),"MMG_zaldy4");
+  //assert(hedg->item);
+
+  for (k=size; k<hedg->nhmax; k++)
+    hedg->item[k].nxt = k+1;
+
+  return(1);
+}
+
+
+/* internal edges */
+int MMG_zaldy5() {
+  return(1);
+}
+
diff --git a/contrib/mobile/Android/app/build.gradle b/contrib/mobile/Android/app/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..bacf8e52cf12e112d8844cbdb5a5ab2d9b028919
--- /dev/null
+++ b/contrib/mobile/Android/app/build.gradle
@@ -0,0 +1,35 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 28
+
+    defaultConfig {
+        applicationId "org.geuz.onelab"
+        minSdkVersion 15
+        targetSdkVersion 28
+    }
+
+    signingConfigs {
+        release {
+            storeFile file("/Users/geuzaine/.geuzaine.keystore")
+            storePassword "Gmsh2013"
+            keyAlias "cgeuzaine"
+            keyPassword "Gmsh2013"
+        }
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+            signingConfig signingConfigs.release
+        }
+    }
+
+    lintOptions {
+          checkReleaseBuilds false
+          // Or, if you prefer, you can continue to check for errors in release builds,
+          // but continue the build even when errors are found:
+          abortOnError false
+      }
+}
diff --git a/contrib/mobile/Android/app/src/main/AndroidManifest.xml b/contrib/mobile/Android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..60b9f75291d0d7f996d0dd0cbd9541494b242421
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="org.geuz.onelab"
+          android:versionName="2.2.0"
+          android:versionCode="39"
+          android:installLocation="auto" >
+
+  <uses-sdk android:minSdkVersion="15"
+            android:targetSdkVersion="28" />
+  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+  <uses-permission android:name="android.permission.VIBRATE"/>
+  <uses-feature android:glEsVersion="0x00010000" android:required="true" />
+
+  <application android:icon="@drawable/ic_launcher"
+               android:label="@string/app_name"
+               android:theme="@style/AppTheme"
+               android:logo="@drawable/ic_launcher"
+               android:allowBackup="true" >
+    <activity android:name=".SplashScreen"
+              android:label="@string/title_activity_main"
+              android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW" />
+        <category android:name="android.intent.category.DEFAULT" />
+        <category android:name="android.intent.category.BROWSABLE" />
+        <data android:scheme="file" />
+        <data android:mimeType="application/zip" />
+        <data android:pathPattern=".*\\.zip" />
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW" />
+        <category android:name="android.intent.category.DEFAULT" />
+        <category android:name="android.intent.category.BROWSABLE" />
+        <data android:scheme="content" />
+        <data android:mimeType="application/zip" />
+      </intent-filter>
+    </activity>
+    <activity android:name=".MainActivity"
+              android:label="@string/title_activity_main"/>
+    <activity android:name=".ModelList"
+              android:label="@string/title_activity_main"/>
+    <activity android:name=".AboutActivity"
+              android:label="@string/title_activity_about"/>
+    <activity android:name=".PostProcessingActivity"
+              android:label="@string/title_activity_main"/>
+    <activity android:name=".OptionsActivity"
+              android:label="@string/title_activity_options">
+    </activity>
+  </application>
+
+</manifest>
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/AboutActivity.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/AboutActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..c004e6df89fc41c7454e590ff3776cddf5281930
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/AboutActivity.java
@@ -0,0 +1,133 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import java.lang.String;
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.webkit.WebView;
+import android.webkit.JavascriptInterface;
+import android.os.Bundle;
+
+class MyJavaScriptInterface {
+  private String _filename;
+  public MyJavaScriptInterface(String filename){
+    _filename = filename;
+  }
+  @JavascriptInterface
+  public void myJsCallback(String jsResult) {
+    File outputFile = new File(_filename);
+    try{
+      BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile));
+      writer.write(jsResult);
+      writer.close();
+    }
+    catch (IOException e) {
+    }
+  }
+}
+
+public class AboutActivity extends Activity{
+  private MenuItem _saveMenuItem;
+  private boolean _editingMode;
+  private WebView _webview;
+
+  protected void onCreate(android.os.Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    getActionBar().setDisplayHomeAsUpEnabled(true);
+
+    _webview = new WebView(this);
+    _webview.getSettings().setJavaScriptEnabled(true);
+
+    String css = "body { background-color: #FFFFFF; color: #252525; margin: 35px 10px 35px 10px; padding: 0; font-family: helvetica,sans-serif; font-size: 1em; }";
+
+    Intent intent = getIntent();
+    Bundle extras = intent.getExtras();
+    if(extras != null) {
+      String name = extras.getString("name");
+      getActionBar().setTitle(name);
+      if(name.equals("Help")){
+        _editingMode = false;
+        _webview.loadDataWithBaseURL("", "<html><head><style type=\"text/css\">" + css + " h3 { text-align: center; } </style></head><h3>Onelab/Mobile</h3> <h4>Running an existing model</h4> <p> The list of available models appears when you launch the app. Selecting a model will load it. You can then press <b>Run</b> to launch a simulation with the default set of parameters. When available, additional information about a model can be obtained by long-pressing on the model description and selecting <b>Visit model website</b>.</p> <h4>Modifying a model</h4> <p>To run a model with different parameters, press <b>Parameters</b> and modify any of the presets. Then press <b>Run</b> again: all the simulation steps will be performed with the new parameter values. To restore the preset parameters values, press <b>Reset</b>. </p> <p> Advanced users can also directly edit the model input files: long-press on the model description and select <b>Edit model files</b>. </p> <p> To free up space, temporary model files (meshes, solution files) can be removed by long-pressing on the model description and selecting <b>Clear results</b>. </p> <p> To completey remove a model, long-press on the model description and select <b>Remove</b>. </p> <h4>Sharing a model</h4> <p> To share a model by email, long-press on the model description and select <b>Email model files</b>. </p> <h4>Installing a new model</h4> <p> To install a new model: <ol> <li>Put all the model files (.pro, .geo) in a directory, which should also contain a file named <code>infos.xml</code> with the model information: <pre>\n&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;models&gt;\n  &lt;model&gt;\n    &lt;title&gt;Model title&lt;/title&gt;\n    &lt;summary&gt;Model summary&lt;/summary&gt;\n    &lt;file type=\"pro\"&gt;main_pro_file.pro&lt;/file&gt;\n    &lt;preview type=\"png\"&gt;128x128_pixel_screenshot.png&lt;/preview&gt;\n    &lt;url&gt;http://model_website.com&lt;/url&gt;\n  &lt;/model&gt;\n&lt;/models&gt;\n</pre><li>Zip the directory. <li>Open the .zip file on your device, e.g. by copying it on an SD card, through Google Drive or by  emailing it to yourself and opening the attachment; or by putting it  on a web server and downloading the file on the device with Chrome. <li>Refresh the list of models: the new model will be extracted  alongside Onelab/Mobile\'s built-in models.</ol> <p style=\"padding-top: 2em;\">Visit <a href=\"http://onelab.info/\">http://onelab.info/</a> for more information.</p> </body></html>", "text/html", "UTF-8", "");
+      }
+      else{
+        _editingMode = true;
+        String filename = extras.getString("file");
+        MyJavaScriptInterface javaInterface = new MyJavaScriptInterface(filename);
+        _webview.addJavascriptInterface(javaInterface, "HTMLOUT");
+        File file = new File(filename);
+        StringBuilder text = new StringBuilder();
+        try {
+          BufferedReader br = new BufferedReader(new FileReader(file));
+          String line;
+          while ((line = br.readLine()) != null) {
+            text.append(line);
+            text.append('\n');
+          }
+          br.close();
+        }
+        catch (IOException e) {
+        }
+        String code = text.toString().replace("<", "&lt;");
+        code = code.replace(">", "&gt;");
+        String js = "!function(a,b){\"function\"==typeof define&&define.amd?define([\"exports\"],b):b(\"undefined\"!=typeof exports?exports:a.microlight={})}(this,function(a){var k,l,m,b=window,c=document,d=\"appendChild\",e=\"test\",g=\"opacity:.\",n=function(a){for(l=c.getElementsByClassName(a||\"microlight\"),k=0;m=l[k++];){var n,o,r,s,t,f=m.textContent,h=0,i=f[0],j=1,p=m.innerHTML=\"\",q=0,u=/(\\d*\\, \\d*\\, \\d*)(, ([.\\d]*))?/g.exec(b.getComputedStyle(m).color);for(\"px rgba(\"+u[1]+\",\",u[3]||1;o=n,n=q<7&&\"\\\\\"==n?1:j;){if(j=i,i=f[++h],s=p.length>1,!j||q>8&&\"\\n\"==j||[/\\S/[e](j),1,1,!/[$\\w]/[e](j),(\"/\"==n||\"\\n\"==n)&&s,\'\"\'==n&&s,\"\'\"==n&&s,f[h-4]+o+n==\"-->\",o+n==\"*/\"][q])for(p&&(m[d](t=c.createElement(\"span\")).setAttribute(\"style\",[\"\",\"font-weight:bold;color:#00c;\",g+6,\"color: #a08;\"+g+8,\"font-style:italic;color: #b00;\"+g+8][q?q<3?2:q>6?4:q>3?3:+/^(If|Else|ElseIf|EndIf|Include|For|EndFor|Include|Macro|Return)$/[e](p):0]),t[d](c.createTextNode(p))),r=q&&q<7?q:r,p=\"\",q=11;![1,/[\\/{}[(\\-+*=<>:;|\\\\.,?!&@~]/[e](j),/[\\])]/[e](j),/[$\\w]/[e](j),\"/\"==j&&r<2&&\"<\"!=n,\'\"\'==j,\"\'\"==j,j+i+f[h+1]+f[h+2]==\"<!--\",j+i==\"/*\",j+i==\"//\",j+i==\"//\"][--q];);p+=j}}};a.reset=n,\"complete\"==c.readyState?n():b.addEventListener(\"load\",function(){n()},0)});";
+        _webview.loadDataWithBaseURL("", "<html><head><script>" + js +
+                                    "</script></head><body><pre contenteditable=\"true\" class=microlight>" +
+                                    code + "</pre></body></html>", "text/html", "UTF-8", "");
+      }
+    }
+    else{
+      _editingMode = false;
+      String aboutOnelab = "<center><h3>Onelab/Mobile</h3>";
+      try {
+        aboutOnelab += "Version " + this.getPackageManager().
+          getPackageInfo(this.getPackageName(), 0).versionName;
+      }
+      catch (android.content.pm.PackageManager.NameNotFoundException e) {
+      }
+      aboutOnelab += "<p>Copyright (C) 2014-2019 Christophe Geuzaine and Maxime Graulich, ";
+      aboutOnelab += "University of Li&egrave;ge</p>";
+      aboutOnelab += "<p>Visit <a href=\"http://onelab.info/\">http://onelab.info/</a> ";
+      aboutOnelab += "for more information</p>";
+      aboutOnelab += "<p>&nbsp;</p><p>This version of Onelab/Mobile contains:</p></center>";
+      _webview.loadDataWithBaseURL("", "<html><head><style type=\"text/css\">" + css + "</style></head><body>" +
+                                  aboutOnelab + Gmsh.getAboutGmsh() + Gmsh.getAboutGetDP() + "</body></html>",
+                                  "text/html", "UTF-8", "");
+    }
+    setContentView(_webview);
+  }
+  @Override
+  public boolean onCreateOptionsMenu(Menu menu)
+  {
+    super.onCreateOptionsMenu(menu);
+    if(_editingMode){
+      _saveMenuItem = menu.add(R.string.menu_save);
+      _saveMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+    }
+    return true;
+  }
+  @Override
+  public boolean onMenuItemSelected(int featureId, MenuItem item)
+  {
+    if(item.getItemId() == android.R.id.home) {
+      Intent returnIntent = new Intent();
+      this.setResult(RESULT_CANCELED, returnIntent);
+      this.finish();
+    }
+    else if(item.getTitle().equals(getString(R.string.menu_save))){
+      _webview.loadUrl("javascript:( function () { window.HTMLOUT.myJsCallback(document.body.innerText); } ) ()");
+      // FIXME: should reload the page to correctly re-highlight
+    }
+    return super.onMenuItemSelected(featureId, item);
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/GLESRender.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/GLESRender.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b618a18b0e9068f7b7c863ac0d51298c4bb75d4
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/GLESRender.java
@@ -0,0 +1,43 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import java.nio.IntBuffer;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.graphics.Bitmap;
+import android.opengl.GLSurfaceView.Renderer;
+
+public class GLESRender implements Renderer{
+  private Gmsh mGModel;
+  private int _width, _height;
+  public GLESRender(Gmsh model)
+  {
+    this.mGModel = model;
+  }
+  public void load(String filename){ mGModel.load(filename); }
+  public void startInteraction(float x, float y) { mGModel.startEvent(x, y); }
+  public void endInteraction(float x, float y) { mGModel.endEvent(x, y); }
+  public void rotateModel(float x, float y) { mGModel.rotate(x, y); }
+  public void scaleModel(float s) { mGModel.scale(s); }
+  public void translateModel(float x, float y) { mGModel.translate(x, y); }
+  public void resetModelPosition() { mGModel.resetPosition(); }
+  public void viewX() { mGModel.viewX(); }
+  public void viewY() { mGModel.viewY(); }
+  public void viewZ() { mGModel.viewZ(); }
+
+  // OpenGL ES methods
+  public void onDrawFrame(GL10 gl)
+  {
+    mGModel.viewDraw();
+  }
+  public void onSurfaceChanged(GL10 gl, int width, int height)
+  {
+    mGModel.viewInit(width, height);
+    _width = width;
+    _height = height;
+  }
+  public void onSurfaceCreated(GL10 gl, EGLConfig config) { }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Gmsh.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Gmsh.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5ae750fba2f73d73d2d03ac1933df5f6aee5c58
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Gmsh.java
@@ -0,0 +1,135 @@
+package org.geuz.onelab;
+
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class Gmsh implements Parcelable {
+  // from C/C++ code:
+  static {
+    System.loadLibrary("f2cblas");
+    System.loadLibrary("f2clapack");
+    System.loadLibrary("petsc");
+    System.loadLibrary("slepc");
+    System.loadLibrary("gmsh");
+    System.loadLibrary("getdp");
+    System.loadLibrary("Onelab");
+  }
+  // init Gmsh
+  private native long init(float fontFactor);
+  // load a file(OpenProjet)
+  private native void loadFile(long ptr, String name);
+  // called each time the GLView changes
+  private native void initView(long ptr, int w, int h);
+  // called each time the GLView requests a render
+  private native void drawView(long ptr);
+  private native void eventHandler(long ptr, int event, float x, float y);
+  // return the parameters for onelab
+  public native String[] getParams();
+  // change a parameter
+  public native int setParam(String type, String name, String value);
+  // access Gmsh options
+  public native int setStringOption(String category, String name, String value, int index);
+  public native int setDoubleOption(String category, String name, double value, int index);
+  public native int setIntegerOption(String category, String name, int value, int index);
+  public native String getStringOption(String category, String name, int index);
+  public native double getDoubleOption(String category, String name, int index);
+  public native int getIntegerOption(String category, String name, int index);
+  // call onelab
+  public native int onelabCB(String action);
+  public boolean haveAnimation() { return numberOfAnimation() > 1; }
+  public native int numberOfAnimation();
+  public native int animationNext();
+  public native int animationPrev();
+  public native void setAnimation(int animation);
+  public static native String getAboutGmsh();
+  public static native String getAboutGetDP();
+
+  // Java class:
+  private long ptr;
+  private Handler handler;
+  public Gmsh(Handler handler, float fontFactor)
+  {
+    ptr = this.init(fontFactor);
+    this.handler = handler;
+  }
+  public void viewInit(int w, int h)
+  {
+    this.initView(ptr, w, h);
+  }
+  public void viewDraw()
+  {
+    this.drawView(ptr);
+  }
+  public void load(String filename)
+  {
+    this.loadFile(ptr, filename);
+  }
+  public void startEvent(float x, float y)
+  {
+    this.eventHandler(ptr, 0, x, y);
+  }
+  public void translate(float x, float y)
+  {
+    this.eventHandler(ptr, 1, x, y);
+  }
+  public void scale(float s)
+  {
+    this.eventHandler(ptr, 2, s, 0);
+  }
+  public void rotate(float x, float y)
+  {
+    this.eventHandler(ptr, 3, x, y);
+  }
+  public void viewX() { this.eventHandler(ptr, 5, 0, 0); }
+  public void viewY() { this.eventHandler(ptr, 6, 0, 0); }
+  public void viewZ() { this.eventHandler(ptr, 7, 0, 0); }
+  public void endEvent(float x, float y)
+  {
+    this.eventHandler(ptr, 4, x, y);
+  }
+  public void resetPosition()
+  {
+    this.eventHandler(ptr, 10, 0, 0);
+  }
+  public void ShowPopup(String message)
+  {
+    if(handler != null)
+      handler.obtainMessage(0, message).sendToTarget();
+  }
+  public void RequestRender()
+  {
+    if(handler != null)
+      handler.obtainMessage(1).sendToTarget();
+  }
+  public void SetLoading(String message)
+  {
+    if(handler != null)
+      handler.obtainMessage(2, message).sendToTarget();
+  }
+  public void SetLoading(int percent)
+  {
+    if(handler != null)
+      handler.obtainMessage(3, percent, 100).sendToTarget();
+  }
+  // Parcelable methods/constructors
+  private Gmsh(Parcel in)
+  {
+    this.ptr = in.readLong();
+  }
+  public int describeContents() { return 0; }
+  public void writeToParcel(Parcel out, int flags)
+  {
+    out.writeLong(this.ptr);
+  }
+  public static Parcelable.Creator<Gmsh> CREATOR = new Parcelable.Creator<Gmsh>(){
+      public Gmsh createFromParcel(Parcel source)
+      {
+        return new Gmsh(source);
+      }
+      public Gmsh[] newArray(int size)
+      {
+        return new Gmsh[size];
+      }
+    };
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/MainActivity.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/MainActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..f9588cdfbf73c7155baffa30f3f0948cf09d20ca
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/MainActivity.java
@@ -0,0 +1,333 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.provider.MediaStore;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+public class MainActivity extends Activity{
+  private Gmsh _gmsh;
+  private boolean _compute, _twoPane, _notify;
+  private MenuItem _runStopMenuItem, _switchFragmentMenuItem;
+  private ModelFragment _modelFragment;
+  private OptionsFragment _optionsFragment;
+  private Dialog _errorDialog;
+
+  public MainActivity() { }
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                         WindowManager.LayoutParams.FLAG_FULLSCREEN);
+    getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+    setContentView(R.layout.main_activity_layout);
+    _gmsh = new Gmsh(mainHandler, getResources().getDisplayMetrics().density);
+    _notify = false;
+    ActionBar actionBar = getActionBar();
+    actionBar.setDisplayHomeAsUpEnabled(true);
+    actionBar.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#64000000")));
+    Intent intent = getIntent();
+    Bundle extras = intent.getExtras();
+    if(savedInstanceState != null){
+      // nothing
+    }
+    else if(extras != null) {
+      // request to load a file (from ModelList)
+      String name = extras.getString("name");
+      this.getActionBar().setTitle(name);
+      String tmp = extras.getString("file");
+      _gmsh.load(tmp);
+    }
+    else{
+      this.finish();
+    }
+    _twoPane = (findViewById(R.id.parameter_fragment) != null);
+    _modelFragment = ModelFragment.newInstance(_gmsh);
+    getFragmentManager().beginTransaction().replace
+      (R.id.model_fragment, _modelFragment).commit();
+    if(_twoPane) {
+      _optionsFragment = OptionsFragment.newInstance(_gmsh);
+      getFragmentManager().beginTransaction().replace
+        (R.id.parameter_fragment, _optionsFragment).commit();
+      _optionsFragment.setOnOptionsChangedListener
+        (new OptionsFragment.OnOptionsChangedListener(){
+            public void OnOptionsChanged() {
+              _modelFragment.requestRender();
+            }
+          });
+    }
+  }
+
+  @Override
+  protected void onSaveInstanceState(Bundle outState)
+  {
+    outState.putBoolean("Compute", _compute);
+    super.onSaveInstanceState(outState);
+  }
+
+  @Override
+  public boolean onCreateOptionsMenu(Menu menu)
+  {
+    super.onCreateOptionsMenu(menu);
+    if(!_twoPane) {
+      _switchFragmentMenuItem = menu.add(R.string.menu_parameters);
+      _switchFragmentMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+    }
+    _runStopMenuItem = menu.add((_compute)?R.string.menu_stop:R.string.menu_run);
+    _runStopMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+    return true;
+  }
+
+  @Override
+  public boolean onMenuOpened(int featureId, Menu menu)
+  {
+    _modelFragment.postDelay();
+    return super.onMenuOpened(featureId, menu);
+  }
+
+  @Override
+  public boolean onMenuItemSelected(int featureId, MenuItem item)
+  {
+    if (item.getTitle().equals(getString(R.string.menu_parameters))) {
+      Intent intent = new Intent(this, OptionsActivity.class);
+      intent.putExtra("Gmsh", (Parcelable)_gmsh);
+      intent.putExtra("Compute", _compute);
+      startActivityForResult(intent, 1);
+      _modelFragment.requestRender();
+    }
+    else if(item.getTitle().equals(getString(R.string.menu_run))){
+      if(_modelFragment != null) _modelFragment.hideControlBar();
+      new Run().execute();
+    }
+    else if(item.getTitle().equals(getString(R.string.menu_stop))){
+      _runStopMenuItem.setEnabled(false);
+      _gmsh.onelabCB("stop");
+    }
+    else if(item.getItemId() == android.R.id.home) {
+      if(this._compute) {
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+        dialogBuilder.setTitle("Cannot show the model list")
+          .setMessage("The computation has to complete before you can select another model")
+          .setPositiveButton("OK", new DialogInterface.OnClickListener() {
+              public void onClick(DialogInterface dialog, int which) {
+                dialog.dismiss();
+              }
+            })
+          .show();
+      }
+      else
+        this.finish();
+    }
+    return super.onMenuItemSelected(featureId, item);
+  }
+
+  @Override
+  protected void onActivityResult(int requestCode, int resultCode, Intent data)
+  {
+    super.onActivityResult(requestCode, resultCode, data);
+    switch (requestCode) {
+    case 1:
+      if(resultCode == RESULT_OK)
+        if(!_compute && data.getBooleanExtra("Compute", false)) new Run().execute();
+      break;
+    }
+  }
+
+  private class Run extends AsyncTask<Void, Void, Integer[]> {
+    @Override
+    protected void onPreExecute()
+    {
+      _compute = true;
+      _runStopMenuItem.setTitle(R.string.menu_stop);
+      super.onPreExecute();
+    }
+
+    @Override
+    protected Integer[] doInBackground(Void... params)
+    {
+      _gmsh.onelabCB("compute");
+      return new Integer[] {1};
+    }
+
+    @Override
+    protected void onPostExecute(Integer[] result)
+    {
+      //(Vibrator) getSystemService(Context.VIBRATOR_SERVICE).vibrate(350);
+      _runStopMenuItem.setTitle(R.string.menu_run);
+      _runStopMenuItem.setEnabled(true);
+      if(_modelFragment != null) _modelFragment.hideProgress();
+      _compute = false;
+      if(_notify) notifyEndComputing();
+      super.onPostExecute(result);
+    }
+  }
+
+  private void showError(String msg)
+  {
+    // show only first error
+    if(_errorDialog != null && _errorDialog.isShowing()) return;
+    // remove doc path from message
+    String str = msg.replace(this.getFilesDir().getAbsolutePath() + File.separator, "");
+    AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+    _errorDialog = dialogBuilder.setTitle("Error")
+      .setMessage(str)
+      .setPositiveButton("Dismiss", new DialogInterface.OnClickListener() {
+          public void onClick(DialogInterface dialog, int which) {
+            dialog.dismiss();
+          }
+        })
+      .show();
+  }
+  @Override
+  protected void onPause()
+  {
+    if(_compute) notifyComputing();
+    super.onPause();
+    _notify = true;
+  }
+
+  @Override
+  protected void onResume()
+  {
+    super.onResume();
+    NotificationManager mNotificationManager =
+      (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+    mNotificationManager.cancel(1337);
+    _notify = false;
+  }
+
+  @Override
+  protected void onStop()
+  {
+    super.onStop();
+    if(_compute) notifyComputing();
+    _notify = true;
+  }
+
+  @Override
+  public void onLowMemory()
+  {
+    if(!_compute) return;
+    _gmsh.onelabCB("stop");
+    Toast.makeText(this, "Low memory! Computation is going to stop",
+                   Toast.LENGTH_LONG).show();
+    super.onLowMemory();
+  }
+
+  @Override
+  public void onTrimMemory(int level)
+  {
+    if(!_compute) return;
+    if(level == Activity.TRIM_MEMORY_COMPLETE){
+      _gmsh.onelabCB("stop");
+      notifyEndComputing("The computation had to stop because your device ran out of memory");
+      _notify = false;
+    }
+    else if(level == Activity.TRIM_MEMORY_MODERATE) {
+      notifyComputing("Computation in progress - low memory", true);
+    }
+    super.onTrimMemory(level);
+  }
+
+  private void notifyComputing(String msg, boolean alert)
+  {
+    Intent intent = new Intent(this, MainActivity.class);
+    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+    PendingIntent pendingIntent = PendingIntent.getActivity
+      (this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+    Notification.Builder notifyBuilder = new Notification.Builder(this);
+    notifyBuilder.setContentTitle("ONELAB")
+      .setContentIntent(pendingIntent)
+      .setContentText(msg)
+      .setSmallIcon(R.drawable.ic_launcher)
+      .setProgress(0, 0, true);
+    if(alert)
+      notifyBuilder.setDefaults(Notification.DEFAULT_SOUND |
+                                Notification.DEFAULT_VIBRATE);
+    NotificationManager mNotificationManager = (NotificationManager)
+      getSystemService(Context.NOTIFICATION_SERVICE);
+    mNotificationManager.notify(1337, notifyBuilder.getNotification());
+  }
+
+  private void notifyComputing()
+  {
+    notifyComputing("Computation in progress", false);
+  }
+
+  private void notifyEndComputing()
+  {
+    notifyEndComputing("Computation done!");
+  }
+
+  private void notifyEndComputing(String msg)
+  {
+    Intent intent = new Intent(this, MainActivity.class);
+    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+    PendingIntent pendingIntent = PendingIntent.getActivity
+      (this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+    Notification.Builder notifyBuilder = new Notification.Builder(this)
+      .setSmallIcon(R.drawable.ic_launcher)
+      .setContentIntent(pendingIntent)
+      .setContentTitle("ONELAB")
+      .setDefaults(Notification.DEFAULT_ALL)
+      .setAutoCancel(true)
+      .setProgress(0, 0, false)
+      .setContentText(msg);
+    NotificationManager mNotificationManager = (NotificationManager)
+      getSystemService(Context.NOTIFICATION_SERVICE);
+    mNotificationManager.notify(1337, notifyBuilder.getNotification());
+  }
+
+  private final Handler mainHandler = new Handler(){
+      public void handleMessage(android.os.Message msg) {
+    		switch (msg.what) {
+        case 0: // we get a message from gmsh library
+          showError((String)msg.obj);
+          break;
+        case 1: // request render from gmsh library
+          if(_modelFragment != null) _modelFragment.requestRender();
+          if(_optionsFragment != null) _optionsFragment.refresh();
+          break;
+        case 2: // we get a message for loading
+          if(_modelFragment != null) _modelFragment.showProgress((String)msg.obj);
+          break;
+        case 3: // we get a progress for loading
+          //loading.setProgress(msg.arg1);
+          break;
+        default:
+          break;
+        }
+      };
+    };
+
+  public boolean isComputing() { return _compute; }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Model.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Model.java
new file mode 100644
index 0000000000000000000000000000000000000000..172a0cc285b976a9968d074cc6670af6634ab74d
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Model.java
@@ -0,0 +1,42 @@
+package org.geuz.onelab;
+
+import java.io.File;
+import android.net.Uri;
+import java.util.Comparator;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+class Model {
+  private String _name, _summary;
+  private File _file;
+  private Bitmap _bitmap;
+  private Uri _url;
+  public Model(String name, String summary, File file)
+  {
+    _name = name;
+    _summary = summary;
+    _file = file;
+  }
+  public String getName() { return _name; }
+  public String getSummary() { return _summary; }
+  public File getFile() { return _file; }
+  public Bitmap getBitmap() { return _bitmap; }
+  public Uri getUrl() { return _url; }
+  public void setBitmap(File f)
+  {
+    BitmapFactory.Options options = new BitmapFactory.Options();
+    options.inJustDecodeBounds = false;
+    options.inPreferredConfig = Bitmap.Config.RGB_565;
+    options.inDither = true;
+    _bitmap = BitmapFactory.decodeFile(f.toString(), options);
+  }
+  public void setUrl(Uri url) { _url = url; }
+}
+
+class ModelComp implements Comparator<Model>{
+  @Override
+  public int compare(Model e1, Model e2) {
+    return e1.getName().compareTo(e2.getName());
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ModelArrayAdapter.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ModelArrayAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..69ff084282593c240f82c13c5feb17bfb9a6f870
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ModelArrayAdapter.java
@@ -0,0 +1,63 @@
+package org.geuz.onelab;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class ModelArrayAdapter extends ArrayAdapter<Model> {
+  private List<Model> _models;
+
+  public ModelArrayAdapter(Context c)
+  {
+    super(c, R.layout.model);
+    _models = new ArrayList<Model>();
+  }
+
+  @Override
+  public void add(Model model)
+  {
+    super.add(model);
+    _models.add(model);
+  }
+
+  public void reset()
+  {
+    super.clear();
+    _models.clear();
+  }
+
+  public void sortByName()
+  {
+    Collections.sort(_models, new ModelComp());
+  }
+
+  public Model getModel(int pos)
+  {
+    return _models.get(pos);
+  }
+
+  @Override
+  public View getView(int position, View convertView, final ViewGroup parent)
+  {
+    LayoutInflater inflater = (LayoutInflater) parent.getContext()
+      .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    final Model m = _models.get(position);
+    View rowView = inflater.inflate(R.layout.model, parent, false);
+    TextView title = (TextView) rowView.findViewById(R.id.title);
+    TextView description = (TextView) rowView.findViewById(R.id.description);
+    ImageView icon = (ImageView) rowView.findViewById(R.id.icon);
+    if(m.getName() != null) title.setText(m.getName());
+    if(m.getSummary() != null) description.setText(m.getSummary());
+    if(m.getBitmap() != null) icon.setImageBitmap(m.getBitmap());
+    else icon.setImageResource(R.drawable.ic_launcher);
+    return rowView;
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ModelFragment.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ModelFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c1057ca2f53620dce83e6b31768e6b73bfa9c47
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ModelFragment.java
@@ -0,0 +1,267 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.app.Fragment;
+import android.graphics.Bitmap;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnGestureListener;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.util.TypedValue;
+
+public class ModelFragment extends Fragment{
+  private Gmsh _gmsh;
+  private mGLSurfaceView _glView;
+  private TextView _progress;
+  private LinearLayout _progressLayout;
+  private LinearLayout _controlBarLayout;
+  private GestureDetector _gestureDetector;
+  private Timer _animation;
+  private Handler _hideDelay;
+  private SeekBar _animationStepper;
+
+  final Runnable hideControlsRunnable = new Runnable() {
+      public void run() { hideControlBar(); }
+    };
+
+  public static ModelFragment newInstance(Gmsh g)
+  {
+    ModelFragment fragment = new ModelFragment();
+    Bundle bundle = new Bundle();
+    bundle.putParcelable("Gmsh", g);
+    fragment.setArguments(bundle);
+    return fragment;
+  }
+
+  public ModelFragment() { }
+
+  @Override
+  public void onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    _gmsh = getArguments().getParcelable("Gmsh");
+  }
+
+  @Override
+  public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                           Bundle savedInstanceState)
+  {
+    View rootView = inflater.inflate(R.layout.fragment_model, container, false);
+    RelativeLayout glViewLayout = (RelativeLayout)rootView.findViewById(R.id.glViewLayout);
+    GLESRender renderer = new GLESRender(_gmsh);
+    _glView = new mGLSurfaceView(glViewLayout.getContext(), renderer);
+    _glView.setEGLContextClientVersion(1);
+    _glView.setRenderer(renderer);
+    _glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+    _glView.requestRender();
+    _hideDelay = new Handler();
+    _gestureDetector = new GestureDetector(getActivity(), new OnGestureListener() {
+        // UNUSED Auto-generated method stub
+        public boolean onSingleTapUp(MotionEvent e) { return false; }
+        // UNUSED Auto-generated method stub
+        public void onShowPress(MotionEvent e) {}
+        // UNUSED Auto-generated method stub
+        public boolean onScroll(MotionEvent e1, MotionEvent e2,
+                                float distanceX, float distanceY) { return false; }
+        // UNUSED Auto-generated method stub
+        public void onLongPress(MotionEvent e) {}
+        // UNUSED Auto-generated method stub
+        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+                               float velocityY) { return false; }
+        // UNUSED Auto-generated method stub
+        public boolean onDown(MotionEvent e) { return false; }
+      });
+    _gestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
+        public boolean onSingleTapConfirmed(MotionEvent e) {
+          if(View.VISIBLE == _controlBarLayout.getVisibility())
+            hideControlBar();
+          else
+            showControlBar();
+          return true;
+        }
+        // UNUSED Auto-generated method stub
+        public boolean onDoubleTapEvent(MotionEvent e) { return false; }
+        // UNUSED Auto-generated method stub
+        public boolean onDoubleTap(MotionEvent e) { return false; }
+      });
+    _glView.setOnTouchListener(new View.OnTouchListener() {
+        public boolean onTouch(View v, MotionEvent event) {
+          return _gestureDetector.onTouchEvent(event);
+        }
+      });
+    glViewLayout.addView(_glView);
+    RelativeLayout topRightLayout = new RelativeLayout(container.getContext());
+    ImageButton rotationButton = new ImageButton(container.getContext());
+    rotationButton.setBackgroundResource(R.drawable.icon_rotate);
+    rotationButton.setLayoutParams(new LinearLayout.LayoutParams(60, 60));
+    rotationButton.setOnClickListener(new View.OnClickListener() {
+        public void onClick(View v) {
+          boolean rotate = !_glView.getRotate();
+          ((ImageButton)v).setBackgroundResource(rotate ?
+                                                 R.drawable.icon_translate :
+                                                 R.drawable.icon_rotate);
+          _glView.setRotate(rotate);
+        }
+      });
+    topRightLayout.addView(rotationButton);
+    RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams
+      (RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
+
+    TypedValue tv = new TypedValue();
+    int actionBarHeight = 80;
+    if(container.getContext().getTheme().resolveAttribute
+       (android.R.attr.actionBarSize, tv, true)){
+      actionBarHeight = TypedValue.complexToDimensionPixelSize
+        (tv.data, getResources().getDisplayMetrics());
+    }
+    layoutParams.setMargins(0, actionBarHeight + 20, 20, 0);
+    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+    glViewLayout.addView(topRightLayout, layoutParams);
+    _progressLayout = new LinearLayout(container.getContext());
+    ProgressBar bar = new ProgressBar(container.getContext());
+    bar.setOnClickListener(new View.OnClickListener() {
+        public void onClick(View v) {
+          _progress.setAlpha((_progress.getAlpha() > 0) ? 0 : 1);
+        }
+      });
+    _progressLayout.addView(bar);
+    _progress = new TextView(container.getContext());
+    _progressLayout.setAlpha(0);
+    _progressLayout.setGravity(Gravity.CENTER);
+    _progressLayout.addView(_progress);
+    layoutParams = new RelativeLayout.LayoutParams
+      (RelativeLayout.LayoutParams.WRAP_CONTENT,
+       RelativeLayout.LayoutParams.WRAP_CONTENT);
+    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+    glViewLayout.addView(_progressLayout, layoutParams);
+    _controlBarLayout = (LinearLayout) getActivity().getLayoutInflater()
+      .inflate(R.layout.control_bar, null);
+    final ImageButton prevButton = (ImageButton)_controlBarLayout.findViewById(R.id.controlPrev);
+    final ImageButton playPauseButton = (ImageButton)_controlBarLayout.findViewById(R.id.controlPlay);
+    final ImageButton nextButton = (ImageButton)_controlBarLayout.findViewById(R.id.controlNext);
+    _animationStepper = (SeekBar)_controlBarLayout.findViewById(R.id.controlStepper);
+    _animationStepper.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+        // UNUSED Auto-generated method stub
+        public void onStopTrackingTouch(SeekBar seekBar) {}
+        // UNUSED Auto-generated method stub
+        public void onStartTrackingTouch(SeekBar seekBar) {}
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+          if(fromUser) {
+            postDelay();
+            _gmsh.setAnimation(progress);
+            requestRender();
+          }
+        }
+      });
+    _controlBarLayout.setEnabled(false);
+    playPauseButton.setOnClickListener(new View.OnClickListener() {
+        public void onClick(View v) {
+          postDelay();
+          if(((ImageButton)v).getContentDescription().equals("play")) {
+            ((ImageButton)v).setContentDescription("pause");
+            ((ImageButton)v).setImageResource(android.R.drawable.ic_media_pause);
+            _animationStepper.setMax(_gmsh.numberOfAnimation()-1);
+            _animation = new Timer();
+            _animation.schedule(new TimerTask() {
+                public void run()  {
+                  _animationStepper.setProgress(_gmsh.animationNext());
+                  requestRender();
+                } }, 0, 500);
+            prevButton.setEnabled(false);
+            nextButton.setEnabled(false);
+          }
+          else {
+            ((ImageButton)v).setContentDescription("play");
+            ((ImageButton)v).setImageResource(android.R.drawable.ic_media_play);
+            _animation.cancel();
+            prevButton.setEnabled(true);
+            nextButton.setEnabled(true);
+          }
+        }
+      });
+    nextButton.setOnClickListener(new View.OnClickListener() {
+        public void onClick(View v) {
+          postDelay();
+          _animationStepper.setProgress(_gmsh.animationNext());
+          requestRender();
+        }
+      });
+    prevButton.setOnClickListener(new View.OnClickListener() {
+        public void onClick(View v) {
+          postDelay();
+          _animationStepper.setProgress(_gmsh.animationPrev());
+          requestRender();
+        }
+      });
+    layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
+                                                   RelativeLayout.LayoutParams.WRAP_CONTENT);
+    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+    glViewLayout.addView(_controlBarLayout, layoutParams);
+    this._controlBarLayout.setVisibility(View.INVISIBLE);
+    return rootView;
+  }
+  public void postDelay(int delay)
+  {
+    _hideDelay.removeCallbacks(hideControlsRunnable);
+    _hideDelay.postDelayed(hideControlsRunnable, delay);
+  }
+  public void postDelay()
+  {
+    this.postDelay(6000);
+  }
+  public void showControlBar()
+  {
+    if(getActivity() == null || ((MainActivity)getActivity()).isComputing() ||
+       !_gmsh.haveAnimation()) return;
+    _controlBarLayout.setEnabled(true);
+    _animationStepper.setMax(_gmsh.numberOfAnimation()-1);
+    this.postDelay();
+    Animation bottomUp = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in);
+    _controlBarLayout.setVisibility(View.VISIBLE);
+    _controlBarLayout.startAnimation(bottomUp);
+  }
+  public void hideControlBar()
+  {
+    if(getActivity() == null || View.INVISIBLE == _controlBarLayout.getVisibility()) return;
+    _hideDelay.removeCallbacks(hideControlsRunnable);
+    Animation bottomDown = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out);
+    _controlBarLayout.startAnimation(bottomDown);
+    _controlBarLayout.setVisibility(View.INVISIBLE);
+  }
+  public void showProgress(String progress)
+  {
+    _progressLayout.setAlpha(1);
+    _progress.setText(progress);
+  }
+  public void hideProgress()
+  {
+    _progressLayout.setAlpha(0);
+    _progress.setText("");
+  }
+  public void requestRender()
+  {
+    _glView.requestRender();
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ModelList.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ModelList.java
new file mode 100644
index 0000000000000000000000000000000000000000..72657e671bc9ed41acfedd815f5231b1173cbc9f
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ModelList.java
@@ -0,0 +1,389 @@
+package org.geuz.onelab;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.util.Xml;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.Toast;
+import android.util.Log;
+import android.os.Environment;
+
+public class ModelList extends Activity {
+  private ModelArrayAdapter _modelArrayAdapter;
+
+  private void deleteRecursive(File fileOrDirectory)
+  {
+    if (fileOrDirectory.isDirectory()){
+      for (File child : fileOrDirectory.listFiles())
+        deleteRecursive(child);
+    }
+    fileOrDirectory.delete();
+  }
+
+  private void copyFile(File src, File dst) throws IOException {
+    FileInputStream in = new FileInputStream(src);
+    FileOutputStream out = new FileOutputStream(dst);
+    byte[] buf = new byte[1024];
+    int len;
+    while ((len = in.read(buf)) > 0) {
+      out.write(buf, 0, len);
+    }
+    in.close();
+    out.close();
+  }
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+
+    _modelArrayAdapter = new ModelArrayAdapter(this);
+    try {
+      this.getModels();
+    } catch (XmlPullParserException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    LinearLayout layout = new LinearLayout(this);
+    layout.setOrientation(LinearLayout.VERTICAL);
+    ListView list = new ListView(this);
+    list.setAdapter(_modelArrayAdapter);
+    list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+        public void onItemClick(AdapterView<?> parent, View view, int position,
+                                long id) {
+          Model m = _modelArrayAdapter.getModel(position);
+          Intent intent = new Intent(ModelList.this, MainActivity.class);
+          intent.putExtra("file", m.getFile().toString());
+          intent.putExtra("name", m.getName());
+          startActivity(intent);
+        }
+      });
+    list.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+        public boolean onItemLongClick(AdapterView<?> parent, View view,
+                                       int position, long id) {
+          final Model m = _modelArrayAdapter.getModel(position);
+          CharSequence[] actions;
+          if(m.getUrl() != null) {
+            actions = new CharSequence[6];
+            actions[0] = "Open";
+            actions[1] = "Remove";
+            actions[2] = "Clear results";
+            actions[3] = "Edit model files";
+            actions[4] = "Email model files";
+            actions[5] = "Visit model website";
+          }
+          else {
+            actions = new CharSequence[5];
+            actions[0] = "Open";
+            actions[1] = "Remove";
+            actions[2] = "Clear results";
+            actions[3] = "Edit model files";
+            actions[4] = "Email model files";
+          }
+          final AdapterView<?> p = parent;
+          AlertDialog.Builder builder = new AlertDialog.Builder(p.getContext());
+          builder.setTitle(m.getName());
+          builder.setItems(actions, new DialogInterface.OnClickListener() {
+              public void onClick(DialogInterface dialog, int position) {
+                switch (position) {
+                case 0:
+                  {
+                    Intent intent = new Intent(ModelList.this, MainActivity.class);
+                    intent.putExtra("file", m.getFile().toString());
+                    intent.putExtra("name", m.getName());
+                    startActivity(intent);
+                  }
+                  break;
+                case 1:
+                  deleteRecursive(m.getFile().getParentFile());
+                  _modelArrayAdapter.reset();
+                  try {
+                    getModels();
+                  } catch (XmlPullParserException e) {
+                    e.printStackTrace();
+                  } catch (IOException e) {
+                    e.printStackTrace();
+                  }
+                  break;
+                case 2:
+                  {
+                    File folder = m.getFile().getParentFile();
+                    File[] files = folder.listFiles();
+                    for (int i = 0; i < files.length; i++) {
+                      if (files[i].isFile()) {
+                        String filenameArray[] = files[i].getName().split("\\.");
+                        String extension = filenameArray[filenameArray.length-1];
+                        if(extension.equalsIgnoreCase("msh")  ||
+                           extension.equalsIgnoreCase("pre")  ||
+                           extension.equalsIgnoreCase("res")  ||
+                           extension.equalsIgnoreCase("pos")){
+                          deleteRecursive(files[i]);
+                        }
+                      }
+                    }
+                  }
+                  break;
+                case 3:
+                  {
+                    AlertDialog.Builder builder = new AlertDialog.Builder(p.getContext());
+                    builder.setTitle("Edit model files");
+                    File folder = m.getFile().getParentFile();
+                    File[] files = folder.listFiles();
+                    ArrayList<String> selection = new ArrayList<String>();
+                    for (int i = 0; i < files.length; i++) {
+                      if (files[i].isFile()) {
+                        String filenameArray[] = files[i].getName().split("\\.");
+                        String extension = filenameArray[filenameArray.length-1];
+                        if(extension.equalsIgnoreCase("txt")  ||
+                           extension.equalsIgnoreCase("geo")  ||
+                           extension.equalsIgnoreCase("pro")  ||
+                           extension.equalsIgnoreCase("dat")){
+                          selection.add(files[i].getName());
+                        }
+                      }
+                    }
+                    final String[] names = new String[selection.size()];
+                    for (int i = 0; i < selection.size(); i++)
+                      names[i] = selection.get(i);
+                    builder.setItems(names, new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int position) {
+                          Intent intent = new Intent(ModelList.this, AboutActivity.class);
+                          File folder = m.getFile().getParentFile();
+                          String file = folder + "/" + names[position];
+                          intent.putExtra("file", file);
+                          intent.putExtra("name", names[position]);
+                          startActivity(intent);
+                        }
+                      });
+                    AlertDialog alert = builder.create();
+                    alert.show();
+                  }
+                  break;
+                case 4:
+                  {
+                    Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
+                    emailIntent.setType("vnd.android.cursor.dir/email");
+                    File folder = m.getFile().getParentFile();
+                    File[] files = folder.listFiles();
+                    emailIntent.putExtra(Intent.EXTRA_SUBJECT, folder+"/"+files[0].getName());
+                    ArrayList<Uri> copies = new ArrayList<Uri>();
+                    for (int i = 0; i < files.length; i++) {
+                      if (files[i].isFile()) {
+                        // need to copy as we cannot attach files directly from
+                        // the app data dir
+                        File copy = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),
+                                             files[i].getName());
+                        try{
+                          copyFile(files[i], copy);
+                        }
+                        catch (IOException e) {
+                        }
+                        // FIXME: Starting with Nougat (api 24+) this is not allowed anymore and it
+                        // throws a android.os.FileUriExposedException
+                        //
+                        // https://proandroiddev.com/sharing-files-though-intents-are-you-ready-for-nougat-70f7e9294a0b
+                        try{
+                          copies.add(Uri.fromFile(copy));
+                        }
+                        catch(Exception e){
+                        }
+                      }
+                    }
+                    emailIntent.putExtra(Intent.EXTRA_STREAM, copies);
+                    startActivity(Intent.createChooser(emailIntent , "Share model files..."));
+                  }
+                  break;
+                case 5:
+                  {
+                    Intent browserIntent = new Intent(Intent.ACTION_VIEW, m.getUrl());
+                    startActivity(browserIntent);
+                  }
+                  break;
+                }
+              }
+            });
+          AlertDialog alert = builder.create();
+          alert.show();
+          return true;
+        }
+      });
+    layout.addView(list);
+    this.setContentView(layout);
+    layout.setPadding(15, 10, 10, 5);
+    layout.setBackgroundColor(Color.argb(255, 67, 67, 67));
+  }
+
+  @Override
+  public boolean onCreateOptionsMenu(Menu menu)
+  {
+    MenuItem help = menu.add("Help");
+    help.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+    MenuItem about = menu.add("About");
+    about.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+    return super.onCreateOptionsMenu(menu);
+  }
+
+  @Override
+  public boolean onMenuItemSelected(int featureId, MenuItem item)
+  {
+    if(item.getTitle().equals("About")) {
+      Intent intent = new Intent(ModelList.this, AboutActivity.class);
+      startActivity(intent);
+    }
+    else if(item.getTitle().equals("Help")) {
+      Intent intent = new Intent(ModelList.this, AboutActivity.class);
+      intent.putExtra("file", "Help");
+      intent.putExtra("name", "Help");
+      startActivity(intent);
+    }
+    return super.onMenuItemSelected(featureId, item);
+  }
+
+  @Override
+  protected void onActivityResult(int requestCode, int resultCode, Intent data)
+  {
+    super.onActivityResult(requestCode, resultCode, data);
+    if(resultCode == RESULT_CANCELED) return;
+    switch (requestCode) {
+    case 1:
+      Uri uri = data.getData();
+      String[] projection = { MediaStore.Images.Media.DATA };
+      Cursor cursor = managedQuery(uri, projection, null, null, null);
+      int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+      cursor.moveToFirst();
+      Intent intent = new Intent(ModelList.this, MainActivity.class);
+      intent.putExtra("file", cursor.getString(column_index));
+      intent.putExtra("name", "None");
+      startActivity(intent);
+      break;
+    }
+  }
+
+  private void getModels() throws XmlPullParserException, IOException
+  {
+    File document = this.getFilesDir();
+    File files[] = document.listFiles();
+    for(int i = 0; i < files.length; i++) {
+      if(files[i].isDirectory()) { // models are in directory
+        File xmlInfos = new File(files[i], "infos.xml");
+        if(!xmlInfos.isFile()) continue;
+        InputStream in = new FileInputStream(xmlInfos);
+        try {
+          XmlPullParser parser = Xml.newPullParser();
+          parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+          parser.setInput(in, null);
+          parser.nextTag();
+          parser.require(XmlPullParser.START_TAG, null, "models");
+          while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+            String tagName = parser.getName();
+            if(tagName.equals("model"))
+              readModel(parser, files[i].toString());
+            else
+              skipTag(parser);
+          }
+        }
+        finally {
+          in.close();
+        }
+      }
+    }
+    _modelArrayAdapter.sortByName();
+  }
+  private void readModel(XmlPullParser parser, String dir)
+    throws XmlPullParserException, IOException
+  {
+    parser.require(XmlPullParser.START_TAG, null, "model");
+    String title = null;
+    String summary = null;
+    String file = null;
+    String bitmap = null;
+    String url = null;
+    while (parser.next() != XmlPullParser.END_TAG) {
+      if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+      String name = parser.getName();
+      if(name.equals("title")) {
+        if (parser.next() == XmlPullParser.TEXT) {
+          title = parser.getText();
+          parser.nextTag();
+        }
+      }
+      else if(name.equals("summary")) {
+        if (parser.next() == XmlPullParser.TEXT) {
+          summary = parser.getText();
+          parser.nextTag();
+        }
+      }
+      else if(name.equals("file")) {
+        //String relType = parser.getAttributeValue(null, "type");
+        if (parser.next() == XmlPullParser.TEXT) {
+          file = parser.getText();
+          parser.nextTag();
+        }
+      }
+      else if(name.equals("preview")) {
+        if (parser.next() == XmlPullParser.TEXT) {
+          bitmap = parser.getText();
+          parser.nextTag();
+        }
+      }
+      else if(name.equals("url")) {
+        if (parser.next() == XmlPullParser.TEXT) {
+          url = parser.getText();
+          parser.nextTag();
+        }
+      }
+      else {
+        skipTag(parser);
+      }
+    }
+    if(title == null || file == null) return;
+    Model newModel = new Model(title, summary, new File(dir+"/"+file));
+    if(bitmap != null) newModel.setBitmap(new File(dir+"/"+bitmap));
+    if(url != null) newModel.setUrl(Uri.parse(url));
+    _modelArrayAdapter.add(newModel);
+  }
+  private void skipTag(XmlPullParser parser)
+    throws XmlPullParserException, IOException
+  {
+    if (parser.getEventType() != XmlPullParser.START_TAG) {
+      throw new IllegalStateException();
+    }
+    int depth = 1;
+    while (depth != 0) {
+      switch (parser.next()) {
+      case XmlPullParser.END_TAG:
+        depth--;
+        break;
+      case XmlPullParser.START_TAG:
+        depth++;
+        break;
+      }
+    }
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsActivity.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c32adb49219b99223504c00e32252223f340f1b
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsActivity.java
@@ -0,0 +1,57 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.WindowManager;
+
+public class OptionsActivity extends Activity {
+  boolean _compute;
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                         WindowManager.LayoutParams.FLAG_FULLSCREEN);
+    setContentView(R.layout.activity_fragment);
+    Bundle extra = getIntent().getExtras();
+    Gmsh gmsh = extra.getParcelable("Gmsh");
+    _compute = extra.getBoolean("Compute", false);
+    getActionBar().setDisplayHomeAsUpEnabled(true);
+    getActionBar().setTitle(R.string.title_activity_options);
+    OptionsFragment optionsFragment = OptionsFragment.newInstance(gmsh);
+    getFragmentManager().beginTransaction().replace
+      (R.id.model_fragment, optionsFragment).commit();
+  }
+
+  @Override
+  public boolean onCreateOptionsMenu(Menu menu)
+  {
+    if(_compute) return super.onCreateOptionsMenu(menu);
+    MenuItem runStopMenuItem = menu.add(R.string.menu_run);
+    runStopMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+    return super.onCreateOptionsMenu(menu);
+  }
+
+  @Override
+  public boolean onMenuItemSelected(int featureId, MenuItem item)
+  {
+    if(item.getTitle().equals(getString(R.string.menu_run))) {
+      Intent returnIntent = new Intent();
+      returnIntent.putExtra("Compute", true);
+      this.setResult(RESULT_OK, returnIntent);
+      this.finish();
+    }
+    else if(item.getItemId() == android.R.id.home) {
+      Intent returnIntent = new Intent();
+      this.setResult(RESULT_CANCELED, returnIntent);
+      this.finish();
+    }
+    return super.onMenuItemSelected(featureId, item);
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsDisplayFragment.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsDisplayFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3cd21eabda8cb2be6ecc6bad2b225f458b6c577
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsDisplayFragment.java
@@ -0,0 +1,156 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.app.Fragment;
+import android.content.Intent;
+import android.graphics.Color;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.AbsListView;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+
+public class OptionsDisplayFragment extends Fragment{
+  private Gmsh _gmsh;
+  private SeparatedListView _listView;
+
+  public static OptionsDisplayFragment newInstance(Gmsh g)
+  {
+    OptionsDisplayFragment fragment = new OptionsDisplayFragment();
+    Bundle bundle = new Bundle();
+    bundle.putParcelable("Gmsh", g);
+    fragment.setArguments(bundle);
+    return fragment;
+  }
+
+  public OptionsDisplayFragment()
+  {
+    super();
+  }
+
+  @Override
+  public void onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    _gmsh = getArguments().getParcelable("Gmsh");
+  }
+
+  @Override
+  public View onCreateView(LayoutInflater inflater, final ViewGroup container,
+                           Bundle savedInstanceState)
+  {
+    _listView = (SeparatedListView)inflater.inflate(R.layout.fragment_options_display,
+                                                    container, false);
+    CheckBox showGeomPoints = new CheckBox(_listView.getContext());
+    showGeomPoints.setText("Show geometry points");
+    showGeomPoints.setChecked(_gmsh.getDoubleOption("Geometry", "Points", 0) > 0.);
+    showGeomPoints.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+          _gmsh.setDoubleOption("Geometry", "Points", (isChecked)?1. : 0., 0);
+          if(mListener != null) mListener.OnModelOptionsChanged();
+        }
+      });
+    _listView.addItem("Display Options", showGeomPoints);
+    CheckBox showGeomLines = new CheckBox(_listView.getContext());
+    showGeomLines.setText("Show geometry lines");
+    showGeomLines.setChecked(_gmsh.getDoubleOption("Geometry", "Lines", 0) > 0.);
+    showGeomLines.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+          _gmsh.setDoubleOption("Geometry", "Lines", (isChecked)?1. : 0., 0);
+          if(mListener != null) mListener.OnModelOptionsChanged();
+        }
+      });
+    _listView.addItem("Display Options", showGeomLines);
+    CheckBox showMeshSurfaceEdges = new CheckBox(_listView.getContext());
+    showMeshSurfaceEdges.setText("Show mesh surface edges");
+    showMeshSurfaceEdges.setChecked(_gmsh.getDoubleOption("Mesh", "SurfaceEdges", 0) > 0.);
+    showMeshSurfaceEdges.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+          _gmsh.setDoubleOption("Mesh", "SurfaceEdges", (isChecked)?1. : 0., 0);
+          if(mListener != null) mListener.OnModelOptionsChanged();
+        }
+      });
+    _listView.addItem("Display Options", showMeshSurfaceEdges);
+    CheckBox showMeshVolumesEdges = new CheckBox(_listView.getContext());
+    showMeshVolumesEdges.setText("Show mesh volume edges");
+    showMeshVolumesEdges.setChecked(_gmsh.getDoubleOption("Mesh", "VolumeEdges", 0) > 0.);
+    showMeshVolumesEdges.setOnCheckedChangeListener
+      (new CompoundButton.OnCheckedChangeListener() {
+          public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            _gmsh.setDoubleOption("Mesh", "VolumeEdges", (isChecked)?1. : 0., 0);
+            if(mListener != null) mListener.OnModelOptionsChanged();
+          }
+        });
+    _listView.addItem("Display Options", showMeshVolumesEdges);
+    this.refresh();
+    return _listView;
+  }
+
+  public void refresh()
+  {
+    if(_gmsh == null) return;
+    int nbviews = (int)_gmsh.getDoubleOption("PostProcessing", "NbViews", 0);
+
+    for(int i = 0; i<_listView.itemsCountInSection("Result Options"); i++) {
+      View v = (View)_listView.getItemAtPosition(6 + i);
+      if(!v.getClass().equals(LinearLayout.class)) continue;
+      for(int j = 0; j < ((LinearLayout)v).getChildCount(); j++) {
+        View sv = ((LinearLayout)v).getChildAt(j);
+        if(sv.getClass().equals(CheckBox.class)){
+          ((CheckBox)sv).setChecked(_gmsh.getDoubleOption("View", "Visible", i) > 0.);
+        }
+      }
+    }
+    for(int i = _listView.itemsCountInSection("Result Options"); i < nbviews; i++) {
+      final int myID = i;
+      LinearLayout layout = new LinearLayout(_listView.getContext());
+      CheckBox checkbox = new CheckBox(_listView.getContext());
+      checkbox.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.WRAP_CONTENT,
+                                                            LayoutParams.WRAP_CONTENT));
+      checkbox.setText(_gmsh.getStringOption("View", "Name", myID));
+      checkbox.setChecked(_gmsh.getDoubleOption("View", "Visible", myID) > 0.);
+      checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+          public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            _gmsh.setDoubleOption("View", "Visible", isChecked ? 1. : 0., myID);
+            if(mListener != null) mListener.OnModelOptionsChanged();
+          }
+        });
+      Button button = new Button(_listView.getContext());
+      button.setText(">");
+      button.setOnClickListener(new View.OnClickListener() {
+          public void onClick(View v) {
+            Intent intent = new Intent(getActivity(), PostProcessingActivity.class);
+            intent.putExtra("Gmsh", (Parcelable)_gmsh);
+            intent.putExtra("PView", myID);
+            startActivity(intent);
+            if(mListener != null) mListener.OnModelOptionsChanged();
+          }
+        });
+      button.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
+                                                          LayoutParams.WRAP_CONTENT));
+      button.setBackgroundColor(Color.TRANSPARENT);
+      button.setGravity(Gravity.RIGHT);
+      layout.addView(checkbox);
+      layout.addView(button);
+      _listView.addItem("Result Options", layout);
+    }
+  }
+
+  private OnModelOptionsChangedListener mListener;
+  public void setOnModelOptionsChangedListener(OnModelOptionsChangedListener listener)
+  {
+    mListener = listener;
+  }
+  public interface OnModelOptionsChangedListener
+  {
+    void OnModelOptionsChanged();
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsFragment.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..a391253a2b4471625ea59a8829eae6c303efca49
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsFragment.java
@@ -0,0 +1,122 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+
+public class OptionsFragment extends Fragment{
+  private Gmsh _gmsh;
+  private int _viewN;
+  private OptionsDisplayFragment _optionDisplayFragment;
+  private OptionsModelFragment _optionModelFragment;
+
+  public static OptionsFragment newInstance(Gmsh g)
+  {
+    OptionsFragment fragment = new OptionsFragment();
+    Bundle bundle = new Bundle();
+    bundle.putParcelable("Gmsh", g);
+    fragment.setArguments(bundle);
+    return fragment;
+  }
+
+  public OptionsFragment() { }
+
+  @Override
+  public void onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    _gmsh = getArguments().getParcelable("Gmsh");
+    _viewN = 0;
+    if(savedInstanceState != null)
+      _viewN = savedInstanceState.getInt("viewN");
+  }
+
+  @Override
+  public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                           Bundle savedInstanceState)
+  {
+    LinearLayout rootView = (LinearLayout)inflater.inflate(R.layout.fragment_options,
+                                                           container, false);
+    _optionModelFragment = OptionsModelFragment.newInstance(_gmsh);
+    _optionModelFragment.setOnModelOptionsChangedListener
+      (new OptionsModelFragment.OnModelOptionsChangedListener() {
+          public void OnModelOptionsChanged() {
+            if(mListener != null) mListener.OnOptionsChanged();
+          }
+        });
+    _optionDisplayFragment = OptionsDisplayFragment.newInstance(_gmsh);
+    _optionDisplayFragment.setOnModelOptionsChangedListener
+      (new OptionsDisplayFragment.OnModelOptionsChangedListener() {
+          public void OnModelOptionsChanged() {
+            if(mListener != null) mListener.OnOptionsChanged();
+          }
+        });
+    final Button optionModel = (Button) rootView.findViewById(R.id.goto_options_model);
+    final Button optionDisplay = (Button) rootView.findViewById(R.id.goto_options_display);
+    if(_viewN == 0) {
+      optionDisplay.setEnabled(true);
+      optionModel.setEnabled(false);
+      getFragmentManager().beginTransaction().replace(R.id.options_fragment,
+                                                      _optionModelFragment).commit();
+    }
+    else {
+      optionDisplay.setEnabled(false);
+      optionModel.setEnabled(true);
+      getFragmentManager().beginTransaction().replace(R.id.options_fragment,
+                                                      _optionDisplayFragment).commit();
+    }
+    optionModel.setOnClickListener(new View.OnClickListener() {
+        public void onClick(View v) {
+          optionModel.setEnabled(false);
+          optionDisplay.setEnabled(true);
+          FragmentTransaction ft = getFragmentManager().beginTransaction();
+          ft.replace(R.id.options_fragment, _optionModelFragment);
+          ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+          ft.commit();
+          _viewN = 0;
+        }
+      });
+    optionDisplay.setOnClickListener(new View.OnClickListener() {
+        public void onClick(View v) {
+          optionDisplay.setEnabled(false);
+          optionModel.setEnabled(true);
+          FragmentTransaction ft = getFragmentManager().beginTransaction();
+          ft.replace(R.id.options_fragment, _optionDisplayFragment);
+          ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+          ft.commit();
+          _viewN = 1;
+        }
+      });
+    return rootView;
+  }
+
+  @Override
+  public void onSaveInstanceState(Bundle outState)
+  {
+    super.onSaveInstanceState(outState);
+    outState.putInt("viewN", _viewN);
+  }
+
+  public void refresh()
+  {
+    if(_optionDisplayFragment != null)_optionDisplayFragment.refresh();
+    if(_optionModelFragment != null)_optionModelFragment.refresh();
+  }
+
+  private OnOptionsChangedListener mListener;
+  public void setOnOptionsChangedListener(OnOptionsChangedListener listener)
+  {
+    mListener = listener;
+  }
+  public interface OnOptionsChangedListener
+  {
+    void OnOptionsChanged();
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsModelFragment.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsModelFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec16c89e6f0d6664d98cb6afdf3bb4c326e70a2d
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsModelFragment.java
@@ -0,0 +1,126 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class OptionsModelFragment extends Fragment{
+  private Gmsh _gmsh;
+  private SeparatedListView _listView;
+  private List<Parameter> params = new ArrayList<Parameter>();
+
+  public static OptionsModelFragment newInstance(Gmsh g)
+  {
+    OptionsModelFragment fragment = new OptionsModelFragment();
+    Bundle bundle = new Bundle();
+    bundle.putParcelable("Gmsh", g);
+    fragment.setArguments(bundle);
+    return fragment;
+  }
+
+  public OptionsModelFragment()
+  {
+    super();
+  }
+
+  @Override
+  public void onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    _gmsh = getArguments().getParcelable("Gmsh");
+  }
+
+  @Override
+  public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                           Bundle savedInstanceState)
+  {
+    params.clear();
+    _listView = (SeparatedListView)inflater.inflate(R.layout.fragment_options_display,
+                                                    container, false);
+    _listView.setDividerHeight(0);
+    this.refresh();
+    return _listView;
+  }
+
+  public void refresh()
+  {
+    if(_gmsh == null) return;
+    // TODO temporary, remove must be implemented in listview
+    if(_listView != null){ _listView.clear(); params.clear(); }
+    this.getAvailableParam();
+    if(_listView != null) _listView.refresh();
+  }
+
+  private void getAvailableParam()
+  {
+    String[] tmp = _gmsh.getParams();
+    for(String s : tmp){ // for each parameter in ONEALB
+      boolean found = false;
+      for(int i = 0; i < params.size(); i++){ // for each parameter in the GUI
+        Parameter p = params.get(i);
+        if(s.split(Character.toString((char)0x03))[2].equals(p.getName())){
+          // the parameter already exist, just refresh it
+          if(p.getType().equals("ParameterNumber")){
+            if(((ParameterNumber)p).fromString(s) == -1){
+              params.remove(i);
+            }
+          }
+          else if(p.getType().equals("ParameterString")){
+            if(((ParameterString)p).fromString(s) == -1)
+              params.remove(i);
+          }
+          found = true;
+          break;
+        }
+      }
+      if(found) continue;
+      // add new parameter
+      if(s.split(Character.toString((char)0x03))[1].equals("number")){
+        final ParameterNumber mParam = new ParameterNumber(_listView.getContext(), _gmsh, "");
+        if(mParam.fromString(s) == -1) continue;
+        mParam.setOnParameterChangedListener(new ParameterNumber.OnParameterChangedListener() {
+            public void OnParameterChanged() {
+              if(_gmsh.onelabCB("check") > 0 && mListener != null)
+                mListener.OnModelOptionsChanged();
+              refresh();
+            }
+          });
+        params.add(mParam);
+        if(_listView != null)
+          _listView.addItem(mParam.getSectionName(), mParam.getView());
+      }
+      else if(s.split("|")[1].equals("string")){
+        ParameterString mParam = new ParameterString(_listView.getContext(), _gmsh, "");
+        if(mParam.fromString(s) != -1){
+          mParam.setOnParameterChangedListener
+            (new ParameterString.OnParameterChangedListener() {
+                public void OnParameterChanged() {
+                  if(_gmsh.onelabCB("check") > 0 && mListener != null)
+                    mListener.OnModelOptionsChanged();
+                  refresh();
+                }
+              });
+          params.add(mParam);
+          if(_listView != null)
+            _listView.addItem(mParam.getSectionName(), mParam.getView());
+        }
+      }
+    }
+  }
+  private OnModelOptionsChangedListener mListener;
+  public void setOnModelOptionsChangedListener(OnModelOptionsChangedListener listener)
+  {
+    mListener = listener;
+  }
+  public interface OnModelOptionsChangedListener
+  {
+    void OnModelOptionsChanged();
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsPostProcessingFragment.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsPostProcessingFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..376a5dd11fb009d1825af72f30d240b1ef739de2
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/OptionsPostProcessingFragment.java
@@ -0,0 +1,120 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import java.util.ArrayList;
+import java.lang.Math;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+
+public class OptionsPostProcessingFragment extends Fragment{
+  private Gmsh _gmsh;
+  private int _pview;
+
+  public static OptionsPostProcessingFragment newInstance(Gmsh g, int p)
+  {
+    OptionsPostProcessingFragment fragment = new OptionsPostProcessingFragment();
+    Bundle bundle = new Bundle();
+    bundle.putParcelable("Gmsh", g);
+    bundle.putInt("PView", p);
+    fragment.setArguments(bundle);
+    return fragment;
+  }
+
+  private OptionsPostProcessingFragment()
+  {
+    super();
+  }
+
+  @Override
+  public void onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    _gmsh = getArguments().getParcelable("Gmsh");
+    _pview = getArguments().getInt("PView");
+  }
+
+  @Override
+  public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                           Bundle savedInstanceState)
+  {
+    getActivity().getActionBar().setTitle(_gmsh.getStringOption("View", "Name", _pview));
+    LinearLayout layout =  (LinearLayout)inflater.inflate(R.layout.fragment_postprocessing,
+                                                          container, false);
+    final Spinner intervalsType = (Spinner)layout.findViewById(R.id.intervals_type);
+    intervalsType.setEnabled(_gmsh.getDoubleOption("View", "Visible", _pview) > 0.);
+    ArrayList<String> choices;
+    ArrayAdapter<String> adapter;
+    choices = new ArrayList<String>();
+    choices.add("Iso-values");
+    choices.add("Continous map");
+    choices.add("Filled iso-values");
+    adapter = new ArrayAdapter<String>(container.getContext(),
+                                       android.R.layout.simple_spinner_dropdown_item, choices);
+    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    intervalsType.setAdapter(adapter);
+    intervalsType.setSelection((int)_gmsh.getDoubleOption("View", "IntervalsType", _pview) - 1);
+    intervalsType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+        public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+          _gmsh.setDoubleOption("View", "IntervalsType", pos + 1, _pview);
+          // TODO glView.requestRender();
+        }
+        // Unused Auto-generated method stub
+        public void onNothingSelected(AdapterView<?> arg0) {}
+      });
+
+    final SeekBar intervals = (SeekBar)layout.findViewById(R.id.intervals);
+    intervals.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+        public void onStopTrackingTouch(SeekBar seekBar) {
+          double d = seekBar.getProgress();
+          if(d < 1.) d = 1.;
+          _gmsh.setDoubleOption("View", "NbIso", d, _pview);
+        }
+        // UNUSED Auto-generated method stub
+        public void onStartTrackingTouch(SeekBar seekBar) {}
+        // UNUSED Auto-generated method stub
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { }
+      });
+    intervals.setProgress((int)_gmsh.getDoubleOption("View", "NbIso", _pview));
+
+    final SeekBar raiseZ = (SeekBar)layout.findViewById(R.id.raisez);
+    raiseZ.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+        public void onStopTrackingTouch(SeekBar seekBar) {
+          double maxval = Math.max(Math.abs(_gmsh.getDoubleOption("View", "Min", _pview)),
+                                   Math.abs(_gmsh.getDoubleOption("View", "Max", _pview)));
+          if(maxval == 0.) maxval = 1.;
+          double val2 = 2. * _gmsh.getDoubleOption("General", "BoundingBoxSize", 0) / maxval;
+          // map [0,100] to [-val2,val2]
+          double d = 2 * val2 * (seekBar.getProgress() / 100. - 0.5);
+          _gmsh.setDoubleOption("View", "RaiseZ", d, _pview);
+        }
+        // UNUSED Auto-generated method stub
+        public void onStartTrackingTouch(SeekBar seekBar) {}
+        // UNUSED Auto-generated method stub
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { }
+      });
+    double maxval = Math.max(Math.abs(_gmsh.getDoubleOption("View", "Min", _pview)),
+                             Math.abs(_gmsh.getDoubleOption("View", "Max", _pview)));
+    if(maxval == 0.) maxval = 1.;
+    double val2 = 2. * _gmsh.getDoubleOption("General", "BoundingBoxSize", 0) / maxval;
+    // map [-val2,val2] to [0,100]
+    double d = 100. * (_gmsh.getDoubleOption("View", "RaiseZ", _pview) / (2 * val2) + 0.5);
+    raiseZ.setProgress((int)d);
+    return layout;
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Parameter.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Parameter.java
new file mode 100644
index 0000000000000000000000000000000000000000..e933709292db045373828c07f093b85bb8bc5e65
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Parameter.java
@@ -0,0 +1,122 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class Parameter {
+  protected Context _context;
+  protected Gmsh _gmsh;
+  protected String _name;
+  protected String _label;
+  protected boolean _readOnly;
+  protected boolean _changed;
+  protected TextView _title;
+
+  public Parameter(Context context, Gmsh gmsh, String name)
+  {
+    _context = context;
+    _gmsh = gmsh;
+    _readOnly = false;
+    _name = name;
+    _title = new TextView(context);
+    _title.setText(name);
+    _title.setTextAppearance(context, android.R.style.TextAppearance_DeviceDefault_Medium);
+    _title.setTextColor(Color.DKGRAY);
+  }
+  public Parameter(Context context, Gmsh gmsh, String name, boolean readOnly)
+  {
+    this(context, gmsh, name);
+    _readOnly = readOnly;
+    _changed = false;
+  }
+
+  protected void update()
+  {
+    _title.setText(getShortName());
+    if(isReadOnly()) _title.setAlpha(0.423f);
+  }
+
+  public void setName(String name) { _name = name; this.update(); }
+  public void setReadOnly(boolean readOnly) { _readOnly = readOnly; this.update(); }
+  public void setLabel(String label)
+  {
+    _label = label;
+    this.update();
+  }
+  public String getName() { return _name; }
+  public String getShortName()
+  {
+    if(_label != null && _label.length() > 0) return _label;
+    String[] split = _name.split("/");
+    String name = split[split.length-1];
+    while(name.length() > 0 && name.charAt(0) == ' ')
+      name = name.substring(1);
+    while(name.length() > 0 && (name.charAt(0) == '{' || name.charAt(0) == '}'))
+      name = name.substring(1);
+    while(name.length() > 0 && name.charAt(0) >= '0' && name.charAt(0) <= '9')
+      name = name.substring(1);
+    return name;
+  }
+  public String getSectionName()
+  {
+    String name = "";
+    if (_name.contains("/")) {
+      String[] split = _name.split("/");
+      for(int i = 0; i < split.length - 1; i++){
+        String s = split[i];
+        while(s.length() > 0 && s.charAt(0) == ' ')
+          s = s.substring(1);
+        while(s.length() > 0 && (s.charAt(0) == '{' || s.charAt(0) == '}'))
+          s = s.substring(1);
+        while(s.length() > 0 && s.charAt(0) >= '0' && s.charAt(0) <= '9')
+          s = s.substring(1);
+        if(i != 0)
+          name += " > ";
+        name += s;
+      }
+    }
+    return name;
+  }
+  public boolean isReadOnly() { return _readOnly; }
+  public int fromString(String s)
+  {
+    String[] infos = s.split(Character.toString((char)0x03));
+    int pos=0;
+    pos++;// version
+    pos++;// type
+    setName(infos[pos++]);// name
+    if(_name.equals("GetDP/}ModelCheck")) // don't show model checking
+      return -1;
+    setLabel(infos[pos++]);// label
+    pos++;// help
+    pos++;// changedValue
+    if(Integer.parseInt(infos[pos++]) != 1) return -1;// visible
+    this.setReadOnly((infos[pos++].equals("1")));// read only
+    int nAttributes = Integer.parseInt(infos[pos++]);// number of attributes
+    pos+=(nAttributes*2);// key+value
+    int nClients = Integer.parseInt(infos[pos++]);// number of client
+    pos+=(nClients*2);// client+changed
+    this.update();
+    return pos;
+  }
+  public boolean changed()
+  {
+    if(_changed){
+      _changed = false;
+      return true;
+    }
+    return _changed;
+  }
+  public String getType(){ return "Parameter"; }
+  public LinearLayout getView()
+  {
+    LinearLayout paramLayout = new LinearLayout(_context);
+    paramLayout.setOrientation(LinearLayout.VERTICAL);
+    paramLayout.addView(_title);
+    return paramLayout;
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ParameterNumber.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ParameterNumber.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab7001fa1447de114362da35f211e708ae00d596
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ParameterNumber.java
@@ -0,0 +1,329 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.content.Context;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.app.Dialog;
+import android.app.AlertDialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class ParameterNumber extends Parameter {
+  private double _value, _tmpValue, _min, _max, _step;
+  private SeekBar _bar;
+  private ArrayList<Double> _values;
+  private ArrayList<String> _choices;
+  private ArrayAdapter<String> _adapter;
+  private Spinner _spinner;
+  private CheckBox _checkbox;
+  private EditText _edittext;
+  private Stepper _stepper;
+
+  public ParameterNumber(Context context, Gmsh gmsh, String name)
+  {
+    super(context, gmsh, name);
+  }
+  public ParameterNumber(Context context, Gmsh gmsh, String name,
+                         double value, double min, double max, double step)
+  {
+    this(context, gmsh, name);
+    _value = value;
+    _min = min;
+    _max = max;
+    _step = step;
+  }
+  public ParameterNumber(Context context, Gmsh gmsh, String name, boolean readOnly,
+                         double value, double min, double max, double step)
+  {
+    this(context, gmsh, name, value, min, max, step);
+    _readOnly = readOnly;
+  }
+
+  public static String formatDouble(double x)
+  {
+    return String.format("%.6g", x).replaceFirst("\\.?0+(e|$)", "$1");
+  }
+
+  protected void update()
+  {
+    super.update();
+    if(_bar != null) {
+      _title.setText(getShortName() + " (" + formatDouble(_value) + ")");
+      _bar.setMax(100);
+      _bar.setProgress((int)(100*(_value-_min)/(_max-_min)));
+      _bar.setEnabled(!this.isReadOnly());
+    }
+    else if(_spinner != null) {
+      for(int i = 0; i < _choices.size(); i++)
+        if(_values.get(i) == _value)
+          _spinner.setSelection(i, true);
+    }
+    else if(_checkbox != null) {
+      _checkbox.setText(getShortName());
+      _checkbox.setChecked((_value == 0)? false : true);
+    }
+    else if(_edittext != null) {
+      _edittext.setText("" + formatDouble(_value));
+    }
+    else if(_stepper != null) {
+      _stepper.setMaximum((int)Math.round(_max));
+      _stepper.setMinimum((int)Math.round(_min));
+      _stepper.setValue((int)Math.round(_value));
+    }
+  }
+  public void setValue(double value)
+  {
+    if(value == _value) return;
+    _value = value;
+    _changed = true;
+    _gmsh.setParam(getType(), getName(), String.valueOf(value));
+    if(mListener != null) mListener.OnParameterChanged();
+  }
+  public void setMin(double min) { _min = min; this.update(); }
+  public void setMax(double max) { _max = max; this.update(); }
+  public void setStep(double step) { _step = step;this.update(); }
+  public void addChoice(double choice, String value)
+  {
+    if(_values == null) {
+      _values = new ArrayList<Double>();
+      _choices = new ArrayList<String>();
+      _values.add(choice);
+      _choices.add(value);
+      if(_spinner == null) {
+        _spinner = new Spinner(_context);
+        _adapter = new ArrayAdapter<String>(_context,
+                                            android.R.layout.simple_spinner_dropdown_item,
+                                            _choices);
+        _adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        _spinner.setAdapter(_adapter);
+      }
+    }
+    else{
+      for(int i=0;i<_values.size();i++) {
+        if(_values.get(i).equals(choice) && _choices.size() > i) {
+          _choices.set(i, value);
+          return;
+        }
+        else if(_values.get(i).equals(choice)){
+          _choices.add(value);
+          return;
+        }
+      }
+      _values.add(choice);
+      _choices.add(value);
+    }
+  }
+  public double getValue() { return _value; }
+  public double getMax() { return _max; }
+  public double getMin() { return _min; }
+  public double getStep() { return _step; }
+  public int fromString(String s)
+  {
+    int pos = super.fromString(s);
+    if(pos <= 0) return -1; // error
+    String[] infos = s.split(Character.toString((char)0x03));
+    int nValues = Integer.parseInt(infos[pos++]);
+    _value = 0;
+    if(nValues > 0){
+      double values[] = new double[nValues];
+      for(int i = 0; i < nValues; i++){
+        String tmpVal = infos[pos++];
+        if(tmpVal.equals("Inf"))
+          values[i] = 0;
+        else
+          values[i] = Double.parseDouble(tmpVal);
+      }
+      // FIXME: generalize to handle list of values
+      _value = values[0];
+    }
+    this.setMin(Double.parseDouble(infos[pos++]));
+    this.setMax(Double.parseDouble(infos[pos++]));
+    this.setStep(Double.parseDouble(infos[pos++]));
+    pos++;// index
+    int nChoices = Integer.parseInt(infos[pos++]); // choices' size
+    double choices[] = new double[nChoices];
+    for(int i = 0; i < nChoices; i++)
+      choices[i] = Double.parseDouble(infos[pos++]); // choice
+    int nLabels = Integer.parseInt(infos[pos++]); // labels' size
+    if(nChoices == 2 && choices[0] == 0 && choices[1] == 1 && nLabels == 0) {
+      _checkbox = new CheckBox(_context);
+      this.update();
+      return pos;
+    }
+    if(_choices != null) _choices.clear();
+    if(_values != null) _values.clear();
+    for(int i = 0; i < nLabels && nChoices == nLabels; i++){
+      double val = Double.parseDouble(infos[pos++]); // choice
+      this.addChoice(val, infos[pos++]); // label
+    }
+    // ...
+    if(nLabels < 1 && _step == 0)
+      _edittext = new EditText(_context);
+    else if(_step == 1)
+      _stepper = new Stepper(_context);
+    else if(nLabels < 1)
+      _bar = new SeekBar(_context);
+    this.update();
+    return pos;
+  }
+  public String getType(){ return "ParameterNumber"; }
+  public LinearLayout getView()
+  {
+    LinearLayout paramLayout = new LinearLayout(_context);
+    paramLayout.setOrientation(LinearLayout.VERTICAL);
+    paramLayout.addView(_title);
+    if(!_readOnly) paramLayout.setOnLongClickListener(new View.OnLongClickListener(){
+        @Override
+        public boolean onLongClick(View v)
+        {
+          AlertDialog.Builder builder = new AlertDialog.Builder(_context);
+          LinearLayout layout = new LinearLayout(_context);
+          layout.setOrientation(LinearLayout.VERTICAL);
+          TextView label = new TextView(_context);
+          label.setText("Edit value of \n" + _name);
+          EditText edit = new EditText(_context);
+          edit.setText(String.valueOf(_value));
+          edit.addTextChangedListener(new TextWatcher() {
+              public void onTextChanged(CharSequence s, int start, int before, int count) {
+                try {
+                  if(s.length() < 1)  _tmpValue = 1;
+                  _tmpValue = Double.parseDouble(s.toString());
+                }
+                catch(NumberFormatException e) {
+                  _tmpValue = 1;
+                }
+              }
+              // UNUSED Auto-generated method stub
+              public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+              // UNUSED Auto-generated method stub
+              public void afterTextChanged(Editable s) {}
+            });
+          edit.requestFocus();
+          //_context.getWindow().setSoftInputMode
+          //    (WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+          layout.addView(label);
+          layout.addView(edit);
+          builder.setView(layout)
+            .setPositiveButton("OK", new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int id) {
+                  setValue(_tmpValue);
+                }
+              })
+            .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int id) {
+                  // User cancelled the dialog
+                }
+              });
+          builder.create().show();
+          return true;
+        }
+      });
+    if(_spinner != null) {
+      paramLayout.addView(_spinner);
+      _spinner.setEnabled(!_readOnly);
+      _spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+          public void onNothingSelected(AdapterView<?> arg0) {}
+          public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+            setValue(_values.get(pos));
+          }
+        });
+    }
+    else if(_bar != null) {
+      paramLayout.addView(_bar);
+      _bar.setEnabled(!_readOnly);
+      _bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+          public void onStopTrackingTouch(SeekBar seekBar) {
+            setValue(getMin() + (getMax() - getMin())*seekBar.getProgress()/100);
+          }
+          public void onStartTrackingTouch(SeekBar seekBar) {}
+          public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
+        });
+    }
+    else if(_checkbox != null) {
+      paramLayout.removeView(_title);
+      paramLayout.addView(_checkbox);
+      _checkbox.setEnabled(!_readOnly);
+      _checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+          public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            setValue((isChecked)? 1 : 0);
+          }
+        });
+    }
+    else if(_edittext != null){
+      paramLayout.addView(_edittext);
+      _edittext.setEnabled(!_readOnly);
+
+      _edittext.setOnKeyListener(new View.OnKeyListener() {
+          public boolean onKey(View v, int keyCode, KeyEvent event) {
+            if(keyCode == KeyEvent.KEYCODE_ENTER){ // hide the keyboard
+              InputMethodManager imm = (InputMethodManager)_context.getSystemService
+                (Context.INPUT_METHOD_SERVICE);
+              imm.hideSoftInputFromWindow(_edittext.getWindowToken(), 0);
+              setValue(_value);
+              _edittext.clearFocus();
+              return true;
+            }
+            if(keyCode > KeyEvent.KEYCODE_9 &&
+               keyCode != KeyEvent.KEYCODE_NUMPAD_DOT &&
+               (keyCode <KeyEvent.KEYCODE_NUMPAD_0 ||
+                keyCode >KeyEvent.KEYCODE_NUMPAD_9) &&
+               keyCode != KeyEvent.KEYCODE_DEL)
+              return true;
+            return false;
+          }
+        });
+      _edittext.addTextChangedListener(new TextWatcher() {
+          public void onTextChanged(CharSequence s, int start, int before, int count) {
+            try {
+              if(s.length() < 1) _value = 1;
+              else _value = Double.parseDouble(s.toString());
+            }
+            catch(NumberFormatException e){
+              _value = 1;
+              //_edittext.setText("");
+            }
+          }
+          // UNUSED Auto-generated method stub
+          public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+          // UNUSED Auto-generated method stub
+          public void afterTextChanged(Editable s) {}
+        });
+    }
+    else if(_stepper != null) {
+      paramLayout.addView(_stepper);
+      _stepper.setOnValueChangedListener(new Stepper.OnValueChangedListener() {
+          public void onValueChanged() {
+            setValue(_stepper.getValue());
+          }
+        });
+    }
+    return paramLayout;
+  }
+  private OnParameterChangedListener mListener;
+  public void setOnParameterChangedListener(OnParameterChangedListener listener)
+  {
+    mListener = listener;
+  }
+  public interface OnParameterChangedListener
+  {
+    void OnParameterChanged();
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ParameterString.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ParameterString.java
new file mode 100644
index 0000000000000000000000000000000000000000..b728d34fbf780d81771d4658dc6709cf00ef0c79
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/ParameterString.java
@@ -0,0 +1,176 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+
+public class ParameterString extends Parameter{
+  private String _kind;
+  private int _index;
+  private ArrayList<String> _choices;
+  private ArrayAdapter<String> _adapter;
+  private Spinner _spinner;
+  private EditText _edittext;
+
+  public ParameterString(Context context, Gmsh gmsh, String name)
+  {
+    super(context, gmsh, name);
+    _choices = new ArrayList<String>();
+    _choices.add("-"); // Default choice
+  }
+
+  private void createSpinner()
+  {
+    if(_spinner != null) return;
+    _spinner = new Spinner(_context);
+    _adapter = new ArrayAdapter<String>(_context,
+                                        android.R.layout.simple_spinner_dropdown_item,
+                                        _choices);
+    _adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    _spinner.setAdapter(_adapter);
+  }
+
+  protected void update()
+  {
+    super.update();
+    if(_spinner != null)
+      _spinner.setSelection(_index);
+    else if(_edittext != null && _choices.size() > 0)
+      _edittext.setText(_choices.get(0));
+  }
+
+  public void setValue(int index)
+  {
+    if(index == _index) return;
+    _changed = true;
+    _index = index;
+    _gmsh.setParam(getType(), getName(), _choices.get(_index));
+    if(mListener != null) mListener.OnParameterChanged();
+  }
+
+  public void setValue(String value)
+  {
+    int index = _choices.indexOf(value);
+    if(index < 0) { // the value is not in the list, add it
+      this.addChoices(value);
+      index = _choices.indexOf(value);
+    }
+    if(index == _index) return;
+    _changed = true;
+    _index = index;
+    _gmsh.setParam(getType(), getName(), value);
+    if(mListener != null) mListener.OnParameterChanged();
+  }
+
+  public void setKind(String kind) { _kind = kind; }
+  public void addChoices(String choice)
+  {
+    if(_edittext == null && _spinner == null) createSpinner();
+    for(String c : _choices) // do not add a duplicate value
+      if(c.equals(choice))return;
+    if(_choices.get(0).equals("-")) // remove the default choice with the first added choice
+      _choices.remove(0);
+    _choices.add(choice);
+    this.update();
+  }
+  public String getValue() {
+    if( _index < 0)
+      return "";
+    return _choices.get(_index);
+  }
+  public String getKind() { return _kind; }
+  public int getIndex() { return _index; }
+  public ArrayList<String> getChoices() { return _choices; }
+  public int fromString(String s)
+  {
+    int pos = super.fromString(s);
+    if(pos <= 0) return -1; // error
+    String[] infos = s.split(Character.toString((char)0x03));
+    int nValues = Integer.parseInt(infos[pos++]);
+    String value = "";
+    if(nValues > 0){
+      String values[] = new String[nValues];
+      for(int i = 0; i < nValues; i++){
+        values[i] = infos[pos++];
+      }
+      value = values[0];
+    }
+    setKind(infos[pos++]); // generic file
+    if(_kind.equals("file"))
+      return -1;
+    int nChoices = Integer.parseInt(infos[pos++]);
+    if(nChoices < 1 && _kind.equals("generic"))
+      _edittext = new EditText(_context);
+    if(_choices != null)_choices.clear();
+    for(int i=0;i<nChoices;i++) this.addChoices(infos[pos++]);
+    // ...
+    setValue(value);
+    this.update();
+    return pos;
+  }
+  public String getType(){ return "ParameterString"; }
+  public LinearLayout getView()
+  {
+    LinearLayout paramLayout = new LinearLayout(_context);
+    paramLayout.setOrientation(LinearLayout.VERTICAL);
+    paramLayout.addView(_title);
+    if(_spinner != null){
+      paramLayout.addView(_spinner);
+      _spinner.setEnabled(!_readOnly);
+      _spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+          public void onNothingSelected(AdapterView<?> arg0) {}
+          public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+            setValue(pos);
+          }
+        });
+    }
+    else if(_edittext != null){
+      paramLayout.addView(_edittext);
+      _edittext.setEnabled(!_readOnly);
+      _edittext.setOnKeyListener(new View.OnKeyListener() {
+          public boolean onKey(View v, int keyCode, KeyEvent event) {
+            if(keyCode == KeyEvent.KEYCODE_ENTER){ // hide the keyboard
+              InputMethodManager imm = (InputMethodManager)_context.getSystemService
+                (Context.INPUT_METHOD_SERVICE);
+              imm.hideSoftInputFromWindow(_edittext.getWindowToken(), 0);
+              _edittext.clearFocus();
+              return true;
+            }
+            return false;
+          }
+        });
+      _edittext.addTextChangedListener(new TextWatcher() {
+          public void onTextChanged(CharSequence s, int start, int before, int count) {
+            _choices.clear(); _choices.add(s.toString());
+          }
+          // UNUSED Auto-generated method stub
+          public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+          public void afterTextChanged(Editable s) {
+            _gmsh.setParam(getType(), getName(), _choices.get(0));
+          }
+        });
+    }
+    return paramLayout;
+  }
+  private OnParameterChangedListener mListener;
+  public void setOnParameterChangedListener(OnParameterChangedListener listener)
+  {
+    mListener = listener;
+  }
+  public interface OnParameterChangedListener
+  {
+    void OnParameterChanged();
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/PostProcessingActivity.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/PostProcessingActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..c83b64389317c1d78bb4c85c821fb3bcd2f3e986
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/PostProcessingActivity.java
@@ -0,0 +1,35 @@
+package org.geuz.onelab;
+
+import org.geuz.onelab.Gmsh;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.WindowManager;
+
+public class PostProcessingActivity extends Activity{
+  @Override
+  protected void onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                         WindowManager.LayoutParams.FLAG_FULLSCREEN);
+    setContentView(R.layout.activity_fragment);
+    Bundle extra = getIntent().getExtras();
+    int id = extra.getInt("PView", 0);
+    Gmsh gmsh = extra.getParcelable("Gmsh");
+    getActionBar().setDisplayHomeAsUpEnabled(true);
+    OptionsPostProcessingFragment optionsFragment =
+      OptionsPostProcessingFragment.newInstance(gmsh, id);
+    getFragmentManager().beginTransaction().add(R.id.model_fragment,
+                                                optionsFragment).commit();
+  }
+
+  @Override
+  public boolean onMenuItemSelected(int featureId, MenuItem item)
+  {
+    if(item.getItemId() == android.R.id.home)
+      this.finish();
+    return super.onMenuItemSelected(featureId, item);
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/SeparatedListView.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/SeparatedListView.java
new file mode 100644
index 0000000000000000000000000000000000000000..44c3019f23041b816078f19362beb44fdf893a13
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/SeparatedListView.java
@@ -0,0 +1,173 @@
+package org.geuz.onelab;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class SeparatedListView extends ListView{
+  private SeparatedListAdaptater adapter;
+  private Context _context;
+
+  public SeparatedListView(Context context)
+  {
+    super(context);
+    _context = context;
+    adapter = new SeparatedListAdaptater();
+    this.setAdapter(adapter);
+  }
+  public SeparatedListView(Context context, AttributeSet attrs)
+  {
+    this(context);
+  }
+  public SeparatedListView(Context context, View[] footer)
+  {
+    super(context);
+    _context = context;
+    for(View v : footer)
+      this.addFooterView(v);
+    adapter = new SeparatedListAdaptater();
+    this.setAdapter(adapter);
+  }
+  public void addItem(String header, View item)
+  {
+    TextView title = (TextView)
+      ((LayoutInflater) _context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
+      .inflate(R.layout.list_header, null);
+    title.setText(header);
+    adapter.addItem(header, title, item);
+    adapter.notifyDataSetChanged();
+    this.invalidateViews();
+  }
+  public int itemsCountInSection(String header)
+  {
+    return adapter.getCountForSection(header);
+  }
+  public void refresh()
+  {
+    adapter.notifyDataSetChanged();
+    this.invalidateViews();
+  }
+  public void clear()
+  {
+    adapter.clear();
+    adapter.notifyDataSetChanged();
+  }
+  private class Section{
+    private String _name;
+    private List<View> _items;
+    public Section(String name)
+    {
+      _name = name;
+      _items = new ArrayList<View>();
+    }
+    public void addItem(View v)
+    {
+      _items.add(v);
+    }
+    public String getName()
+    {
+      return _name;
+    }
+    public int getItemsCount()
+    {
+      return _items.size();
+    }
+    public View getItem(int pos)
+    {
+      return _items.get(pos);
+    }
+  }
+  private class SeparatedListAdaptater extends BaseAdapter{
+    List<Section> sections;
+    List<View> titles;
+    public SeparatedListAdaptater()
+    {
+      sections = new ArrayList<SeparatedListView.Section>();
+      titles = new ArrayList<View>();
+    }
+    public void addItem(String header, View title, View item)
+    {
+      for(Section s : sections){
+        if(s.getName().equals(header)){
+          s.addItem(item);
+          return;
+        }
+      }
+      Section s = new Section(header);
+      s.addItem(item);
+      sections.add(s);
+      titles.add(title);
+    }
+    //@Override
+    public int getCount()
+    {
+      int count = 0;
+      for(Section s : sections) count += s.getItemsCount() + 1;
+      return count;
+    }
+    public int getCountForSection(String header)
+    {
+      for(Section s : sections)
+        if(s.getName().equals(header))
+          return s.getItemsCount();
+      return 0;
+    }
+    //@Override
+    public Object getItem(int position)
+    {
+      int section = -1, lastPosition = -1;
+      while(lastPosition<position){
+        int itemsCount = sections.get(section+1).getItemsCount();
+        if(lastPosition+1 == position)// this is a section
+          return titles.get(section+1);
+        else if(lastPosition+1+itemsCount >= position){ // the view is in this section
+          if(section<0) return sections.get(section+1).getItem(position-1);
+          return sections.get(section+1).getItem(position-lastPosition-2);
+        }
+        lastPosition+= 1 + itemsCount;
+        section++;
+      }
+      return null;
+    }
+
+    //@Override
+    public long getItemId(int position)
+    {
+      // UNUSED Auto-generated method stub
+      return 0;
+    }
+
+    //@Override
+    public View getView(int position, View convertView, ViewGroup parent)
+    {
+      int section = -1, lastPosition = -1;
+      while(lastPosition<position){
+        int itemsCount = sections.get(section+1).getItemsCount();
+        if(lastPosition+1 == position)// this is a section
+          return titles.get(section+1);
+        else if(lastPosition+1+itemsCount >= position){ // the view is in this section
+          if(section<0) return sections.get(section+1).getItem(position-1);
+          return sections.get(section+1).getItem(position-lastPosition-2);
+        }
+        lastPosition+= 1 + itemsCount;
+        section++;
+      }
+      return null;
+    }
+    public void clear()
+    {
+      sections.clear();
+      titles.clear();
+    }
+
+  }
+
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/SplashScreen.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/SplashScreen.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1e3f9e31500db77f02847d82ca5207ef162b9ed
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/SplashScreen.java
@@ -0,0 +1,104 @@
+package org.geuz.onelab;
+
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+public class SplashScreen extends Activity{
+  private final Handler handler = new Handler() {
+      public void handleMessage(Message msg) {
+        Intent i = new Intent(SplashScreen.this, ModelList.class);
+        startActivity(i);
+        finish();
+      };
+    };
+
+  protected void onCreate(android.os.Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.splash);
+
+    // import built-in models
+    SharedPreferences sharedPref =
+      getSharedPreferences(getPackageName(), Context.MODE_PRIVATE);
+    int codev = 0;
+    try {
+      codev = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
+    }
+    catch (android.content.pm.PackageManager.NameNotFoundException e) {
+    }
+    int modelsv = sharedPref.getInt("OnelabModelsVersion", 0);
+    if(modelsv == 0 || modelsv != codev) {
+      Log.d("Models", "Updating models to version "+codev);
+      SharedPreferences.Editor editor = sharedPref.edit();
+      editor.putInt("OnelabModelsVersion", codev);
+      editor.commit();
+      // import built-in models from from res/raw/
+      ImportZipArchive(new BufferedInputStream(getResources().openRawResource(R.raw.models)));
+    }
+    else{
+      Log.d("Models", "Leaving models as-is (version "+modelsv+")");
+    }
+
+    // import user model
+    Intent intent = getIntent();
+    String action = intent.getAction();
+    if(action != null && action.equals(Intent.ACTION_VIEW)) {
+      Log.d("Models", "Importing user model " + intent.getData());
+      try {
+        ImportZipArchive(getContentResolver().openInputStream(intent.getData()));
+      }
+      catch(IOException e1) {
+        e1.printStackTrace();
+      }
+    }
+
+    final Message msg = new Message();
+    handler.sendMessageDelayed(msg, 500); // 500 milliseconds
+  }
+
+  // Uncompress zip archive into the files directory of the application.
+  private boolean ImportZipArchive(InputStream stream)
+  {
+    try {
+      String path = this.getFilesDir().getAbsolutePath() + File.separator;
+      ZipInputStream zis = new ZipInputStream(stream);
+      byte[] buffer = new byte[1024];
+      ZipEntry ze;
+      while ((ze = zis.getNextEntry()) != null) {
+        String filename = ze.getName();
+        if (ze.isDirectory()) {
+          File fmd = new File(path + filename);
+          fmd.mkdirs();
+          continue;
+        }
+        FileOutputStream fout = new FileOutputStream(path + filename);
+        int count;
+        while ((count = zis.read(buffer)) != -1) {
+          fout.write(buffer, 0, count);
+        }
+        fout.close();
+        zis.closeEntry();
+      }
+      zis.close();
+    }
+    catch(IOException e) {
+      e.printStackTrace();
+      return false;
+    }
+    return true;
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Stepper.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Stepper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a8682c28dd674ca2be521afc4573b59d2a74d4be
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/Stepper.java
@@ -0,0 +1,84 @@
+package org.geuz.onelab;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.Button;
+import android.widget.EditText;
+import android.text.TextWatcher;
+import android.text.Editable;
+
+class Stepper extends LinearLayout{
+  private int _min, _max, _val;
+  private Button _incBtn, _decBtn;
+  private EditText _valTxt;
+  private OnValueChangedListener _listener;
+
+  public Stepper(Context context)
+  {
+    super(context);
+    _max = _min = _val = 0;
+    _incBtn = new Button(context);
+    _decBtn = new Button(context);
+    _valTxt = new EditText(context);
+    _incBtn.setText("+");
+    _decBtn.setText("-");
+    _valTxt.setText(Integer.toString(_val));
+    this.addView(_decBtn);
+    this.addView(_valTxt);
+    this.addView(_incBtn);
+    _incBtn.setOnClickListener(new OnClickListener() {
+        public void onClick(View v) {
+          inc();
+        }
+      });
+    _decBtn.setOnClickListener(new OnClickListener() {
+        public void onClick(View v) {
+          dec();
+        }
+      });
+    _valTxt.addTextChangedListener(new TextWatcher() {
+        public void afterTextChanged(Editable s){}
+        public void beforeTextChanged(CharSequence s, int start, int count, int after){}
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+          try {
+            setValueButText(Integer.parseInt(s.toString()));
+          }
+          catch (NumberFormatException e) {}
+        }
+      });
+  }
+
+  public interface OnValueChangedListener
+  {
+    public void onValueChanged();
+  }
+
+  public void inc(){setValue(_val+1);}
+  public void dec(){setValue(_val-1);}
+
+  public void setOnValueChangedListener(OnValueChangedListener listener)
+  {
+    _listener = listener;
+  }
+  public void setMaximum(int max){_max = max;}
+  public void setMinimum(int min){_min = min;}
+  public void setValue(int val)
+  {
+    setValueButText(val);
+    _valTxt.setText(Integer.toString(_val));
+  }
+  public void setValueButText(int val)
+  {
+    if(_max > _min) {
+      if(val == _max) _incBtn.setEnabled(false);
+      else if(val == _min) _decBtn.setEnabled(false);
+      else {_incBtn.setEnabled(true); _decBtn.setEnabled(true);}
+    }
+    _val = val;
+    if(_listener != null) _listener.onValueChanged();
+  }
+  public int getMaximum(){ return _max; }
+  public int getMinimum(){ return _min; }
+  public int getValue(){ return _val; }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/StringTexture.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/StringTexture.java
new file mode 100644
index 0000000000000000000000000000000000000000..f555ad14eb99e256dbd6224765914c2b92e9d6b9
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/StringTexture.java
@@ -0,0 +1,166 @@
+package org.geuz.onelab;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.opengles.GL10;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Bitmap.CompressFormat;
+import android.opengl.GLUtils;
+import android.os.Environment;
+
+public class StringTexture {
+  private String _text;
+  private Bitmap _bitmap;
+  private int[] _textures = new int[1]; // Texture pointer
+
+  public StringTexture(String s) {
+    _text = s;
+    getBitmapFromText(12.0f, Color.BLACK);
+  }
+
+  private void getBitmapFromText(float textSize, int textColor)
+  {
+    Paint paint = new Paint();
+    paint.setTextSize(textSize);
+    paint.setColor(textColor);
+    paint.setTextAlign(Paint.Align.LEFT);
+    int width = (int) (paint.measureText(_text) + 2.5f); // round
+    int baseline = (int) (textSize + 2.5f);
+    int height = (int) (baseline + paint.descent() + 2.5f);
+    _bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+    _bitmap.eraseColor(Color.TRANSPARENT);
+    Canvas canvas = new Canvas(_bitmap);
+    canvas.setBitmap(_bitmap);
+    canvas.drawText(_text, 0, baseline, paint);
+  }
+
+  private void loadGLTexture(GL10 gl)
+  {
+    if(_bitmap == null) return;
+    gl.glGenTextures(1, _textures, 0);
+
+    gl.glBindTexture(GL10.GL_TEXTURE_2D, _textures[0]);
+
+    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
+    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
+
+    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, _bitmap, 0);
+
+    _bitmap.recycle();
+  }
+
+  public void draw(GL10 gl, int x, int y)
+  {
+    gl.glEnable(GL10.GL_TEXTURE_2D);
+    // VERTEX
+    float vertex[] = {
+      -1.0f, -1.0f,  0.0f,		// bottom left
+      -1.0f,  1.0f,  0.0f,		// top left
+      1.0f, -1.0f,  0.0f,		// bottom right
+      1.0f,  1.0f,  0.0f			// top right
+    };
+    FloatBuffer vertexBuffer;
+    ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertex.length * 4);
+    vertexByteBuffer.order(ByteOrder.nativeOrder());
+    vertexBuffer = vertexByteBuffer.asFloatBuffer();
+    vertexBuffer.put(vertex);
+    vertexBuffer.position(0);
+
+    // TEXTURE
+    FloatBuffer textureBuffer;	// buffer holding the texture coordinates
+    float texture[] = {
+      0.0f, 1.0f,		// top left
+      0.0f, 0.0f,		// bottom left
+      1.0f, 1.0f,		// top right
+      1.0f, 0.0f		// bottom right
+    };
+    ByteBuffer textureByteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
+    textureByteBuffer.order(ByteOrder.nativeOrder());
+    textureBuffer = textureByteBuffer.asFloatBuffer();
+    textureBuffer.put(texture);
+    textureBuffer.position(0);
+    loadGLTexture(gl);
+    gl.glBindTexture(GL10.GL_TEXTURE_2D, _textures[0]);
+
+    // DRAW
+    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+    gl.glColor4f(1,1,1,1);
+    gl.glEnable(GL10.GL_BLEND);
+    gl.glBlendFunc(GL10.GL_SRC_COLOR, GL10.GL_ONE_MINUS_SRC_ALPHA);
+    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
+    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
+    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertex.length / 3);
+    gl.glDisable(GL10.GL_BLEND);
+    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
+    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+    gl.glDisable(GL10.GL_TEXTURE_2D);
+  }
+
+  public static byte[] getBytesFromString(String s, int textSize)
+  {
+    // Generate the bitmap
+    int textColor = Color.BLACK;
+    Paint paint = new Paint();
+    paint.setTextSize(textSize);
+    paint.setColor(textColor);
+    paint.setTextAlign(Paint.Align.LEFT);
+    int width = (int) (paint.measureText(s) + 0.5f); // round
+    int i;
+    for(i=2;i<=width;i*=2); width = i;
+    int baseline = (int) (textSize + 0.5f);
+    int height = (int) (baseline + paint.descent() + 0.5f);
+    for(i=2;i<=height;i*=2); height = i;
+    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+    bitmap.eraseColor(Color.WHITE);
+    Canvas canvas = new Canvas(bitmap);
+    canvas.setBitmap(bitmap);
+    canvas.drawText(s, 0, baseline, paint);
+    // Get the pixel in a map
+    ByteBuffer buffer = ByteBuffer.allocateDirect(width*height);
+    buffer.order(ByteOrder.nativeOrder());
+    buffer.position(0);
+    for(int y = 0; y<height;y++)
+      for(int x = 0; x<width;x++)
+        buffer.put((byte) ((bitmap.getPixel(x, y) == Color.BLACK)? 0xFF : 0x00));
+    buffer.position(0);
+    byte[] b = new byte[buffer.capacity()];
+    buffer.get(b);
+    return b;
+  }
+  public static int getWidthFromString(String s, int textSize)
+  {
+    Paint paint = new Paint();
+    paint.setTextSize(textSize);
+    paint.setTextAlign(Paint.Align.LEFT);
+    int ret = (int) (paint.measureText(s) + 0.5f);
+    int i;
+    for(i=2;i<=ret;i*=2); ret = i;
+    return ret;
+  }
+  public static int getRealWidthFromString(String s, int textSize)
+  {
+    Paint paint = new Paint();
+    paint.setTextSize(textSize);
+    paint.setTextAlign(Paint.Align.LEFT);
+    return (int) (paint.measureText(s) + 0.5f);
+  }
+  public static int getHeightFromString(String s, int textSize)
+  {
+    Paint paint = new Paint();
+    paint.setTextSize(textSize);
+    paint.setTextAlign(Paint.Align.LEFT);
+    int ret = (int) (textSize + 0.5f + paint.descent() + 0.5f);
+    int i;
+    for(i=2;i<=ret;i*=2); ret = i;
+    return ret;
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/mGLSurfaceView.java b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/mGLSurfaceView.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b35244adb2745ace3757cdc6935a4662f44eb43
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/java/org/geuz/onelab/mGLSurfaceView.java
@@ -0,0 +1,100 @@
+package org.geuz.onelab;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.opengl.GLSurfaceView;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.GestureDetector.OnDoubleTapListener;
+import android.view.GestureDetector.OnGestureListener;
+import android.view.ScaleGestureDetector.OnScaleGestureListener;
+
+class mGLSurfaceView extends GLSurfaceView {
+  private float scaleFactor = 1f;
+  private GestureDetector gesture;
+  private ScaleGestureDetector scaleGesture;
+  private GLESRender _renderer;
+  private boolean _rotate;
+
+  public mGLSurfaceView(Context context, GLESRender renderer)
+  {
+    super(context);
+    _renderer = renderer;
+    gesture = new GestureDetector(context, new GestureListener());
+    scaleGesture = new ScaleGestureDetector(context, new OnScaleGestureListener() {
+        public void onScaleEnd(ScaleGestureDetector detector)
+        {
+          _renderer.endInteraction(detector.getFocusX(), detector.getFocusY());
+        }
+        public boolean onScaleBegin(ScaleGestureDetector detector)
+        {
+          _renderer.startInteraction(detector.getFocusX(), detector.getFocusY());
+          return true;
+        }
+        public boolean onScale(ScaleGestureDetector detector)
+        {
+          scaleFactor *= detector.getScaleFactor();
+          scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 50.0f)); // limit the scale factor
+          _renderer.scaleModel(scaleFactor);
+          requestRender();
+          return true;
+
+        }
+      });
+  }
+
+  @Override
+  public boolean onTouchEvent(MotionEvent event)
+  {
+    scaleGesture.onTouchEvent(event);
+    return gesture.onTouchEvent(event);
+  }
+
+  private class GestureListener implements OnGestureListener, OnDoubleTapListener{
+    public boolean onDown(MotionEvent e)
+    {
+      _renderer.startInteraction(e.getX(),e.getY());
+      return true;
+    }
+    // UNUSED Auto-generated method stub
+    public boolean onFling(MotionEvent e1, MotionEvent e2,
+                           float velocityX, float velocityY) { return false; }
+    // UNUSED Auto-generated method stub
+    public void onLongPress(MotionEvent e) { }
+    public boolean onScroll(MotionEvent e1, MotionEvent e2,
+                            float distanceX, float distanceY)
+    {
+      if(e1.getPointerCount() > 1 || e2.getPointerCount() > 1) return false;
+      if(_rotate)
+        _renderer.rotateModel(e2.getX(), e2.getY());
+      else
+        _renderer.translateModel(e2.getX(), e2.getY());
+      requestRender();
+      return true;
+    }
+    // UNUSED Auto-generated method stub
+    public void onShowPress(MotionEvent e) { }
+    // UNUSED Auto-generated method stub
+    public boolean onSingleTapUp(MotionEvent e) { return false; }
+    // UNUSED Auto-generated method stub
+    public boolean onDoubleTap(MotionEvent e) { return false; }
+    public boolean onDoubleTapEvent(MotionEvent e)
+    {
+      scaleFactor = 1f;
+      _renderer.resetModelPosition();
+      requestRender();
+      return true;
+    }
+    // UNUSED Auto-generated method stub
+    public boolean onSingleTapConfirmed(MotionEvent e) { return false; }
+
+  }
+  public boolean getRotate() { return _rotate; }
+  public void setRotate(boolean r) { _rotate = r; }
+  public void resetScale()
+  {
+    scaleFactor = 1f;
+    _renderer.scaleModel(scaleFactor);
+  }
+}
diff --git a/contrib/mobile/Android/app/src/main/res/drawable-hdpi/ic_launcher.png b/contrib/mobile/Android/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..c8b799020dcd4a7028f1efda3117824840b5db1b
Binary files /dev/null and b/contrib/mobile/Android/app/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/contrib/mobile/Android/app/src/main/res/drawable-ldpi/ic_launcher.png b/contrib/mobile/Android/app/src/main/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..19a3ab1f5693c445ae1a77ea4e1cdb1b58c379cc
Binary files /dev/null and b/contrib/mobile/Android/app/src/main/res/drawable-ldpi/ic_launcher.png differ
diff --git a/contrib/mobile/Android/app/src/main/res/drawable-mdpi/ic_launcher.png b/contrib/mobile/Android/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..262b7f517d3a65478f8bc319f9ae2eee05974024
Binary files /dev/null and b/contrib/mobile/Android/app/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/contrib/mobile/Android/app/src/main/res/drawable-mdpi/icon_rotate.png b/contrib/mobile/Android/app/src/main/res/drawable-mdpi/icon_rotate.png
new file mode 100644
index 0000000000000000000000000000000000000000..28ae466429a7acf9ea9e4b0ab3f17a84c9668473
Binary files /dev/null and b/contrib/mobile/Android/app/src/main/res/drawable-mdpi/icon_rotate.png differ
diff --git a/contrib/mobile/Android/app/src/main/res/drawable-mdpi/icon_translate.png b/contrib/mobile/Android/app/src/main/res/drawable-mdpi/icon_translate.png
new file mode 100644
index 0000000000000000000000000000000000000000..acde225dd957e6e02df7557448b4d2a66ebfb032
Binary files /dev/null and b/contrib/mobile/Android/app/src/main/res/drawable-mdpi/icon_translate.png differ
diff --git a/contrib/mobile/Android/app/src/main/res/layout/activity_fragment.xml b/contrib/mobile/Android/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4fc7a01d57f810febcdfb3813c4d22a5d49af376
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:id="@+id/model_fragment"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:layout_marginLeft="0dp"
+             android:layout_marginRight="0dp" />
diff --git a/contrib/mobile/Android/app/src/main/res/layout/activity_model.xml b/contrib/mobile/Android/app/src/main/res/layout/activity_model.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f50072f640926b5e3d6990eeb6d3f9d86f2a49a1
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/activity_model.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:tools="http://schemas.android.com/tools"
+             android:id="@+id/model_fragment"
+             android:name="org.geuz.onelab.ModelFragment"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:layout_marginLeft="0dp"
+             android:layout_marginRight="0dp"
+             tools:context=".MainActivity" />
diff --git a/contrib/mobile/Android/app/src/main/res/layout/activity_twopane.xml b/contrib/mobile/Android/app/src/main/res/layout/activity_twopane.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b9ea76e989d3dbcd38fb761d1a520a75e69e1150
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/activity_twopane.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:baselineAligned="false"
+              android:divider="?android:attr/dividerHorizontal"
+              android:orientation="horizontal"
+              android:showDividers="middle"
+              tools:context=".MainActivity" >
+  <FrameLayout android:id="@+id/parameter_fragment"
+               android:name="org.geuz.onelab.OptionsDisplayFragment"
+               android:layout_marginTop="48dp"
+               android:layout_width="0dp"
+               android:layout_height="match_parent"
+               android:layout_weight="1" />
+  <FrameLayout android:id="@+id/model_fragment"
+               android:name="org.geuz.onelab.ModelFragment"
+               android:layout_width="0dp"
+               android:layout_height="match_parent"
+               android:layout_marginLeft="0dp"
+               android:layout_marginRight="0dp"
+               android:layout_weight="2" />
+</LinearLayout>
diff --git a/contrib/mobile/Android/app/src/main/res/layout/control_bar.xml b/contrib/mobile/Android/app/src/main/res/layout/control_bar.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a8a8e0e86c269c3f61660d2e7dd8e8708788ab9f
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/control_bar.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical"
+              android:background="@android:color/black"
+              android:alpha=".50"
+              android:gravity="center" >
+  <SeekBar android:id="@+id/controlStepper"
+           android:layout_width="match_parent"
+           android:layout_height="wrap_content" />
+  <LinearLayout android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:orientation="horizontal"
+                android:background="@android:color/black"
+                android:alpha=".50"
+                android:gravity="center" >
+    
+    <ImageButton android:id="@+id/controlPrev"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:src="@android:drawable/ic_media_previous"
+                 android:contentDescription="previous" />
+    
+    <ImageButton android:id="@+id/controlPlay"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:src="@android:drawable/ic_media_play"
+                 android:contentDescription="play" />
+    
+    <ImageButton android:id="@+id/controlNext"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:src="@android:drawable/ic_media_next"
+                 android:contentDescription="next" />
+    
+  </LinearLayout>
+</LinearLayout>
diff --git a/contrib/mobile/Android/app/src/main/res/layout/fragment_model.xml b/contrib/mobile/Android/app/src/main/res/layout/fragment_model.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b65fd9dcbb74f1b240a936234c450d179dfb1708
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/fragment_model.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:id="@+id/glViewLayout" />
\ No newline at end of file
diff --git a/contrib/mobile/Android/app/src/main/res/layout/fragment_options.xml b/contrib/mobile/Android/app/src/main/res/layout/fragment_options.xml
new file mode 100644
index 0000000000000000000000000000000000000000..01cbbb1f71b45d27da99c070e62f53ded3ef0fe6
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/fragment_options.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:padding="4dip"
+              android:gravity="center_horizontal"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+  
+  <FrameLayout android:id="@+id/options_fragment"
+               android:name="org.geuz.onelab.OptionsModelFragment"
+               android:layout_width="match_parent"
+               android:layout_height="0px"
+               android:layout_weight="1" />
+  
+  <View android:layout_width="fill_parent"
+        android:layout_height="1dp"
+        android:background="@android:color/darker_gray" />
+  <LinearLayout android:orientation="horizontal"
+		android:gravity="center" android:measureWithLargestChild="true"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:layout_weight="0">
+    <Button android:id="@+id/goto_options_model"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Model">
+    </Button>
+    <Button android:id="@+id/goto_options_display"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Display">
+    </Button>
+  </LinearLayout>
+  
+</LinearLayout>
diff --git a/contrib/mobile/Android/app/src/main/res/layout/fragment_options_display.xml b/contrib/mobile/Android/app/src/main/res/layout/fragment_options_display.xml
new file mode 100644
index 0000000000000000000000000000000000000000..480d0ae008f9aca879026134c34e67d5c14381f3
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/fragment_options_display.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<org.geuz.onelab.SeparatedListView xmlns:android="http://schemas.android.com/apk/res/android"
+                                   android:layout_width="match_parent"
+                                   android:layout_height="match_parent"
+                                   android:id="@+id/displayOptionsList">
+</org.geuz.onelab.SeparatedListView>
diff --git a/contrib/mobile/Android/app/src/main/res/layout/fragment_postprocessing.xml b/contrib/mobile/Android/app/src/main/res/layout/fragment_postprocessing.xml
new file mode 100644
index 0000000000000000000000000000000000000000..20d5adc76ba96bd319ed764221b2a410da2fdcc4
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/fragment_postprocessing.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical"
+              android:padding="15dp" >
+  <TextView
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:text="@string/postpro_intervalstype" />
+  <Spinner
+      android:id="@+id/intervals_type"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content" />
+  <TextView
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:text="@string/postpro_intervals" />
+  <SeekBar 
+      android:id="@+id/intervals"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:max="100" />
+  <TextView
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:text="@string/postpro_raisez" />
+  <SeekBar 
+      android:id="@+id/raisez"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:max="100" />
+</LinearLayout>
diff --git a/contrib/mobile/Android/app/src/main/res/layout/list_header.xml b/contrib/mobile/Android/app/src/main/res/layout/list_header.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9244e49ce630879d8152480a6b04f3322bb1064e
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/list_header.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="@+id/list_header_title"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:paddingTop="2dip"
+          android:paddingBottom="2dip"
+          android:paddingLeft="5dip"
+          style="?android:attr/listSeparatorTextViewStyle"
+          >
+  
+</TextView>
\ No newline at end of file
diff --git a/contrib/mobile/Android/app/src/main/res/layout/model.xml b/contrib/mobile/Android/app/src/main/res/layout/model.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a936060d6e55cfbd0d8cdf3b7a04f9a845d491e8
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/model.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:gravity="center_vertical">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_height="64dp"
+        android:layout_width="64dp"
+	android:layout_gravity="center"
+        android:contentDescription="preview"
+        android:src="@drawable/ic_launcher"
+	android:paddingRight="10dp"
+        android:paddingTop="5dp"
+        android:paddingBottom="5dp" />
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="#ffffff"
+            android:textIsSelectable="false" />
+
+        <TextView
+            android:id="@+id/description"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="#ffffff"
+            android:textIsSelectable="false" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/contrib/mobile/Android/app/src/main/res/layout/splash.xml b/contrib/mobile/Android/app/src/main/res/layout/splash.xml
new file mode 100644
index 0000000000000000000000000000000000000000..18ce46253b1af86b13ca9eb92d887733b5de5c19
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/layout/splash.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent" 
+                android:background="@color/black">
+  <ImageView android:id="@+id/splashImage"
+             android:layout_centerInParent="true"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:contentDescription="@string/app_name"
+             android:src="@drawable/ic_launcher" />
+</RelativeLayout>
diff --git a/contrib/mobile/Android/app/src/main/res/values-sw600dp/refs.xml b/contrib/mobile/Android/app/src/main/res/values-sw600dp/refs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..398bcc82160b67434bc96093d22685b82c72d7c0
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/values-sw600dp/refs.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item type="layout" name="main_activity_layout">@layout/activity_twopane</item>
+    
+</resources>
\ No newline at end of file
diff --git a/contrib/mobile/Android/app/src/main/res/values-v11/styles.xml b/contrib/mobile/Android/app/src/main/res/values-v11/styles.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d408cbc37ad420e5564150e1989cb39f73539d35
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/values-v11/styles.xml
@@ -0,0 +1,5 @@
+<resources>
+
+    <style name="AppTheme" parent="android:Theme.Holo.Light" />
+
+</resources>
\ No newline at end of file
diff --git a/contrib/mobile/Android/app/src/main/res/values-v14/styles.xml b/contrib/mobile/Android/app/src/main/res/values-v14/styles.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1c089a788c5a75c13949344137ed3698f6d2341e
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/values-v14/styles.xml
@@ -0,0 +1,5 @@
+<resources>
+
+    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar" />
+
+</resources>
\ No newline at end of file
diff --git a/contrib/mobile/Android/app/src/main/res/values/colors.xml b/contrib/mobile/Android/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4dd336640c4cc1f17c33cbc1cba454265789c88c
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/values/colors.xml
@@ -0,0 +1,3 @@
+<resources>
+  <color name="black">#ff000000</color>
+</resources>
diff --git a/contrib/mobile/Android/app/src/main/res/values/refs.xml b/contrib/mobile/Android/app/src/main/res/values/refs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f6ed1412d47ff1b524751851f50fe1abf289049e
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/values/refs.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item type="layout" name="main_activity_layout">@layout/activity_fragment</item>
+    
+</resources>
\ No newline at end of file
diff --git a/contrib/mobile/Android/app/src/main/res/values/strings.xml b/contrib/mobile/Android/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..86b0d470c5874528c24e38028f242f0b3342b024
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/values/strings.xml
@@ -0,0 +1,13 @@
+<resources>
+  <string name="app_name">Onelab</string>
+  <string name="title_activity_main">Onelab</string>
+  <string name="title_activity_about">About</string>
+  <string name="title_activity_options">Parameters</string>
+  <string name="menu_parameters">Parameters</string>
+  <string name="menu_run">Run</string>
+  <string name="menu_stop">Stop</string>
+  <string name="menu_save">Save</string>
+  <string name="postpro_intervalstype">Intervals type</string>
+  <string name="postpro_intervals">Intervals</string>
+  <string name="postpro_raisez">Raise (Z)</string>
+</resources>
diff --git a/contrib/mobile/Android/app/src/main/res/values/styles.xml b/contrib/mobile/Android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a0978b340d6b44a792c82311da7a19fc86530fca
--- /dev/null
+++ b/contrib/mobile/Android/app/src/main/res/values/styles.xml
@@ -0,0 +1,3 @@
+<resources>
+  <style name="AppTheme" parent="android:Theme.Light" />
+</resources>
diff --git a/contrib/mobile/Android/build.gradle b/contrib/mobile/Android/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..82d00ddb2ab9b3b82e7c5749b7458235db35d22e
--- /dev/null
+++ b/contrib/mobile/Android/build.gradle
@@ -0,0 +1,17 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+    repositories {
+       google()
+       jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.2.1'
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+    }
+}
diff --git a/contrib/mobile/Android/local.properties b/contrib/mobile/Android/local.properties
new file mode 100644
index 0000000000000000000000000000000000000000..fe6a64b9e91ba91adea48e760d1f99a2f23e257f
--- /dev/null
+++ b/contrib/mobile/Android/local.properties
@@ -0,0 +1,12 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+#Sat Jun 03 17:49:42 CEST 2017
+ndk.dir=/Users/geuzaine/Library/Android/sdk/ndk-bundle
+sdk.dir=/Users/geuzaine/Library/Android/sdk
diff --git a/contrib/mobile/Android/settings.gradle b/contrib/mobile/Android/settings.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..e7b4def49cb53d9aa04228dd3edb14c9e635e003
--- /dev/null
+++ b/contrib/mobile/Android/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/contrib/mobile/CMakeLists.txt b/contrib/mobile/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6805e5a503777403a6f8fc3c6182343a558203ce
--- /dev/null
+++ b/contrib/mobile/CMakeLists.txt
@@ -0,0 +1,230 @@
+cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
+
+if(APPLE)
+  option(ENABLE_BUILD_IOS "Build XCode project for iOS (ARM)" OFF)
+  option(ENABLE_BUILD_IOS_EMULATOR "Build XCode project for iOS emulator (x86)" ON)
+  option(ENABLE_BUILD_ANDROID "Build library for Android NDK (ARMv7)" OFF)
+else(APPLE)
+  option(ENABLE_BUILD_ANDROID "Build library for Android NDK (ARMv7)" ON)
+endif()
+
+if(APPNAME)
+  message(STATUS "Rebranding Onelab as " ${APPNAME})
+else(APPNAME)
+  set(APPNAME Onelab)
+endif()
+
+# find all benchmarks (in a two-level dir hierarchy) who provide infos.xml
+file(GLOB BENCHMARKSDIR ${MODELS_DIR}/*)
+foreach(SUBDIR ${BENCHMARKSDIR})
+  if(IS_DIRECTORY ${SUBDIR})
+    file(GLOB INFOSFILE ${SUBDIR}/infos.xml)
+    if(INFOSFILE)
+      message(STATUS "Found benchmark ${SUBDIR}")
+      list(APPEND BENCHMARKS ${SUBDIR})
+    else()
+      file(GLOB BENCHMARKSDIR2 ${SUBDIR}/*)
+      foreach(SUBDIR2 ${BENCHMARKSDIR2})
+        if(IS_DIRECTORY ${SUBDIR2})
+          file(GLOB INFOSFILE ${SUBDIR2}/infos.xml)
+          if(INFOSFILE)
+            message(STATUS "Found benchmark ${SUBDIR2}")
+            list(APPEND BENCHMARKS ${SUBDIR2})
+          endif()
+        endif()
+      endforeach()
+    endif()
+  endif()
+endforeach()
+
+macro(append_src FILES)
+  foreach(FILE ${FILES})
+    list(APPEND LIST ${FILE})
+  endforeach(FILE)
+  set(ONELAB_SRC ${ONELAB_SRC};${LIST})
+endmacro(append_src)
+
+if(ENABLE_BUILD_IOS_EMULATOR OR ENABLE_BUILD_IOS)
+  # getdp framework
+  find_path(GETDP_FRAMEWORK getdp.framework)
+  if(GETDP_FRAMEWORK)
+    set(GETDP_FRAMEWORK ${GETDP_FRAMEWORK}/getdp.framework)
+    message(STATUS "Found framework " ${GETDP_FRAMEWORK})
+  else(GETDP_FRAMEWORK)
+    message(SEND_ERROR "Could not find getdp.framework")
+  endif()
+  # gmsh framework
+  find_path(GMSH_FRAMEWORK gmsh.framework)
+  if(GMSH_FRAMEWORK)
+    set(GMSH_FRAMEWORK ${GMSH_FRAMEWORK}/gmsh.framework)
+    message(STATUS "Found framework " ${GMSH_FRAMEWORK})
+  else(GMSH_FRAMEWORK)
+    message(SEND_ERROR "Could not find gmsh.framework")
+  endif()
+  # PETSc framework
+  find_path(PETSC_FRAMEWORK petsc.framework)
+  if(PETSC_FRAMEWORK)
+    set(PETSC_FRAMEWORK ${PETSC_FRAMEWORK}/petsc.framework)
+    message(STATUS "Found framework " ${PETSC_FRAMEWORK})
+  else(PETSC_FRAMEWORK)
+    message(SEND_ERROR "Could not find petsc.framework")
+  endif()
+  # SLEPc framework
+  find_path(SLEPC_FRAMEWORK slepc.framework)
+  if(SLEPC_FRAMEWORK)
+    set(SLEPC_FRAMEWORK ${SLEPC_FRAMEWORK}/slepc.framework)
+    message(STATUS "Found framework " ${SLEPC_FRAMEWORK})
+  else(SLEPC_FRAMEWORK)
+    message(SEND_ERROR "Could not find slepc.framework")
+  endif()
+  # OpenCASCADE framework
+  find_path(OCCT_FRAMEWORK occt.framework)
+  if(OCCT_FRAMEWORK)
+    set(OCCT_FRAMEWORK ${OCCT_FRAMEWORK}/occt.framework)
+    message(STATUS "Found framework " ${OCCT_FRAMEWORK})
+  else(OCCT_FRAMEWORK)
+    message(SEND_ERROR "Could not find occt.framework")
+  endif()
+  # add target
+  add_custom_target(xcodeProject
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/models/
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/iOS/Onelab/
+                                               ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/iOS/Onelab.xcodeproj/
+                                               ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}.xcodeproj/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/drawContext.cpp ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/drawContext.h ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/drawGeom.cpp ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/drawMesh.cpp ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/drawString.cpp ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/drawString.h ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Trackball.cpp ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Trackball.h ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/iosUtils.h ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/iosUtils.cpp ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/frameworks/
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${GETDP_FRAMEWORK}/ ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/frameworks/getdp.framework/
+    COMMAND ${CMAKE_COMMAND} -E remove ${GMSH_FRAMEWORK}/Headers/gmsh
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${GMSH_FRAMEWORK}/ ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/frameworks/gmsh.framework/
+    COMMAND ${CMAKE_COMMAND} -E create_symlink . ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/frameworks/gmsh.framework/Headers/gmsh
+    COMMAND ${CMAKE_COMMAND} -E create_symlink . ${GMSH_FRAMEWORK}/Headers/gmsh
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${PETSC_FRAMEWORK}/ ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/frameworks/petsc.framework/
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${SLEPC_FRAMEWORK}/ ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/frameworks/slepc.framework/
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${OCCT_FRAMEWORK}/ ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/frameworks/occt.framework/
+  )
+  add_custom_command(TARGET xcodeProject POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove_directory
+                    ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/files)
+  foreach(DIR ${BENCHMARKS})
+    get_filename_component(DIRNAME ${DIR} NAME)
+    add_custom_command(TARGET xcodeProject POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory
+                       ${DIR} ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/${APPNAME}/files/${DIRNAME})
+  endforeach(DIR)
+  if("${APPNAME}" STREQUAL "BBEMG")
+    add_custom_command(TARGET xcodeProject POST_BUILD COMMAND
+      sed -e \"s|Onelab|BBEMG|\" -i \"\" "${CMAKE_CURRENT_BINARY_DIR}/BBEMG/BBEMG.xcodeproj/project.pbxproj")
+    add_custom_command(TARGET xcodeProject POST_BUILD COMMAND
+      sed -e \"s|Images.xcassets|Images_BBEMG.xcassets|g\" -i \"\" "${CMAKE_CURRENT_BINARY_DIR}/BBEMG/BBEMG.xcodeproj/project.pbxproj")
+    add_custom_command(TARGET xcodeProject POST_BUILD COMMAND
+      sed -e \"s|ONELAB|BBEMG|g\" -i \"\" "${CMAKE_CURRENT_BINARY_DIR}/BBEMG/BBEMG/iPhoneiPodStoryboard.storyboard")
+    add_custom_command(TARGET xcodeProject POST_BUILD COMMAND
+      sed -e \"s|ONELAB|BBEMG|g\" -i \"\" "${CMAKE_CURRENT_BINARY_DIR}/BBEMG/BBEMG/iPadStoryboard.storyboard")
+    add_custom_command(TARGET xcodeProject POST_BUILD COMMAND
+      sed -e \"s|Onelab/Mobile|BBEMG|g\" -i \"\" "${CMAKE_CURRENT_BINARY_DIR}/BBEMG/BBEMG/AboutViewController.mm")
+    add_custom_command(TARGET xcodeProject POST_BUILD COMMAND
+      sed -e \"s|onelab.info|www.bbemg.be|g\" -i \"\" "${CMAKE_CURRENT_BINARY_DIR}/BBEMG/BBEMG/AboutViewController.mm")
+  endif()
+  message(STATUS "")
+  message(STATUS "${APPNAME} for iOS successfully configured:")
+  message(STATUS " * Run 'make xcodeProject' to create the XCode project")
+  message(STATUS " * Then run 'open ${APPNAME}/${APPNAME}.xcodeproj' to launch XCode")
+  message(STATUS "")
+endif()
+
+if(ENABLE_BUILD_ANDROID)
+  append_src(drawContext.cpp)
+  append_src(Trackball.cpp)
+  append_src(androidUtils.cpp)
+  append_src(drawString.cpp)
+  append_src(drawGeom.cpp)
+  append_src(drawMesh.cpp)
+  # getdp lib
+  find_library(GETDP_LIB Getdp PATH_SUFFIXES lib)
+  find_path(GETDP_INC "getdp.h" PATH_SUFFIXES include getdp include/getdp)
+  if(GETDP_LIB AND GETDP_INC)
+    list(APPEND EXTERNAL_LIBRARIES ${GETDP_LIB})
+    list(APPEND EXTERNAL_INCLUDES ${GETDP_INC})
+  else(GETDP_LIB AND GETDP_INC)
+    message(SEND_ERROR "Could not find getdp library")
+  endif()
+  # gmsh lib
+  find_library(GMSH_LIB Gmsh PATH_SUFFIXES lib)
+  find_path(GMSH_INC "GmshGlobal.h" PATH_SUFFIXES include gmsh include/gmsh)
+  if(GMSH_LIB AND GMSH_INC)
+    list(APPEND EXTERNAL_LIBRARIES ${GMSH_LIB})
+    list(APPEND EXTERNAL_INCLUDES ${GMSH_INC})
+  else(GMSH_LIB AND GMSH_INC)
+    message(SEND_ERROR "Could not find gmsh library")
+  endif()
+  # Onelab android lib
+  include_directories(${EXTERNAL_INCLUDES})
+  set(CMAKE_BUILD_TYPE Release)
+  add_definitions(-DBUILD_ANDROID)
+  add_definitions(-DPICOJSON_USE_LOCALE=0)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+  set(LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_CURRENT_BINARY_DIR})
+  set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/libs/)
+  add_library(androidOnelab SHARED ${ONELAB_SRC})
+  set(LIBRARY_DEPS GLESv1_CM log)
+  set_target_properties(androidOnelab PROPERTIES OUTPUT_NAME Onelab)
+  target_link_libraries(androidOnelab ${LIBRARY_DEPS} ${EXTERNAL_LIBRARIES})
+  find_library(PETSC_LIB petsc PATH_SUFFIXES lib)
+  if(NOT PETSC_LIB)
+    message(SEND_ERROR "Could not find PETSc library")
+  endif()
+  find_library(SLEPC_LIB slepc PATH_SUFFIXES lib)
+  if(NOT SLEPC_LIB)
+    message(SEND_ERROR "Could not find SLEPc library")
+  endif()
+  find_library(BLAS_LIB f2cblas PATH_SUFFIXES lib)
+  if(NOT BLAS_LIB)
+    message(SEND_ERROR "Could not find BLAS library")
+  endif()
+  find_library(LAPACK_LIB f2clapack PATH_SUFFIXES lib)
+  if(NOT LAPACK_LIB)
+    message(SEND_ERROR "Could not find LAPACK library")
+  endif()
+  set(ONELAB_LIB ${CMAKE_CURRENT_BINARY_DIR}/libs/libOnelab.so)
+  add_custom_target(androidProject
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/models/
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Android/ ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/
+    COMMAND ${CMAKE_COMMAND} -E copy ${GMSH_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/app/src/main/jniLibs/armeabi-v7a/
+    COMMAND ${CMAKE_COMMAND} -E copy ${GETDP_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/app/src/main/jniLibs/armeabi-v7a/
+    COMMAND ${CMAKE_COMMAND} -E copy ${ONELAB_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/app/src/main/jniLibs/armeabi-v7a/
+    COMMAND ${CMAKE_COMMAND} -E copy ${PETSC_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/app/src/main/jniLibs/armeabi-v7a/
+    COMMAND ${CMAKE_COMMAND} -E copy ${SLEPC_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/app/src/main/jniLibs/armeabi-v7a/
+    COMMAND ${CMAKE_COMMAND} -E copy ${LAPACK_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/app/src/main/jniLibs/armeabi-v7a/
+    COMMAND ${CMAKE_COMMAND} -E copy ${BLAS_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/app/src/main/jniLibs/armeabi-v7a/
+  )
+  add_custom_command(TARGET androidProject POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove_directory
+                    ${CMAKE_CURRENT_BINARY_DIR}/models)
+  add_custom_command(TARGET androidProject POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory
+                    ${CMAKE_CURRENT_BINARY_DIR}/models)
+  add_custom_command(TARGET androidProject POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove
+                    ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/app/src/main/res/raw/models.zip)
+  foreach(DIR ${BENCHMARKS})
+    get_filename_component(DIRNAME ${DIR} NAME)
+
+    add_custom_command(TARGET androidProject POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory
+                      ${DIR} ${CMAKE_CURRENT_BINARY_DIR}/models/${DIRNAME})
+  endforeach(DIR)
+  find_program(ZIP_COMMAND zip)
+  add_custom_command(TARGET androidProject POST_BUILD COMMAND cd ${CMAKE_CURRENT_BINARY_DIR}/models/ && zip -r
+                     ${CMAKE_CURRENT_BINARY_DIR}/${APPNAME}/app/src/main/res/raw/models.zip * --exclude=*.svn*)
+  message(STATUS "")
+  message(STATUS "ONELAB for Android successfully configured:")
+  message(STATUS " * Run 'make androidProject' to create the project")
+  message(STATUS " * Finally build the app with gradle 'gradle assembleRelease'")
+  message(STATUS "")
+endif()
diff --git a/contrib/mobile/README.txt b/contrib/mobile/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d111600a81be572c8898b3d9499e7769f3e253cc
--- /dev/null
+++ b/contrib/mobile/README.txt
@@ -0,0 +1,365 @@
+This directory contains the source code for the ONELAB app, the mobile version
+of ONELAB for iOS and Android devices.
+
+Copyright (C) 2014-2019 Christophe Geuzaine and Maxime Graulich, University of Liege
+
+The ONELAB (http://onelab.info) mobile app is a finite element package based on
+the open source mesh generator Gmsh (http://gmsh.info) and the solver GetDP
+(http://getdp.info). It can be used to simulate a wide variety of multi-physic
+problems: electromagnetics, thermics, mechanics... It comes packaged with a
+selection of ready-to-use examples. New models can be added simply by opening
+the corresponding zip archive on your device.
+
+The ONELAB mobile app is available on the AppStore and the Google Play store:
+
+https://itunes.apple.com/us/app/onelab/id845930897
+https://play.google.com/store/apps/details?id=org.geuz.onelab
+
+Sample scripts for building the ONELAB mobile app are available in
+utils/*_build.sh
+
+                              ***
+
+The ONELAB mobile app is provided under the terms of the GNU General Public
+License (GPL), Version 2 or later.
+
+		    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/mobile/Trackball.cpp b/contrib/mobile/Trackball.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..25a5714d632407165388dcc86959bcbd568d82f2
--- /dev/null
+++ b/contrib/mobile/Trackball.cpp
@@ -0,0 +1,327 @@
+/*
+ * (c) Copyright 1993, 1994, Silicon Graphics, Inc.
+ * ALL RIGHTS RESERVED
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose and without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies and that both the copyright notice
+ * and this permission notice appear in supporting documentation, and that
+ * the name of Silicon Graphics, Inc. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission.
+ *
+ * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
+ * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
+ * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
+ * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
+ * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
+ * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
+ * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
+ * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * US Government Users Restricted Rights
+ * Use, duplication, or disclosure by the Government is subject to
+ * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
+ * (c)(1)(ii) of the Rights in Technical Data and Computer Software
+ * clause at DFARS 252.227-7013 and/or in similar or successor
+ * clauses in the FAR or the DOD or NASA FAR Supplement.
+ * Unpublished-- rights reserved under the copyright laws of the
+ * United States.  Contractor/manufacturer is Silicon Graphics,
+ * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
+ *
+ * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+ */
+/*
+ * Trackball code:
+ *
+ * Implementation of a virtual trackball.
+ * Implemented by Gavin Bell, lots of ideas from Thant Tessman and
+ *   the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129.
+ *
+ * Vector manip code:
+ *
+ * Original code from:
+ * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli
+ *
+ * Much mucking with by:
+ * Gavin Bell
+ */
+/*
+ * Modified for inclusion in Gmsh (rotmatrix as a vector +
+ * float->double + optional use of hyperbolic sheet for z-rotation)
+ */
+#include <math.h>
+#include "Trackball.h"
+#include <iostream>
+/*
+ * This size should really be based on the distance from the center of
+ * rotation to the point on the object underneath the mouse.  That
+ * point would then track the mouse as closely as possible.  This is a
+ * simple example, though, so that is left as an Exercise for the
+ * Programmer.
+ */
+#define TRACKBALLSIZE  (.8)
+
+/*
+ * Local function prototypes (not defined in trackball.h)
+ */
+static double tb_project_to_sphere(double, double, double);
+static void normalize_quat(double [4]);
+using namespace std ;
+
+void
+vzero(double *v)
+{
+    v[0] = 0.0;
+    v[1] = 0.0;
+    v[2] = 0.0;
+}
+
+void
+vset(double *v, double x, double y, double z)
+{
+    v[0] = x;
+    v[1] = y;
+    v[2] = z;
+}
+
+void
+vsub(const double *src1, const double *src2, double *dst)
+{
+    dst[0] = src1[0] - src2[0];
+    dst[1] = src1[1] - src2[1];
+    dst[2] = src1[2] - src2[2];
+}
+
+void
+vcopy(const double *v1, double *v2)
+{
+    /* register */ int i;
+    for (i = 0 ; i < 3 ; i++)
+        v2[i] = v1[i];
+}
+
+void
+vcross(const double *v1, const double *v2, double *cross)
+{
+    double temp[3];
+
+    temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
+    temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
+    temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
+    vcopy(temp, cross);
+}
+
+double
+vlength(const double *v)
+{
+    return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+}
+
+void
+vscale(double *v, double div)
+{
+    v[0] *= div;
+    v[1] *= div;
+    v[2] *= div;
+}
+
+void
+vnormal(double *v)
+{
+    vscale(v,1.0/vlength(v));
+}
+
+double
+vdot(const double *v1, const double *v2)
+{
+    return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void
+vadd(const double *src1, const double *src2, double *dst)
+{
+    dst[0] = src1[0] + src2[0];
+    dst[1] = src1[1] + src2[1];
+    dst[2] = src1[2] + src2[2];
+}
+
+/*
+ * Ok, simulate a track-ball.  Project the points onto the virtual
+ * trackball, then figure out the axis of rotation, which is the cross
+ * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
+ * Note:  This is a deformed trackball-- is a trackball in the center,
+ * but is deformed into a hyperbolic sheet of rotation away from the
+ * center.  This particular function was chosen after trying out
+ * several variations.
+ *
+ * It is assumed that the arguments to this routine are in the range
+ * (-1.0 ... 1.0)
+ */
+void
+trackball(double q[4], double p1x, double p1y, double p2x, double p2y)
+{
+  double a[3]; /* Axis of rotation */
+  double phi;  /* how much to rotate about axis */
+  double p1[3], p2[3], d[3];
+  double t;
+
+  if (p1x == p2x && p1y == p2y) {
+    /* Zero rotation */
+    vzero(q);
+    q[3] = 1.0;
+    return;
+  }
+
+  /*
+   * First, figure out z-coordinates for projection of P1 and P2 to
+   * deformed sphere
+   */
+  vset(p1,p1x,p1y,tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y));
+  vset(p2,p2x,p2y,tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y));
+  /*
+   *  Now, we want the cross product of P1 and P2
+   */
+  vcross(p2,p1,a);
+
+  /*
+   *  Figure out how much to rotate around that axis.
+   */
+  vsub(p1,p2,d);
+  t = vlength(d);
+
+  /*
+   * Avoid problems with out-of-control values...
+   */
+  if (t > 1.0) t = 1.0;
+  if (t < -1.0) t = -1.0;
+  phi = 2.0 * asin(t);
+
+  axis_to_quat(a,phi,q);
+}
+
+/*
+ *  Given an axis and angle, compute quaternion.
+ */
+void axis_to_quat(double a[3], double phi, double q[4])
+{
+    vnormal(a);
+    vcopy(a,q);
+    vscale(q,sin(phi/2.0));
+    q[3] = cos(phi/2.0);
+}
+
+/*
+ * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
+ * if we are away from the center of the sphere.
+ */
+static double
+tb_project_to_sphere(double r, double x, double y)
+{
+  double d, z;
+
+  d = sqrt(x*x + y*y);
+
+  if (d < r ) {
+    z = sqrt(r*r - d*d);
+  } else {
+    z = 0.;
+  }
+
+  return z;
+}
+
+/*
+ * Given two rotations, e1 and e2, expressed as quaternion rotations,
+ * figure out the equivalent single rotation and stuff it into dest.
+ *
+ * This routine also normalizes the result every RENORMCOUNT times it is
+ * called, to keep error from creeping in.
+ *
+ * NOTE: This routine is written so that q1 or q2 may be the same
+ * as dest (or each other).
+ */
+
+#define RENORMCOUNT 97
+
+void
+add_quats(double q1[4], double q2[4], double dest[4])
+{
+    static int count=0;
+    double t1[4], t2[4], t3[4];
+    double tf[4];
+
+    vcopy(q1,t1);
+    vscale(t1,q2[3]);
+
+    vcopy(q2,t2);
+    vscale(t2,q1[3]);
+
+    vcross(q2,q1,t3);
+    vadd(t1,t2,tf);
+    vadd(t3,tf,tf);
+    tf[3] = q1[3] * q2[3] - vdot(q1,q2);
+
+    dest[0] = tf[0];
+    dest[1] = tf[1];
+    dest[2] = tf[2];
+    dest[3] = tf[3];
+
+    if (++count > RENORMCOUNT) {
+        count = 0;
+        normalize_quat(dest);
+    }
+}
+
+/*
+ * Quaternions always obey:  a^2 + b^2 + c^2 + d^2 = 1.0
+ * If they don't add up to 1.0, dividing by their magnitued will
+ * renormalize them.
+ *
+ * Note: See the following for more information on quaternions:
+ *
+ * - Shoemake, K., Animating rotation with quaternion curves, Computer
+ *   Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985.
+ * - Pletinckx, D., Quaternion calculus as a basic tool in computer
+ *   graphics, The Visual Computer 5, 2-13, 1989.
+ */
+static void
+normalize_quat(double q[4])
+{
+    int i;
+    double mag;
+
+    mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
+    for (i = 0; i < 4; i++) q[i] /= mag;
+}
+
+/*
+ * Build a rotation matrix, given a quaternion rotation.
+ *
+ */
+void
+build_rotmatrix(double m[16], double q[4])
+{
+    m[0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]);
+    m[1] = 2.0 * (q[0] * q[1] - q[2] * q[3]);
+    m[2] = 2.0 * (q[2] * q[0] + q[1] * q[3]);
+    m[3] = 0.0;
+
+    m[4] = 2.0 * (q[0] * q[1] + q[2] * q[3]);
+    m[5]= 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]);
+    m[6] = 2.0 * (q[1] * q[2] - q[0] * q[3]);
+    m[7] = 0.0;
+
+    m[8] = 2.0 * (q[2] * q[0] - q[1] * q[3]);
+    m[9] = 2.0 * (q[1] * q[2] + q[0] * q[3]);
+    m[10] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]);
+    m[11] = 0.0;
+
+    m[12] = 0.0;
+    m[13] = 0.0;
+    m[14] = 0.0;
+    m[15] = 1.0;
+}
diff --git a/contrib/mobile/Trackball.h b/contrib/mobile/Trackball.h
new file mode 100644
index 0000000000000000000000000000000000000000..6a4683202243686c3c87572f23f37ca3de9846e4
--- /dev/null
+++ b/contrib/mobile/Trackball.h
@@ -0,0 +1,79 @@
+/*
+ * (c) Copyright 1993, 1994, Silicon Graphics, Inc.
+ * ALL RIGHTS RESERVED
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose and without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies and that both the copyright notice
+ * and this permission notice appear in supporting documentation, and that
+ * the name of Silicon Graphics, Inc. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission.
+ *
+ * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
+ * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
+ * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
+ * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
+ * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
+ * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
+ * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
+ * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * US Government Users Restricted Rights
+ * Use, duplication, or disclosure by the Government is subject to
+ * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
+ * (c)(1)(ii) of the Rights in Technical Data and Computer Software
+ * clause at DFARS 252.227-7013 and/or in similar or successor
+ * clauses in the FAR or the DOD or NASA FAR Supplement.
+ * Unpublished-- rights reserved under the copyright laws of the
+ * United States.  Contractor/manufacturer is Silicon Graphics,
+ * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
+ *
+ * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+ */
+/*
+ * trackball.h
+ * A virtual trackball implementation
+ * Written by Gavin Bell for Silicon Graphics, November 1988.
+ */
+
+/*
+ * Pass the x and y coordinates of the last and current positions of
+ * the mouse, scaled so they are from (-1.0 ... 1.0).
+ *
+ * The resulting rotation is returned as a quaternion rotation in the
+ * first paramater.
+ */
+void
+trackball(double q[4], double p1x, double p1y, double p2x, double p2y);
+
+/*
+ * Given two quaternions, add them together to get a third quaternion.
+ * Adding quaternions to get a compound rotation is analagous to adding
+ * translations to get a compound translation.  When incrementally
+ * adding rotations, the first argument here should be the new
+ * rotation, the second and third the total rotation (which will be
+ * over-written with the resulting new total rotation).
+ */
+void
+add_quats(double *q1, double *q2, double *dest);
+
+/*
+ * A useful function, builds a rotation matrix in Matrix based on
+ * given quaternion.
+ */
+void
+build_rotmatrix(double m[16], double q[4]);
+
+/*
+ * This function computes a quaternion based on an axis (defined by
+ * the given vector) and an angle about which to rotate.  The angle is
+ * expressed in radians.  The result is put into the third argument.
+ */
+void
+axis_to_quat(double a[3], double phi, double q[4]);
+
+double trackballsize() ;
diff --git a/contrib/mobile/androidUtils.cpp b/contrib/mobile/androidUtils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f3b0c26613e8f806ee777523443ae739d698005b
--- /dev/null
+++ b/contrib/mobile/androidUtils.cpp
@@ -0,0 +1,347 @@
+#undef NDEBUG
+
+#ifndef NDEBUG
+#include <android/log.h>
+#define  LOG_TAG    "Gmsh"
+#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+#endif
+
+#include <dlfcn.h>
+
+#include <gmsh/GmshGlobal.h>
+#include <gmsh/GmshConfig.h>
+#include <gmsh/GmshVersion.h>
+#include <gmsh/GModel.h>
+#include <gmsh/onelab.h>
+#include <gmsh/onelabUtils.h>
+#include <gmsh/Context.h>
+#include <gmsh/PView.h>
+#include <gmsh/PViewData.h>
+#include <gmsh/PViewOptions.h>
+
+#include <getdp/GetDPConfig.h>
+#include <getdp/GetDPVersion.h>
+
+#include "androidUtils.h"
+#include "drawContext.h"
+
+extern "C"
+{
+static JavaVM *gJavaVM;
+static jobject gCallbackObject = NULL;
+};
+
+class MobileMessage : public GmshMessage
+{
+public:
+  MobileMessage(){}
+  ~MobileMessage(){}
+  void operator()(std::string level, std::string message)
+  {
+    if(level == "Error"){
+#ifndef NDEBUG
+      LOGE("Error:\t%s", message.c_str());
+#endif
+      JNIEnv *env;
+      if(gJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK ||
+         !gCallbackObject || (gJavaVM->AttachCurrentThread(&env, NULL)) < 0) return;
+      jstring jstr = env->NewStringUTF(message.c_str());
+      jclass jClass = env->FindClass("org/geuz/onelab/Gmsh");
+      if(jClass == 0 || env->ExceptionCheck())
+        return;
+      jmethodID mid = env->GetMethodID(jClass, "ShowPopup", "(Ljava/lang/String;)V");
+      if (mid == 0 || env->ExceptionCheck())
+        return;
+      env->CallVoidMethod(gCallbackObject, mid, jstr);
+      env->DeleteLocalRef(jstr);
+      env->DeleteLocalRef(jClass);
+      return;
+    }
+    else if(level == "Progress"){
+      JNIEnv *env;
+      if(gJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK ||
+         !gCallbackObject || (gJavaVM->AttachCurrentThread(&env, NULL)) < 0) return;
+      jstring jstr = env->NewStringUTF(message.c_str());
+      jclass jClass = env->FindClass("org/geuz/onelab/Gmsh");
+      if(jClass == 0 || env->ExceptionCheck())
+        return;
+      jmethodID mid = env->GetMethodID(jClass, "SetLoading", "(Ljava/lang/String;)V");
+      if (mid == 0 || env->ExceptionCheck())
+        return;
+      env->CallVoidMethod(gCallbackObject, mid, jstr);
+      env->DeleteLocalRef(jstr);
+      env->DeleteLocalRef(jClass);
+      return;
+    }
+    else if(level == "RequestRender"){
+      requestRender();
+      return;
+    }
+#ifndef NDEBUG
+    LOGI("%s:\t%s", level.c_str(), message.c_str());
+#endif
+  }
+};
+
+void requestRender()
+{
+  JNIEnv *env;
+  if(gJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK ||
+     !gCallbackObject || (gJavaVM->AttachCurrentThread(&env, NULL)) < 0) return;
+  jclass jClass = env->FindClass("org/geuz/onelab/Gmsh");
+  if(jClass == 0 || env->ExceptionCheck())
+    return;
+  jmethodID mid = env->GetMethodID(jClass, "RequestRender", "()V");
+  if (mid == 0 || env->ExceptionCheck())
+    return;
+  env->CallVoidMethod(gCallbackObject, mid);
+  env->DeleteLocalRef(jClass);
+}
+
+void getBitmapFromString(const char *text, int textsize, unsigned char **map,
+                         int *height, int *width, int *realWidth)
+{
+  JNIEnv *env;
+  if(gJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK ||
+     !gCallbackObject || (gJavaVM->AttachCurrentThread(&env, NULL)) < 0) return;
+  jclass jClass = env->FindClass("org/geuz/onelab/StringTexture");
+  if(jClass == 0 || env->ExceptionCheck())
+    return;
+  jstring jtext = env->NewStringUTF(text);
+  jmethodID mid = env->GetStaticMethodID(jClass, "getHeightFromString",
+                                         "(Ljava/lang/String;I)I");
+  if(mid == 0 || env->ExceptionCheck())
+    return;
+  *height = env->CallStaticIntMethod(jClass, mid, jtext, textsize);
+  mid = env->GetStaticMethodID(jClass, "getWidthFromString", "(Ljava/lang/String;I)I");
+  if(mid == 0 || env->ExceptionCheck())
+    return;
+  *width = env->CallStaticIntMethod(jClass, mid, jtext, textsize);
+  if(realWidth != NULL){
+    mid = env->GetStaticMethodID(jClass, "getRealWidthFromString", "(Ljava/lang/String;I)I");
+    if(mid == 0 || env->ExceptionCheck())
+      return;
+    *realWidth = env->CallStaticIntMethod(jClass, mid, jtext, textsize);
+  }
+  mid = env->GetStaticMethodID(jClass, "getBytesFromString", "(Ljava/lang/String;I)[B");
+  if(mid == 0 || env->ExceptionCheck())
+    return;
+  jobject jbuffer = env->CallStaticObjectMethod(jClass, mid, jtext, textsize);
+  jbyteArray *jarray = reinterpret_cast<jbyteArray*>(&jbuffer);
+  int ms = (*height) * (*width);
+  if(ms){
+    *map = (unsigned char *)malloc(ms);
+    env->GetByteArrayRegion(*jarray, 0, ms, (jbyte*)*map);
+  }
+  env->DeleteLocalRef(jClass);
+  env->DeleteLocalRef(jtext);
+}
+
+extern "C" {
+  JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
+  {
+    gJavaVM = vm;
+    return JNI_VERSION_1_6;
+  }
+  JNIEXPORT jlong JNICALL Java_org_geuz_onelab_Gmsh_init
+  (JNIEnv *env, jobject obj, jfloat fontFactor)
+  {
+    if(gCallbackObject != NULL) env->DeleteGlobalRef(gCallbackObject);
+    gCallbackObject = env->NewGlobalRef(obj);
+    gJavaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
+    Msg::SetCallback(new MobileMessage());
+    return reinterpret_cast<jlong>(new drawContext(fontFactor));
+  }
+  JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_loadFile
+  (JNIEnv *env, jobject obj, jlong jptr, jstring jname)
+  {
+    const char*  filename = env->GetStringUTFChars(jname, NULL);
+    ((drawContext *)jptr)->load(filename);
+  }
+  JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_initView
+  (JNIEnv *env, jobject obj, jlong jptr, jint w, jint h)
+  {
+    ((drawContext *)jptr)->initView(w,h);
+  }
+  JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_drawView
+  (JNIEnv *env, jobject obj, jlong jptr)
+  {
+    ((drawContext *)jptr)->drawView();
+  }
+  JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_eventHandler
+  (JNIEnv *env, jobject obj, jlong jptr, jint jevent, jfloat jx, jfloat jy)
+  {
+    ((drawContext *)jptr)->eventHandler(jevent, jx, jy);
+  }
+  JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setStringOption
+  (JNIEnv *env, jobject obj, jstring c, jstring n, jstring v, jint idx)
+  {
+    const char* tmp;
+    tmp = env->GetStringUTFChars(v, NULL);
+    const std::string value(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(v, tmp);
+    tmp = env->GetStringUTFChars(n, NULL);
+    const std::string name(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(n, tmp);
+    tmp = env->GetStringUTFChars(c, NULL);
+    std::string category(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(c, tmp);
+    GmshSetStringOption(category, name, value, (int)idx);
+  }
+  JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setDoubleOption
+  (JNIEnv *env, jobject obj, jstring c, jstring n, jdouble v, jint idx)
+  {
+    const char* tmp;
+    tmp = env->GetStringUTFChars(n, NULL);
+    const std::string name(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(n, tmp);
+    tmp = env->GetStringUTFChars(c, NULL);
+    const std::string category(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(c, tmp);
+    GmshSetNumberOption(category, name, (double)v, (int)idx);
+  }
+  JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setIntegerOption
+  (JNIEnv *env, jobject obj, jstring c, jstring n, jint v, jint idx)
+  {
+    const char* tmp;
+    tmp = env->GetStringUTFChars(n, NULL);
+    const std::string name(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(n, tmp);
+    tmp = env->GetStringUTFChars(c, NULL);
+    const std::string category(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(c, tmp);
+    GmshSetColorOption(category, name, (unsigned int)v, (int)idx);
+  }
+  JNIEXPORT jstring JNICALL Java_org_geuz_onelab_Gmsh_getStringOption
+  (JNIEnv *env, jobject obj, jstring c, jstring n, jint idx)
+  {
+    const char* tmp;
+    tmp = env->GetStringUTFChars(n, NULL);
+    const std::string name(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(n, tmp);
+    tmp = env->GetStringUTFChars(c, NULL);
+    const std::string category(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(c, tmp);
+    std::string value;
+    value = GmshGetStringOption(category, name, (int)idx);
+    return env->NewStringUTF(value.c_str());
+  }
+  JNIEXPORT jdouble JNICALL Java_org_geuz_onelab_Gmsh_getDoubleOption
+  (JNIEnv *env, jobject obj, jstring c, jstring n, jint idx)
+  {
+    const char* tmp;
+    tmp = env->GetStringUTFChars(n, NULL);
+    const std::string name(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(n, tmp);
+    tmp = env->GetStringUTFChars(c, NULL);
+    const std::string category(tmp, strlen(tmp));
+    double value = GmshGetNumberOption(category, name, (int)idx);
+    return value;
+  }
+  JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_getIntegerOption
+  (JNIEnv *env, jobject obj, jstring c, jstring n, jint idx)
+  {
+    const char* tmp;
+    tmp = env->GetStringUTFChars(n, NULL);
+    const std::string name(tmp, strlen(tmp));
+    env->ReleaseStringUTFChars(n, tmp);
+    tmp = env->GetStringUTFChars(c, NULL);
+    const std::string category(tmp, strlen(tmp));
+    unsigned int value = GmshGetColorOption(category, name, (int)idx);
+    return value;
+  }
+  JNIEXPORT jobjectArray JNICALL Java_org_geuz_onelab_Gmsh_getParams
+  (JNIEnv *env, jobject obj)
+  {
+    jclass stringClass = env->FindClass("java/lang/String");
+    std::vector<std::string> tmp =  onelab::server::instance()->toChar();
+    for(unsigned int i = 0; i < tmp.size(); i++)
+      for(unsigned int j = 0; j < tmp[i].size(); j++)
+        if(tmp[i][j] == '\0') tmp[i][j] = 0x03;
+    jobjectArray params = env->NewObjectArray(tmp.size(), stringClass, 0);
+    for(int i=0; i<tmp.size();i++){
+      jstring s = env->NewStringUTF(tmp[i].c_str());
+      env->SetObjectArrayElement(params, i, s);
+      env->DeleteLocalRef(s);
+    }
+    return params;
+  }
+  JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setParam
+  (JNIEnv *env, jobject obj, jstring jtype, jstring jname, jstring jvalue)
+  {
+    const char *type = env->GetStringUTFChars(jtype, NULL);
+    const char *name = env->GetStringUTFChars(jname, NULL);
+    const char *value = env->GetStringUTFChars(jvalue, NULL);
+    if(strcmp(type, "ParameterNumber") == 0){
+      std::vector<onelab::number> s;
+      if(onelab::server::instance()->get(s,  name)){
+        s[0].setValue(atof(value));
+        onelab::server::instance()->set(s[0]);
+      }
+    }
+    else if(strcmp(type, "ParameterString") == 0){
+      std::vector<onelab::string> s;
+      if(onelab::server::instance()->get(s,  name)){
+        s[0].setValue(value);
+        onelab::server::instance()->set(s[0]);
+      }
+    }
+  }
+
+  JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_onelabCB
+  (JNIEnv *env, jobject obj, jstring jaction)
+  {
+    const char*  action = env->GetStringUTFChars(jaction, NULL);
+    return onelab_cb(action);
+  }
+
+  JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_numberOfAnimation
+  (JNIEnv *, jobject)
+  {
+    return number_of_animation();
+  }
+  JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_animationNext
+  (JNIEnv *, jobject)
+  {
+    return animation_next();
+  }
+  JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_animationPrev
+  (JNIEnv *, jobject)
+  {
+    return animation_prev();
+  }
+  JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setAnimation
+  (JNIEnv *, jobject, jint animation)
+  {
+    set_animation(animation);
+  }
+  JNIEXPORT jstring JNICALL Java_org_geuz_onelab_Gmsh_getAboutGmsh
+  (JNIEnv *env, jclass c)
+  {
+    std::ostringstream sstream;
+    sstream << "<center><h3>Gmsh</h3>"
+            << "Version " << GMSH_VERSION << " "
+            << "(<i>Build date:</i> " << GMSH_DATE << ")"
+            << "<p>Copyright (C) 1997-2019 Christophe Geuzaine and Jean-Fran&ccedil;ois Remacle</p>"
+            << "<p><a href=\"http://gmsh.info/doc/CREDITS.txt\">Credits</a> "
+            << "and <a href=\"http://gmsh.info/doc/LICENSE.txt\">licensing information</a></p>"
+            << "<p><i>Build options:</i> " << GMSH_CONFIG_OPTIONS << "</p>"
+            << "<p>Visit <a href=\"http://gmsh.info\">http://gmsh.info</a> for more information</p></center>";
+    return env->NewStringUTF(sstream.str().c_str());
+  }
+  JNIEXPORT jstring JNICALL Java_org_geuz_onelab_Gmsh_getAboutGetDP
+  (JNIEnv *env, jclass c)
+  {
+    std::ostringstream sstream;
+    sstream << "<center><h3>GetDP</h3>"
+            << "Version " << GETDP_VERSION << " "
+            << "(<i>Build date:</i> " << GETDP_DATE << ")"
+            << "<p>Copyright (C) 1997-2019 Patrick Dular and Christophe Geuzaine, University of Li&egrave;ge</p>"
+            << "<p><a href=\"http://getdp.info/doc/CREDITS.txt\">Credits</a> "
+            << "and <a href=\"http://getdp.info/doc/LICENSE.txt\">licensing information</a></p>"
+            << "<p><i>Build options:</i> " << GETDP_CONFIG_OPTIONS << "</p>"
+            << "<p>Visit <a href=\"http://getdp.info\">http://getdp.info</a> for more information</p></center>";
+    return env->NewStringUTF(sstream.str().c_str());
+  }
+}
diff --git a/contrib/mobile/androidUtils.h b/contrib/mobile/androidUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..8e33b612918b53cddb6d41ee7903d907ad3d3474
--- /dev/null
+++ b/contrib/mobile/androidUtils.h
@@ -0,0 +1,59 @@
+#ifndef _ANDROID_UTILS_ONELAB_H_
+#define _ANDROID_UTILS_ONELAB_H_
+
+#include <jni.h>
+
+void requestRender();
+
+void getBitmapFromString(const char *text, int textsize, unsigned char **map,
+                         int *height, int *width, int *realWidth=NULL);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+JNIEXPORT jlong JNICALL Java_org_geuz_onelab_Gmsh_init
+  (JNIEnv *, jobject, jfloat);
+JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_loadFile
+  (JNIEnv *, jobject, jlong, jstring);
+JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_initView
+  (JNIEnv *, jobject, jlong, jint, jint);
+JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_drawView
+  (JNIEnv *, jobject, jlong);
+JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_eventHandler
+  (JNIEnv *, jobject, jlong, jint, jfloat, jfloat);
+JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setStringOption
+  (JNIEnv *, jobject, jstring, jstring, jstring, jint);
+JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setDoubleOption
+  (JNIEnv *, jobject, jstring, jstring, jdouble, jint);
+JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setIntegerOption
+  (JNIEnv *, jobject, jstring, jstring, jint, jint);
+JNIEXPORT jstring JNICALL Java_org_geuz_onelab_Gmsh_getStringOption
+  (JNIEnv *, jobject, jstring, jstring, jint);
+JNIEXPORT jdouble JNICALL Java_org_geuz_onelab_Gmsh_getDoubleOption
+(JNIEnv *, jobject, jstring, jstring, jint);
+JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_getIntegerOption
+(JNIEnv *, jobject, jstring, jstring, jint);
+JNIEXPORT jobjectArray JNICALL Java_org_geuz_onelab_Gmsh_getParams
+  (JNIEnv *, jobject);
+JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setParam
+  (JNIEnv *, jobject, jstring, jstring, jstring);
+JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_onelabCB
+  (JNIEnv *, jobject, jstring);
+JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_numberOfAnimation
+  (JNIEnv *, jobject);
+JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_animationNext
+  (JNIEnv *, jobject);
+JNIEXPORT jint JNICALL Java_org_geuz_onelab_Gmsh_animationPrev
+  (JNIEnv *, jobject);
+JNIEXPORT void JNICALL Java_org_geuz_onelab_Gmsh_setAnimation
+  (JNIEnv *, jobject, jint);
+JNIEXPORT jstring JNICALL Java_org_geuz_onelab_Gmsh_getAboutGmsh
+  (JNIEnv *, jclass);
+JNIEXPORT jstring JNICALL Java_org_geuz_onelab_Gmsh_getAboutGetDP
+  (JNIEnv *, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/mobile/drawContext.cpp b/contrib/mobile/drawContext.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a8b3b4156a8c3896b87d3c8c4dcf84a8ab29de82
--- /dev/null
+++ b/contrib/mobile/drawContext.cpp
@@ -0,0 +1,1268 @@
+#include <map>
+
+#include <gmsh/GmshGlobal.h>
+#include <gmsh/OpenFile.h>
+#include <gmsh/GModel.h>
+#include <gmsh/MElement.h>
+#include <gmsh/VertexArray.h>
+#include <gmsh/onelab.h>
+#include <gmsh/onelabUtils.h>
+#include <gmsh/PView.h>
+#include <gmsh/PViewOptions.h>
+#include <gmsh/PViewData.h>
+#include <gmsh/Context.h>
+#include <gmsh/StringUtils.h>
+#include <getdp/getdp.h>
+
+#if defined(BUILD_ANDROID)
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include "androidUtils.h"
+#else // iOS
+#include <OpenGLES/ES1/gl.h>
+#include <OpenGLES/ES1/glext.h>
+#endif
+
+#include "drawContext.h"
+#include "drawString.h"
+#include "Trackball.h"
+
+static bool locked = false;
+static bool onelabStop = false;
+
+drawContext::drawContext(float fontFactor, bool retina)
+{
+  GmshInitialize();
+  GmshSetOption("General", "Terminal", 1.0);
+  onelabUtils::setFirstComputationFlag(false);
+  for(int i = 0; i < 3; i++){
+    _translate[i] = 0.;
+    _scale[i] = 1.;
+  }
+  setQuaternion(0., 0., 0., 1.);
+  _fontFactor = fontFactor;
+  _retina = retina;
+  _pixel_equiv_x = _pixel_equiv_y = 0.;
+}
+
+static void checkGlError(const char* op)
+{
+  //for (GLint error = glGetError(); error; error = glGetError())
+  //  Msg::Error("%s: glError (0x%x)",op,error);
+}
+
+void drawContext::load(std::string filename)
+{
+  if(locked) return;
+
+  // delete all models and post-processing views
+  GmshClearProject();
+
+  // reset onelab database
+  onelab::server::instance()->clear();
+
+  // restore default options
+  GmshRestoreDefaultOptions();
+
+  // output messages on console
+  GmshSetOption("General", "Terminal", 1.0);
+
+  // FIXME: force mesh output in version 2, as with version 4 GetDP reads the
+  // file through Gmsh. Since in the mobile app GetDP lives in the same memory
+  // space as Gmsh, this creates a new (discrete) model, which the app now uses
+  // instead of the full CAD model. We should better handle MSH4 reads in GetDP,
+  // e.g. create a temp model for MSH4 read if GetDP is compiled in the same
+  // memory space as Gmsh... or not :-)
+  GmshSetOption("Mesh", "MshFileVersion", 2.2);
+
+  // open the file with Gmsh
+  GmshOpenProject(filename);
+
+  // run onelab clients to populate the database
+  onelab_cb("check");
+
+  // set openGL view using CTX options
+  eventHandler(8);
+
+  // mark all parameters as changed to force complete first run
+  onelab::server::instance()->setChanged(3);
+}
+
+void drawContext::eventHandler(int event, float x, float y)
+{
+  int width = _width, height = _height;
+  if(_retina){ // x,y for retina are still the same as for non-retina
+    width /= 2;
+    height /= 2;
+  }
+
+  _current.set(_scale, _translate, _right, _left,
+               _bottom, _top, width, height, x, y);
+  double xx[3] = {1.,0.,0.};
+  double yy[3] = {0.,1.,0.};
+  double q[4];
+  switch(event){
+  case 0: // finger(s) press the screen
+    // in this case x and y represent the start point
+    _start.set(_scale, _translate, _right, _left,
+               _bottom, _top, width, height, x, y);
+    _previous.set(_scale, _translate, _right, _left,
+                  _bottom, _top, width, height, x, y);
+    break;
+  case 1: // finger move (translate)
+    // in this case x and y represent the current point
+    _translate[0] += (_current.wnr[0] - _previous.wnr[0]);
+    _translate[1] += (_current.wnr[1] - _previous.wnr[1]);
+    _translate[2] = 0.;
+    break;
+  case 2: // fingers move (scale)
+    // in this case we don't care about previous and current position, x
+    // represent the scale
+    _scale[0] = _scale[1] = _scale[2] = x;
+    _start.recenter(_scale, _translate);
+    break;
+  case 3: // fingers move (rotate)
+    addQuaternion((2. * _previous.win[0] - width) / width,
+                  (height - 2. * _previous.win[1]) / height,
+                  (2. * _current.win[0] - width) / width,
+                  (height - 2. * _current.win[1]) / height);
+    break;
+  case 4: // release the finger(s)
+    // Do nothing ?
+    break;
+  case 5: // X view
+    axis_to_quat(xx, M_PI/2, q);
+    setQuaternion(q[0], q[1], q[2], q[3]);
+    break;
+  case 6: // Y view
+    axis_to_quat(yy, M_PI/2, q);
+    setQuaternion(q[0], q[1], q[2], q[3]);
+    break;
+  case 7: // Z view
+    setQuaternion(0., 0., 0., 1.);
+    break;
+  case 8: // CTX options
+    for(int i = 0; i < 3; i++){
+      _translate[i] = CTX::instance()->tmpTranslation[i];
+      _scale[i] = CTX::instance()->tmpScale[i];
+    }
+    setQuaternion(CTX::instance()->tmpQuaternion[0],
+                  CTX::instance()->tmpQuaternion[1],
+                  CTX::instance()->tmpQuaternion[2],
+                  CTX::instance()->tmpQuaternion[3]);
+    break;
+  default: // all other reset the position
+    setQuaternion(0., 0., 0., 1.);
+    for(int i = 0; i < 3; i++){
+      _translate[i] = 0.;
+      _scale[i] = 1.;
+    }
+    break;
+  }
+  _previous.set(_scale, _translate, _right, _left,
+                _bottom, _top, width, height, x, y);
+}
+
+void drawContext::setQuaternion(double q0, double q1, double q2, double q3)
+{
+  _quaternion[0] = q0;
+  _quaternion[1] = q1;
+  _quaternion[2] = q2;
+  _quaternion[3] = q3;
+}
+
+void drawContext::addQuaternion(double p1x, double p1y, double p2x, double p2y)
+{
+  double quat[4];
+  trackball(quat, p1x, p1y, p2x, p2y);
+  add_quats(quat, _quaternion, _quaternion);
+}
+
+void drawContext::buildRotationMatrix()
+{
+  build_rotmatrix(_rotate, _quaternion);
+  for(int i = 0; i < 16; i++)
+    _rotatef[i] = (float)_rotate[i];
+}
+
+void drawContext::OrthofFromGModel()
+{
+  double Va = (double)_height / (double)_width;
+  double Wa = (CTX::instance()->max[1] - CTX::instance()->min[1]) /
+    (CTX::instance()->max[0] - CTX::instance()->min[0]);
+  double vxmin, vxmax, vymin, vymax;
+  if(Va > Wa) {
+    vxmin = CTX::instance()->min[0];
+    vxmax = CTX::instance()->max[0];
+    vymin = 0.5 * (CTX::instance()->min[1] + CTX::instance()->max[1] -
+                   Va * (CTX::instance()->max[0] - CTX::instance()->min[0]));
+    vymax = 0.5 * (CTX::instance()->min[1] + CTX::instance()->max[1] +
+                   Va * (CTX::instance()->max[0] - CTX::instance()->min[0]));
+  }
+  else {
+    vxmin = 0.5 * (CTX::instance()->min[0] + CTX::instance()->max[0] -
+                   (CTX::instance()->max[1] - CTX::instance()->min[1]) / Va);
+    vxmax = 0.5 * (CTX::instance()->min[0] + CTX::instance()->max[0] +
+                   (CTX::instance()->max[1] - CTX::instance()->min[1]) / Va);
+    vymin = CTX::instance()->min[1];
+    vymax = CTX::instance()->max[1];
+  }
+  double fact = CTX::instance()->displayBorderFactor;
+  double xborder = fact * (vxmax - vxmin), yborder = fact * (vymax - vymin);
+  vxmin -= xborder;
+  vxmax += xborder;
+  vymin -= yborder;
+  vymax += yborder;
+
+  // store what one pixel represents in world coordinates
+  _pixel_equiv_x = (vxmax - vxmin) / (double)_width;
+  _pixel_equiv_y = (vymax - vymin) / (double)_height;
+
+  // set up the near and far clipping planes so that the box is large enough to
+  // manipulate the model and zoom, but not too big (otherwise the z-buffer
+  // resolution e.g. with Mesa can become insufficient)
+  double zmax = std::max(fabs(CTX::instance()->min[2]),
+                         fabs(CTX::instance()->max[2]));
+  if(zmax < CTX::instance()->lc) zmax = CTX::instance()->lc;
+  double clip_near = -zmax * _scale[2] * CTX::instance()->clipFactor;
+  double clip_far = -clip_near;
+
+  GLint matrixMode;
+  glGetIntegerv(GL_MATRIX_MODE, &matrixMode);
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glOrthof(vxmin, vxmax, vymin, vymax, clip_near, clip_far);
+  glMatrixMode(matrixMode);
+
+  _left = vxmin;
+  _right = vxmax;
+  _top = vymax;
+  _bottom = vymin;
+  _far = clip_far;
+}
+
+void drawContext::initView(int w, int h)
+{
+  _height = h;
+  _width = w;
+  glViewport(0, 0, w, h);
+
+  OrthofFromGModel();
+
+  glClearColor(.83, .85, .98, 1.);
+  glDepthMask(GL_TRUE);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glDepthFunc(GL_LESS);
+}
+
+void drawArray(VertexArray *va, GLint type, bool useColorArray, bool useNormalArray)
+{
+  if(!va) return;
+  glEnable(GL_RESCALE_NORMAL);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glEnable(GL_BLEND);
+  glShadeModel(GL_SMOOTH);
+  glVertexPointer(3, GL_FLOAT, 0, va->getVertexArray());
+  glEnableClientState(GL_VERTEX_ARRAY);
+  if(useNormalArray){
+    glNormalPointer(GL_BYTE, 0, va->getNormalArray());
+    glEnableClientState(GL_NORMAL_ARRAY);
+  }
+  if(useColorArray){
+    glColorPointer(4, GL_UNSIGNED_BYTE, 0, va->getColorArray());
+    glEnableClientState(GL_COLOR_ARRAY);
+  }
+  glDrawArrays(type, 0, va->getNumVertices());
+  glDisableClientState(GL_VERTEX_ARRAY);
+  glDisableClientState(GL_NORMAL_ARRAY);
+  glDisableClientState(GL_COLOR_ARRAY);
+  glDisable(GL_BLEND);
+  glDisable(GL_RESCALE_NORMAL);
+}
+
+void drawVector(double x, double y, double z, double dx, double dy, double dz)
+{
+  double l = sqrt(dx * dx + dy * dy + dz * dz), lt;
+  double n[3], t[3], u[3];
+
+  if(l == 0.0) return;
+
+  GLfloat line[] = {
+    (GLfloat)x, (GLfloat)y, (GLfloat)z,
+    (GLfloat)(x+dx), (GLfloat)(y+dy), (GLfloat)(z+dz),
+  };
+  glVertexPointer(3, GL_FLOAT, 0, line);
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glDrawArrays(GL_LINES, 0, 2);
+  glDisableClientState(GL_VERTEX_ARRAY);
+
+  n[0] = dx / l;
+  n[1] = dy / l;
+  n[2] = dz / l;
+
+  if((fabs(n[0]) >= fabs(n[1]) && fabs(n[0]) >= fabs(n[2])) ||
+     (fabs(n[1]) >= fabs(n[0]) && fabs(n[1]) >= fabs(n[2]))) {
+    t[0] = n[1];
+    t[1] = -n[0];
+    t[2] = 0.;
+  }
+  else {
+    t[0] = 0.;
+    t[1] = n[2];
+    t[2] = -n[1];
+  }
+
+  lt = sqrt(t[0] * t[0] + t[1] * t[1] + t[2] * t[2]);
+  t[0] /= lt;
+  t[1] /= lt;
+  t[2] /= lt;
+
+  u[0] = n[1] * t[2] - n[2] * t[1];
+  u[1] = n[2] * t[0] - n[0] * t[2];
+  u[2] = n[0] * t[1] - n[1] * t[0];
+
+  lt = sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]);
+  u[0] /= lt;
+  u[1] /= lt;
+  u[2] /= lt;
+
+  double f1 = 0.75; // Stem lenght
+  double b = 0.1 * l;
+
+  GLfloat arrow[] = {
+    (GLfloat)(x + dx), (GLfloat)(y + dy), (GLfloat)(z + dz),
+    (GLfloat)(x + f1 * dx + b * (t[0])), (GLfloat)(y + f1 * dy + b * (t[1])),
+    (GLfloat)(z + f1 * dz + b * (t[2])),
+    (GLfloat)(x + f1 * dx + b * (-t[0])), (GLfloat)(y + f1 * dy + b * (-t[1])),
+    (GLfloat)(z + f1 * dz + b * (-t[2])),
+
+    (GLfloat)(x + dx), (GLfloat)(y + dy), (GLfloat)(z + dz),
+    (GLfloat)(x + f1 * dx + b * (-u[0])), (GLfloat)(y + f1 * dy + b * (-u[1])),
+    (GLfloat)(z + f1 * dz + b * (-u[2])),
+    (GLfloat)(x + f1 * dx + b * (u[0])), (GLfloat)(y + f1 * dy + b * (u[1])),
+    (GLfloat)(z + f1 * dz + b * (u[2])),
+  };
+  glVertexPointer(3, GL_FLOAT, 0, arrow);
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glEnable(GL_LINE_SMOOTH);
+  glDrawArrays(GL_TRIANGLES, 0, 6);
+  glDisableClientState(GL_VERTEX_ARRAY);
+  glDisable(GL_LINE_SMOOTH);
+}
+
+void drawContext::drawVectorArray(PViewOptions *opt, VertexArray *va)
+{
+  if(!va || va->getNumVerticesPerElement() != 2) return;
+
+  for(int i = 0; i < va->getNumVertices(); i += 2){
+    float *s = va->getVertexArray(3 * i);
+    float *v = va->getVertexArray(3 * (i + 1));
+    double l = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+    double lmax = opt->tmpMax;
+    if((l && opt->vectorType) && lmax){
+      double scale = (opt->arrowSizeMax - opt->arrowSizeMin) / lmax;
+      if(opt->arrowSizeMin && l) scale += opt->arrowSizeMin / l;
+      double dx = scale * v[0];
+      double dy = scale * v[1];
+      double dz = scale * v[2];
+      GLubyte *color = (GLubyte *)va->getColorArray(4 * i);
+      glColor4ub(*(color), *(color+1), *(color+2), *(color+3));
+      if(fabs(dx) > 1. || fabs(dy) > 1. || fabs(dz) > 1.){
+        double d = (_right - _left) / _width / _scale[0];
+        dx *= d; dy *= d; dz *= d;
+        double x = s[0], y = s[1], z = s[2];
+        drawVector(x,y,z,dx,dy,dz);
+      }
+    }
+  }
+}
+
+void drawContext::drawPView(PView *p)
+{
+  PViewOptions *opt = p->getOptions();
+  if(!opt->visible) return;
+
+  glPointSize((GLfloat)opt->pointSize);
+  glLineWidth((GLfloat)opt->lineWidth);
+
+  drawArray(p->va_points, GL_POINTS, true);
+
+  drawArray(p->va_lines, GL_LINES, true);
+
+  glEnable(GL_LIGHTING);
+  drawArray(p->va_triangles, GL_TRIANGLES, true, true);
+  glDisable(GL_LIGHTING);
+
+  glLineWidth(1);
+  glPointSize(1);
+
+  drawVectorArray(p->getOptions(), p->va_vectors);
+}
+
+void drawContext::drawScale()
+{
+  glPushMatrix();
+  glLoadIdentity();
+
+  double size = std::max(_right -_left, _top - _bottom);
+  double width = size / 3.5;
+  double height = size / 10.;
+  double dh = height / 5;
+
+  // Draw the scale bar
+  int nPview = 0;
+  for(int i=0; i<PView::list.size();i++){
+    PView *p = PView::list[i];
+    PViewOptions *opt = p->getOptions();
+    if(!opt->visible) continue;
+    PViewData *data = p->getData();
+
+    double box = width / (opt->nbIso ? opt->nbIso : 1);
+    double xmin = _left + (_right - _left - width)/2.;
+    double ymin = _bottom + 0.7 * height + height * nPview;
+
+    std::vector<GLfloat> vertex(opt->nbIso*3*4);
+    std::vector<GLubyte> color(opt->nbIso*4*4);
+    for(int i = 0; i < opt->nbIso; i++){
+      if(opt->intervalsType == PViewOptions::Discrete ||
+         opt->intervalsType == PViewOptions::Numeric){
+        unsigned int col = opt->getColor(i, opt->nbIso);
+        color[i*4*4+0] = color[i*4*4+4] = color[i*4*4+8] = color[i*4*4+12] =
+          (GLubyte)CTX::instance()->unpackRed(col);
+        color[i*4*4+1] = color[i*4*4+5] = color[i*4*4+9] = color[i*4*4+13] =
+          (GLubyte)CTX::instance()->unpackGreen(col);
+        color[i*4*4+2] = color[i*4*4+6] = color[i*4*4+10] = color[i*4*4+14] =
+          (GLubyte)CTX::instance()->unpackBlue(col);
+        color[i*4*4+3] = color[i*4*4+7] = color[i*4*4+11] = color[i*4*4+15] =
+          (GLubyte)CTX::instance()->unpackAlpha(col);
+        vertex[i*3*4+0] = xmin + i * box;
+        vertex[i*3*4+1] = ymin;
+        vertex[i*3*4+2] = 0.;
+        vertex[i*3*4+3] = xmin + i * box;
+        vertex[i*3*4+4] = ymin + dh;
+        vertex[i*3*4+5] = 0.;
+        vertex[i*3*4+6] = xmin + (i + 1) * box;
+        vertex[i*3*4+7] = ymin;
+        vertex[i*3*4+8] = 0.;
+        vertex[i*3*4+9] = xmin + (i + 1) * box;
+        vertex[i*3*4+10] = ymin + dh;
+        vertex[i*3*4+11] = 0.;
+      }
+      else if(opt->intervalsType == PViewOptions::Continuous){
+        double dv = (opt->tmpMax - opt->tmpMin) / (opt->nbIso ? opt->nbIso : 1);
+        double v1 = opt->tmpMin + i * dv;
+        unsigned int col1 = opt->getColor(v1, opt->tmpMin, opt->tmpMax, true);
+        color[i*4*4+0] = color[i*4*4+4] = (GLubyte)CTX::instance()->unpackRed(col1);
+        color[i*4*4+1] = color[i*4*4+5] = (GLubyte)CTX::instance()->unpackGreen(col1);
+        color[i*4*4+2] = color[i*4*4+6] = (GLubyte)CTX::instance()->unpackBlue(col1);
+        color[i*4*4+3] = color[i*4*4+7] = (GLubyte)CTX::instance()->unpackAlpha(col1);
+        vertex[i*3*4+0] = xmin + i * box;
+        vertex[i*3*4+1] = ymin;
+        vertex[i*3*4+2] = 0.;
+        vertex[i*3*4+3] = xmin + i * box;
+        vertex[i*3*4+4] = ymin + dh;
+        vertex[i*3*4+5] = 0.;
+        double v2 = opt->tmpMin + (i + 1) * dv;
+        unsigned int col2 = opt->getColor(v2, opt->tmpMin, opt->tmpMax, true);
+        color[i*4*4+8] = color[i*4*4+12] = (GLubyte)CTX::instance()->unpackRed(col2);
+        color[i*4*4+9] = color[i*4*4+13] = (GLubyte)CTX::instance()->unpackGreen(col2);
+        color[i*4*4+10] = color[i*4*4+14] = (GLubyte)CTX::instance()->unpackBlue(col2);
+        color[i*4*4+11] = color[i*4*4+15] = (GLubyte)CTX::instance()->unpackAlpha(col2);
+        vertex[i*3*4+6] = xmin + (i + 1) * box;
+        vertex[i*3*4+7] = ymin;
+        vertex[i*3*4+8] = 0.;
+        vertex[i*3*4+9] = xmin + (i + 1) * box;
+        vertex[i*3*4+10] = ymin + dh;
+        vertex[i*3*4+11] = 0.;
+      }
+      else{
+        unsigned int col = opt->getColor(i, opt->nbIso);
+        color[i*4*4+0] = color[i*4*4+4] = color[i*4*4+8] = color[i*4*4+12] =
+          (GLubyte)CTX::instance()->unpackRed(col);
+        color[i*4*4+1] = color[i*4*4+5] = color[i*4*4+9] = color[i*4*4+13] =
+          (GLubyte)CTX::instance()->unpackGreen(col);
+        color[i*4*4+2] = color[i*4*4+6] = color[i*4*4+10] = color[i*4*4+14] =
+          (GLubyte)CTX::instance()->unpackBlue(col);
+        color[i*4*4+3] = color[i*4*4+7] = color[i*4*4+11] = color[i*4*4+15] =
+          (GLubyte)CTX::instance()->unpackAlpha(col);
+        vertex[i*3*4+0] = xmin + i * box;
+        vertex[i*3*4+1] = ymin;
+        vertex[i*3*4+2] = 0.;
+        vertex[i*3*4+3] = xmin + i * box;
+        vertex[i*3*4+4] = ymin + dh;
+        vertex[i*3*4+5] = 0.;
+        vertex[i*3*4+6] = xmin + (i + 1) * box;
+        vertex[i*3*4+7] = ymin;
+        vertex[i*3*4+8] = 0.;
+        vertex[i*3*4+9] = xmin + (i + 1) * box;
+        vertex[i*3*4+10] = ymin + dh;
+        vertex[i*3*4+11] = 0.;
+      }
+    }
+
+    glVertexPointer(3, GL_FLOAT, 0, &vertex[0]);
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glColorPointer(4, GL_UNSIGNED_BYTE, 0, &color[0]);
+    glEnableClientState(GL_COLOR_ARRAY);
+    if(opt->intervalsType == PViewOptions::Discrete ||
+       opt->intervalsType == PViewOptions::Numeric ||
+       opt->intervalsType == PViewOptions::Continuous)
+      glDrawArrays(GL_TRIANGLE_STRIP, 0, opt->nbIso*4);
+    else
+      glDrawArrays(GL_LINES, 0, opt->nbIso*4);
+    glDisableClientState(GL_COLOR_ARRAY);
+    glDisableClientState(GL_VERTEX_ARRAY);
+
+    char label[1024];
+
+    int nt = data->getNumTimeSteps();
+    int n0 = data->getFirstNonEmptyTimeStep();
+    int n = (nt - n0 > 0) ? nt - n0 : 1;
+    char time[256];
+    sprintf(time, opt->format.c_str(), data->getTime(opt->timeStep));
+    int choice = opt->showTime;
+    if(choice == 3){ // automatic
+      if(n == 1) choice = 0; // nothing
+      else if(n == 2) choice = 2; // harmonic
+      else choice = 5; // multi-step data
+    }
+    switch(choice){
+    case 1: // time series
+      sprintf(label, "%s - time %s", data->getName().c_str(), time);
+      break;
+    case 2: // harmonic data
+      if(n <= 2)
+        sprintf(label, "%s - %s part", data->getName().c_str(),
+                ((opt->timeStep - n0) % 2) ? "imaginary" : "real");
+      else
+        sprintf(label, "%s - harmonic %s (%s part)",
+                data->getName().c_str(), time,
+                ((opt->timeStep - n0) % 2) ? "imaginary" : "real");
+      break;
+    case 3: // automatic
+      // never here
+      break;
+    case 4: // step data
+      sprintf(label, "%s - step %d", data->getName().c_str(), opt->timeStep);
+      break;
+    case 5: // multi-step data
+      sprintf(label, "%s - step %d in [0,%d]", data->getName().c_str(),
+              opt->timeStep, data->getNumTimeSteps() - 1);
+      break;
+    case 6: // real eigenvalues
+      sprintf(label, "%s - eigenvalue %s", data->getName().c_str(),
+              time);
+      break;
+    case 7: // complex eigenvalues
+      sprintf(label, "%s - eigenvalue %s (%s part)", data->getName().c_str(),
+              time, ((opt->timeStep - n0) % 2) ? "imaginary" : "real");
+      break;
+    default:
+      sprintf(label, "%s", data->getName().c_str());
+      break;
+    }
+
+    if(strlen(label)){
+      drawString lbl(label, 20 * _fontFactor);
+      lbl.draw(xmin + width / 2, ymin + 2.8 * dh, 0.,
+               _width/(_right-_left), _height/(_top-_bottom));
+    }
+
+    drawString val(data->getName().c_str(), 15 * _fontFactor);
+    for(int i = 0; i < 3; i++) {
+      double v = opt->getScaleValue(i, 3, opt->tmpMin, opt->tmpMax);
+      sprintf(label, opt->format.c_str(), v);
+      val.setText(label);
+      val.draw(xmin + i * width/ 2, ymin + 1.5 * dh, 0.,
+               _width/(_right-_left), _height/(_top-_bottom));
+    }
+    nPview++;
+  }
+  glPopMatrix();
+}
+
+void drawContext::drawPost()
+{
+  if(PView::list.empty()) return ;
+
+  for(unsigned int i = 0; i < PView::list.size(); i++){
+    PView::list[i]->fillVertexArrays();
+    drawPView(PView::list[i]);
+  }
+}
+
+void drawContext::drawAxes()
+{
+  glLineWidth(1.);
+  bool geometryExists = false;
+  for(unsigned int i = 0; i < GModel::list.size(); i++){
+    if(!GModel::list[i]->empty()){
+      geometryExists = true;
+      break;
+    }
+  }
+  if(!CTX::instance()->axesAutoPosition){
+    drawAxes(CTX::instance()->axes, CTX::instance()->axesTics,
+             CTX::instance()->axesFormat, CTX::instance()->axesLabel,
+             CTX::instance()->axesPosition, CTX::instance()->axesMikado,
+             CTX::instance()->axesForceValue ? CTX::instance()->axesValue :
+             CTX::instance()->axesPosition);
+  }
+  else if(geometryExists){
+    double bb[6] =
+      {CTX::instance()->min[0], CTX::instance()->max[0], CTX::instance()->min[1],
+       CTX::instance()->max[1], CTX::instance()->min[2], CTX::instance()->max[2]};
+    drawAxes(CTX::instance()->axes, CTX::instance()->axesTics,
+             CTX::instance()->axesFormat, CTX::instance()->axesLabel,
+             bb, CTX::instance()->axesMikado, CTX::instance()->axesForceValue ?
+             CTX::instance()->axesValue : bb);
+  }
+}
+
+static void drawAxis(float xmin, float ymin, float zmin,
+                     float xmax, float ymax, float zmax,
+                     int ntics, int mikado)
+{
+  GLfloat axes[] = {
+    xmin, ymin, zmin,
+    xmax, ymax, zmax
+  };
+  GLfloat colors[] = {
+    0, 0, 0, 1,
+    0, 0, 0, 1,
+  };
+  glVertexPointer(3, GL_FLOAT, 0, axes);
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glColorPointer(4, GL_FLOAT, 0, colors);
+  glEnableClientState(GL_COLOR_ARRAY);
+  glDrawArrays(GL_LINES, 0, 2);
+  glDisableClientState(GL_VERTEX_ARRAY);
+  glDisableClientState(GL_COLOR_ARRAY);
+}
+
+static void drawGridStipple(int n1, int n2, double p1[3], double p2[3], double p3[3])
+{
+  double t1[3] = {p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]};
+  double t2[3] = {p3[0] - p1[0], p3[1] - p1[1], p3[2] - p1[2]};
+  double l1 = norme(t1);
+  double l2 = norme(t2);
+
+  std::vector<GLfloat> v, c;
+  for(int i = 1; i < n1 - 1; i++){
+    double d = (double)i / (double)(n1 - 1) * l1;
+    v.push_back(p1[0] + t1[0] * d);
+    v.push_back(p1[1] + t1[1] * d);
+    v.push_back(p1[2] + t1[2] * d);
+    v.push_back(p1[0] + t1[0] * d + t2[0] * l2);
+    v.push_back(p1[1] + t1[1] * d + t2[1] * l2);
+    v.push_back(p1[2] + t1[2] * d + t2[2] * l2);
+  }
+  for(int i = 1; i < n2 - 1; i++){
+    double d = (double)i/(double)(n2 - 1) * l2;
+    v.push_back(p1[0] + t2[0] * d);
+    v.push_back(p1[1] + t2[1] * d);
+    v.push_back(p1[2] + t2[2] * d);
+    v.push_back(p1[0] + t2[0] * d + t1[0] * l1);
+    v.push_back(p1[1] + t2[1] * d + t1[1] * l1);
+    v.push_back(p1[2] + t2[2] * d + t1[2] * l1);
+  }
+  for(unsigned int i = 0; i < v.size(); i++){
+    c.push_back(0);
+    c.push_back(0);
+    c.push_back(0);
+    c.push_back(0.2);
+  }
+
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glEnable(GL_BLEND);
+  glVertexPointer(3, GL_FLOAT, 0, &v[0]);
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glColorPointer(4, GL_FLOAT, 0, &c[0]);
+  glEnableClientState(GL_COLOR_ARRAY);
+  glDrawArrays(GL_LINES, 0, v.size() / 3);
+  glDisableClientState(GL_VERTEX_ARRAY);
+  glDisableClientState(GL_COLOR_ARRAY);
+  glDisable(GL_BLEND);
+}
+
+int drawContext::drawTics(drawContext *ctx, int comp, double n, std::string &format,
+                          std::string &label, double p1[3], double p2[3],
+                          double perp[3], int mikado, double pixelfact,
+                          double value_p1[3], double value_p2[3])
+{
+  // draws n tic marks (in direction perp) and labels along the line p1->p2
+
+  double t[3] = {p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]};
+  double l = norme(t);
+  double value_t[3] = {value_p2[0] - value_p1[0], value_p2[1] - value_p1[1],
+                       value_p2[2] - value_p1[2]};
+  double value_l = norme(value_t);
+  double w = 10 * pixelfact; // tic marks are 10 pixels long
+  double w2 = w * 4.5; // distance to labels
+
+  // draw label at the end of the axis
+  if(label.size()){
+    drawString lbl(label, 15 * _fontFactor);
+    lbl.draw(p2[0] + t[0] * w2, p2[1] + t[1] * w2, p2[2] + t[2] * w2,
+             _width/(_right-_left)*_scale[0], _height/(_top-_bottom)*_scale[0]);
+  }
+
+  // return number of tics in special cases
+  if(n < 2.) return 0;
+  if(format.empty()) return n;
+
+  // select perp direction automatically if it is not provided
+  double lp = norme(perp);
+  if(!lp){
+    switch(comp){
+    case 0: perp[1] = -1.; break;
+    case 1: perp[0] = -1.; break;
+    case 2: perp[0] = 1.; break;
+    default: break;
+    }
+  }
+
+  char tmp[256];
+
+  // reduce number of vertical tics if not zoomed enough
+  double ww = _width/(_right-_left)*_scale[0];
+  double hh = _height/(_top-_bottom)*_scale[0];
+  if(hh < 10 && comp == 1){
+    n = 2;
+  }
+
+  // draw n tics
+  double step = l / (double)(n - 1);
+  double value_step = value_l / (double)(n - 1);
+
+  for(int i = 0; i < n; i++){
+    double d = i * step;
+    double p[3] = {p1[0] + t[0] * d, p1[1] + t[1] * d, p1[2] + t[2] * d};
+    double q[3] = {p[0] + perp[0] * w, p[1] + perp[1] * w, p[2] + perp[2] * w};
+    double r[3] = {p[0] + perp[0] * w2, p[1] + perp[1] * w2, p[2] + perp[2] * w2};
+
+    double value_d = i * value_step;
+    double value_p[3] = {value_p1[0] + value_t[0] * value_d,
+                         value_p1[1] + value_t[1] * value_d,
+                         value_p1[2] + value_t[2] * value_d};
+
+    // draw tic labels
+    if(comp < 0) // display the length value (ruler-mode, starting at 0)
+      sprintf(tmp, format.c_str(), value_d);
+    else // display the coordinate value
+      sprintf(tmp, format.c_str(), value_p[comp]);
+
+    if(strlen(tmp)){
+      drawString lbl(tmp, 15 * _fontFactor);
+      lbl.draw(r[0], r[1], r[2], ww, hh);
+    }
+
+    GLfloat lines[] = {
+      (float)p[0], (float)p[1], (float)p[2],
+      (float)q[0], (float)q[1], (float)q[2]
+    };
+    GLfloat colors[] = {
+      0, 0, 0, 1,
+      0, 0, 0, 1,
+    };
+    glVertexPointer(3, GL_FLOAT, 0, lines);
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glColorPointer(4, GL_FLOAT, 0, colors);
+    glEnableClientState(GL_COLOR_ARRAY);
+    glDrawArrays(GL_LINES, 0, 2);
+    glDisableClientState(GL_VERTEX_ARRAY);
+    glDisableClientState(GL_COLOR_ARRAY);
+  }
+  return n;
+}
+
+void drawContext::drawAxes(int mode, double tics[3], std::string format[3],
+                           std::string label[3], double bb[6], int mikado,
+                           double value_bb[6])
+{
+  // mode 0: nothing
+  //      1: axes
+  //      2: box
+  //      3: full grid
+  //      4: open grid
+  //      5: ruler
+
+  if((mode < 1) || (bb[0] == bb[1] && bb[2] == bb[3] && bb[4] == bb[5]))
+    return;
+
+  double xmin = bb[0], xmax = bb[1];
+  double ymin = bb[2], ymax = bb[3];
+  double zmin = bb[4], zmax = bb[5];
+  double orig[3] = {xmin, ymin, zmin};
+
+  double value_xmin = value_bb[0], value_xmax = value_bb[1];
+  double value_ymin = value_bb[2], value_ymax = value_bb[3];
+  double value_zmin = value_bb[4], value_zmax = value_bb[5];
+  double value_orig[3] = {value_xmin, value_ymin, value_zmin};
+
+  double pixelfact = _pixel_equiv_x / _scale[0];
+
+  if(mode == 5){ // draw ruler from xyz_min to xyz_max
+    double end[3] = {xmax, ymax, zmax};
+    double dir[3] = {xmax - xmin, ymax - ymin, zmax - zmin};
+    double perp[3];
+    if((fabs(dir[0]) >= fabs(dir[1]) && fabs(dir[0]) >= fabs(dir[2])) ||
+       (fabs(dir[1]) >= fabs(dir[0]) && fabs(dir[1]) >= fabs(dir[2]))){
+      perp[0] = dir[1]; perp[1] = -dir[0]; perp[2] = 0.;
+    }
+    else{
+      perp[0] = 0.; perp[1] = dir[2]; perp[2] = -dir[1];
+    }
+    double value_end[3] = {value_xmax, value_ymax, value_zmax};
+    drawTics(this, -1, tics[0], format[0], label[0], orig, end, perp, mikado,
+             pixelfact, value_orig, value_end);
+    drawAxis(xmin, ymin, zmin, xmax, ymax, zmax, tics[0], mikado);
+    return;
+  }
+  double xx[3] = {xmax, ymin, zmin};
+  double yy[3] = {xmin, ymax, zmin};
+  double zz[3] = {xmin, ymin, zmax};
+  double value_xx[3] = {value_xmax, value_ymin, value_zmin};
+  double value_yy[3] = {value_xmin, value_ymax, value_zmin};
+  double value_zz[3] = {value_xmin, value_ymin, value_zmax};
+  double dxm[3] = {0., (ymin != ymax) ? -1. : 0., (zmin != zmax) ? -1. : 0.};
+  double dym[3] = {(xmin != xmax) ? -1. : 0., 0., (zmin != zmax) ? -1. : 0.};
+  double dzm[3] = {(xmin != xmax) ? -1. : 0., (ymin != ymax) ? -1. : 0., 0.};
+
+  int nx = (xmin != xmax) ? drawTics(this, 0, tics[0], format[0], label[0], orig, xx, dxm,
+                                     mikado, pixelfact, value_orig, value_xx) : 0;
+  int ny = (ymin != ymax) ? drawTics(this, 1, tics[1], format[1], label[1], orig, yy, dym,
+                                     mikado, pixelfact, value_orig, value_yy) : 0;
+  int nz = (zmin != zmax) ? drawTics(this, 2, tics[2], format[2], label[2], orig, zz, dzm,
+                                     mikado, pixelfact, value_orig, value_zz) : 0;
+
+  drawAxis(xmin, ymin, zmin, xmax, ymin, zmin, nx, mikado);
+  drawAxis(xmin, ymin, zmin, xmin, ymax, zmin, ny, mikado);
+  drawAxis(xmin, ymin, zmin, xmin, ymin, zmax, nz, mikado);
+
+  // open box
+  if(mode > 1){
+    drawAxis(xmin, ymax, zmin, xmax, ymax, zmin, nx, mikado);
+    drawAxis(xmax, ymin, zmin, xmax, ymax, zmin, ny, mikado);
+    drawAxis(xmax, ymin, zmin, xmax, ymin, zmax, nz, mikado);
+    drawAxis(xmin, ymin, zmax, xmax, ymin, zmax, nx, mikado);
+    drawAxis(xmin, ymin, zmax, xmin, ymax, zmax, ny, mikado);
+    drawAxis(xmin, ymax, zmin, xmin, ymax, zmax, nz, mikado);
+  }
+
+  // closed box
+  if(mode == 2 || mode == 3){
+    drawAxis(xmin, ymax, zmax, xmax, ymax, zmax, nx, mikado);
+    drawAxis(xmax, ymin, zmax, xmax, ymax, zmax, ny, mikado);
+    drawAxis(xmax, ymax, zmin, xmax, ymax, zmax, nz, mikado);
+  }
+
+  if(mode > 2){
+    drawGridStipple(nx, ny, orig, xx, yy);
+    drawGridStipple(ny, nz, orig, yy, zz);
+    drawGridStipple(nx, nz, orig, xx, zz);
+  }
+
+  if(mode == 3){
+    double orig2[3] = {xmax, ymax, zmax};
+    double xy[3] = {xmax, ymax, zmin};
+    double yz[3] = {xmin, ymax, zmax};
+    double xz[3] = {xmax, ymin, zmax};
+    if(zmin != zmax) drawGridStipple(nx, ny, orig2, yz, xz);
+    if(xmin != xmax) drawGridStipple(ny, nz, orig2, xz, xy);
+    if(ymin != ymax) drawGridStipple(nx, nz, orig2, yz, xy);
+  }
+}
+
+void drawContext::drawSmallAxes()
+{
+  glLineWidth(1.);
+  glPushMatrix();
+  glLoadIdentity();
+
+  GLfloat h = std::max(_top - _bottom, _right - _left) / 25. ;
+  GLfloat x0 = _right - 1.8 * h;
+  GLfloat y0 = _bottom + 1.5 * h;
+
+  GLfloat xx = h * _rotatef[0];
+  GLfloat xy = h * _rotatef[1];
+  GLfloat yx = h * _rotatef[4];
+  GLfloat yy = h * _rotatef[5];
+  GLfloat zx = h * _rotatef[8];
+  GLfloat zy = h * _rotatef[9];
+  GLfloat o = h / 10;
+
+  const GLfloat axes[] = {
+    x0, y0,
+    x0 + xx, y0 + xy,
+    x0, y0,
+    x0 + yx, y0 + yy,
+    x0, y0,
+    x0 + zx, y0 + zy
+  };
+  GLfloat colors[] = {
+    0, 0, 0, 1,
+    0, 0, 0, 1,
+    0, 0, 0, 1,
+    0, 0, 0, 1,
+    0, 0, 0, 1,
+    0, 0, 0, 1,
+  };
+  glVertexPointer(2, GL_FLOAT, 0, axes);
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glColorPointer(4, GL_FLOAT, 0, colors);
+  glEnableClientState(GL_COLOR_ARRAY);
+  glDrawArrays(GL_LINES, 0, 6);
+  glDisableClientState(GL_VERTEX_ARRAY);
+  glDisableClientState(GL_COLOR_ARRAY);
+
+  drawString x("X", 15 * _fontFactor, colors);
+  x.draw(x0 + xx + o, y0 + xy + o, 0, _width/(_right-_left), _height/(_top-_bottom), false);
+  drawString y("Y", 15 * _fontFactor, colors+8);
+  y.draw(x0 + yx + o, y0 + yy + o, 0, _width/(_right-_left), _height/(_top-_bottom), false);
+  drawString z("Z", 15 * _fontFactor, colors+16);
+  z.draw(x0 + zx + o, y0 + zy + o, 0, _width/(_right-_left), _height/(_top-_bottom), false);
+  glPopMatrix();
+}
+
+int drawContext::fix2dCoordinates(double *x, double *y)
+{
+  int ret = (*x > 99999 && *y > 99999) ? 3 : (*y > 99999) ? 2 : (*x > 99999) ? 1 : 0;
+
+  if(*x < 0) // measure from right border
+    *x = _right + *x;
+  else if(*x > 99999) // by convention, x-centered
+    *x = _right / 2;
+
+  if(*y < 0) // measure from bottom border
+    *y = -(*y);
+  else if(*y > 99999) // by convention, y-centered
+    *y = _top / 2.;
+  else
+    *y = _top - *y;
+  return ret;
+}
+
+void drawContext::drawText2d()
+{
+  glPushMatrix();
+  glLoadIdentity();
+
+  for(unsigned int i = 0; i < PView::list.size(); i++){
+    PViewData *data = PView::list[i]->getData();
+    PViewOptions *opt = PView::list[i]->getOptions();
+    if(opt->visible && opt->drawStrings){
+      for(int j = 0; j < data->getNumStrings2D(); j++){
+        double x, y, style;
+        std::string str;
+        data->getString2D(j, opt->timeStep, str, x, y, style);
+        //fix2dCoordinates(&x, &y);
+        GLfloat colors[] = {0., 0, 0, 1.};
+        drawString s(str.c_str(), 20 * _fontFactor, colors);
+        // FIXME:
+        s.draw(_left + (_right - _left) / 2.,
+               _bottom + 0.8 * (_top - _bottom), 0,
+               _width/(_right-_left), _height/(_top-_bottom), true);
+      }
+    }
+  }
+
+  glPopMatrix();
+}
+
+void drawGraph2d()
+{
+  glPushMatrix();
+  glLoadIdentity();
+
+  std::vector<PView*> graphs;
+  for(unsigned int i = 0; i < PView::list.size(); i++){
+    PViewData *data = PView::list[i]->getData();
+    PViewOptions *opt = PView::list[i]->getOptions();
+    if(!data->getDirty() && opt->visible && opt->type != PViewOptions::Plot3D)
+      graphs.push_back(PView::list[i]);
+  }
+  if(graphs.empty()) return;
+
+  // FIXME: draw 2d graph(s)
+
+  glPopMatrix();
+}
+
+void drawContext::drawView()
+{
+  OrthofFromGModel();
+
+  glMatrixMode(GL_MODELVIEW);
+
+  // draw the background
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  if(CTX::instance()->bgGradient){
+    glPushMatrix();
+    glLoadIdentity();
+    const GLfloat squareVertices[] = {
+      (GLfloat)_top,	(GLfloat)_left, 2*_far,
+      (GLfloat)_top,	(GLfloat)_right, 2*_far,
+      (GLfloat)_bottom,	(GLfloat)_left, 2*_far,
+      (GLfloat)_bottom,	(GLfloat)_right, 2*_far,
+    };
+    const GLubyte squareColors[] = {
+      255, 255, 255, 255,
+      255, 255, 255, 255,
+      190, 200, 255, 255,
+      190, 200, 255, 255,
+    };
+    glVertexPointer(3, GL_FLOAT, 0, squareVertices);
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
+    glEnableClientState(GL_COLOR_ARRAY);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    glDisableClientState(GL_COLOR_ARRAY);
+    glDisableClientState(GL_VERTEX_ARRAY);
+    glPopMatrix();
+  }
+  checkGlError("Draw background");
+
+  // init lights
+  glPushMatrix();
+  glLoadIdentity();
+  glScalef(_scale[0], _scale[1], _scale[2]);
+  glTranslatef(_translate[0], _translate[1], _translate[2]);
+  for(int i = 0; i < 6; i++) {
+    if(CTX::instance()->light[i]) {
+      GLfloat position[4] = {
+        (GLfloat)CTX::instance()->lightPosition[i][0],
+        (GLfloat)CTX::instance()->lightPosition[i][1],
+        (GLfloat)CTX::instance()->lightPosition[i][2],
+        (GLfloat)CTX::instance()->lightPosition[i][3]
+      };
+      glLightfv((GLenum)(GL_LIGHT0 + i), GL_POSITION, position);
+      GLfloat r = (GLfloat)(CTX::instance()->unpackRed
+                            (CTX::instance()->color.ambientLight[i]) / 255.);
+      GLfloat g = (GLfloat)(CTX::instance()->unpackGreen
+                            (CTX::instance()->color.ambientLight[i]) / 255.);
+      GLfloat b = (GLfloat)(CTX::instance()->unpackBlue
+                            (CTX::instance()->color.ambientLight[i]) / 255.);
+      GLfloat ambient[4] = {r, g, b, 1.0F};
+      glLightfv((GLenum)(GL_LIGHT0 + i), GL_AMBIENT, ambient);
+      r = (GLfloat)(CTX::instance()->unpackRed
+                    (CTX::instance()->color.diffuseLight[i]) / 255.);
+      g = (GLfloat)(CTX::instance()->unpackGreen
+                    (CTX::instance()->color.diffuseLight[i]) / 255.);
+      b = (GLfloat)(CTX::instance()->unpackBlue
+                    (CTX::instance()->color.diffuseLight[i]) / 255.);
+      GLfloat diffuse[4] = {r, g, b, 1.0F};
+      glLightfv((GLenum)(GL_LIGHT0 + i), GL_DIFFUSE, diffuse);
+      r = (GLfloat)(CTX::instance()->unpackRed
+                    (CTX::instance()->color.specularLight[i]) / 255.);
+      g = (GLfloat)(CTX::instance()->unpackGreen
+                    (CTX::instance()->color.specularLight[i]) / 255.);
+      b = (GLfloat)(CTX::instance()->unpackBlue
+                    (CTX::instance()->color.specularLight[i]) / 255.);
+      GLfloat specular[4] = {r, g, b, 1.0F};
+      glLightfv((GLenum)(GL_LIGHT0 + i), GL_SPECULAR, specular);
+      glEnable((GLenum)(GL_LIGHT0 + i));
+    }
+    else{
+      glDisable((GLenum)(GL_LIGHT0 + i));
+    }
+  }
+  glPopMatrix();
+  // ambient and diffuse material colors track glColor automatically
+  //glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
+  glEnable(GL_COLOR_MATERIAL);
+  // "white"-only specular material reflection color
+  GLfloat spec[4] = {(GLfloat)CTX::instance()->shine,
+                     (GLfloat)CTX::instance()->shine,
+                     (GLfloat)CTX::instance()->shine, 1.0F};
+  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
+  // specular exponent in [0,128] (larger means more "focused"
+  // reflection)
+  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS,
+              (GLfloat)CTX::instance()->shineExponent);
+  glShadeModel(GL_SMOOTH);
+  glEnable(GL_RESCALE_NORMAL);
+  glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1.);
+  glDisable(GL_LIGHTING);
+  checkGlError("Initialize lights");
+
+  // init position
+  glLoadIdentity();
+  glScalef(_scale[0], _scale[1], _scale[2]);
+  glTranslatef(_translate[0], _translate[1], _translate[2]);
+  if(CTX::instance()->rotationCenterCg)
+    glTranslatef(CTX::instance()->cg[0],
+                 CTX::instance()->cg[1],
+                 CTX::instance()->cg[2]);
+  else
+    glTranslatef(CTX::instance()->rotationCenter[0],
+                 CTX::instance()->rotationCenter[1],
+                 CTX::instance()->rotationCenter[2]);
+  buildRotationMatrix();
+  glMultMatrixf(_rotatef);
+  if(CTX::instance()->rotationCenterCg)
+    glTranslatef(-CTX::instance()->cg[0],
+                 -CTX::instance()->cg[1],
+                 -CTX::instance()->cg[2]);
+  else
+    glTranslatef(-CTX::instance()->rotationCenter[0],
+                 -CTX::instance()->rotationCenter[1],
+                 -CTX::instance()->rotationCenter[2]);
+  checkGlError("Initialize position");
+
+  // draw everything
+  glEnable(GL_DEPTH_TEST);
+  drawMesh(); checkGlError("Draw mesh");
+  drawGeom(); checkGlError("Draw geometry");
+  drawPost(); checkGlError("Draw post-pro");
+  drawAxes(); checkGlError("Draw axes");
+  glDisable(GL_DEPTH_TEST);
+  drawScale(); checkGlError("Draw scales");
+  drawSmallAxes(); checkGlError("Draw small axes");
+  drawText2d(); checkGlError("Draw text2d");
+  drawGraph2d(); checkGlError("Draw graph2d");
+}
+
+std::vector<std::string> commandToVector(const std::string cmd)
+{
+  std::vector<std::string> ret;
+  int pos = 0, last = 0;
+  while((pos = cmd.find("-", last+1)) != std::string::npos){
+    ret.push_back(cmd.substr(last,pos-1-last));
+    last = pos;
+  }
+  ret.push_back(cmd.substr(last));
+  return ret;
+}
+
+int onelab_cb(std::string action)
+{
+  if(action == "stop"){
+    onelab::string o("GetDP/Action", "stop");
+    o.setVisible(false);
+    o.setNeverChanged(true);
+    onelab::server::instance()->set(o);
+    onelabStop = true;
+    return 0;
+  }
+
+  if(locked) return -1;
+  locked = true;
+
+  if(action == "reset"){
+    onelab::server::instance()->clear();
+    onelabUtils::runGmshClient(action, true);
+    action = "check";
+  }
+
+  Msg::ResetErrorCounter();
+
+  if(action == "compute"){
+    onelabUtils::initializeLoop("1");
+    onelabUtils::initializeLoop("2");
+    onelabUtils::initializeLoop("3");
+  }
+
+  do{
+    // run Gmsh (only if necessary)
+    onelabUtils::runGmshClient(action, true);
+
+    // run GetDP (always -- to not confuse the user)
+    onelab::string o("GetDP/Action", action);
+    o.setVisible(false);
+    o.setNeverChanged(true);
+    onelab::server::instance()->set(o);
+
+    std::vector<std::string> args;
+    args.push_back("getdp");
+    std::vector<onelab::string> ps;
+    onelab::server::instance()->get(ps, "GetDP/1ModelName");
+    if(ps.empty()){
+      std::vector<std::string> split = SplitFileName(GModel::current()->getFileName());
+      std::string name(split[0] + split[1]);
+      onelab::string o("GetDP/1ModelName", name, "Model name");
+      o.setKind("file");
+      onelab::server::instance()->set(o);
+      ps.push_back(o);
+    }
+    args.push_back(ps[0].getValue());
+    if(action == "check")
+      onelab::server::instance()->get(ps, "GetDP/9CheckCommand");
+    else if(action == "compute")
+      onelab::server::instance()->get(ps, "GetDP/9ComputeCommand");
+    else
+      ps.clear();
+    std::vector<std::string> c = commandToVector(ps.empty() ? "" : ps[0].getValue().c_str());
+    args.insert(args.end(), c.begin(), c.end());
+    args.push_back("-onelab");
+    args.push_back("GetDP");
+    getdp(args, onelab::server::instance());
+  } while(action == "compute" && !onelabStop && (onelabUtils::incrementLoop("3") ||
+                                                 onelabUtils::incrementLoop("2") ||
+                                                 onelabUtils::incrementLoop("1")));
+  onelabStop = false;
+  locked = false;
+  return onelab::server::instance()->getChanged();
+}
+
+int number_of_animation()
+{
+  int ret = 0;
+  for(unsigned int i = 0; i < PView::list.size(); i++){
+    PView * p = PView::list[i];
+    if(p->getOptions()->visible){
+      int numSteps = (int)p->getData()->getNumTimeSteps();
+      if(numSteps > ret) ret = numSteps;
+    }
+  }
+  return ret;
+}
+
+void set_animation(int step)
+{
+  for(unsigned int i = 0; i < PView::list.size(); i++){
+    PView * p = PView::list[i];
+    if(p->getOptions()->visible){
+      p->getOptions()->timeStep = step;
+      p->setChanged(true);
+    }
+  }
+}
+
+int animation_next()
+{
+  int ret = 0;
+  for(unsigned int i = 0; i < PView::list.size(); i++){
+    PView * p = PView::list[i];
+    if(p->getOptions()->visible){
+      int step = (int)p->getOptions()->timeStep + 1;
+      int numSteps = (int)p->getData()->getNumTimeSteps();
+      if(step < 0) step = numSteps - 1;
+      if(step > numSteps - 1) step = 0;
+      p->getOptions()->timeStep = step;
+      p->setChanged(true);
+      ret = step;
+    }
+  }
+  return ret;
+}
+
+int animation_prev()
+{
+  int ret = 0;
+  for(unsigned int i = 0; i < PView::list.size(); i++){
+    PView * p = PView::list[i];
+    if(p->getOptions()->visible){
+      // skip empty steps
+      int step = (int)p->getOptions()->timeStep - 1;
+      int numSteps = (int)p->getData()->getNumTimeSteps();
+      if(step < 0) step = numSteps - 1;
+      if(step > numSteps - 1) step = 0;
+      p->getOptions()->timeStep = step;
+      p->setChanged(true);
+      ret = step;
+    }
+  }
+  return ret;
+}
diff --git a/contrib/mobile/drawContext.h b/contrib/mobile/drawContext.h
new file mode 100644
index 0000000000000000000000000000000000000000..f5b60dba45f493013b41d5af2e18625cfb82cb43
--- /dev/null
+++ b/contrib/mobile/drawContext.h
@@ -0,0 +1,110 @@
+#ifndef _DRAW_CONTEXT_H_
+#define _DRAW_CONTEXT_H_
+
+#include <string>
+
+#include <gmsh/PView.h>
+#include <gmsh/PViewOptions.h>
+
+class movePosition {
+ public:
+  float win[3]; // window coordinates
+  float wnr[3]; // world coordinates BEFORE rotation
+  float s[3]; // scaling state when the event was recorded
+  float t[3]; // translation state when the event was recorded
+  movePosition()
+  {
+    for(int i = 0; i < 3; i++)
+      win[i] = wnr[i] = s[i] = t[i] = 0.;
+  }
+  movePosition(const movePosition &instance)
+  {
+    for(int i = 0; i < 3; i++){
+      win[i] = instance.win[i];
+      wnr[i] = instance.wnr[i];
+      s[i] = instance.s[i];
+      t[i] = instance.t[i];
+    }
+  }
+  void set(float scale[3], float translate[3], float vxmax, float vxmin,
+           float vymin, float vymax, int width, int height, int x, int y)
+  {
+    for(int i = 0; i < 3; i++){
+      s[i] = scale[i];
+      t[i] = translate[i];
+    }
+    win[0] = (float)x;
+    win[1] = (float)y;
+    win[2] = 0.;
+
+    wnr[0] =
+      (vxmin + win[0] / (float)width * (vxmax - vxmin)) / scale[0] - translate[0];
+    wnr[1] =
+      (vymax - win[1] / (float)height * (vymax - vymin)) / scale[1] - translate[1];
+    wnr[2] = 0.;
+  }
+  void recenter(float scale[3], float translate[3]) const
+  {
+    // compute the equivalent translation to apply *after* the scaling so that
+    // the scaling is done around the point which was clicked:
+    translate[0] = t[0] * (s[0] / scale[0]) - wnr[0] * (1. - (s[0] / scale[0]));
+    translate[1] = t[1] * (s[1] / scale[1]) - wnr[1] * (1. - (s[1] / scale[1]));
+  }
+};
+
+class drawContext{
+private:
+  float _translate[3], _scale[3]; // current translation and scale
+  double _rotate[16]; // current rotation matrix (double for Trackball)
+  float _rotatef[16]; // current rotation matrix (float for OpenGL ES)
+  double _quaternion[4]; // current quaternion used for rotation
+  movePosition _start, _previous, _current; // store informations about user interactions
+  int _width, _height; // size of OpenGL context in pixel
+  float _left, _right, _top, _bottom, _far; // value of "border"
+  float _fontFactor;
+  bool _retina; // retina display
+  double _pixel_equiv_x, _pixel_equiv_y;
+
+  void OrthofFromGModel(void);
+  void drawPView(PView *p);
+  void drawVectorArray(PViewOptions *opt, VertexArray *va);
+public:
+  drawContext(float fontFactor=1., bool retina=false);
+  ~drawContext(){}
+  void load(std::string filename);
+  void eventHandler(int event, float x=0, float y=0);
+  void setQuaternion(double q0, double q1, double q2, double q3);
+  void addQuaternion(double p1x, double p1y, double p2x, double p2y);
+  void buildRotationMatrix();
+  void setTranslate(int i, float t) {if(i>=0 && i<3) _translate[i] = t;}
+  float getTranslate(int i) {if(i>=0 && i<3) return _translate[i]; return 0;}
+  void setScale(int i, float s) {if(i>=0 && i<3) _scale[i] = s;}
+  float getScale(int i) {if(i>=0 && i<3) return _scale[i]; return 0;}
+  void initView(int w, int h);
+  int fix2dCoordinates(double *x, double *y);
+  void drawView();
+  void drawAxes();
+  void drawAxes(int mode, double tics[3], std::string format[3],
+                std::string label[3], double bb[6], int mikado,
+                double value_bb[6]);
+  int drawTics(drawContext *ctx, int comp, double n, std::string &format,
+               std::string &label, double p1[3], double p2[3],
+               double perp[3], int mikado, double pixelfact,
+               double value_p1[3], double value_p2[3]);
+  void drawSmallAxes();
+  void drawGeom();
+  void drawMesh();
+  void drawPost();
+  void drawScale();
+  void drawText2d();
+};
+
+void drawArray(VertexArray *va, int type, bool useColorArray=false,
+               bool useNormalArray=false);
+int onelab_cb(std::string);
+int animation_next();
+int animation_prev();
+int number_of_animation();
+void set_animation(int step);
+
+#endif
diff --git a/contrib/mobile/drawGeom.cpp b/contrib/mobile/drawGeom.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..757b169f59c8b3df8a5318b954b4537853757759
--- /dev/null
+++ b/contrib/mobile/drawGeom.cpp
@@ -0,0 +1,89 @@
+#include <stdlib.h>
+
+#if defined(BUILD_ANDROID)
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#else
+#include <OpenGLES/ES1/gl.h>
+#include <OpenGLES/ES1/glext.h>
+#endif
+
+#include <gmsh/GmshGlobal.h>
+#include <gmsh/GModel.h>
+#include <gmsh/Context.h>
+
+#include "drawContext.h"
+
+void drawGeomVertex(GVertex *v)
+{
+  if(!v->getVisibility()) return;
+  if(v->geomType() == GEntity::BoundaryLayerPoint) return;
+
+  unsigned int col = CTX::instance()->color.geom.point;
+  glColor4ub((GLubyte)CTX::instance()->unpackRed(col),
+             (GLubyte)CTX::instance()->unpackGreen(col),
+             (GLubyte)CTX::instance()->unpackBlue(col),
+             (GLubyte)CTX::instance()->unpackAlpha(col));
+  const GLfloat p[] = {(GLfloat)v->x(), (GLfloat)v->y(), (GLfloat)v->z()};
+  glPointSize((GLfloat)CTX::instance()->geom.pointSize);
+  glVertexPointer(3, GL_FLOAT, 0, p);
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glDrawArrays(GL_POINTS, 0, 1);
+  glDisableClientState(GL_VERTEX_ARRAY);
+  glPointSize(1);
+}
+
+void drawGeomEdge(GEdge *e)
+{
+  if(!e->getVisibility()) return;
+  if(e->geomType() == GEntity::DiscreteCurve) return;
+  if(e->geomType() == GEntity::PartitionCurve) return;
+  if(e->geomType() == GEntity::BoundaryLayerCurve) return;
+
+  unsigned int col = CTX::instance()->color.geom.curve;
+  glColor4ub((GLubyte)CTX::instance()->unpackRed(col),
+             (GLubyte)CTX::instance()->unpackGreen(col),
+             (GLubyte)CTX::instance()->unpackBlue(col),
+             (GLubyte)CTX::instance()->unpackAlpha(col));
+  int N = e->minimumDrawSegments() + 1;
+  Range<double> t_bounds = e->parBounds(0);
+  double t_min = t_bounds.low();
+  double t_max = t_bounds.high();
+
+  // Create a VA for this edge
+  std::vector<GLfloat> edge(N*3);
+  for(unsigned int i=0; i < N; i++) {
+    double t = t_min + (double)i / (double)(N-1) * (t_max - t_min);
+    GPoint p = e->point(t);
+    edge[i*3] = p.x(); edge[i*3+1] = p.y(); edge[i*3+2] = p.z();
+  }
+  // Then print the VA
+  glLineWidth((GLfloat)CTX::instance()->geom.curveWidth);
+  glVertexPointer(3, GL_FLOAT, 0, &edge[0]);
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glEnable(GL_LINE_SMOOTH);
+  glDrawArrays(GL_LINE_STRIP, 0, N);
+  glDisable(GL_LINE_SMOOTH);
+  glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+void drawGeomFace(GFace *f)
+{
+	// TODO
+}
+
+void drawContext::drawGeom()
+{
+  for(unsigned int i=0; i<GModel::list.size(); i++) {
+    GModel *m = GModel::list[i];
+    if(!m->getVisibility()) continue;
+    if(CTX::instance()->geom.points || CTX::instance()->geom.pointsNum)
+      std::for_each(m->firstVertex(), m->lastVertex(), drawGeomVertex);
+    if(CTX::instance()->geom.curves || CTX::instance()->geom.curvesNum ||
+       CTX::instance()->geom.tangents)
+      std::for_each(m->firstEdge(), m->lastEdge(), drawGeomEdge);
+    if(CTX::instance()->geom.surfaces || CTX::instance()->geom.surfacesNum ||
+       CTX::instance()->geom.normals)
+      std::for_each(m->firstFace(), m->lastFace(), drawGeomFace);
+  }
+}
diff --git a/contrib/mobile/drawMesh.cpp b/contrib/mobile/drawMesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..42d12fe1021f270d1f1fc3b9c7f5683a8dbab0c9
--- /dev/null
+++ b/contrib/mobile/drawMesh.cpp
@@ -0,0 +1,105 @@
+#if defined(BUILD_ANDROID)
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#else
+#include <OpenGLES/ES1/gl.h>
+#include <OpenGLES/ES1/glext.h>
+#endif
+
+#include <gmsh/GmshGlobal.h>
+#include <gmsh/GModel.h>
+#include <gmsh/PView.h>
+#include <gmsh/PViewData.h>
+#include <gmsh/Context.h>
+
+#include "drawContext.h"
+
+// from GModelVertexArrays
+extern unsigned int getColorByEntity(GEntity *e);
+
+void drawMeshVertex(GVertex *e)
+{
+  if(!CTX::instance()->mesh.points && !CTX::instance()->mesh.pointsNum) return;
+  if(!CTX::instance()->mesh.points) return;
+  std::vector<GLfloat> vertex;
+  std::vector<GLubyte> color;
+  for(unsigned int i = 0; i < e->mesh_vertices.size(); i++){
+    MVertex *v = e->mesh_vertices[i];
+    if(!v->getVisibility()) continue;
+    unsigned int col;
+    if(CTX::instance()->mesh.colorCarousel == 0 ||
+       CTX::instance()->mesh.volumesFaces || CTX::instance()->mesh.surfacesFaces) {
+      if(v->getPolynomialOrder() > 1)
+        col = CTX::instance()->color.mesh.nodeSup;
+      else
+        col = CTX::instance()->color.mesh.node;
+    }
+    else
+      col = getColorByEntity(e);
+    color.push_back((GLubyte)CTX::instance()->unpackRed(col));
+    color.push_back((GLubyte)CTX::instance()->unpackGreen(col));
+    color.push_back((GLubyte)CTX::instance()->unpackBlue(col));
+    color.push_back((GLubyte)CTX::instance()->unpackAlpha(col));
+    vertex.push_back(v->x());
+    vertex.push_back(v->y());
+    vertex.push_back(v->z());
+  }
+  glVertexPointer(3, GL_FLOAT, 0, &vertex.front());
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glColorPointer(4, GL_UNSIGNED_BYTE, color.size()/4, &color.front());
+  glEnableClientState(GL_COLOR_ARRAY);
+  glDrawArrays(GL_POINTS, 0, vertex.size()/3);
+  glDisableClientState(GL_VERTEX_ARRAY);
+  glDisableClientState(GL_COLOR_ARRAY);
+}
+
+void drawMeshEdge(GEdge *e)
+{
+  if(!e->getVisibility()) return;
+  glLineWidth(CTX::instance()->mesh.lineWidth);
+  drawArray(e->va_lines, GL_LINES, true);
+}
+
+void drawMeshFace(GFace *f)
+{
+  if(!f->getVisibility()) return;
+  drawArray(f->va_lines, GL_LINES, true);
+}
+
+void drawMeshRegion(GRegion *r)
+{
+  if(!r->getVisibility()) return;
+  drawArray(r->va_lines, GL_LINES, true);
+}
+
+void drawContext::drawMesh()
+{
+  if(!CTX::instance()->mesh.draw) return;
+
+  if(CTX::instance()->mesh.changed)
+    for(unsigned int i = 0; i < GModel::list.size(); i++)
+      for(unsigned int j = 0; j < PView::list.size(); j++)
+        if(PView::list[j]->getData()->hasModel(GModel::list[i]))
+          PView::list[j]->setChanged(true);
+
+  unsigned int col = CTX::instance()->color.mesh.line;
+  glColor4ub((GLubyte)CTX::instance()->unpackRed(col),
+             (GLubyte)CTX::instance()->unpackGreen(col),
+             (GLubyte)CTX::instance()->unpackBlue(col),
+             (GLubyte)CTX::instance()->unpackAlpha(col));
+  for(unsigned int i = 0; i < GModel::list.size(); i++){
+    GModel *m = GModel::list[i];
+    m->fillVertexArrays();
+    if(!m->getVisibility()) continue;
+    int status = m->getMeshStatus();
+    if(status >= 0)
+      std::for_each(m->firstVertex(), m->lastVertex(), drawMeshVertex);
+    if(status >= 1)
+      std::for_each(m->firstEdge(), m->lastEdge(), drawMeshEdge);
+    if(status >= 2)
+      std::for_each(m->firstFace(), m->lastFace(), drawMeshFace);
+    if(status >= 3)
+      std::for_each(m->firstRegion(), m->lastRegion(), drawMeshRegion);
+  }
+  CTX::instance()->mesh.changed = 0;
+}
diff --git a/contrib/mobile/drawString.cpp b/contrib/mobile/drawString.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d98bd51eaa109e7ee1741c58fa451f61b9996bfa
--- /dev/null
+++ b/contrib/mobile/drawString.cpp
@@ -0,0 +1,85 @@
+#if defined(BUILD_ANDROID)
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#else
+#include <OpenGLES/ES1/gl.h>
+#include <OpenGLES/ES1/glext.h>
+#endif
+
+#include "drawString.h"
+
+drawString::drawString(std::string text, int size, float color[4])
+{
+  _map = 0;
+  _size = size;
+  _height = 0;
+  _width = 0;
+  _realWidth = 0;
+  if(color == NULL)
+    setColor(0.0f, 0.0f, 0.0f, 1.0f);
+  else
+    setColor(color);
+  setText(text);
+}
+
+void drawString::setText(std::string text)
+{
+  _text = text;
+  getBitmapFromString(_text.c_str(), _size, &_map,
+                      &_height, &_width, &_realWidth);
+}
+
+void drawString::setColor(float color[4])
+{
+  _color[0] = color[0];
+  _color[1] = color[1];
+  _color[2] = color[2];
+  _color[3] = color[3];
+}
+
+void drawString::setColor(float r, float g, float b, float a)
+{
+  _color[0] = r;
+  _color[1] = g;
+  _color[2] = b;
+  _color[3] = a;
+}
+
+void drawString::draw(float x, float y, float z, float w, float h, bool center)
+{
+  if(!_map || !_width || !_height) return;
+  GLuint textureId;
+  glGenTextures(1, &textureId);
+  glBindTexture(GL_TEXTURE_2D, textureId);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, _width, _height, 0,
+               GL_ALPHA, GL_UNSIGNED_BYTE, _map);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  glColor4f(_color[0], _color[1], _color[2], _color[3]);
+  if(center)
+    x -= (float)_realWidth/w/2;
+  GLfloat vertex[] = {
+    x, y-_height/h+_size/h, z, // bottom left
+    x, y+(float)_height/h-_height/h+_size/h, z, // top left
+    x+(float)_width/w, y-_height/h+_size/h, z, // bottom right
+    x+(float)_width/w, y+(float)_height/h-_height/h+_size/h, z, // top right
+  };
+  GLfloat texture[] = {
+    0.0f, 1.0f, // top left
+    0.0f, 0.0f, // bottom left
+    1.0f, 1.0f, // top right
+    1.0f, 0.0f, // bottom right
+  };
+  glEnable(GL_TEXTURE_2D);
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glTexCoordPointer(2, GL_FLOAT, 0, texture);
+  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+  glVertexPointer(3, GL_FLOAT, 0, vertex);
+  glEnableClientState(GL_VERTEX_ARRAY);
+  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+  glDisableClientState(GL_VERTEX_ARRAY);
+  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+  glDisable(GL_BLEND);
+  glDisable(GL_TEXTURE_2D);
+  glDeleteTextures(1, &textureId);
+}
diff --git a/contrib/mobile/drawString.h b/contrib/mobile/drawString.h
new file mode 100644
index 0000000000000000000000000000000000000000..b1afad21fabccb38fad6b1168f82e3f8dd778e2c
--- /dev/null
+++ b/contrib/mobile/drawString.h
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+#include <string>
+
+#if defined(BUILD_ANDROID)
+#include "androidUtils.h"
+#else
+#include "iosUtils.h"
+#endif
+
+class drawString
+{
+private:
+  std::string _text; // Text to draw
+  float _color[4]; // Text color
+  int _size; // Text size in px
+  int _height, _width, _realWidth; // Size of the texture in px
+  unsigned char *_map;
+
+public:
+  drawString(std::string text, int size=12, float *color=NULL);
+  ~drawString(){ if(_map) free(_map); }
+
+  void setText(std::string text);
+  void setColor(float *color);
+  void setColor(float r, float g, float b, float a);
+  void draw(float x, float y, float z, float w, float h, bool center=true);
+};
diff --git a/contrib/mobile/iOS/Onelab.xcodeproj/project.pbxproj b/contrib/mobile/iOS/Onelab.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000000000000000000000000000000000..cb205d407a109c66ce440a4d24a88a81e4c5ad3a
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab.xcodeproj/project.pbxproj
@@ -0,0 +1,542 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		2901F1211BB0086C004C328B /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 2901F1201BB0086C004C328B /* libz.tbd */; };
+		2907CCEC193DE6560011341A /* icon_onelab.png in Resources */ = {isa = PBXBuildFile; fileRef = 2907CCEB193DE6560011341A /* icon_onelab.png */; };
+		291191DB1E93ACF700069C0C /* occt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 291191DA1E93ACF700069C0C /* occt.framework */; };
+		295056611D9AF3D200B9D9C4 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 295056601D9AF3D200B9D9C4 /* MessageUI.framework */; };
+		2988FF1E18E59558001435B6 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2988FF1C18E59558001435B6 /* Accelerate.framework */; };
+		2988FF1F18E59558001435B6 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2988FF1D18E59558001435B6 /* Accelerate.framework */; };
+		29A4AC80193CE6DA0007B5A5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 29A4AC7F193CE6DA0007B5A5 /* Images.xcassets */; };
+		29D83C8320A5E24700970050 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2924309A20A5D3830055B9AB /* LaunchScreen.storyboard */; };
+		9C1B9912194F4E0400507EFD /* slepc.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C1B9911194F4E0400507EFD /* slepc.framework */; };
+		9C1C10FA17BA5E7D00BFD483 /* OptionsViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C1C10F917BA5E7D00BFD483 /* OptionsViewController.mm */; };
+		9C2C3A1E187FDF9200E87F78 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C2C3A1D187FDF9200E87F78 /* libc++.tbd */; };
+		9C2C3A20187FDF9900E87F78 /* libstdc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C2C3A1F187FDF9900E87F78 /* libstdc++.tbd */; };
+		9C6A645817A7C3DB00DEDAFC /* drawString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9C6A645617A7C3DB00DEDAFC /* drawString.cpp */; };
+		9C95B7F61726C88E00C0CCE2 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C95B7F51726C88E00C0CCE2 /* main.mm */; };
+		9C96083B1712C16300E1D4A0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C96083A1712C16300E1D4A0 /* UIKit.framework */; };
+		9C96083D1712C16300E1D4A0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C96083C1712C16300E1D4A0 /* Foundation.framework */; };
+		9C96083F1712C16300E1D4A0 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C96083E1712C16300E1D4A0 /* CoreGraphics.framework */; };
+		9C96084B1712C16400E1D4A0 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C96084A1712C16300E1D4A0 /* AppDelegate.mm */; };
+		9C9608511712C16400E1D4A0 /* ParametersViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C9608501712C16400E1D4A0 /* ParametersViewController.mm */; };
+		9C9608541712C16400E1D4A0 /* ModelViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C9608531712C16400E1D4A0 /* ModelViewController.mm */; };
+		9C9608741712C47200E1D4A0 /* EAGLView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C9608731712C47200E1D4A0 /* EAGLView.mm */; };
+		9C96089B1712C7BE00E1D4A0 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C96089A1712C7BE00E1D4A0 /* OpenGLES.framework */; };
+		9C96089D1712C7F600E1D4A0 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C96089C1712C7F600E1D4A0 /* QuartzCore.framework */; };
+		9C96089F1712C8EB00E1D4A0 /* emulatorFix.c in Sources */ = {isa = PBXBuildFile; fileRef = 9C96089E1712C8EB00E1D4A0 /* emulatorFix.c */; };
+		9C9608AC1712EF0900E1D4A0 /* iPadStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9C9608AA1712EF0900E1D4A0 /* iPadStoryboard.storyboard */; };
+		9CB1CD9818DB2D8700110882 /* icon_rotate.png in Resources */ = {isa = PBXBuildFile; fileRef = 9CB1CD9618DB2D8700110882 /* icon_rotate.png */; };
+		9CB1CD9B18DC36AC00110882 /* AboutViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CB1CD9A18DC36AC00110882 /* AboutViewController.mm */; };
+		9CB1CD9D18DC57DE00110882 /* icon_translate.png in Resources */ = {isa = PBXBuildFile; fileRef = 9CB1CD9C18DC57DE00110882 /* icon_translate.png */; };
+		9CC6EBB717BA0A38001CA21A /* drawGeom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9CC6EBB617BA0A38001CA21A /* drawGeom.cpp */; };
+		9CC6EBB917BA1CC7001CA21A /* drawMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9CC6EBB817BA1CC7001CA21A /* drawMesh.cpp */; };
+		9CC85C021790286C00F241C4 /* files in Resources */ = {isa = PBXBuildFile; fileRef = 9CC85C011790286C00F241C4 /* files */; };
+		9CDCED2317D5C00500B39082 /* Model.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CDCED2217D5C00500B39082 /* Model.mm */; };
+		9CE08E10178AEB1600A83B4B /* getdp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9CE08E0D178AEB1600A83B4B /* getdp.framework */; };
+		9CE08E11178AEB1600A83B4B /* gmsh.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9CE08E0E178AEB1600A83B4B /* gmsh.framework */; };
+		9CE08E12178AEB1600A83B4B /* petsc.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9CE08E0F178AEB1600A83B4B /* petsc.framework */; };
+		9CE08E13178AEC5F00A83B4B /* drawContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9CE08E01178AE6BE00A83B4B /* drawContext.cpp */; };
+		9CE08E17178AEC5F00A83B4B /* Trackball.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9CE08E04178AE6BE00A83B4B /* Trackball.cpp */; };
+		9CE18C2017B27EDB009BA06E /* Parameter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CE18C1F17B27EDB009BA06E /* Parameter.mm */; };
+		9CE1A65717B0F39D00E5152F /* Utils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CE1A65617B0F39D00E5152F /* Utils.mm */; };
+		9CE1A65A17B0FB9700E5152F /* iPhoneiPodStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9CE1A65817B0FB9600E5152F /* iPhoneiPodStoryboard.storyboard */; };
+		9CE2773B17E197F50076E728 /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9CE2773917E197DA0076E728 /* Social.framework */; };
+		9CEAE86E17AF824B00813524 /* iosUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9CEAE86D17AF824B00813524 /* iosUtils.cpp */; };
+		9CEAE87D17AFD5BB00813524 /* SplitViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CEAE87C17AFD5BB00813524 /* SplitViewController.mm */; };
+		9CEAECC717A91CD20014D229 /* ModelListController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CEAECC617A91CD20014D229 /* ModelListController.mm */; };
+		9CF34DE417C62FC500A3D5E3 /* PostProcessingViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CF34DE317C62FC500A3D5E3 /* PostProcessingViewController.mm */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		2901F1201BB0086C004C328B /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-tbd-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
+		2907CCEB193DE6560011341A /* icon_onelab.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_onelab.png; sourceTree = "<group>"; };
+		291191DA1E93ACF700069C0C /* occt.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = occt.framework; path = Onelab/frameworks/occt.framework; sourceTree = "<group>"; };
+		2924309A20A5D3830055B9AB /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
+		295056601D9AF3D200B9D9C4 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
+                2988FF1C18E59558001435B6 /* libf2cblas.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libf2cblas.a; path = Onelab/frameworks/petsc.framework/libf2cblas.a; sourceTree = "<group>"; };
+                2988FF1D18E59558001435B6 /* libf2clapack.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libf2clapack.a; path = Onelab/frameworks/petsc.framework/libf2clapack.a; sourceTree = "<group>"; };
+		29A4AC7F193CE6DA0007B5A5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		9C1B9911194F4E0400507EFD /* slepc.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = slepc.framework; path = Onelab/frameworks/slepc.framework; sourceTree = "<group>"; };
+		9C1C10F817BA5E7D00BFD483 /* OptionsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OptionsViewController.h; sourceTree = "<group>"; };
+		9C1C10F917BA5E7D00BFD483 /* OptionsViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OptionsViewController.mm; sourceTree = "<group>"; };
+		9C2C3A1D187FDF9200E87F78 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.tbd"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
+		9C2C3A1F187FDF9900E87F78 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.tbd"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
+		9C6A645617A7C3DB00DEDAFC /* drawString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drawString.cpp; sourceTree = "<group>"; usesTabs = 1; };
+		9C6A645717A7C3DB00DEDAFC /* drawString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = drawString.h; sourceTree = "<group>"; usesTabs = 1; };
+		9C95B7F51726C88E00C0CCE2 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
+		9C9608361712C16300E1D4A0 /* Onelab.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Onelab.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		9C96083A1712C16300E1D4A0 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+		9C96083C1712C16300E1D4A0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+		9C96083E1712C16300E1D4A0 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+		9C9608491712C16300E1D4A0 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; usesTabs = 1; };
+		9C96084A1712C16300E1D4A0 /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = "<group>"; usesTabs = 1; };
+		9C96084F1712C16400E1D4A0 /* ParametersViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParametersViewController.h; sourceTree = "<group>"; usesTabs = 1; };
+		9C9608501712C16400E1D4A0 /* ParametersViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ParametersViewController.mm; sourceTree = "<group>"; usesTabs = 1; };
+		9C9608521712C16400E1D4A0 /* ModelViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ModelViewController.h; sourceTree = "<group>"; usesTabs = 1; };
+		9C9608531712C16400E1D4A0 /* ModelViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ModelViewController.mm; sourceTree = "<group>"; usesTabs = 1; };
+		9C96085B1712C16400E1D4A0 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
+		9C9608721712C47200E1D4A0 /* EAGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EAGLView.h; sourceTree = "<group>"; usesTabs = 1; };
+		9C9608731712C47200E1D4A0 /* EAGLView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EAGLView.mm; sourceTree = "<group>"; usesTabs = 1; };
+		9C96089A1712C7BE00E1D4A0 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
+		9C96089C1712C7F600E1D4A0 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+		9C96089E1712C8EB00E1D4A0 /* emulatorFix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emulatorFix.c; sourceTree = "<group>"; usesTabs = 1; };
+		9C9608AB1712EF0900E1D4A0 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = iPadStoryboard.storyboard; sourceTree = "<group>"; };
+		9C99754C17390DEE0036EC24 /* iosUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iosUtils.h; sourceTree = "<group>"; usesTabs = 1; };
+		9CB1CD9618DB2D8700110882 /* icon_rotate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_rotate.png; sourceTree = "<group>"; };
+		9CB1CD9918DC36AC00110882 /* AboutViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutViewController.h; sourceTree = "<group>"; };
+		9CB1CD9A18DC36AC00110882 /* AboutViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AboutViewController.mm; sourceTree = "<group>"; };
+		9CB1CD9C18DC57DE00110882 /* icon_translate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_translate.png; sourceTree = "<group>"; };
+		9CC6EBB617BA0A38001CA21A /* drawGeom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drawGeom.cpp; sourceTree = "<group>"; usesTabs = 1; };
+		9CC6EBB817BA1CC7001CA21A /* drawMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drawMesh.cpp; sourceTree = "<group>"; };
+		9CC85C011790286C00F241C4 /* files */ = {isa = PBXFileReference; lastKnownFileType = folder; path = files; sourceTree = "<group>"; };
+		9CDCED2117D5C00500B39082 /* Model.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Model.h; sourceTree = "<group>"; };
+		9CDCED2217D5C00500B39082 /* Model.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Model.mm; sourceTree = "<group>"; };
+		9CE08E01178AE6BE00A83B4B /* drawContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drawContext.cpp; sourceTree = "<group>"; usesTabs = 1; };
+		9CE08E02178AE6BE00A83B4B /* drawContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = drawContext.h; sourceTree = "<group>"; usesTabs = 1; };
+		9CE08E04178AE6BE00A83B4B /* Trackball.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Trackball.cpp; sourceTree = "<group>"; usesTabs = 1; };
+		9CE08E05178AE6BE00A83B4B /* Trackball.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Trackball.h; sourceTree = "<group>"; usesTabs = 1; };
+		9CE08E0D178AEB1600A83B4B /* getdp.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = getdp.framework; path = Onelab/frameworks/getdp.framework; sourceTree = "<group>"; };
+		9CE08E0E178AEB1600A83B4B /* gmsh.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = gmsh.framework; path = Onelab/frameworks/gmsh.framework; sourceTree = "<group>"; };
+		9CE08E0F178AEB1600A83B4B /* petsc.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = petsc.framework; path = Onelab/frameworks/petsc.framework; sourceTree = "<group>"; };
+		9CE18C1E17B27EDB009BA06E /* Parameter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Parameter.h; sourceTree = "<group>"; usesTabs = 1; };
+		9CE18C1F17B27EDB009BA06E /* Parameter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Parameter.mm; sourceTree = "<group>"; usesTabs = 1; };
+		9CE1A65517B0F39C00E5152F /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = "<group>"; usesTabs = 1; };
+		9CE1A65617B0F39D00E5152F /* Utils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Utils.mm; sourceTree = "<group>"; usesTabs = 1; };
+		9CE1A65917B0FB9700E5152F /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = iPhoneiPodStoryboard.storyboard; sourceTree = "<group>"; };
+		9CE2773917E197DA0076E728 /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; };
+		9CEAE86D17AF824B00813524 /* iosUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iosUtils.cpp; sourceTree = "<group>"; usesTabs = 1; };
+		9CEAE87B17AFD5BB00813524 /* SplitViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SplitViewController.h; sourceTree = "<group>"; usesTabs = 1; };
+		9CEAE87C17AFD5BB00813524 /* SplitViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SplitViewController.mm; sourceTree = "<group>"; usesTabs = 1; };
+		9CEAECC517A91CD20014D229 /* ModelListController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModelListController.h; sourceTree = "<group>"; usesTabs = 1; };
+		9CEAECC617A91CD20014D229 /* ModelListController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ModelListController.mm; sourceTree = "<group>"; usesTabs = 1; };
+		9CF1C1EE17AA8997002CD2E3 /* Onelab-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Onelab-Info.plist"; sourceTree = "<group>"; };
+		9CF1C1F117AA8A7D002CD2E3 /* Onelab-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Onelab-Prefix.pch"; sourceTree = "<group>"; };
+		9CF34DE217C62FC500A3D5E3 /* PostProcessingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostProcessingViewController.h; sourceTree = "<group>"; };
+		9CF34DE317C62FC500A3D5E3 /* PostProcessingViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PostProcessingViewController.mm; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		9C9608331712C16300E1D4A0 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				2988FF1E18E59558001435B6 /* Accelerate.framework in Frameworks */,
+				2988FF1F18E59558001435B6 /* Accelerate.framework in Frameworks */,
+				2901F1211BB0086C004C328B /* libz.tbd in Frameworks */,
+				9C2C3A20187FDF9900E87F78 /* libstdc++.tbd in Frameworks */,
+				9C2C3A1E187FDF9200E87F78 /* libc++.tbd in Frameworks */,
+				9C96089D1712C7F600E1D4A0 /* QuartzCore.framework in Frameworks */,
+				9C96089B1712C7BE00E1D4A0 /* OpenGLES.framework in Frameworks */,
+				9C96083B1712C16300E1D4A0 /* UIKit.framework in Frameworks */,
+				9C96083D1712C16300E1D4A0 /* Foundation.framework in Frameworks */,
+				9C96083F1712C16300E1D4A0 /* CoreGraphics.framework in Frameworks */,
+				9CE2773B17E197F50076E728 /* Social.framework in Frameworks */,
+				295056611D9AF3D200B9D9C4 /* MessageUI.framework in Frameworks */,
+				9CE08E10178AEB1600A83B4B /* getdp.framework in Frameworks */,
+				9CE08E11178AEB1600A83B4B /* gmsh.framework in Frameworks */,
+				9CE08E12178AEB1600A83B4B /* petsc.framework in Frameworks */,
+				9C1B9912194F4E0400507EFD /* slepc.framework in Frameworks */,
+				291191DB1E93ACF700069C0C /* occt.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		9C928621180D216500AAABD4 /* icons */ = {
+			isa = PBXGroup;
+			children = (
+				2907CCEB193DE6560011341A /* icon_onelab.png */,
+				9CB1CD9C18DC57DE00110882 /* icon_translate.png */,
+				9CB1CD9618DB2D8700110882 /* icon_rotate.png */,
+			);
+			name = icons;
+			sourceTree = "<group>";
+		};
+		9C96082B1712C16300E1D4A0 = {
+			isa = PBXGroup;
+			children = (
+				9C9608401712C16300E1D4A0 /* Onelab */,
+				9C9608391712C16300E1D4A0 /* Frameworks */,
+				9C9608371712C16300E1D4A0 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		9C9608371712C16300E1D4A0 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				9C9608361712C16300E1D4A0 /* Onelab.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		9C9608391712C16300E1D4A0 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				291191DA1E93ACF700069C0C /* occt.framework */,
+				295056601D9AF3D200B9D9C4 /* MessageUI.framework */,
+				2901F1201BB0086C004C328B /* libz.tbd */,
+				9C1B9911194F4E0400507EFD /* slepc.framework */,
+				2988FF1C18E59558001435B6 /* Accelerate.framework */,
+				2988FF1D18E59558001435B6 /* Accelerate.framework */,
+				9C2C3A1F187FDF9900E87F78 /* libstdc++.tbd */,
+				9C2C3A1D187FDF9200E87F78 /* libc++.tbd */,
+				9CE2773917E197DA0076E728 /* Social.framework */,
+				9CE08E0D178AEB1600A83B4B /* getdp.framework */,
+				9CE08E0E178AEB1600A83B4B /* gmsh.framework */,
+				9CE08E0F178AEB1600A83B4B /* petsc.framework */,
+				9C96089C1712C7F600E1D4A0 /* QuartzCore.framework */,
+				9C96089A1712C7BE00E1D4A0 /* OpenGLES.framework */,
+				9C96083A1712C16300E1D4A0 /* UIKit.framework */,
+				9C96083C1712C16300E1D4A0 /* Foundation.framework */,
+				9C96083E1712C16300E1D4A0 /* CoreGraphics.framework */,
+				9C96085B1712C16400E1D4A0 /* SenTestingKit.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		9C9608401712C16300E1D4A0 /* Onelab */ = {
+			isa = PBXGroup;
+			children = (
+				9C928621180D216500AAABD4 /* icons */,
+				9CC85C011790286C00F241C4 /* files */,
+				2924309A20A5D3830055B9AB /* LaunchScreen.storyboard */,
+				9C9608AA1712EF0900E1D4A0 /* iPadStoryboard.storyboard */,
+				9CE1A65817B0FB9600E5152F /* iPhoneiPodStoryboard.storyboard */,
+				9C9608491712C16300E1D4A0 /* AppDelegate.h */,
+				9C96084A1712C16300E1D4A0 /* AppDelegate.mm */,
+				9C99754C17390DEE0036EC24 /* iosUtils.h */,
+				9CEAE86D17AF824B00813524 /* iosUtils.cpp */,
+				9CE08E02178AE6BE00A83B4B /* drawContext.h */,
+				9CE08E01178AE6BE00A83B4B /* drawContext.cpp */,
+				9CC6EBB617BA0A38001CA21A /* drawGeom.cpp */,
+				9CC6EBB817BA1CC7001CA21A /* drawMesh.cpp */,
+				9C6A645717A7C3DB00DEDAFC /* drawString.h */,
+				9C6A645617A7C3DB00DEDAFC /* drawString.cpp */,
+				9CE08E05178AE6BE00A83B4B /* Trackball.h */,
+				9CE08E04178AE6BE00A83B4B /* Trackball.cpp */,
+				9CE1A65517B0F39C00E5152F /* Utils.h */,
+				9CE1A65617B0F39D00E5152F /* Utils.mm */,
+				9CDCED2117D5C00500B39082 /* Model.h */,
+				9CDCED2217D5C00500B39082 /* Model.mm */,
+				9CE18C1E17B27EDB009BA06E /* Parameter.h */,
+				9CE18C1F17B27EDB009BA06E /* Parameter.mm */,
+				9CEAECC517A91CD20014D229 /* ModelListController.h */,
+				9CEAECC617A91CD20014D229 /* ModelListController.mm */,
+				9CB1CD9918DC36AC00110882 /* AboutViewController.h */,
+				9CB1CD9A18DC36AC00110882 /* AboutViewController.mm */,
+				9CEAE87B17AFD5BB00813524 /* SplitViewController.h */,
+				9CEAE87C17AFD5BB00813524 /* SplitViewController.mm */,
+				9C96084F1712C16400E1D4A0 /* ParametersViewController.h */,
+				9C9608501712C16400E1D4A0 /* ParametersViewController.mm */,
+				9C9608521712C16400E1D4A0 /* ModelViewController.h */,
+				9C9608531712C16400E1D4A0 /* ModelViewController.mm */,
+				9C1C10F817BA5E7D00BFD483 /* OptionsViewController.h */,
+				9C1C10F917BA5E7D00BFD483 /* OptionsViewController.mm */,
+				9CF34DE217C62FC500A3D5E3 /* PostProcessingViewController.h */,
+				9CF34DE317C62FC500A3D5E3 /* PostProcessingViewController.mm */,
+				9C9608721712C47200E1D4A0 /* EAGLView.h */,
+				9C9608731712C47200E1D4A0 /* EAGLView.mm */,
+				9C96089E1712C8EB00E1D4A0 /* emulatorFix.c */,
+				29A4AC7F193CE6DA0007B5A5 /* Images.xcassets */,
+				9CF1C1F017AA8A46002CD2E3 /* Supporting Files */,
+			);
+			path = Onelab;
+			sourceTree = "<group>";
+		};
+		9CF1C1F017AA8A46002CD2E3 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				9CF1C1F117AA8A7D002CD2E3 /* Onelab-Prefix.pch */,
+				9C95B7F51726C88E00C0CCE2 /* main.mm */,
+				9CF1C1EE17AA8997002CD2E3 /* Onelab-Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		9C9608351712C16300E1D4A0 /* Onelab */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 9C96086C1712C16400E1D4A0 /* Build configuration list for PBXNativeTarget "Onelab" */;
+			buildPhases = (
+				9C9608321712C16300E1D4A0 /* Sources */,
+				9C9608331712C16300E1D4A0 /* Frameworks */,
+				9C9608341712C16300E1D4A0 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Onelab;
+			productName = Onelab;
+			productReference = 9C9608361712C16300E1D4A0 /* Onelab.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		9C96082D1712C16300E1D4A0 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0500;
+				ORGANIZATIONNAME = "Christophe Geuzaine";
+				TargetAttributes = {
+					9C9608351712C16300E1D4A0 = {
+						DevelopmentTeam = 7KGA566932;
+					};
+				};
+			};
+			buildConfigurationList = 9C9608301712C16300E1D4A0 /* Build configuration list for PBXProject "Onelab" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = 9C96082B1712C16300E1D4A0;
+			productRefGroup = 9C9608371712C16300E1D4A0 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				9C9608351712C16300E1D4A0 /* Onelab */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		9C9608341712C16300E1D4A0 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9C9608AC1712EF0900E1D4A0 /* iPadStoryboard.storyboard in Resources */,
+				29D83C8320A5E24700970050 /* LaunchScreen.storyboard in Resources */,
+				9CB1CD9818DB2D8700110882 /* icon_rotate.png in Resources */,
+				9CC85C021790286C00F241C4 /* files in Resources */,
+				29A4AC80193CE6DA0007B5A5 /* Images.xcassets in Resources */,
+				2907CCEC193DE6560011341A /* icon_onelab.png in Resources */,
+				9CB1CD9D18DC57DE00110882 /* icon_translate.png in Resources */,
+				9CE1A65A17B0FB9700E5152F /* iPhoneiPodStoryboard.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		9C9608321712C16300E1D4A0 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9CE08E13178AEC5F00A83B4B /* drawContext.cpp in Sources */,
+				9CE08E17178AEC5F00A83B4B /* Trackball.cpp in Sources */,
+				9C96084B1712C16400E1D4A0 /* AppDelegate.mm in Sources */,
+				9C9608511712C16400E1D4A0 /* ParametersViewController.mm in Sources */,
+				9C9608541712C16400E1D4A0 /* ModelViewController.mm in Sources */,
+				9C9608741712C47200E1D4A0 /* EAGLView.mm in Sources */,
+				9CB1CD9B18DC36AC00110882 /* AboutViewController.mm in Sources */,
+				9C96089F1712C8EB00E1D4A0 /* emulatorFix.c in Sources */,
+				9C95B7F61726C88E00C0CCE2 /* main.mm in Sources */,
+				9C6A645817A7C3DB00DEDAFC /* drawString.cpp in Sources */,
+				9CEAECC717A91CD20014D229 /* ModelListController.mm in Sources */,
+				9CEAE86E17AF824B00813524 /* iosUtils.cpp in Sources */,
+				9CEAE87D17AFD5BB00813524 /* SplitViewController.mm in Sources */,
+				9CE1A65717B0F39D00E5152F /* Utils.mm in Sources */,
+				9CE18C2017B27EDB009BA06E /* Parameter.mm in Sources */,
+				9CC6EBB717BA0A38001CA21A /* drawGeom.cpp in Sources */,
+				9CC6EBB917BA1CC7001CA21A /* drawMesh.cpp in Sources */,
+				9C1C10FA17BA5E7D00BFD483 /* OptionsViewController.mm in Sources */,
+				9CF34DE417C62FC500A3D5E3 /* PostProcessingViewController.mm in Sources */,
+				9CDCED2317D5C00500B39082 /* Model.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		9C9608AA1712EF0900E1D4A0 /* iPadStoryboard.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				9C9608AB1712EF0900E1D4A0 /* en */,
+			);
+			name = iPadStoryboard.storyboard;
+			sourceTree = "<group>";
+		};
+		9CE1A65817B0FB9600E5152F /* iPhoneiPodStoryboard.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				9CE1A65917B0FB9700E5152F /* en */,
+			);
+			name = iPhoneiPodStoryboard.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		9C96086A1712C16400E1D4A0 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				ONLY_ACTIVE_ARCH = YES;
+				PROVISIONING_PROFILE = "";
+				"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = 2;
+				VALID_ARCHS = "armv7 armv7s arm64";
+			};
+			name = Debug;
+		};
+		9C96086B1712C16400E1D4A0 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+				PROVISIONING_PROFILE = "";
+				"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = 2;
+				VALIDATE_PRODUCT = YES;
+				VALID_ARCHS = "armv7 armv7s arm64";
+			};
+			name = Release;
+		};
+		9C96086D1712C16400E1D4A0 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD)";
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Onelab/frameworks",
+				);
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "Onelab/Onelab-Prefix.pch";
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					BUILD_IOS,
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = "Onelab/Onelab-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Onelab/frameworks/petsc.framework",
+				);
+				OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE = "";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALID_ARCHS = "armv7 armv7s arm64";
+				WRAPPER_EXTENSION = app;
+			};
+			name = Debug;
+		};
+		9C96086E1712C16400E1D4A0 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD)";
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Onelab/frameworks",
+				);
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "Onelab/Onelab-Prefix.pch";
+				GCC_PREPROCESSOR_DEFINITIONS = BUILD_IOS;
+				INFOPLIST_FILE = "Onelab/Onelab-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Onelab/frameworks/petsc.framework",
+				);
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE = "";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALID_ARCHS = "armv7 armv7s arm64";
+				WRAPPER_EXTENSION = app;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		9C9608301712C16300E1D4A0 /* Build configuration list for PBXProject "Onelab" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				9C96086A1712C16400E1D4A0 /* Debug */,
+				9C96086B1712C16400E1D4A0 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		9C96086C1712C16400E1D4A0 /* Build configuration list for PBXNativeTarget "Onelab" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				9C96086D1712C16400E1D4A0 /* Debug */,
+				9C96086E1712C16400E1D4A0 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 9C96082D1712C16300E1D4A0 /* Project object */;
+}
diff --git a/contrib/mobile/iOS/Onelab/AboutViewController.h b/contrib/mobile/iOS/Onelab/AboutViewController.h
new file mode 100644
index 0000000000000000000000000000000000000000..7dc42f359742c8099ba1c6def3f90001158723ae
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/AboutViewController.h
@@ -0,0 +1,8 @@
+#import <UIKit/UIKit.h>
+
+@interface AboutViewController : UIViewController <UIWebViewDelegate>
+
+@property (weak, nonatomic) IBOutlet UIWebView *aboutView;
+@property (nonatomic, retain) NSString *fileToEdit;
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/AboutViewController.mm b/contrib/mobile/iOS/Onelab/AboutViewController.mm
new file mode 100644
index 0000000000000000000000000000000000000000..f63b9187a90c984fb560752eb8e7c96d0909750d
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/AboutViewController.mm
@@ -0,0 +1,122 @@
+#import "AboutViewController.h"
+#import "Gmsh/GmshVersion.h"
+#import "Gmsh/GmshConfig.h"
+#import "GetDP/GetDPVersion.h"
+#import "GetDP/GetDPConfig.h"
+
+@interface AboutViewController ()
+
+@end
+
+@implementation AboutViewController
+
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
+{
+  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
+  if (self) {
+    // Custom initialization
+  }
+  return self;
+}
+
+// this allows to open links in Safari, instead of opening them in the
+// AboutViewController
+-(BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest
+ navigationType:(UIWebViewNavigationType)inType
+{
+  if ( inType == UIWebViewNavigationTypeLinkClicked ) {
+    [[UIApplication sharedApplication] openURL:[inRequest URL]];
+    return NO;
+  }
+
+  return YES;
+}
+
+- (void)viewDidLoad
+{
+  [super viewDidLoad];
+  // Do any additional setup after loading the view.
+  self.aboutView.delegate = self;
+  self.aboutView.dataDetectorTypes = UIDataDetectorTypeNone;
+  [self loadContent];
+  if(![self.fileToEdit isEqual:@"About"] && ![self.fileToEdit isEqual:@"Help"]){
+    UIBarButtonItem *save =
+      [[UIBarButtonItem alloc] initWithTitle:@"Save"
+                                       style:UIBarButtonItemStylePlain
+                                      target:self action:@selector(saveFile)];
+    [self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects: save, nil]];
+  }
+}
+
+-(void)loadContent
+{
+  UIColor *tintColor = self.view.tintColor;
+  CGFloat red, green, blue, alpha;
+  [tintColor getRed: &red green: &green blue: &blue alpha: &alpha];
+  int r = (int)(red*255), g = (int)(green*255), b = (int)(blue*255);
+
+  NSString *css = [NSString stringWithFormat:@"body { background-color: #FFFFFF; color: #252525; margin: 35px 10px 35px 10px; padding: 0; font-family: helvetica-neue,sans-serif; font-size: 1em; }  b { font-weight: normal; color: rgb(%d,%d,%d); } a { color: rgb(%d,%d,%d); }", r, g, b, r, g, b];
+
+  if([self.fileToEdit isEqual:@"About"]){
+    [self.aboutView loadHTMLString:[NSString stringWithFormat:@"<html><head><style type=\"text/css\">%@</style></head><body><center><p><!-- img width=32 src=\"icon_onelab.png\"--></p><h3>Onelab/Mobile</h3>Version %@<p>Copyright (C) 2014-2019 Christophe Geuzaine and Maxime Graulich, University of Li&egrave;ge</p><p>Visit <a href=\"http://onelab.info/\">http://onelab.info/</a> for more information</p><p style=\"padding-top: 35px;\">This version of Onelab/Mobile contains:</p><h3>Gmsh</h3>Version %s (<i>Build date:</i> %s)<p>Copyright (C) 1997-2019 Christophe Geuzaine and Jean-Fran&ccedil;ois Remacle</p><p><a href=\"http://geuz.org/gmsh/doc/CREDITS.txt\">Credits</a> and <a href=\"http://geuz.org/gmsh/doc/LICENSE.txt\">licensing information</a></p><p><i>Build options:</i> %s</p><p>Visit <a href=\"http://gmsh.info/\">http://gmsh.info</a> for more information</p><h3>GetDP</h3>Version %s (<i>Build date:</i> %s)<p>Copyright (C) 1997-2019 Patrick Dular and Christophe Geuzaine, University of Li&egrave;ge</p><p><a href=\"http://geuz.org/getdp/doc/CREDITS.txt\">Credits</a> and <a href=\"http://geuz.org/getdp/doc/LICENSE.txt\">licensing information</a></p><p><i>Build options:</i> %s</p><p>Visit <a href=\"http://getdp.info\">http://getdp.info</a> for more information</p></center></body></html>", css,
+                                             [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"],
+                                             GMSH_VERSION,
+                                             GMSH_DATE,
+                                             GMSH_CONFIG_OPTIONS,
+                                             GETDP_VERSION,
+                                             GETDP_DATE,
+                                             GETDP_CONFIG_OPTIONS]
+                           baseURL:[[NSBundle mainBundle] bundleURL]];
+  }
+  else if([self.fileToEdit isEqual:@"Help"]){
+    [self.aboutView loadHTMLString:[NSString stringWithFormat:@"<html><head><style type=\"text/css\">%@ h3 { text-align: center; }</style></head><!-- img width=32 src=\"icon_onelab.png\"--><h3>Onelab/Mobile</h3> <h4>Running an existing model</h4> <p> The list of available models appears when you launch the app. Selecting a model will load it. You can then press <b>Run</b> to launch a simulation with the default set of parameters. When available, additional information about a model can be obtained by long-pressing on the model description and selecting <b>Visit model website</b>.</p> <h4>Modifying a model</h4> <p>To run a model with different parameters, press <b>Parameters</b> and modify any of the presets. Then press <b>Run</b> again: all the simulation steps will be performed with the new parameter values. To restore the preset parameters values, press <b>Reset</b>. </p> <p> Advanced users can also directly edit the model input files: long-press on the model description and select <b>Edit model files</b>. </p> <p> To free up space, temporary model files (meshes, solution files) can be removed by long-pressing on the model description and selecting <b>Clear results</b>. </p> <p> To completey remove a model, long-press on the model description and select <b>Remove</b>. </p> <h4>Sharing a model</h4> <p> To share a model by email, long-press on the model description and select <b>Email model files</b>. </p> <h4>Installing a new model</h4> <p> To install a new model: <ol> <li>Put all the model files (.pro, .geo) in a directory, which should also contain a file named <code>infos.xml</code> with the model information: <pre>\n&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;models&gt;\n  &lt;model&gt;\n    &lt;title&gt;Model title&lt;/title&gt;\n    &lt;summary&gt;Model summary&lt;/summary&gt;\n    &lt;file type=\"pro\"&gt;main_pro_file.pro&lt;/file&gt;\n    &lt;preview type=\"png\"&gt;128x128_pixel_screenshot.png&lt;/preview&gt;\n    &lt;url&gt;http://model_website.com&lt;/url&gt;\n  &lt;/model&gt;\n&lt;/models&gt;\n</pre><li>Zip the directory. <li>Open the .zip file on your device, e.g. through iCloud or by  emailing it to yourself and opening the attachment; or by putting it  on a web server and downloading the file on the device with Safari. <li>Refresh the list of models: the new model will be extracted  alongside Onelab/Mobile\'s built-in models.</ol> <p style=\"padding-top: 2em;\">Visit <a href=\"http://onelab.info/\">http://onelab.info/</a> for more information.</p> </body></html>", css]
+                           baseURL:[[NSBundle mainBundle] bundleURL]];
+  }
+  else{
+    NSData *fileData = [NSData dataWithContentsOfFile:self.fileToEdit];
+    NSString* aStr = [[NSString alloc] initWithData:fileData encoding:NSUTF8StringEncoding];
+    if(!aStr) aStr = [[NSString alloc] initWithData:fileData encoding:NSASCIIStringEncoding];
+    if(aStr){
+      aStr = [aStr stringByReplacingOccurrencesOfString:@"<"
+                                             withString:@"&lt;"];
+      aStr = [aStr stringByReplacingOccurrencesOfString:@">"
+                                             withString:@"&gt;"];
+    }
+    // custom microlight.js for basic syntax highlighting
+    const char *js = "!function(a,b){\"function\"==typeof define&&define.amd?define([\"exports\"],b):b(\"undefined\"!=typeof exports?exports:a.microlight={})}(this,function(a){var k,l,m,b=window,c=document,d=\"appendChild\",e=\"test\",g=\"opacity:.\",n=function(a){for(l=c.getElementsByClassName(a||\"microlight\"),k=0;m=l[k++];){var n,o,r,s,t,f=m.textContent,h=0,i=f[0],j=1,p=m.innerHTML=\"\",q=0,u=/(\\d*\\, \\d*\\, \\d*)(, ([.\\d]*))?/g.exec(b.getComputedStyle(m).color);for(\"px rgba(\"+u[1]+\",\",u[3]||1;o=n,n=q<7&&\"\\\\\"==n?1:j;){if(j=i,i=f[++h],s=p.length>1,!j||q>8&&\"\\n\"==j||[/\\S/[e](j),1,1,!/[$\\w]/[e](j),(\"/\"==n||\"\\n\"==n)&&s,\'\"\'==n&&s,\"\'\"==n&&s,f[h-4]+o+n==\"-->\",o+n==\"*/\"][q])for(p&&(m[d](t=c.createElement(\"span\")).setAttribute(\"style\",[\"\",\"font-weight:bold;color:#00c;\",g+6,\"color: #a08;\"+g+8,\"font-style:italic;color: #b00;\"+g+8][q?q<3?2:q>6?4:q>3?3:+/^(If|Else|ElseIf|EndIf|Include|For|EndFor|Include|Macro|Return)$/[e](p):0]),t[d](c.createTextNode(p))),r=q&&q<7?q:r,p=\"\",q=11;![1,/[\\/{}[(\\-+*=<>:;|\\\\.,?!&@~]/[e](j),/[\\])]/[e](j),/[$\\w]/[e](j),\"/\"==j&&r<2&&\"<\"!=n,\'\"\'==j,\"\'\"==j,j+i+f[h+1]+f[h+2]==\"<!--\",j+i==\"/*\",j+i==\"//\",j+i==\"//\"][--q];);p+=j}}};a.reset=n,\"complete\"==c.readyState?n():b.addEventListener(\"load\",function(){n()},0)});";
+    [self.aboutView loadHTMLString:[NSString stringWithFormat:@"<html><head><script>%s</script></head><body><pre contenteditable=\"true\" class=microlight>%@</pre></body></html>", js, aStr] baseURL:[[NSBundle mainBundle] bundleURL]];
+  }
+}
+
+-(void)saveFile
+{
+  //CGPoint offset = self.aboutView.scrollView.contentOffset;
+  NSString *text = [self.aboutView stringByEvaluatingJavaScriptFromString:
+                          @"document.body.innerText"];
+  NSLog(@"Saving file %@", self.fileToEdit);
+  NSError *error;
+  [text writeToFile:self.fileToEdit atomically:YES
+           encoding:NSUTF8StringEncoding error:&error];
+  [self loadContent];
+  // trying to restore position, but this is not correct
+  //[self.aboutView.scrollView setContentOffset:offset animated:NO];
+}
+
+- (void)didReceiveMemoryWarning
+{
+  [super didReceiveMemoryWarning];
+  // Dispose of any resources that can be recreated.
+}
+
+/*
+  #pragma mark - Navigation
+
+// In a storyboard-based application, you will often want to do a little preparation before navigation
+- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
+{
+    // Get the new view controller using [segue destinationViewController].
+    // Pass the selected object to the new view controller.
+}
+*/
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/AppDelegate.h b/contrib/mobile/iOS/Onelab/AppDelegate.h
new file mode 100644
index 0000000000000000000000000000000000000000..e06267808aed735af7a1d334bf473a8cca9d51b2
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/AppDelegate.h
@@ -0,0 +1,14 @@
+#import <UIKit/UIKit.h>
+
+#include "SplitViewController.h"
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>{
+  @public
+  bool compute;
+}
+
+@property (strong, nonatomic) UIWindow *window;
+@property (strong, nonatomic) UITableViewController *modelListController;
+@property (strong, nonatomic) SplitViewController *splitViewController; // iPad
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/AppDelegate.mm b/contrib/mobile/iOS/Onelab/AppDelegate.mm
new file mode 100644
index 0000000000000000000000000000000000000000..44ede0eb1811a1deb146f68710c8d361334349a0
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/AppDelegate.mm
@@ -0,0 +1,92 @@
+#import "AppDelegate.h"
+#import "ModelListController.h"
+#import "Utils.h"
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+  // Override point for customization after application launch.
+  self.modelListController = (ModelListController *)self.window.rootViewController;
+  if([[UIDevice currentDevice].model isEqualToString:@"iPad"] ||
+     [[UIDevice currentDevice].model isEqualToString:@"iPad Simulator"]) {
+    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPadStoryboard" bundle:nil];
+    self.splitViewController = [storyboard instantiateViewControllerWithIdentifier:@"SplitViewController"];
+  }
+  compute = false;
+  // Copy resource files if the version of the app has changed
+  NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
+  NSString *prefsv = [prefs stringForKey:@"OnelabModelsVersion"];
+  NSString *bundlev = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
+  if (!prefsv || ![prefsv isEqualToString:bundlev]) {
+    NSLog(@"Updating models to version %@", bundlev);
+    [prefs setObject:bundlev forKey:@"OnelabModelsVersion"];
+    [prefs synchronize];
+    [Utils copyRes];
+  }
+  else{
+    NSLog(@"Leaving models as-is (version %@)", prefsv);
+  }
+
+  // Check if there is a model to extract (unzip)
+  NSURL* url = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
+  if(url) [Utils openModelURL:url];
+
+  return YES;
+}
+
+- (BOOL)application:(UIApplication *)app
+            openURL:(NSURL *)url
+            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
+{
+  [Utils openModelURL:url];
+
+  return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application
+{
+  // Sent when the application is about to move from active to inactive
+  // state. This can occur for certain types of temporary interruptions (such as
+  // an incoming phone call or SMS message) or when the user quits the
+  // application and it begins the transition to the background state.  Use this
+  // method to pause ongoing tasks, disable timers, and throttle down OpenGL ES
+  // frame rates. Games should use this method to pause the game.
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application
+{
+  // Use this method to release shared resources, save user data, invalidate
+  // timers, and store enough application state information to restore your
+  // application to its current state in case it is terminated later.  If your
+  // application supports background execution, this method is called instead
+  // of applicationWillTerminate: when the user quits.
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application
+{
+  // Called as part of the transition from the background to the inactive state;
+  // here you can undo many of the changes made on entering the background.
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application
+{
+  // Restart any tasks that were paused (or not yet started) while the
+  // application was inactive. If the application was previously in the
+  // background, optionally refresh the user interface.
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application
+{
+  // Called when the application is about to terminate. Save data if
+  // appropriate. See also applicationDidEnterBackground:.
+}
+
+-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
+{
+  application.applicationIconBadgeNumber = -1;
+  [UIApplication sharedApplication].applicationIconBadgeNumber = -1;
+}
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/EAGLView.h b/contrib/mobile/iOS/Onelab/EAGLView.h
new file mode 100644
index 0000000000000000000000000000000000000000..32d1756a186be76d25742a6b83798c67be25fac4
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/EAGLView.h
@@ -0,0 +1,41 @@
+#import <UIKit/UIKit.h>
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+
+#import "drawContext.h"
+
+/*
+ This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
+ The view content is basically an EAGL surface you render your OpenGL scene into.
+ Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel.
+ */
+@interface EAGLView : UIView {
+
+  @private
+  /* The pixel dimensions of the backbuffer */
+  GLint backingWidth;
+  GLint backingHeight;
+
+  EAGLContext *context;
+
+  /* OpenGL names for the renderbuffer and framebuffers used to render to this view */
+  GLuint viewRenderbuffer, viewFramebuffer;
+
+  /* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it
+     exists (0 if it does not exist) */
+  GLuint depthRenderbuffer;
+
+  BOOL rendering;
+
+  @public
+  /* our GModel drawing class */
+  drawContext *mContext;
+  BOOL rotate;
+}
+
+- (void)drawView;
+
+- (void)load:(NSString*) file;
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/EAGLView.mm b/contrib/mobile/iOS/Onelab/EAGLView.mm
new file mode 100644
index 0000000000000000000000000000000000000000..64e54670d681aebfea8ab7e0f1da61b23d1da6d6
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/EAGLView.mm
@@ -0,0 +1,152 @@
+#import <QuartzCore/QuartzCore.h>
+#import <OpenGLES/EAGLDrawable.h>
+
+#import "EAGLView.h"
+
+#define USE_DEPTH_BUFFER 1
+
+// A class extension to declare private methods
+@interface EAGLView ()
+
+@property (nonatomic, retain) EAGLContext *context;
+
+- (BOOL) createFramebuffer;
+- (void) destroyFramebuffer;
+
+@end
+
+@implementation EAGLView
+
+@synthesize context;
+
+// You must implement this
++ (Class)layerClass
+{
+  return [CAEAGLLayer class];
+}
+
+//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
+- (id)initWithCoder:(NSCoder*)coder
+{
+  if ((self = [super initWithCoder:coder])) {
+    // Get the layer
+    CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
+
+    // detect retina display
+    if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
+        ([UIScreen mainScreen].scale == 2.0)) {
+      self.contentScaleFactor = 2.0;
+      eaglLayer.contentsScale = 2;
+    }
+
+    eaglLayer.opaque = YES;
+    eaglLayer.drawableProperties =
+      [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],
+                    kEAGLDrawablePropertyRetainedBacking,
+                    kEAGLColorFormatRGBA8,
+                    kEAGLDrawablePropertyColorFormat, nil];
+    context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
+    if (!context || ![EAGLContext setCurrentContext:context]) {
+      //[self release];
+      return nil;
+    }
+    mContext = new drawContext((eaglLayer.contentsScale == 2) ? 1.5 : 1,
+                               eaglLayer.contentsScale == 2);
+  }
+  rendering = NO;
+  return self;
+}
+
+- (void)drawView
+{
+  if(rendering) return;
+  rendering = YES;
+  [EAGLContext setCurrentContext:context];
+
+  glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
+  mContext->initView(backingWidth, backingHeight);
+  mContext->drawView();
+
+  glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
+  [context presentRenderbuffer:GL_RENDERBUFFER_OES];
+  rendering = NO;
+}
+
+- (void)load:(NSString*) file
+{
+  mContext->load(*new std::string([file fileSystemRepresentation]));
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"resetParameters" object:nil];
+  [self drawView];
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+{
+  NSUInteger ntouch = [[event allTouches] count];
+  UITouch* touch = [touches anyObject];
+  CGPoint position = [touch locationInView:self];
+  if(ntouch != 1) return;
+  if(rotate)
+    mContext->eventHandler(3,position.x,position.y);
+  else
+    mContext->eventHandler(1,position.x,position.y);
+
+  [self drawView];
+}
+
+- (void)layoutSubviews
+{
+  [super layoutSubviews];
+  [EAGLContext setCurrentContext:context];
+  [self destroyFramebuffer];
+  [self createFramebuffer];
+  [self drawView];
+}
+
+- (BOOL)createFramebuffer
+{
+  glGenFramebuffersOES(1, &viewFramebuffer);
+  glGenRenderbuffersOES(1, &viewRenderbuffer);
+
+  glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
+  glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
+  [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
+  glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
+
+  glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+  glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+
+  if (USE_DEPTH_BUFFER) {
+    glGenRenderbuffersOES(1, &depthRenderbuffer);
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
+    glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
+    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
+  }
+
+  if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
+    NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
+    return NO;
+  }
+
+  return YES;
+}
+
+- (void)destroyFramebuffer
+{
+  glDeleteFramebuffersOES(1, &viewFramebuffer);
+  viewFramebuffer = 0;
+  glDeleteRenderbuffersOES(1, &viewRenderbuffer);
+  viewRenderbuffer = 0;
+  if(depthRenderbuffer) {
+    glDeleteRenderbuffersOES(1, &depthRenderbuffer);
+    depthRenderbuffer = 0;
+  }
+}
+
+- (void)dealloc
+{
+  if ([EAGLContext currentContext] == context) {
+    [EAGLContext setCurrentContext:nil];
+  }
+}
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/Contents.json b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..ffbaddcca3b9c789f662cd1084036fd2f600f85d
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,103 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "icon_app_iphone_retina.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "icon_app_ipad.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "icon_app_ipad_retina.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "83.5x83.5",
+      "idiom" : "ipad",
+      "filename" : "icon_app_ipad_pro.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "1024x1024",
+      "idiom" : "ios-marketing",
+      "filename" : "icon_app_marketing.png",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ios_marketing.png b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ios_marketing.png
new file mode 100644
index 0000000000000000000000000000000000000000..e4fdbf74e98928559ec0038c8252835f2a419597
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ios_marketing.png differ
diff --git a/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ipad.png b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ipad.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc9bad93802b2c92f34490b0f59cb39907c45e9b
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ipad.png differ
diff --git a/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ipad_pro.png b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ipad_pro.png
new file mode 100644
index 0000000000000000000000000000000000000000..a03e0bf938a935d9d30fd5ff3ec80a9390ab34f2
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ipad_pro.png differ
diff --git a/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ipad_retina.png b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ipad_retina.png
new file mode 100644
index 0000000000000000000000000000000000000000..32594d35d7183eb085f618d1f5e65082d1f69f53
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_ipad_retina.png differ
diff --git a/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_iphone_retina.png b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_iphone_retina.png
new file mode 100644
index 0000000000000000000000000000000000000000..6f04e5a561f6dc052ab82ab9c18ed75c9f242e0e
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_iphone_retina.png differ
diff --git a/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_marketing.png b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_marketing.png
new file mode 100644
index 0000000000000000000000000000000000000000..e4fdbf74e98928559ec0038c8252835f2a419597
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/Images.xcassets/AppIcon.appiconset/icon_app_marketing.png differ
diff --git a/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/Contents.json b/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..07ee4c759577f9a8be1177bb4f4b53c076aae70e
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,56 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "40x40"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "icon_app_iphone_retina.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "icon_app_ipad.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "icon_app_ipad_retina.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "40x40"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "40x40"
+    },
+    {
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "29x29"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "29x29"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "29x29"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/icon_app_ipad.png b/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/icon_app_ipad.png
new file mode 100644
index 0000000000000000000000000000000000000000..f9de893258b0451ab6542a222ae0b49d2107cfaa
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/icon_app_ipad.png differ
diff --git a/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/icon_app_ipad_retina.png b/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/icon_app_ipad_retina.png
new file mode 100644
index 0000000000000000000000000000000000000000..4e0c84a8ba5952d465ddd75a84f25656377a55ed
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/icon_app_ipad_retina.png differ
diff --git a/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/icon_app_iphone_retina.png b/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/icon_app_iphone_retina.png
new file mode 100644
index 0000000000000000000000000000000000000000..4e50e475c8baed47ec3826275bfe63ad094cac79
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/Images_BBEMG.xcassets/AppIcon.appiconset/icon_app_iphone_retina.png differ
diff --git a/contrib/mobile/iOS/Onelab/LaunchScreen.storyboard b/contrib/mobile/iOS/Onelab/LaunchScreen.storyboard
new file mode 100644
index 0000000000000000000000000000000000000000..97999abc3a51d50c77d1760298b8fc38667d7354
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/LaunchScreen.storyboard
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <device id="retina3_5" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" image="icon_onelab.png" translatesAutoresizingMaskIntoConstraints="NO" id="r7i-LQ-XXJ">
+                                <rect key="frame" x="128" y="208" width="64" height="64"/>
+                            </imageView>
+                        </subviews>
+                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="r7i-LQ-XXJ" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="Km0-Xa-0OK"/>
+                            <constraint firstItem="r7i-LQ-XXJ" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="Nxc-iv-ZxM"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="52" y="374.66266866566718"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="icon_onelab.png" width="64" height="64"/>
+    </resources>
+</document>
diff --git a/contrib/mobile/iOS/Onelab/Model.h b/contrib/mobile/iOS/Onelab/Model.h
new file mode 100644
index 0000000000000000000000000000000000000000..a427a5db787f16707c2e71487c2e12a62036acce
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Model.h
@@ -0,0 +1,23 @@
+#import <Foundation/Foundation.h>
+
+@interface Model : NSObject
+{
+    @private
+    NSString *_name, *_summary, *_file;
+    NSURL *_url;
+    UIImage *_preview;
+}
+-(id) initWithName:(NSString *)name;
+-(id) initWithName:(NSString *)name withSummary:(NSString *)summary withFile:(NSString *)file;
+-(NSString *) getName;
+-(NSString *) getSummary;
+-(NSString *) getFile;
+-(void) setSummary:(NSString *)summary;
+-(void) setFile:(NSString *)file;
+-(NSURL *) getUrl;
+-(UIImage *) getPreview;
+-(void) setPreview:(NSString *)path;
+-(void) setUrl:(NSString *)url;
+-(NSComparisonResult)compare:(Model *)p;
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/Model.mm b/contrib/mobile/iOS/Onelab/Model.mm
new file mode 100644
index 0000000000000000000000000000000000000000..dd77734ce75eb09a9533bbb8c4c57b3f1abd533b
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Model.mm
@@ -0,0 +1,81 @@
+#import "Model.h"
+
+@implementation Model
+
+-(id) initWithName:(NSString *)name
+{
+  self = [super init];
+  if(self) {
+    _name = name;
+    _summary = nil;
+    _file = nil;
+    _url = nil;
+    _preview = nil;
+  }
+  return self;
+}
+
+-(id) initWithName:(NSString *)name withSummary:(NSString *)summary withFile:(NSString *)file
+{
+  self = [super init];
+  if(self) {
+    _name = name;
+    _summary = summary;
+    _file = file;
+    _url = nil;
+    _preview = nil;
+  }
+  return self;
+}
+
+-(NSString *) getName
+{
+  return _name;
+}
+
+-(NSString *) getSummary
+{
+  return _summary;
+}
+
+-(NSString *) getFile
+{
+  return _file;
+}
+
+-(NSURL *) getUrl
+{
+  return _url;
+}
+
+-(UIImage *) getPreview
+{
+  return _preview;
+}
+
+-(void) setSummary:(NSString *)summary
+{
+  _summary = summary;
+}
+
+-(void) setFile:(NSString *)file
+{
+  _file = file;
+}
+
+-(void) setPreview:(NSString *)path
+{
+  _preview = [UIImage imageWithContentsOfFile:path];
+}
+
+-(void) setUrl:(NSString *)url
+{
+  _url = [NSURL URLWithString:url];
+}
+
+-(NSComparisonResult) compare:(Model *)p
+{
+  return [[self getName] compare:[p getName]];
+}
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/ModelListController.h b/contrib/mobile/iOS/Onelab/ModelListController.h
new file mode 100644
index 0000000000000000000000000000000000000000..b7deab23da8f4ff9bf8ac4d19601c5103b9a11e3
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/ModelListController.h
@@ -0,0 +1,17 @@
+#import <UIKit/UIKit.h>
+#import <MessageUI/MessageUI.h>
+#import "EAGLView.h"
+
+@interface ModelListController : UITableViewController <NSXMLParserDelegate, MFMailComposeViewControllerDelegate>
+{
+    @private
+    NSMutableArray *models;
+    NSString *currentElement;
+    NSMutableString *currentElementValue;
+    NSString *currentDir;
+    NSString *selectedModel;
+    NSString *currentFileToEdit;
+}
+@property (nonatomic, retain) EAGLView *glView;
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/ModelListController.mm b/contrib/mobile/iOS/Onelab/ModelListController.mm
new file mode 100644
index 0000000000000000000000000000000000000000..aadc46cc8679191222a53b93515c6476f364b2bd
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/ModelListController.mm
@@ -0,0 +1,397 @@
+#import "AppDelegate.h"
+#import "ModelListController.h"
+#import "AboutViewController.h"
+
+#import "Utils.h"
+#import "Model.h"
+
+@implementation ModelListController
+-(void)viewDidLoad
+{
+  UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
+  [refreshControl addTarget:self action:@selector(refreshList)
+           forControlEvents:UIControlEventValueChanged];
+  self.refreshControl = refreshControl;
+
+  UILongPressGestureRecognizer *lpgr =
+    [[UILongPressGestureRecognizer alloc] initWithTarget:self
+                                                  action:@selector(handleLongPress:)];
+  lpgr.minimumPressDuration = 1.0;
+  [self.tableView addGestureRecognizer:lpgr];
+
+  models = [[NSMutableArray alloc] init];
+  NSString *docsPath = [Utils getApplicationDocumentsDirectory];
+  NSArray *docs = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:docsPath error:NULL];
+  for(NSString* doc in docs) {
+    NSString *docPath = [NSString stringWithFormat:@"%@/%@/", docsPath, doc];
+    BOOL isDir = NO; [[NSFileManager defaultManager] fileExistsAtPath:docPath isDirectory:&isDir];
+    if(isDir){
+      NSString *infos = [NSString stringWithFormat:@"%@%@", docPath, @"infos.xml"];
+      if([[NSFileManager defaultManager] fileExistsAtPath:infos]) {
+        currentDir = docPath;
+        [self parseInfosFile:infos];
+      }
+    }
+  }
+  [models sortUsingSelector:@selector(compare:)];
+
+  UIBarButtonItem *about =
+    [[UIBarButtonItem alloc] initWithTitle:@"About"
+                                     style:UIBarButtonItemStylePlain
+                                    target:self
+                                    action:@selector(showAbout)];
+  [self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects: about, nil]];
+
+  UIBarButtonItem *help =
+    [[UIBarButtonItem alloc] initWithTitle:@"Help"
+                                     style:UIBarButtonItemStylePlain
+                                    target:self
+                                    action:@selector(showHelp)];
+  [self.navigationItem setLeftBarButtonItems:[NSArray arrayWithObjects: help, nil]];
+}
+
+-(void)showAbout
+{
+  currentFileToEdit = @"About";
+  [self performSegueWithIdentifier:@"showAboutSegue" sender:self];
+}
+
+-(void)showHelp
+{
+  currentFileToEdit = @"Help";
+  [self performSegueWithIdentifier:@"showAboutSegue" sender:self];
+}
+
+-(void)refreshList
+{
+  NSString *docsPath = [Utils getApplicationDocumentsDirectory];
+  NSArray *docs = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:docsPath error:NULL];
+  for(NSString* doc in docs) {
+    NSString *docPath = [NSString stringWithFormat:@"%@/%@/", docsPath, doc];
+    BOOL isDir = NO; [[NSFileManager defaultManager] fileExistsAtPath:docPath isDirectory:&isDir];
+    if(isDir){
+      NSString *infos = [NSString stringWithFormat:@"%@%@", docPath, @"infos.xml"];
+      if([[NSFileManager defaultManager] fileExistsAtPath:infos]) {
+        currentDir = docPath;
+        [self parseInfosFile:infos];
+      }
+    }
+  }
+  for (int i=0;i<[models count]; i++) {
+    if(![[NSFileManager defaultManager] fileExistsAtPath:[models[i] getFile]]) {
+      [models removeObject:models[i]];
+      i--;
+    }
+  }
+  [models sortUsingSelector:@selector(compare:)];
+  [self.tableView reloadData];
+  [self.refreshControl endRefreshing];
+}
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+  return 1;
+}
+
+-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+  return [models count];
+}
+
+-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+  UITableViewCell *cell;
+  if(indexPath.row >= [models count])
+    return cell;
+  Model *m = [models objectAtIndex:indexPath.row];
+  cell = [tableView dequeueReusableCellWithIdentifier:[m getName]];
+  if(cell != nil) return cell;
+  cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
+                                reuseIdentifier:[m getName]];
+  [cell.textLabel setText:[m getName]];
+  if([m getSummary] != nil) [cell.detailTextLabel setText:[m getSummary]];
+  if([m getPreview] != nil) cell.imageView.image = [m getPreview];
+  if([m getFile] == nil) {
+    cell.selectionStyle = UITableViewCellSelectionStyleNone;
+    cell.userInteractionEnabled = NO;
+    cell.textLabel.alpha = 0.75;
+  }
+  cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+  return cell;
+}
+
+-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
+{
+  return @"Select a model";
+}
+
+-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+  AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
+  selectedModel = [[models objectAtIndex:indexPath.row] getFile];
+  if([[UIDevice currentDevice].model isEqualToString:@"iPad"] ||
+     [[UIDevice currentDevice].model isEqualToString:@"iPad Simulator"]){
+    appDelegate.splitViewController.initialModel = selectedModel;
+    [UIView transitionWithView:appDelegate.window
+                      duration:0.5
+                       options:UIViewAnimationOptionTransitionFlipFromLeft
+                    animations:^{
+        appDelegate.window.rootViewController = appDelegate.splitViewController;
+      }
+    completion:nil];
+  }
+  else{
+    [self performSegueWithIdentifier:@"showModelSegue" sender:self];
+  }
+}
+
+-(void)handleLongPress:(UILongPressGestureRecognizer *)sender
+{
+  CGPoint p = [sender locationInView:self.tableView];
+  if(sender.state == UIGestureRecognizerStateCancelled) return;
+  NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
+  if(indexPath == nil) return;
+  UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
+
+  UIAlertController *actionSheet =
+    [UIAlertController alertControllerWithTitle:[[models objectAtIndex:indexPath.row] getName]
+                                        message:nil
+                                 preferredStyle:UIAlertControllerStyleActionSheet];
+
+  [actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel"
+                                                  style:UIAlertActionStyleCancel
+                                                handler:^(UIAlertAction *action) {
+      }]];
+  [actionSheet addAction:[UIAlertAction actionWithTitle:@"Open"
+                                                  style:UIAlertActionStyleDefault
+                                                handler:^(UIAlertAction *action) {
+        [self tableView:self.tableView
+              didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
+      }]];
+  [actionSheet addAction:[UIAlertAction actionWithTitle:@"Remove"
+                                                  style:UIAlertActionStyleDestructive
+                                                handler:^(UIAlertAction *action) {
+        NSString *file = [[models objectAtIndex:indexPath.row] getFile];
+        NSString *path = [file stringByDeletingLastPathComponent];
+        [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
+        [self refreshList];
+      }]];
+  [actionSheet addAction:[UIAlertAction actionWithTitle:@"Clear results"
+                                                  style:UIAlertActionStyleDefault
+                                                handler:^(UIAlertAction *action) {
+        NSString *modelFile = [[models objectAtIndex:indexPath.row] getFile];
+        NSString *modelPath = [modelFile stringByDeletingLastPathComponent];
+        NSArray *modelFiles = [[NSFileManager defaultManager]
+                                contentsOfDirectoryAtPath:modelPath error:NULL];
+        for (NSString *obj in modelFiles){
+          NSString *extension = [obj pathExtension];
+          if([extension isEqualToString:@"msh"] ||
+             [extension isEqualToString:@"pre"] ||
+             [extension isEqualToString:@"res"] ||
+             [extension isEqualToString:@"pos"]){
+            NSString *file = [[modelPath stringByAppendingString:@"/"] stringByAppendingString:obj];
+            NSLog(@"Removing file %@", file);
+            [[NSFileManager defaultManager] removeItemAtPath:file error:nil];
+          }
+        }
+      }]];
+  [actionSheet addAction:[UIAlertAction actionWithTitle:@"Edit model files"
+                                                  style:UIAlertActionStyleDefault
+                                                handler:^(UIAlertAction *action) {
+        NSString *modelFile = [[models objectAtIndex:indexPath.row] getFile];
+        NSString *modelPath = [modelFile stringByDeletingLastPathComponent];
+        NSArray *modelFiles = [[NSFileManager defaultManager]
+                                contentsOfDirectoryAtPath:modelPath error:NULL];
+        UIAlertController *actionSheet2 =
+          [UIAlertController alertControllerWithTitle:@"Edit model files"
+                                              message:nil
+                                       preferredStyle:UIAlertControllerStyleActionSheet];
+        [actionSheet2 addAction:[UIAlertAction actionWithTitle:@"Cancel"
+                                                         style:UIAlertActionStyleCancel
+                                                       handler:^(UIAlertAction *action) {
+            }]];
+        for(NSString *file in modelFiles)  {
+          NSString *extension = [file pathExtension];
+          if([extension isEqualToString:@"txt"] ||
+             [extension isEqualToString:@"geo"] ||
+             [extension isEqualToString:@"pro"] ||
+             [extension isEqualToString:@"dat"]){
+            [actionSheet2 addAction: [UIAlertAction actionWithTitle:file
+                                                              style:UIAlertActionStyleDefault
+                                                            handler:^(UIAlertAction *action) {
+                  NSString *modelFile = [[models objectAtIndex:indexPath.row] getFile];
+                  NSString *modelPath = [modelFile stringByDeletingLastPathComponent];
+                  currentFileToEdit = [[modelPath stringByAppendingString:@"/"] stringByAppendingString:file];
+                  [self performSegueWithIdentifier:@"showAboutSegue" sender:self];
+                }]];
+          }
+        }
+        actionSheet2.popoverPresentationController.sourceView = cell;
+        actionSheet2.popoverPresentationController.sourceRect = cell.bounds;
+        [self presentViewController:actionSheet2 animated:YES completion:nil];
+      }]];
+  [actionSheet addAction:[UIAlertAction actionWithTitle:@"Email model files"
+                                                  style:UIAlertActionStyleDefault
+                                                handler:^(UIAlertAction *action) {
+        NSString *modelFile = [[models objectAtIndex:indexPath.row] getFile];
+        NSString *modelPath = [modelFile stringByDeletingLastPathComponent];
+        NSArray *modelFiles = [[NSFileManager defaultManager]
+                                contentsOfDirectoryAtPath:modelPath error:NULL];
+        // TODO: would probably be better to email a zip archive? (this ignores subdirectories)
+        [self attachFilesToEmail:modelFiles filePath:modelPath];
+      }]];
+  if([[models objectAtIndex:indexPath.row] getUrl]){
+    [actionSheet addAction:[UIAlertAction actionWithTitle:@"Visit model website"
+                                                    style:UIAlertActionStyleDefault
+                                                  handler:^(UIAlertAction *action) {
+          [[UIApplication sharedApplication] openURL:[[models objectAtIndex:indexPath.row] getUrl]];
+        }]];
+  }
+
+  actionSheet.popoverPresentationController.sourceView = cell;
+  actionSheet.popoverPresentationController.sourceRect = cell.bounds;
+
+  [self presentViewController:actionSheet animated:YES completion:nil];
+}
+
+- (void)attachFilesToEmail:(NSArray*)files filePath:(NSString*)path
+{
+  if([MFMailComposeViewController canSendMail] == NO) return;
+
+  MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
+  mc.mailComposeDelegate = self;
+  [mc setSubject:@"My ONELAB model"];
+
+  // Add attachments
+  for (NSString *file in files){
+    NSString *extension = [file pathExtension];
+    NSString *filePath = [[path stringByAppendingString:@"/"] stringByAppendingString:file];
+    NSData *fileData = [NSData dataWithContentsOfFile:filePath];
+    if(fileData){
+      NSString *mimeType;
+      if ([extension isEqualToString:@"jpg"]) {
+        mimeType = @"image/jpeg";
+      }
+      else if([extension isEqualToString:@"png"]) {
+        mimeType = @"image/png";
+      }
+      else if([extension isEqualToString:@"doc"]) {
+        mimeType = @"application/msword";
+      }
+      else if([extension isEqualToString:@"ppt"]) {
+        mimeType = @"application/vnd.ms-powerpoint";
+      }
+      else if([extension isEqualToString:@"html"]) {
+        mimeType = @"text/html";
+      }
+      else if([extension isEqualToString:@"pdf"]) {
+        mimeType = @"application/pdf";
+      }
+      else{
+        mimeType = @"text/plain";
+      }
+
+      [mc addAttachmentData:fileData mimeType:@"" fileName:file];
+    }
+  }
+
+  // Present mail view controller on screen
+  [self presentViewController:mc animated:YES completion:NULL];
+
+}
+
+- (void) mailComposeController:(MFMailComposeViewController *)controller
+           didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
+{
+  switch (result){
+  case MFMailComposeResultCancelled: NSLog(@"Mail cancelled"); break;
+  case MFMailComposeResultSaved: NSLog(@"Mail saved"); break;
+  case MFMailComposeResultSent: NSLog(@"Mail sent"); break;
+  case MFMailComposeResultFailed: NSLog(@"Mail sent failure: %@",
+                                        [error localizedDescription]); break;
+  default: break;
+  }
+  // Close the Mail Interface
+  [self dismissViewControllerAnimated:YES completion:NULL];
+}
+
+- (BOOL) parseInfosFile:(NSString *)file
+{
+  NSData *xmlFile = [[NSFileManager defaultManager] contentsAtPath:file];
+  NSXMLParser *parser;
+  parser = [[NSXMLParser alloc] initWithData:xmlFile];
+  [parser setDelegate:self];
+
+  [parser setShouldProcessNamespaces:NO];
+  [parser setShouldReportNamespacePrefixes:NO];
+  [parser setShouldResolveExternalEntities:NO];
+
+  return [parser parse];
+}
+
+-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
+ namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
+   attributes:(NSDictionary *)attributeDict
+{
+  currentElement = elementName;
+  //[currentElementValue release];
+  currentElementValue = nil;
+}
+
+-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
+{
+  if (!currentElementValue)
+    currentElementValue = [[NSMutableString alloc] initWithString:string];
+  else
+    [currentElementValue appendString:string];
+}
+
+-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
+ namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
+{
+  if([elementName isEqual:@"title"]) {
+    for(Model *mp in models) {
+      if([[mp getName] isEqual:currentElementValue]){
+        [parser abortParsing];
+        return;
+      }
+    }
+    Model *m = [[Model alloc] initWithName:currentElementValue];
+    [models addObject:m];
+  }
+  else {
+    if(models.count < 1) return;
+    if([elementName isEqual:@"summary"]) {
+      Model *m = [models lastObject];
+      [m setSummary:currentElementValue];
+    }
+    else if([elementName isEqual:@"file"]) {
+      Model *m = [models lastObject];
+      [m setFile:[NSString stringWithFormat:@"%@%@", currentDir, currentElementValue]];
+    }
+    else if([elementName isEqual:@"url"]) {
+      Model *m = [models lastObject];
+      [m setUrl:currentElementValue];
+    }
+    else if([elementName isEqual:@"preview"]) {
+      Model *m = [models lastObject];
+      [m setPreview:[NSString stringWithFormat:@"%@%@", currentDir, currentElementValue]];
+    }
+
+  }
+  //[currentElementValue release];
+  currentElementValue = nil;
+}
+
+-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
+{
+  if ([[segue identifier] isEqualToString:@"showModelSegue"]) {
+    ModelViewController *modelViewController = [segue destinationViewController];
+    modelViewController.initialModel = selectedModel;
+  }
+  else if ([[segue identifier] isEqualToString:@"showAboutSegue"]) {
+    AboutViewController *aboutViewController = [segue destinationViewController];
+    aboutViewController.fileToEdit = currentFileToEdit;
+  }
+}
+@end
diff --git a/contrib/mobile/iOS/Onelab/ModelViewController.h b/contrib/mobile/iOS/Onelab/ModelViewController.h
new file mode 100644
index 0000000000000000000000000000000000000000..a2666f6115e4b4f547f9b3ce606ecb6baf697d74
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/ModelViewController.h
@@ -0,0 +1,29 @@
+#import <UIKit/UIKit.h>
+#import "EAGLView.h"
+
+@interface ModelViewController : UIViewController <UISplitViewControllerDelegate>
+{
+  @private
+  double scaleFactor;
+  UIBarButtonItem *_runStopButton, *_playButton, *_stopButton;
+  NSTimer *_animation;
+}
+
+- (IBAction)pinch:(UIPinchGestureRecognizer *)sender;
+- (IBAction)singleTap:(UITapGestureRecognizer *)sender;
+- (IBAction)doubleTap:(UITapGestureRecognizer *)sender;
+
+@property (strong, nonatomic) IBOutlet UITapGestureRecognizer *singleTap;
+@property (strong, nonatomic) IBOutlet UITapGestureRecognizer *doubleTap;
+@property (nonatomic, retain) EAGLView *glView;
+@property (strong, nonatomic) id detailItem;
+@property (weak, nonatomic) IBOutlet UILabel *progressLabel;
+@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *progressIndicator;
+
+@property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
+
+@property (nonatomic, retain) NSString *initialModel;
+
+- (IBAction)startRotation:(UIButton *)sender;
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/ModelViewController.mm b/contrib/mobile/iOS/Onelab/ModelViewController.mm
new file mode 100644
index 0000000000000000000000000000000000000000..fb6a103be4da610f10673631718450c0957e1bca
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/ModelViewController.mm
@@ -0,0 +1,411 @@
+#import <QuartzCore/QuartzCore.h>
+#import <Social/Social.h>
+
+#import "ModelViewController.h"
+#import "drawContext.h"
+#import "iosUtils.h"
+#import "Utils.h"
+
+#import "AppDelegate.h"
+
+@interface ModelViewController ()
+- (void)configureView;
+@end
+
+@implementation ModelViewController
+
+#pragma mark - Managing the detail item
+@synthesize glView;
+
+- (void)setDetailItem:(id)newDetailItem
+{
+  if (_detailItem != newDetailItem) {
+    _detailItem = newDetailItem;
+    // Update the view.
+    [self configureView];
+  }
+}
+
+- (void)configureView
+{
+  // Update the user interface for the detail item.
+  if (self.detailItem) {
+    self.detailDescriptionLabel.text = [self.detailItem description];
+  }
+}
+
+-(void)viewDidAppear:(BOOL)animated
+{
+  _progressLabel.frame = CGRectMake(50, self.view.frame.size.height - 25,
+                                    _progressLabel.frame.size.width,
+                                    _progressLabel.frame.size.height);
+  _progressIndicator.frame = CGRectMake(20, self.view.frame.size.height - 25,
+                                        _progressIndicator.frame.size.width,
+                                        _progressIndicator.frame.size.height);
+  [_progressIndicator
+    addGestureRecognizer:
+      [[UITapGestureRecognizer alloc] initWithTarget:self
+                                              action:@selector(handleProgressIndicatorTap:)]];
+  [_progressLabel setHidden:YES];
+  [_progressIndicator setHidden:YES];
+  [self.navigationController setToolbarHidden:YES animated:NO];
+  if(self.initialModel != nil){
+    [self.glView load:self.initialModel];
+    [[NSNotificationCenter defaultCenter] postNotificationName:@"refreshParameters" object:nil];
+    //[self.initialModel release];
+    self.initialModel = nil;
+  }
+}
+
+- (void)viewDidLoad
+{
+  [super viewDidLoad];
+  // Do any additional setup after loading the view, typically from a nib.
+  [self configureView];
+  [_singleTap requireGestureRecognizerToFail:_doubleTap];
+  scaleFactor = 1.;
+  setObjCBridge((__bridge void*) self);
+  [[NSNotificationCenter defaultCenter] addObserver:self
+                                           selector:@selector(requestRender)
+                                               name:@"requestRender" object:nil];
+
+  _runStopButton = [[UIBarButtonItem alloc] initWithTitle:@"Run"
+                                                    style:UIBarButtonItemStylePlain
+                                                   target:self
+                                                   action:@selector(compute)];
+  if([[UIDevice currentDevice].model isEqualToString:@"iPad"] ||
+     [[UIDevice currentDevice].model isEqualToString:@"iPad Simulator"]){
+    UIBarButtonItem *model = [[UIBarButtonItem alloc] initWithTitle:@"Model list"
+                                                              style:UIBarButtonItemStylePlain
+                                                             target:self
+                                                             action:@selector(showModelsList)];
+    [self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:_runStopButton,
+                                                         model, nil]];
+  }
+  else {
+    UIBarButtonItem *settings = [[UIBarButtonItem alloc] initWithTitle:@"Parameters"
+                                                                 style:UIBarButtonItemStylePlain
+                                                                target:self
+                                                                action:@selector(showSettings)];
+    [self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:_runStopButton,
+                                                         settings, nil]];
+  }
+
+  UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc]
+                                     initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
+                                                          target:nil action:nil];
+  UIBarButtonItem *prevButton = [[UIBarButtonItem alloc ]
+                                  initWithBarButtonSystemItem:UIBarButtonSystemItemRewind
+                                                       target:self
+                                                       action:@selector(prevAnimation)];
+  _stopButton = [[UIBarButtonItem alloc]
+                  initWithBarButtonSystemItem:UIBarButtonSystemItemPause
+                                       target:self
+                                       action:@selector(stopAnimation:)];
+  [_stopButton setEnabled:NO];
+  _playButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay
+                                                              target:self
+                                                              action:@selector(playAnimation:)];
+  UIBarButtonItem *nextButton = [[UIBarButtonItem alloc ]
+                                  initWithBarButtonSystemItem:UIBarButtonSystemItemFastForward
+                                                       target:self
+                                                       action:@selector(nextAnimation)];
+  self.toolbarItems = [[NSArray alloc] initWithObjects:flexibleSpace, prevButton,
+                                       _stopButton, _playButton, nextButton, flexibleSpace, nil];
+}
+
+- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
+{
+  _progressLabel.frame = CGRectMake(50, self.view.frame.size.height - 25,
+                                    _progressLabel.frame.size.width,
+                                    _progressLabel.frame.size.height);
+  _progressIndicator.frame = CGRectMake(20, self.view.frame.size.height - 25,
+                                        _progressIndicator.frame.size.width,
+                                        _progressIndicator.frame.size.height);
+}
+
+- (void)compute
+{
+  AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
+  appDelegate->compute = YES;
+  [_runStopButton setAction:@selector(stop)];
+  [_runStopButton setTitle:@"Stop"];
+  [_progressLabel setText:@""];
+  [_progressLabel setHidden:NO];
+  [_progressIndicator setHidden:NO];
+  [_progressIndicator startAnimating];
+  self.navigationItem.hidesBackButton = YES;
+
+  [[UIApplication sharedApplication] cancelAllLocalNotifications];
+  dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
+      onelab_cb("compute");
+      dispatch_async(dispatch_get_main_queue(), ^{
+          AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
+          appDelegate->compute = NO;
+          [[NSNotificationCenter defaultCenter] postNotificationName:@"refreshParameters" object:nil];
+          [_runStopButton setAction:@selector(compute)];
+          [_runStopButton setTitle:@"Run"];
+          [_progressLabel setHidden:YES];
+          [_progressIndicator stopAnimating];
+          [_progressIndicator setHidden:YES];
+          self.navigationItem.hidesBackButton = NO;
+        });
+    });
+}
+
+- (void)stop
+{
+  onelab_cb("stop");
+}
+
+-(void)playAnimation:(UIBarButtonItem *)sender
+{
+  [_playButton setEnabled:NO];
+  [_stopButton setEnabled:YES];
+  _animation = [NSTimer scheduledTimerWithTimeInterval:0.5
+                                                target:self
+                                              selector:@selector(nextAnimation)
+                                              userInfo:nil
+                                               repeats:YES];
+}
+
+-(void)stopAnimation:(UIBarButtonItem *)sender
+{
+  [_animation invalidate];
+  _animation = nil;
+  [_playButton setEnabled:YES];
+  [_stopButton setEnabled:NO];
+}
+
+-(void)nextAnimation { animation_next(); [self requestRender]; }
+
+-(void)prevAnimation { animation_prev(); [self requestRender]; }
+
+-(IBAction)pinch:(UIPinchGestureRecognizer *)sender
+{
+  if([sender numberOfTouches] <= 2) {
+    float mScale = scaleFactor;
+    if (sender.state == UIGestureRecognizerStateBegan)
+      mScale = scaleFactor;
+    else if(sender.state == UIGestureRecognizerStateChanged)
+      mScale = scaleFactor * [sender scale];
+    else if(sender.state == UIGestureRecognizerStateEnded){
+      scaleFactor *= [sender scale];
+      mScale = scaleFactor;
+    }
+    else if(sender.state == UIGestureRecognizerStateCancelled){
+      mScale = scaleFactor;
+    }
+    mScale = MAX(0.1, mScale);
+    glView->mContext->eventHandler(2,mScale);
+  }
+  [glView drawView];
+}
+
+-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+{
+  UITouch *touch = [[event allTouches] anyObject];
+  CGPoint touchPoint = [touch locationInView:self.view];
+  glView->mContext->eventHandler(0, touchPoint.x, touchPoint.y);
+}
+
+-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
+{
+  [self touchesEnded:touches withEvent:event];
+}
+
+-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
+{
+  UITouch *touch = [[event allTouches] anyObject];
+  CGPoint touchPoint = [touch locationInView:self.view];
+  glView->mContext->eventHandler(4, touchPoint.x, touchPoint.y);
+}
+
+- (IBAction)singleTap:(UITapGestureRecognizer *)sender
+{
+  [self.navigationController setToolbarHidden:
+         (!(self.navigationController.toolbarHidden &&
+            !((AppDelegate *)[UIApplication sharedApplication].delegate)->compute &&
+            number_of_animation() > 0)) animated:YES];
+}
+
+- (IBAction)doubleTap:(UITapGestureRecognizer *)sender
+{
+  scaleFactor = 1;
+  glView->mContext->eventHandler(10);
+  [glView drawView];
+}
+
+- (void) showModelsList
+{
+  AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
+  if(appDelegate->compute) {
+    UIAlertController *alert =
+      [UIAlertController
+        alertControllerWithTitle:@"Cannot show model list"
+                         message:@"Computation has to complete before a new model can be selected"
+                  preferredStyle:UIAlertControllerStyleAlert];
+    UIAlertAction *dismissButton =
+      [UIAlertAction actionWithTitle:@"Dismiss"
+                               style:UIAlertActionStyleDefault
+                             handler:^(UIAlertAction * action) { }];
+    [alert addAction:dismissButton];
+    [self presentViewController:alert animated:YES completion:nil];
+    return;
+  }
+  if([[UIDevice currentDevice].model isEqualToString:@"iPad"] ||
+     [[UIDevice currentDevice].model isEqualToString:@"iPad Simulator"]){
+    [UIView transitionWithView:appDelegate.window
+                      duration:0.5
+                       options:UIViewAnimationOptionTransitionFlipFromRight
+                    animations:^{
+        appDelegate.window.rootViewController = appDelegate.modelListController; }
+    completion:nil];
+  }
+  [self.navigationController popToRootViewControllerAnimated:YES];
+}
+
+- (void) showSettings
+{
+  [self performSegueWithIdentifier:@"showSettingsSegue" sender:self];
+}
+
+- (void)viewDidUnload
+{
+  [self setGlView:nil];
+  [self setProgressLabel:nil];
+  [self setProgressIndicator:nil];
+  [self setSingleTap:nil];
+  [self setDoubleTap:nil];
+  [super viewDidUnload];
+  // Release any retained subviews of the main view.
+}
+
+- (void)requestRender
+{
+  if([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
+    [glView drawView];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+  return YES;
+}
+
+#pragma mark - Split view
+
+-(BOOL)splitViewController:(UISplitViewController *)svc
+  shouldHideViewController:(UIViewController *)vc
+             inOrientation:(UIInterfaceOrientation)orientation
+{
+  return NO;
+}
+
+void messageFromCpp (void *self, std::string level, std::string msg)
+{
+  if(level == "RequestRender"){
+    [(__bridge id)self performSelectorOnMainThread:@selector(requestRender)
+                                        withObject:nil waitUntilDone:YES];
+    [[NSNotificationCenter defaultCenter] postNotificationName:@"refreshParameters"
+                                                        object:nil];
+  }
+  else if(level == "Progress"){
+    [(__bridge id) self performSelectorOnMainThread:@selector(setProgress:)
+                                         withObject:[Utils getStringFromCString:msg.c_str()]
+                                      waitUntilDone:YES];
+  }
+  else if(level == "Error"){
+    [(__bridge id) self performSelectorOnMainThread:@selector(showError:)
+                                         withObject:[Utils getStringFromCString:msg.c_str()]
+                                      waitUntilDone:YES];
+  }
+}
+
+-(void)setProgress:(NSString *)progress
+{
+  [_progressLabel setText:progress];
+}
+
+-(void)showError:(NSString *)msg
+{
+  // remove document path from error message
+  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+  NSString *docPath = [[paths objectAtIndex:0] stringByAppendingString:@"/"];
+  NSString *str = [msg stringByReplacingOccurrencesOfString:docPath withString:@""];
+  UIAlertController *alert =
+    [UIAlertController
+        alertControllerWithTitle:@"Error"
+                         message:str
+                  preferredStyle:UIAlertControllerStyleAlert];
+  UIAlertAction *dismissButton =
+    [UIAlertAction actionWithTitle:@"Dismiss"
+                             style:UIAlertActionStyleDefault
+                           handler:^(UIAlertAction * action) { }];
+  [alert addAction:dismissButton];
+  [self presentViewController:alert animated:YES completion:nil];
+}
+
+-(void)handleProgressIndicatorTap:(id)sender
+{
+  [_progressLabel setHidden:!_progressLabel.hidden];
+}
+
+void getBitmap(void *self, const char *text, int textsize, unsigned char **map,
+               int *height, int *width, int *realWidth)
+{
+  [(__bridge id)self getBitmapFromStringObjC:text withTextSize:textsize inMap:map
+                                    inHeight:height inWidth:width inRealWidth:realWidth];
+}
+
+-(void) getBitmapFromStringObjC:(const char *)text withTextSize:(int)textsize inMap:(unsigned char **)map
+                       inHeight:(int *)height inWidth:(int *)width inRealWidth:(int *) realWidth
+{
+  UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1024, 7*textsize/6)];
+  lbl.font = [UIFont systemFontOfSize:textsize];
+  [lbl setText:[Utils getStringFromCString:text]];
+  [lbl setBackgroundColor:[UIColor clearColor]];
+  CGSize lblSize = [[lbl text] sizeWithAttributes:@{NSFontAttributeName:[lbl font]}];
+  *realWidth = lblSize.width;
+  int i=2;
+  while(i<*realWidth) i*=2;
+  *width = i;
+  *height = lblSize.height;
+  i=2;
+  while(i<*height) i*=2;
+  *height = i;
+
+  UIGraphicsBeginImageContextWithOptions(CGSizeMake(*width, *height), NO, 0.0);
+  [lbl.layer renderInContext:UIGraphicsGetCurrentContext()];
+  UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
+  UIGraphicsEndImageContext();
+  CGImageRef bitmap = [img CGImage];
+  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+  unsigned char *rawData = (unsigned char*) calloc(*height * *width * 4, sizeof(unsigned char));
+  *map = (unsigned char*) calloc(*height * *width, sizeof(unsigned char));
+  NSUInteger bytesPerPixel = 4;
+  NSUInteger bytesPerRow = bytesPerPixel * *width;
+  NSUInteger bitsPerComponent = 8;
+  CGContextRef context = CGBitmapContextCreate
+    (rawData, *width, *height,
+     bitsPerComponent, bytesPerRow, colorSpace,
+     kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
+  CGColorSpaceRelease(colorSpace);
+
+  CGContextDrawImage(context, CGRectMake(0, 0, *width, *height), bitmap);
+  CGContextRelease(context);
+
+  // rawData contains the image data in the RGBA8888 pixel format.
+  for (int byteIndex = 0 ; byteIndex < *width * *height * 4 ; byteIndex+=4)
+    *(*map+byteIndex/4) = rawData[byteIndex + 3];
+  free(rawData);
+}
+
+- (IBAction)startRotation:(UIButton *)sender {
+  glView->rotate = !glView->rotate;
+  if(glView->rotate)
+    [sender setImage:[UIImage imageNamed:@"icon_translate.png"] forState:UIControlStateNormal];
+  else
+    [sender setImage:[UIImage imageNamed:@"icon_rotate.png"] forState:UIControlStateNormal];
+}
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/Onelab-Info.plist b/contrib/mobile/iOS/Onelab/Onelab-Info.plist
new file mode 100644
index 0000000000000000000000000000000000000000..8e1f60db1aab98d5ed497fc69141a6bc1f5e1018
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Onelab-Info.plist
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+    <key>CFBundleExecutable</key>
+    <string>${EXECUTABLE_NAME}</string>
+    <key>CFBundleIcons</key>
+    <dict>
+      <key>CFBundlePrimaryIcon</key>
+      <dict>
+        <key>CFBundleIconFiles</key>
+        <array>
+          <string>icon_app_iphone_retina</string>
+        </array>
+      </dict>
+    </dict>
+    <key>CFBundleIcons~ipad</key>
+    <dict>
+      <key>CFBundlePrimaryIcon</key>
+      <dict>
+        <key>CFBundleIconFiles</key>
+        <array>
+          <string>icon_app_ipad</string>
+          <string>icon_app_ipad_retina</string>
+          <string>icon_app_iphone_retina</string>
+        </array>
+      </dict>
+    </dict>
+    <key>CFBundleIdentifier</key>
+    <string>org.geuz.${PRODUCT_NAME:rfc1034identifier}</string>
+    <key>CFBundleInfoDictionaryVersion</key>
+    <string>6.0</string>
+    <key>CFBundleName</key>
+    <string>${PRODUCT_NAME}</string>
+    <key>CFBundlePackageType</key>
+    <string>APPL</string>
+    <key>CFBundleShortVersionString</key>
+    <string>2.2.0</string>
+    <key>CFBundleSignature</key>
+    <string>????</string>
+    <key>CFBundleVersion</key>
+    <string>2.2.0.0</string>
+    <key>LSRequiresIPhoneOS</key>
+    <true/>
+    <key>UIFileSharingEnabled</key>
+    <true/>
+    <key>LSSupportsOpeningDocumentsInPlace</key>
+    <true/>
+    <key>UIMainStoryboardFile</key>
+    <string>iPhoneiPodStoryboard</string>
+    <key>UIMainStoryboardFile~ipad</key>
+    <string>iPadStoryboard</string>
+    <key>UILaunchStoryboardName</key>
+    <string>LaunchScreen</string>
+    <key>UIRequiresFullScreen</key>
+    <true/>
+    <key>UIRequiredDeviceCapabilities</key>
+    <array>
+      <string>opengles-1</string>
+    </array>
+    <key>ITSAppUsesNonExemptEncryption</key><false/>
+    <key>UISupportedInterfaceOrientations</key>
+    <array>
+      <string>UIInterfaceOrientationPortrait</string>
+      <string>UIInterfaceOrientationLandscapeLeft</string>
+      <string>UIInterfaceOrientationLandscapeRight</string>
+      <string>UIInterfaceOrientationPortraitUpsideDown</string>
+    </array>
+    <key>UISupportedInterfaceOrientations~ipad</key>
+    <array>
+      <string>UIInterfaceOrientationPortrait</string>
+      <string>UIInterfaceOrientationPortraitUpsideDown</string>
+      <string>UIInterfaceOrientationLandscapeLeft</string>
+      <string>UIInterfaceOrientationLandscapeRight</string>
+    </array>
+    <key>CFBundleDocumentTypes</key>
+    <array>
+      <dict>
+        <key>CFBundleTypeName</key>
+        <string>Zip archive</string>
+        <key>CFBundleTypeRole</key>
+        <string>Editor</string>
+        <key>LSItemContentTypes</key>
+        <array>
+          <string>com.pkware.zip-archive</string>
+        </array>
+        <key>UTTypeTagSpecification</key>
+        <dict>
+          <key>public.filename-extension</key>
+          <string>zip</string>
+          <key>public.mime-type</key>
+          <string>application/zip</string>
+        </dict>
+        <key>LSHandlerRank</key>
+        <string>Owner</string>
+      </dict>
+    </array>
+  </dict>
+</plist>
diff --git a/contrib/mobile/iOS/Onelab/Onelab-Prefix.pch b/contrib/mobile/iOS/Onelab/Onelab-Prefix.pch
new file mode 100644
index 0000000000000000000000000000000000000000..a2693277fbebc1b3c896fb96fa3836b7e1f0cc53
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Onelab-Prefix.pch
@@ -0,0 +1,14 @@
+//
+// Prefix header for all source files of the 'Onelab' target in the 'Onelab' project
+//
+
+#import <Availability.h>
+
+#ifndef __IPHONE_5_0
+#warning "This project uses features only available in iOS SDK 5.0 and later."
+#endif
+
+#ifdef __OBJC__
+    #import <UIKit/UIKit.h>
+    #import <Foundation/Foundation.h>
+#endif
diff --git a/contrib/mobile/iOS/Onelab/OptionsViewController.h b/contrib/mobile/iOS/Onelab/OptionsViewController.h
new file mode 100644
index 0000000000000000000000000000000000000000..9f0efc913ad69927d81786430befa6deed597504
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/OptionsViewController.h
@@ -0,0 +1,8 @@
+#import <UIKit/UIKit.h>
+
+@interface OptionsViewController : UITableViewController <UITextFieldDelegate>
+{
+@private
+    UISegmentedControl *control;
+}
+@end
diff --git a/contrib/mobile/iOS/Onelab/OptionsViewController.mm b/contrib/mobile/iOS/Onelab/OptionsViewController.mm
new file mode 100644
index 0000000000000000000000000000000000000000..bf864526fcbe484883b195d44a3983b09a93a375
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/OptionsViewController.mm
@@ -0,0 +1,272 @@
+#import "OptionsViewController.h"
+#import "ParametersViewController.h"
+#import "PostProcessingViewController.h"
+#import "ModelViewController.h"
+#import "Utils.h"
+
+#include <gmsh/Context.h>
+#include <gmsh/PView.h>
+#include <gmsh/PViewData.h>
+#include <gmsh/PViewOptions.h>
+#include <gmsh/GmshDefines.h>
+
+@interface OptionsViewController ()
+
+@end
+
+@implementation OptionsViewController
+
+- (id)initWithStyle:(UITableViewStyle)style
+{
+  self = [super initWithStyle:style];
+  if (self) {
+    // Custom initialization
+  }
+  return self;
+}
+
+- (void)viewDidLoad
+{
+  [super viewDidLoad];
+
+  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshOptions:)
+                                               name:@"refreshParameters" object:nil];
+  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshOptions:)
+                                               name:@"resetParameters" object:nil];
+
+  self.navigationItem.title = @"Display";
+
+  [self.navigationController setToolbarHidden:NO];
+  control = [[UISegmentedControl alloc] initWithItems:
+                     [[NSArray alloc] initWithObjects:@"Model", @"Display", nil]];
+  UIBarButtonItem *controlBtn = [[UIBarButtonItem alloc] initWithCustomView:control];
+  UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:
+                                              UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
+  self.toolbarItems = [[NSArray alloc] initWithObjects:flexibleSpace, controlBtn, flexibleSpace, nil];
+  [control addTarget:self action:@selector(indexDidChangeForSegmentedControl:)
+    forControlEvents:UIControlEventValueChanged];
+  if(![[UIDevice currentDevice].model isEqualToString:@"iPad"]
+     && ![[UIDevice currentDevice].model isEqualToString:@"iPad Simulator"])
+    self.navigationItem.leftBarButtonItem =
+      [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStylePlain
+                                      target:self action:@selector(backButtonPressed:)];
+  else
+    self.navigationItem.hidesBackButton = true;
+}
+- (void)viewWillAppear:(BOOL)animated
+{
+  control.selectedSegmentIndex = 1;
+  [super viewWillAppear:animated];
+}
+
+-(void)backButtonPressed:(id)sender
+{
+  for(UIViewController *v in [self.navigationController viewControllers])
+    if([v isKindOfClass:[ModelViewController class]])
+      [self.navigationController popToViewController:v animated:YES];
+}
+
+- (void)indexDidChangeForSegmentedControl:(id)sender
+{
+  [self.navigationController popViewControllerAnimated:YES];
+}
+
+- (void)didReceiveMemoryWarning
+{
+  [super didReceiveMemoryWarning];
+  // Dispose of any resources that can be recreated.
+}
+
+- (void)refreshOptions:(id)sender
+{
+  [self performSelectorOnMainThread:@selector(refreshOptions) withObject:nil waitUntilDone:NO];
+}
+
+- (void)refreshOptions
+{
+  [self.tableView reloadData];
+}
+
+#pragma mark - Table view data source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+  return 2;
+  // Section 0: Display options
+  // Section 1: Result options
+}
+
+-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
+{
+  switch (section) {
+  case 0:
+    return @"Display options";
+  case 1:
+    return @"Result options";
+  default:
+    return @"Other options";
+  }
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+  switch (section) {
+  case 0:
+    return 4;
+  case 1:
+    return PView::list.size();
+  default:
+    return 0;
+  }
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+  UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
+  if(cell == nil){
+    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
+                                  reuseIdentifier:@"postproCell"];
+  }
+  else {
+    cell = nil;
+    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
+                                  reuseIdentifier:@"postproCell"];
+  }
+  [cell setFrame:CGRectMake(cell.frame.origin.x, cell.frame.origin.x,
+                            tableView.frame.size.width, cell.frame.size.height)];
+  switch (indexPath.section) {
+  case 0:
+    {
+      [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
+      UISwitch *showHideOptions = [[UISwitch alloc] initWithFrame: CGRectMake(15, 6.5, 100, 30)];
+      UILabel *lblOptions =
+        [[UILabel alloc] initWithFrame:CGRectMake
+                         (25 + (showHideOptions.frame.size.width), 8,
+                          (tableView.frame.size.width - (showHideOptions.frame.size.width) - 50), 30)];
+      if(indexPath.row == 0) {
+        [lblOptions setText:@"Show geometry points"];
+        [showHideOptions setOn:(CTX::instance()->geom.points)];
+        [showHideOptions addTarget:self action:@selector(setShowGeomPoints:)
+                  forControlEvents:UIControlEventValueChanged];
+      }
+      else if(indexPath.row == 1) {
+        [lblOptions setText:@"Show geometry lines"];
+        [showHideOptions setOn:(CTX::instance()->geom.curves)];
+        [showHideOptions addTarget:self action:@selector(setShowGeomLines:)
+                  forControlEvents:UIControlEventValueChanged];
+      }
+      else if(indexPath.row == 2) {
+        [lblOptions setText:@"Show mesh surface edges"];
+        [showHideOptions setOn:(CTX::instance()->mesh.surfacesEdges)];
+        [showHideOptions addTarget:self action:@selector(setShowMeshSurfacesEdges:)
+                  forControlEvents:UIControlEventValueChanged];
+      }
+      else if(indexPath.row == 3) {
+        [lblOptions setText:@"Show mesh volumes edges"];
+        [showHideOptions setOn:CTX::instance()->mesh.volumesEdges];
+        [showHideOptions addTarget:self action:@selector(setShowMeshVolumesEdges:)
+                  forControlEvents:UIControlEventValueChanged];
+      }
+      [cell addSubview:showHideOptions];
+      [cell addSubview:lblOptions];
+    }
+    break;
+  case 1:
+    {
+      NSArray *rows = [tableView indexPathsForVisibleRows];
+      for(NSIndexPath *mIndexpath in rows)
+        if(![mIndexpath isEqual:indexPath]){
+          UITableViewCell *tmp = [tableView cellForRowAtIndexPath:indexPath];
+          for(UIView *tmpv in tmp.subviews)for(UIView *v in tmpv.subviews)
+                                             if([v isKindOfClass:[UISwitch class]])
+                                               [(UISwitch *)v setOn:PView::list[v.tag]->getOptions()->visible];
+        }
+
+      [cell setSelectionStyle:UITableViewCellSelectionStyleGray];
+      int i = PView::list.size() - 1 - indexPath.row;
+      cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+      UISwitch *showHide = [[UISwitch alloc] initWithFrame: CGRectMake(15, 6.5, 100, 30)];
+      UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake
+                                      (25 + (showHide.frame.size.width), 8,
+                                       (tableView.frame.size.width - showHide.frame.size.width - 50), 30)];
+      [showHide setOn:(PView::list[i]->getOptions()->visible == 1)];
+      [showHide setTag:i];
+      [showHide addTarget:self action:@selector(PViewVisible:) forControlEvents:UIControlEventValueChanged];
+      [lbl setBackgroundColor: [UIColor clearColor]];
+      [lbl setText:[Utils getStringFromCString:PView::list[i]->getData()->getName().c_str()]];
+      [cell addSubview:showHide];
+      [cell addSubview:lbl];
+    }
+    break;
+  }
+  return cell;
+}
+
+-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+  if(indexPath.section != 1) return;
+  UIStoryboard *storyboard;
+  if([[UIDevice currentDevice].model isEqualToString:@"iPad"] ||
+     [[UIDevice currentDevice].model isEqualToString:@"iPad Simulator"])
+    storyboard = [UIStoryboard storyboardWithName:@"iPadStoryboard" bundle:nil];
+  else
+    storyboard = [UIStoryboard storyboardWithName:@"iPhoneiPodStoryboard" bundle:nil];
+  PostProcessingViewController *postPro =
+    [storyboard instantiateViewControllerWithIdentifier:@"PostProcessingViewController"];
+  [postPro setPView:PView::list[[tableView numberOfRowsInSection:1]-1- indexPath.row]];
+  [self.navigationController pushViewController:postPro animated:YES];
+}
+
+- (void)setShowGeomPoints:(UISwitch*)sender
+{
+  CTX::instance()->geom.points = sender.on;
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+- (void)setShowGeomLines:(UISwitch*)sender
+{
+  CTX::instance()->geom.curves = sender.on;
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+- (void)setShowMeshVolumesEdges:(UISwitch*)sender
+{
+  CTX::instance()->mesh.volumesEdges = sender.on;
+  CTX::instance()->mesh.changed = ENT_VOLUME;
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+- (void)setShowMeshSurfacesEdges:(UISwitch*)sender
+{
+  CTX::instance()->mesh.surfacesEdges = sender.on;
+  CTX::instance()->mesh.changed = ENT_SURFACE;
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+-(IBAction)PViewVisible:(id)sender
+{
+  PView::list[((UISwitch*)sender).tag]->getOptions()->visible = (((UISwitch*)sender).on)? 1 : 0;
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+#pragma mark - textfield
+
+-(BOOL)textFieldShouldEndEditing:(UITextField *)textField
+{
+  int val = (int)[textField.text integerValue];
+  val = (val > 0)? val : 1;
+  val = (val < 1000)? val : 1000;
+  [textField setText:[NSString stringWithFormat:@"%d",val]];
+  PView::list[textField.tag]->getOptions()->nbIso = val;
+  PView::list[textField.tag]->setChanged(true);
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+  return YES;
+}
+
+-(BOOL)textFieldShouldReturn:(UITextField *)textField
+{
+  [textField endEditing:YES];
+  return YES;
+}
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/Parameter.h b/contrib/mobile/iOS/Onelab/Parameter.h
new file mode 100644
index 0000000000000000000000000000000000000000..5b0af90ec12b7868ad14e8ce7d9dd357cb78557d
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Parameter.h
@@ -0,0 +1,73 @@
+#import <Foundation/Foundation.h>
+#import <Gmsh/onelab.h>
+
+#import "drawContext.h"
+
+@interface Parameter : NSObject
+{
+@protected
+  NSString *name;
+  UILabel *label;
+}
+-(id)init;
+-(void)setFrame:(CGRect)frame;
+-(void)setLabelFrame:(CGRect)frame;
+-(NSString *)getName;
+-(UILabel *)getLabel;
++(double)getHeight;
+-(void)refresh;
+@end
+
+@interface ParameterStringList : Parameter <UIActionSheetDelegate>
+{
+@protected
+  UIButton *button;
+}
+-(id)initWithString:(onelab::string)string;
+-(UIPickerView *)getUIView;
+@end
+
+@interface ParameterNumberList : Parameter <UIActionSheetDelegate>
+{
+@protected
+  UIButton *button;
+}
+-(id)initWithNumber:(onelab::number)number;
+-(UITextField *)getUIView;
+@end
+
+@interface ParameterNumberCheckbox : Parameter
+{
+@protected
+  UISwitch *checkbox;
+}
+-(id)initWithNumber:(onelab::number)number;
+-(UISwitch *)getCheckbox;
+@end
+
+@interface ParameterNumberStepper : Parameter
+{
+@protected
+  UIStepper *stepper;
+}
+-(id)initWithNumber:(onelab::number)number;
+-(UIStepper *)getStepper;
+@end
+
+@interface ParameterNumberRange : Parameter
+{
+@protected
+  UISlider *slider;
+}
+-(id)initWithNumber:(onelab::number)number;
+-(UISlider *)getSlider;
+@end
+
+@interface ParameterNumberTextbox : Parameter <UITextFieldDelegate>
+{
+@protected
+  UITextField *textbox;
+}
+-(id)initWithNumber:(onelab::number)number;
+-(UITextField *)getTextbox;
+@end
diff --git a/contrib/mobile/iOS/Onelab/Parameter.mm b/contrib/mobile/iOS/Onelab/Parameter.mm
new file mode 100644
index 0000000000000000000000000000000000000000..03e92d9d475b94a9be0db270cfd08407ebb422d5
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Parameter.mm
@@ -0,0 +1,464 @@
+#import "Parameter.h"
+#import "Utils.h"
+
+@implementation Parameter
+-(id)init
+{
+  self = [super init];
+  if(self) {
+    label = [[UILabel alloc] init];
+    [label setBackgroundColor:[UIColor clearColor]];
+  }
+  return self;
+}
+
+-(void)refresh
+{
+  return;
+}
+
+-(NSString *)getName
+{
+  return name;
+}
+
+-(UILabel *)getLabel
+{
+  return label;
+}
+
+-(void)setFrame:(CGRect)frame
+{
+  return;
+}
+
+-(void)setLabelFrame:(CGRect)frame
+{
+  [label setFrame:frame];
+}
+
+-(void)editValue
+{
+  if(onelab_cb("check") > 0){
+    [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+    [[NSNotificationCenter defaultCenter] postNotificationName:@"refreshParameters" object:nil];
+  }
+}
+
++(double)getHeight
+{
+  return 60.0f;
+}
+@end
+
+@implementation ParameterStringList
+-(id) initWithString:(onelab::string)string
+{
+  self = [super init];
+  if(self){
+    [label setText:[Utils getStringFromCString:string.getShortName().c_str()]];
+    name = [Utils getStringFromCString:string.getName().c_str()];
+    button = [UIButton buttonWithType:UIButtonTypeSystem];
+    [button addTarget:self action:@selector(selectValue) forControlEvents:UIControlEventTouchDown];
+    [button setTitle:[Utils getStringFromCString:string.getValue().c_str()] forState:UIControlStateNormal];
+    [button setEnabled:(string.getReadOnly() ? FALSE : TRUE)];
+    [label setEnabled:(string.getReadOnly() ? FALSE : TRUE)];
+  }
+  return self;
+}
+
+-(void)selectValue
+{
+  std::vector<onelab::string> string;
+  onelab::server::instance()->get(string, [name UTF8String]);
+  if(string.size() < 1) return;
+  UIAlertController *alertController =
+    [UIAlertController alertControllerWithTitle:nil message:nil
+                                 preferredStyle:UIAlertControllerStyleActionSheet];
+  [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel"
+                                                      style:UIAlertActionStyleCancel
+                                                    handler:^(UIAlertAction *action) {
+      }]];
+  std::vector<std::string> choices = string[0].getChoices();
+  for(unsigned int i = 0; i < choices.size(); i++){
+    NSString *t = [Utils getStringFromCString:choices[i].c_str()];
+    [alertController addAction:[UIAlertAction actionWithTitle:t
+                                                        style:UIAlertActionStyleDefault
+                                                      handler:^(UIAlertAction *action) {
+          [self updateString:string[0] withValue:choices[i]];
+          [button setTitle:[Utils getStringFromCString:choices[i].c_str()]
+                  forState:UIControlStateNormal];
+        }]];
+  }
+  [alertController setModalPresentationStyle:UIModalPresentationPopover];
+  UIPopoverPresentationController *popPresenter = [alertController popoverPresentationController];
+  popPresenter.sourceView = button;
+  popPresenter.sourceRect = button.bounds;
+  // FIXME: is traverseResponderChainForUIViewController a good idea?
+  [[Utils traverseResponderChainForUIViewController:button] presentViewController:alertController
+                                                                         animated:YES completion:nil];
+}
+
+-(void) updateString: (onelab::string)s withValue:(std::string)v
+{
+	s.setValue(v);
+	onelab::server::instance()->set(s);
+	[super editValue];
+}
+
+-(void)refresh
+{
+  std::vector<onelab::string> string;
+  onelab::server::instance()->get(string, [name UTF8String]);
+  if(string.size() < 1) return;
+  [button setTitle:[Utils getStringFromCString:string[0].getValue().c_str()] forState:UIControlStateNormal];
+  [button setEnabled:(string[0].getReadOnly() ? FALSE : TRUE)];
+  [label setEnabled:(string[0].getReadOnly() ? FALSE : TRUE)];
+}
+
+-(void)setFrame:(CGRect)frame
+{
+  [button setFrame:frame];
+}
+
+-(UIButton *)getUIView
+{
+  return button;
+}
+
++(double)getHeight
+{
+  return 60.f;
+}
+@end
+
+@implementation ParameterNumberList
+-(id) initWithNumber:(onelab::number) number
+{
+  self = [super init];
+  if(self) {
+    [label setText:[Utils getStringFromCString:number.getShortName().c_str()]];
+    name = [Utils getStringFromCString:number.getName().c_str()];
+    button = [UIButton buttonWithType:UIButtonTypeSystem];
+    [button addTarget:self action:@selector(selectValue) forControlEvents:UIControlEventTouchDown];
+    [button setTitle:[Utils getStringFromCString:number.getValueLabel(number.getValue()).c_str()]
+            forState:UIControlStateNormal];
+    [button setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+    [label setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+  }
+  return self;
+}
+
+-(void)selectValue
+{
+  std::vector<onelab::number> numbers;
+  onelab::server::instance()->get(numbers, [name UTF8String]);
+  if(numbers.size() < 1) return;
+  UIAlertController *alertController =
+    [UIAlertController alertControllerWithTitle:nil message:nil
+                                 preferredStyle:UIAlertControllerStyleActionSheet];
+  [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel"
+                                                      style:UIAlertActionStyleCancel
+                                                    handler:^(UIAlertAction *action) {
+      }]];
+  std::vector<double> choices = numbers[0].getChoices();
+  for(unsigned int i = 0; i < choices.size(); i++){
+    NSString *t = [Utils getStringFromCString:numbers[0].getValueLabel(choices[i]).c_str()];
+    [alertController addAction:[UIAlertAction actionWithTitle:t
+                                                        style:UIAlertActionStyleDefault
+                                                      handler:^(UIAlertAction *action) {
+          [self updateNumber:numbers[0] withValue:choices[i]];
+          [button setTitle:[Utils getStringFromCString:numbers[0].getValueLabel(i).c_str()]
+                  forState:UIControlStateNormal];
+        }]];
+  }
+  [alertController setModalPresentationStyle:UIModalPresentationPopover];
+  UIPopoverPresentationController *popPresenter = [alertController popoverPresentationController];
+  popPresenter.sourceView = button;
+  popPresenter.sourceRect = button.bounds;
+  // FIXME: is traverseResponderChainForUIViewController a good idea?
+  [[Utils traverseResponderChainForUIViewController:button] presentViewController:alertController
+                                                                         animated:YES completion:nil];
+}
+
+-(void) updateNumber: (onelab::number)n withValue:(double)v
+{
+  n.setValue(v);
+  onelab::server::instance()->set(n);
+  [super editValue];
+}
+
+-(void)refresh
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() < 1) return;
+  [button setTitle:[Utils getStringFromCString:number[0].getValueLabel(number[0].getValue()).c_str()]
+          forState:UIControlStateNormal];
+  [button setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+  [label setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+}
+
+-(void)setFrame:(CGRect)frame
+{
+  [button setFrame:frame];
+}
+
+-(UIButton *)getUIView
+{
+  return button;
+}
+
++(double)getHeight
+{
+  return 60.f;
+}
+@end
+
+@implementation ParameterNumberCheckbox
+-(id) initWithNumber:(onelab::number) number
+{
+  self = [super init];
+  if(self) {
+    [label setText:[Utils getStringFromCString:number.getShortName().c_str()]];
+    name = [Utils getStringFromCString:number.getName().c_str()];
+    checkbox = [[UISwitch alloc] init];
+    [checkbox setOn:(number.getValue() == 1)];
+    [checkbox addTarget:self action:@selector(valueChange:) forControlEvents:UIControlEventValueChanged];
+    [checkbox setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+    [label setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+  }
+  return self;
+}
+
+-(void)refresh
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() < 1) return;
+  [checkbox setSelected:(number[0].getValue() == 1)];
+  [checkbox setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+  [label setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+}
+
+-(void) valueChange:(UISwitch *)sender
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() < 1) return;
+  number[0].setValue(([sender isOn])? 1 : 0);
+  onelab::server::instance()->set(number[0]);
+  [super editValue];
+}
+
+-(void)setFrame:(CGRect)frame
+{
+  [checkbox setFrame:frame];
+}
+
+-(UISwitch *)getCheckbox
+{
+  return checkbox;
+}
+
++(double)getHeight
+{
+  return 40.0f;
+}
+@end
+
+@implementation ParameterNumberStepper
+-(id) initWithNumber:(onelab::number) number
+{
+  self = [super init];
+  if(self) {
+    name = [Utils getStringFromCString:number.getName().c_str()];
+    stepper = [[UIStepper alloc] init];
+    [stepper setValue:number.getValue()];
+    [stepper setStepValue:1];
+    [stepper setMaximumValue:number.getMax()];
+    [stepper setMinimumValue:number.getMin()];
+    [stepper addTarget:self action:@selector(stepperValueChanged:) forControlEvents:UIControlEventValueChanged];
+    [label setText:[NSString stringWithFormat:@"%@ %d", [Utils getStringFromCString:number.getShortName().c_str()],
+                             (int)number.getValue()]];
+    [stepper setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+    [label setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+  }
+  return self;
+}
+
+-(void)stepperValueChanged:(UIStepper *)sender
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() < 1) return;
+  number[0].setValue(sender.value);
+  onelab::server::instance()->set(number[0]);
+  [label setText:[NSString stringWithFormat:@"%@ %d", [Utils getStringFromCString:number[0].getShortName().c_str()],
+                           (int)number[0].getValue()]];
+  [super editValue];
+}
+
+-(void)refresh
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() < 1) return;
+  [stepper setValue:number[0].getValue()];
+  [label setText:[NSString stringWithFormat:@"%@ %d", [Utils getStringFromCString:number[0].getShortName().c_str()],
+                           (int)number[0].getValue()]];
+  [stepper setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+  [label setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+}
+
+-(void)setFrame:(CGRect)frame
+{
+  [stepper setFrame:frame];
+}
+
+-(UIStepper *)getStepper
+{
+  return stepper;
+}
+
++(double)getHeight
+{
+  return 60.0f;
+}
+@end
+
+@implementation ParameterNumberRange
+-(id) initWithNumber:(onelab::number) number
+{
+  self = [super init];
+  if(self) {
+    name = [Utils getStringFromCString:number.getName().c_str()];
+    slider = [[UISlider alloc] init];
+    [slider setMaximumValue:number.getMax()];
+    [slider setMinimumValue:number.getMin()];
+    [slider setValue:number.getValue()];
+    //TODO add step ?
+    [slider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventTouchUpOutside];
+    [slider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventTouchUpInside];
+    [label setText:[NSString stringWithFormat:@"%@ %g", [Utils getStringFromCString:number.getShortName().c_str()],
+                             number.getValue()]];
+    [slider setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+    [label setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+  }
+  return self;
+}
+
+-(void)refresh
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() < 1) return;
+  [slider setMaximumValue:number[0].getMax()];
+  [slider setMinimumValue:number[0].getMin()];
+  [slider setValue:number[0].getValue()];
+  [label setText:[NSString stringWithFormat:@"%@ %g", [Utils getStringFromCString:number[0].getShortName().c_str()],
+                           number[0].getValue()]];
+  [slider setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+  [label setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+}
+
+-(void)sliderValueChanged:(UISlider *)sender
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() < 1) return;
+  number[0].setValue(sender.value);
+  onelab::server::instance()->set(number[0]);
+  [label setText:[NSString stringWithFormat:@"%@ %g", [Utils getStringFromCString:number[0].getShortName().c_str()],
+                           number[0].getValue()]];
+  [super editValue];
+}
+
+-(void)setFrame:(CGRect)frame
+{
+  [slider setFrame:frame];
+}
+
+-(UISlider *)getSlider
+{
+  return slider;
+}
+
++(double)getHeight
+{
+  return 65.0f;
+}
+@end
+
+@implementation ParameterNumberTextbox
+-(id)initWithNumber:(onelab::number)number
+{
+  self = [super init];
+  if(self) {
+    [label setText:[Utils getStringFromCString:number.getShortName().c_str()]];
+    name = [Utils getStringFromCString:number.getName().c_str()];
+    textbox = [[UITextField alloc] init];
+    [textbox setBorderStyle:UITextBorderStyleRoundedRect];
+    [textbox setText:[NSString stringWithFormat:@"%g", number.getValue()]];
+    [textbox setDelegate:self];
+    UIToolbar* numberToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 50)];
+    numberToolbar.items = [NSArray arrayWithObjects:
+                                     [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
+                                   [[UIBarButtonItem alloc]initWithTitle:@"Apply" style:UIBarButtonItemStyleDone target:self action:@selector(doneWithNumberPad)],
+                                   nil];
+    [numberToolbar sizeToFit];
+    textbox.inputAccessoryView = numberToolbar;
+    [textbox setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+    [label setEnabled:(number.getReadOnly() ? FALSE : TRUE)];
+  }
+  return self;
+}
+
+-(void)refresh
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() < 1) return;
+  [textbox setText:[NSString stringWithFormat:@"%g", number[0].getValue()]];
+  [textbox setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+  [label setEnabled:(number[0].getReadOnly() ? FALSE : TRUE)];
+  [textbox reloadInputViews];
+}
+
+-(BOOL)textFieldShouldEndEditing:(UITextField *)textField
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() < 1) return YES;
+  number[0].setValue([textField.text doubleValue]);
+  onelab::server::instance()->set(number[0]);
+  [textField setText:[NSString stringWithFormat:@"%g", number[0].getValue()]];
+  [super editValue];
+  return YES;
+}
+
+-(BOOL)textFieldShouldReturn:(UITextField *)textField
+{
+  return [textField endEditing:YES];
+}
+
+-(void)doneWithNumberPad
+{
+  [textbox endEditing:YES];
+}
+
+-(void)setFrame:(CGRect)frame
+{
+  [textbox setFrame:frame];
+}
+
+-(UITextField *)getTextbox
+{
+  return textbox;
+}
+
++(double)getHeight
+{
+  return 60.f;
+}
+@end
diff --git a/contrib/mobile/iOS/Onelab/ParametersViewController.h b/contrib/mobile/iOS/Onelab/ParametersViewController.h
new file mode 100644
index 0000000000000000000000000000000000000000..e6b378eea2ee6730da46136b5eed5047d1fdc828
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/ParametersViewController.h
@@ -0,0 +1,21 @@
+#import <UIKit/UIKit.h>
+#import <Gmsh/onelab.h>
+
+@class ModelListController;
+
+@interface ParametersViewController : UITableViewController
+{
+    @private
+    NSMutableArray *_sections;
+    NSMutableArray *_sectionstitle;
+    NSDate *_lastRefresh;
+    UIBarButtonItem *runButton;
+    UIBarButtonItem *stopButton;
+    UISegmentedControl *control;
+}
+
+@property (strong, nonatomic) ModelListController *modelListController;
+
+- (void)resetParameters:(id)sender;
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/ParametersViewController.mm b/contrib/mobile/iOS/Onelab/ParametersViewController.mm
new file mode 100644
index 0000000000000000000000000000000000000000..1ac3b917e415e5dec8d0d3c8d8cb3779f8a8f127
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/ParametersViewController.mm
@@ -0,0 +1,448 @@
+#import "ParametersViewController.h"
+#import "ModelViewController.h"
+#import "OptionsViewController.h"
+#import "AppDelegate.h"
+#import "Parameter.h"
+#import "Utils.h"
+
+@interface ParametersViewController () {
+
+}
+@end
+
+@implementation ParametersViewController
+
+- (void)awakeFromNib
+{
+  self.clearsSelectionOnViewWillAppear = NO;
+  [super awakeFromNib];
+}
+
+- (void)viewDidLoad
+{
+  [super viewDidLoad];
+
+  // Do any additional setup after loading the view, typically from a nib.
+  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshParameters:)
+                                               name:@"refreshParameters" object:nil];
+  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resetParameters:)
+                                               name:@"resetParameters" object:nil];
+
+  self.navigationItem.title = @"Model";
+
+  _sections = [[NSMutableArray alloc] init];
+  _sectionstitle = [[NSMutableArray alloc] init];
+
+  UILongPressGestureRecognizer *lpgr =
+    [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
+  lpgr.minimumPressDuration = 1.0;
+  [self.tableView addGestureRecognizer:lpgr];
+
+  [self.navigationController setToolbarHidden:NO];
+  control = [[UISegmentedControl alloc] initWithItems:
+                     [[NSArray alloc] initWithObjects:@"Model", @"Display", nil]];
+  UIBarButtonItem *controlBtn = [[UIBarButtonItem alloc] initWithCustomView:control];
+  UIBarButtonItem *flexibleSpace =
+    [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
+                                                  target:nil action:nil];
+  self.toolbarItems = [[NSArray alloc] initWithObjects:flexibleSpace, controlBtn, flexibleSpace, nil];
+  [control addTarget:self action:@selector(indexDidChangeForSegmentedControl:)
+    forControlEvents:UIControlEventValueChanged];
+  if(![[UIDevice currentDevice].model isEqualToString:@"iPad"] &&
+     ![[UIDevice currentDevice].model isEqualToString:@"iPad Simulator"]){
+    self.navigationItem.leftBarButtonItem =
+      [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStylePlain
+                                      target:self action:@selector(backButtonPressed:)];
+  }
+  self.navigationItem.rightBarButtonItem =
+    [[UIBarButtonItem alloc] initWithTitle:@"Reset" style:UIBarButtonItemStylePlain
+                                    target:self action:@selector(resetParameters:)];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+  [self refreshParameters:nil];
+  control.selectedSegmentIndex = 0;
+  [super viewWillAppear:animated];
+}
+
+-(void)backButtonPressed:(id)sender
+{
+  [self.navigationController popViewControllerAnimated:YES];
+}
+
+-(void)handleLongPress:(UILongPressGestureRecognizer *)sender
+{
+  CGPoint p = [sender locationInView:self.tableView];
+  if(sender.state == UIGestureRecognizerStateCancelled) return;
+  NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
+  if(!indexPath) return;
+  NSMutableArray* section = [_sections objectAtIndex:indexPath.section];
+  if(!section) return;
+  Parameter * parameter = [section objectAtIndex:indexPath.row];
+  if(!parameter) return;
+  NSString *name = [parameter getName];
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number, [name UTF8String]);
+  if(number.size() && !number[0].getReadOnly()){
+    UIAlertController * alertController =
+      [UIAlertController alertControllerWithTitle:@"Edit parameter"
+                                          message:[Utils getStringFromCString:number[0].getShortName().c_str()]
+                                   preferredStyle:UIAlertControllerStyleAlert];
+    [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+        textField.text = name;
+        [textField setEnabled:FALSE];
+      }];
+    [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+        textField.text = [NSString stringWithFormat:@"%g", number[0].getValue()];
+    }];
+    [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
+                                                      handler:^(UIAlertAction *action) {
+          NSArray * textfields = alertController.textFields;
+          UITextField * namefield = textfields[0];
+          UITextField * valuefield = textfields[1];
+          std::vector<onelab::number> number;
+          onelab::server::instance()->get(number, [namefield.text UTF8String]);
+          if(number.size()){
+            double value = [valuefield.text doubleValue];
+            number[0].setValue(value);
+            onelab::server::instance()->set(number[0]);
+            if(onelab_cb("check") > 0){
+              [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+              [[NSNotificationCenter defaultCenter] postNotificationName:@"refreshParameters" object:nil];
+            }
+          }
+        }]];
+    [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault
+                                                      handler:^(UIAlertAction *action) { }]];
+    [self presentViewController:alertController animated:YES completion:nil];
+  }
+}
+
+- (void)indexDidChangeForSegmentedControl:(id)sender
+{
+  OptionsViewController *optionsViewController = [[OptionsViewController alloc] init];
+  [self.navigationController pushViewController:optionsViewController animated:YES];
+}
+
+- (void)addParameterNumber:(onelab::number)p inSection:(NSMutableArray*)section
+{
+  if(p.getChoices().size() && p.getChoices().size() == p.getValueLabels().size()) { // enumeration
+    ParameterNumberList *param = [[ParameterNumberList alloc] initWithNumber:p];
+    [section addObject:param];
+  }
+  else if(p.getChoices().size() == 2 && p.getChoices()[0] == 0 && p.getChoices()[1] == 1) { // check box
+    ParameterNumberCheckbox *param = [[ParameterNumberCheckbox alloc] initWithNumber:p];
+    [section addObject:param];
+  }
+  else if(p.getStep() == 1) { // stepper
+    ParameterNumberStepper *param = [[ParameterNumberStepper alloc] initWithNumber:p];
+    [section addObject:param];
+  }
+  else if(p.getMin() >= p.getMax() ||
+          p.getMin() == -onelab::number::maxNumber() ||
+          p.getMax() == onelab::number::maxNumber()) { // text box
+    ParameterNumberTextbox *param = [[ParameterNumberTextbox alloc] initWithNumber:p];
+    [section addObject:param];
+  }
+  else { // slider
+    ParameterNumberRange *param = [[ParameterNumberRange alloc] initWithNumber:p];
+    [section addObject:param];
+  }
+}
+
+- (void)addParameterString:(onelab::string)p inSection:(NSMutableArray*)section
+{
+  ParameterStringList *param = [[ParameterStringList alloc] initWithString:p];
+  [section addObject:param];
+}
+
+- (void)addSection:(NSMutableArray*)s withTitle:(NSString*)t withParameterNumber:(onelab::number)p
+{
+  [_sections addObject:s];
+  [_sectionstitle addObject:t];
+  [self addParameterNumber:p inSection:s];
+}
+
+- (void)addSection:(NSMutableArray*)s withTitle:(NSString*)t withParameterString:(onelab::string)p
+{
+  [_sections addObject:s];
+  [_sectionstitle addObject:t];
+  [self addParameterString:p inSection:s];
+}
+
+NSString *GetSectionTitle(NSString *name)
+{
+  NSString *s = @"";
+  NSArray *split = [name componentsSeparatedByString:@"/"];
+  for(int i = 0; i < [split count] - 1; i++){
+    NSString *name = [split objectAtIndex:i];
+    while([name length] && [name characterAtIndex:0] == ' ')
+      name = [name substringFromIndex:1];
+    while([name length] && ([name characterAtIndex:0] == '{' || [name characterAtIndex:0] == '}'))
+      name = [name substringFromIndex:1];
+    while([name length] && [name characterAtIndex:0] >= '0' && [name characterAtIndex:0] <= '9')
+      name = [name substringFromIndex:1];
+    if(i)
+      s = [s stringByAppendingString:@" > "];
+    s = [s stringByAppendingString:name];
+  }
+  return s;
+}
+
+NSInteger compareParameter(id p1, id p2, void *context)
+{
+  return [[p1 getName] compare:[p2 getName]];
+}
+
+- (void)refreshTableView
+{
+  std::vector<onelab::number> number;
+  onelab::server::instance()->get(number);
+
+  // check for new/updated parameters (number)
+  for(int i = 0; i < number.size(); i++) {
+    if(!number[i].getVisible()) continue; // do not add invisible parameter
+    if(number[i].getName() == "GetDP/}ModelCheck") continue; // don't show model checking
+
+    NSString *name = [Utils getStringFromCString:number[i].getName().c_str()];
+    NSString *sectiontitle = GetSectionTitle(name);
+    Boolean found = false;
+    for(int iSection = 0; iSection < [_sectionstitle count]; iSection++) {
+      if([sectiontitle isEqualToString:[_sectionstitle objectAtIndex:iSection]]) {
+        NSMutableArray *section = [_sections objectAtIndex:iSection];
+        for(int iparameter = 0; iparameter<[section count]; iparameter++) {
+          if([[[section objectAtIndex: iparameter] getName] isEqualToString:name]) {
+            // the parameter is in the section
+            Parameter * p = [section objectAtIndex: iparameter];
+            [p refresh]; // just refresh the parameter
+            found = true;
+            break;
+          }
+        }
+        if(!found){ // the parameter is not in the section, add it
+          [self addParameterNumber:number[i] inSection:section];
+        }
+        found = true;
+        break;
+      }
+    }
+    if(found) continue; // the parameter is in the tableView
+    // the section has to be created
+    NSMutableArray *newSection = [[NSMutableArray alloc] init];
+    [self addSection:newSection withTitle:sectiontitle withParameterNumber:number[i]];
+  }
+
+  std::vector<onelab::string> string;
+  onelab::server::instance()->get(string);
+
+  // check for new/updated parameters (string)
+  for(int i = 0; i < string.size(); i++) {
+    if(!string[i].getVisible() || string[i].getKind() == "file") continue;
+    NSString *name = [Utils getStringFromCString:string[i].getName().c_str()];
+    NSString *sectiontitle = GetSectionTitle(name);
+    Boolean found = false;
+    for(int iSection = 0; iSection < [_sectionstitle count]; iSection++) {
+      if([sectiontitle isEqualToString:[_sectionstitle objectAtIndex:iSection]]) {
+        NSMutableArray *section = [_sections objectAtIndex:iSection];
+        for(int iparameter = 0; iparameter<[section count]; iparameter++) {
+          if([[[section objectAtIndex: iparameter] getName] isEqualToString:name]) {
+            Parameter * p = [section objectAtIndex: iparameter];
+            [p refresh]; // just refresh the parameter
+            found = true;
+            break;
+          }
+        }
+        if(!found){ // the parameter is not in the section, add it
+          [self addParameterString:string[i] inSection:section];
+        }
+        found = true;
+        break;
+      }
+    }
+    if(found) continue; // the parameter is in the tableView
+    // the section has to be created
+    NSMutableArray *newSection = [[NSMutableArray alloc] init];
+    [self addSection:newSection withTitle:sectiontitle withParameterString:string[i]];
+  }
+
+  // check for hidden/deleted parameters
+  for(int iSection = [_sectionstitle count] - 1; iSection >= 0; iSection--) {
+    NSMutableArray *section = [_sections objectAtIndex:iSection];
+    for(int iparameter = [section count] - 1; iparameter >= 0; iparameter--) {
+      Parameter * p = [section objectAtIndex: iparameter];
+      std::vector<onelab::number> number;
+      onelab::server::instance()->get(number, [[p getName] UTF8String]);
+      std::vector<onelab::string> string;
+      onelab::server::instance()->get(string, [[p getName] UTF8String]);
+      if((number.size() < 1 && string.size() < 1) ||
+         (number.size() > 0 && !number[0].getVisible()) ||
+         (string.size() > 0 && !string[0].getVisible())){
+        [section removeObjectAtIndex:iparameter];
+      }
+    }
+  }
+
+  // sort parameters in each section
+  for(int iSection = 0; iSection < [_sections count]; iSection++) {
+    NSMutableArray *section = [_sections objectAtIndex:iSection];
+    [section sortUsingFunction:compareParameter context:nil];
+  }
+
+  [self.tableView reloadData];
+}
+
+- (void)viewDidUnload
+{
+  [super viewDidUnload];
+  // Release any retained subviews of the main view.
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+  return YES;
+}
+
+- (void)refreshParameters:(id)sender
+{
+  [self performSelectorOnMainThread:@selector(refreshParameters) withObject:nil waitUntilDone:NO];
+}
+
+- (void)refreshParameters
+{
+  if(!_lastRefresh){
+    _lastRefresh = [NSDate date];
+  }
+  else {
+    if([_lastRefresh timeIntervalSinceNow] >= -0.1) return;
+    _lastRefresh = [NSDate date];
+  }
+  [self refreshTableView]; // Get the param
+}
+
+- (void)resetParameters:(id)sender
+{
+  if(((AppDelegate *)[UIApplication sharedApplication].delegate)->compute) {
+    UIAlertController *alert =
+      [UIAlertController
+        alertControllerWithTitle:@"Cannot reset model parameters"
+                         message:@"Computation has to complete before parameters can be reset"
+                  preferredStyle:UIAlertControllerStyleAlert];
+    UIAlertAction *dismissButton =
+      [UIAlertAction actionWithTitle:@"Dismiss"
+                               style:UIAlertActionStyleDefault
+                             handler:^(UIAlertAction * action) { }];
+    [alert addAction:dismissButton];
+    [self presentViewController:alert animated:YES completion:nil];
+    return;
+  }
+  onelab_cb("reset");
+  [_sections removeAllObjects];
+  [_sectionstitle removeAllObjects];
+  [self.tableView reloadData];
+  onelab_cb("check");
+  [self refreshTableView];
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+#pragma mark - Table View
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+  return [_sections count];
+}
+
+- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
+{
+  // Return NO if you do not want the specified item to be editable.
+  return YES;
+}
+
+-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+  if([_sections count] < section)
+    return 0;
+  NSMutableArray *mSection = [_sections objectAtIndex:section];
+  return [mSection count];
+}
+
+-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+  // get the param with his name
+  static NSString *CellIdentifier = @"parameterCell";
+  if(indexPath.section >= _sections.count)
+    return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
+                                  reuseIdentifier:CellIdentifier];
+  NSMutableArray *sectionContent = [_sections objectAtIndex:[indexPath section]];
+  if(indexPath.row >= sectionContent.count)
+    return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
+                                  reuseIdentifier:CellIdentifier];
+  Parameter *tmp = [sectionContent objectAtIndex:[indexPath row]];
+
+  UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
+  if(cell == nil)
+    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
+                                  reuseIdentifier:CellIdentifier];
+  else {
+    cell = nil;
+    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
+                                  reuseIdentifier:CellIdentifier];
+  }
+  [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
+  [tmp setLabelFrame:CGRectMake(20, 5, tableView.frame.size.width - 40,
+                                cell.frame.size.height/2)];
+  [cell addSubview:[tmp getLabel]];
+  if([tmp isKindOfClass:[ParameterStringList class]]) {
+    ParameterStringList *param = (ParameterStringList *)tmp;
+    [param setFrame:CGRectMake(20, 35, tableView.frame.size.width - 40,
+                               cell.frame.size.height/2)];
+    [cell addSubview:[param getUIView]];
+  }
+  else if([tmp isKindOfClass:[ParameterNumberList class]]) {
+    ParameterNumberList *param = (ParameterNumberList *)tmp;
+    [param setFrame:CGRectMake(20, 35, tableView.frame.size.width - 40,
+                               cell.frame.size.height/2)];
+    [cell addSubview:[param getUIView]];
+  }
+  else if([tmp isKindOfClass:[ParameterNumberCheckbox class]]) {
+    ParameterNumberCheckbox *param = (ParameterNumberCheckbox *)tmp;
+    [param setLabelFrame:CGRectMake(85, 10, tableView.frame.size.width - 95,
+                                    cell.frame.size.height/2)];
+    [param setFrame:CGRectMake(20, 5, tableView.frame.size.width - 40,
+                               cell.frame.size.height)];
+    [cell addSubview:[param getCheckbox]];
+  }
+  else if([tmp isKindOfClass:[ParameterNumberStepper class]]) {
+    ParameterNumberStepper *param = (ParameterNumberStepper *)tmp;
+    [param setFrame:CGRectMake(20, cell.frame.size.height/2 + 5,
+                               tableView.frame.size.width - 40, cell.frame.size.height/2)];
+    [cell addSubview:[param getStepper]];
+  }
+  else if([tmp isKindOfClass:[ParameterNumberRange class]]) {
+    ParameterNumberRange *param = (ParameterNumberRange *)tmp;
+    [param setFrame:CGRectMake(20, cell.frame.size.height/2 + 10,
+                               tableView.frame.size.width - 40, cell.frame.size.height/2)];
+    [cell addSubview:[param getSlider]];
+  }
+  else if([tmp isKindOfClass:[ParameterNumberTextbox class]]) {
+    ParameterNumberTextbox *param = (ParameterNumberTextbox *)tmp;
+    [param setFrame:CGRectMake(20, cell.frame.size.height/2 + 10,
+                               tableView.frame.size.width - 40, cell.frame.size.height/2)];
+    [cell addSubview:[param getTextbox]];
+  }
+
+  return cell;
+}
+
+-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+  Parameter *param = [[_sections objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
+  return [[param class] getHeight];
+}
+
+-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
+{
+  return [_sectionstitle objectAtIndex:section];
+}
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/PostProcessingViewController.h b/contrib/mobile/iOS/Onelab/PostProcessingViewController.h
new file mode 100644
index 0000000000000000000000000000000000000000..74a874028cd39ffec10d62fc47c5c5d40d46e197
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/PostProcessingViewController.h
@@ -0,0 +1,23 @@
+#import <UIKit/UIKit.h>
+
+#include <gmsh/PView.h>
+#include <gmsh/PViewData.h>
+#include <gmsh/PViewOptions.h>
+
+@interface PostProcessingViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate>
+{
+@private
+    PView *_pview;
+}
+
+@property (weak, nonatomic) IBOutlet UILabel *Name;
+@property (weak, nonatomic) IBOutlet UIPickerView *IntervalsType;
+@property (weak, nonatomic) IBOutlet UITextField *Intervals;
+@property (weak, nonatomic) IBOutlet UIStepper *IntervalsStepper;
+@property (weak, nonatomic) IBOutlet UISlider *RaiseZ;
+
+- (IBAction)stepperValueChanged:(UIStepper *)sender;
+
+- (void)setPView:(PView*)p;
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/PostProcessingViewController.mm b/contrib/mobile/iOS/Onelab/PostProcessingViewController.mm
new file mode 100644
index 0000000000000000000000000000000000000000..9ae897a74d7cc3e0f53bf3bb11b90fb54d5b9a52
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/PostProcessingViewController.mm
@@ -0,0 +1,140 @@
+#import "PostProcessingViewController.h"
+#import "Utils.h"
+
+#include <gmsh/Context.h>
+
+@interface PostProcessingViewController ()
+
+@end
+
+@implementation PostProcessingViewController
+
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
+{
+  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
+  if (self) {
+    // Custom initialization
+  }
+  return self;
+}
+
+- (void)setPView:(PView*)p
+{
+  _pview = p;
+}
+
+- (void)viewDidLoad
+{
+  [super viewDidLoad];
+  // Do any additional setup after loading the view.
+  if(_pview) {
+    [_Name setText:[Utils getStringFromCString:_pview->getData()->getName().c_str()]];
+    [_IntervalsType setDataSource:self];
+    [_IntervalsType setDelegate:self];
+    [_Intervals setText:[NSString stringWithFormat:@"%d",_pview->getOptions()->nbIso]];
+    UIToolbar* numberToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 50)];
+    numberToolbar.items =
+      [NSArray arrayWithObjects:
+                 [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
+                                                              target:nil action:nil],
+               [[UIBarButtonItem alloc]initWithTitle:@"Done" style:UIBarButtonItemStyleDone
+                                              target:self action:@selector(doneWithNumberPad)],
+               nil];
+    [numberToolbar sizeToFit];
+    _Intervals.delegate = self;
+    _Intervals.inputAccessoryView = numberToolbar;
+    [_RaiseZ setValue:_pview->getOptions()->raise[2]];
+
+    double maxval = std::max(fabs(_pview->getData()->getMin()), fabs(_pview->getData()->getMax()));
+    if(!maxval) maxval = 1.;
+    double val2 = 2. * CTX::instance()->lc / maxval;
+    [_RaiseZ setMinimumValue:-val2];
+    [_RaiseZ setMaximumValue:val2];
+    [_RaiseZ addTarget:self action:@selector(slideRaiseZ:) forControlEvents:UIControlEventValueChanged];
+    [_IntervalsStepper setStepValue:1];
+    [_IntervalsStepper setValue:_pview->getOptions()->nbIso];
+    [_IntervalsStepper setMaximumValue:1000];
+    [_IntervalsStepper setMinimumValue:1];
+  }
+}
+
+-(void)viewDidAppear:(BOOL)animated
+{
+  [_IntervalsType selectRow:_pview->getOptions()->intervalsType-1 inComponent:0 animated:YES];
+}
+
+- (IBAction)stepperValueChanged:(UIStepper *)sender
+{
+  [_Intervals setText:[NSString stringWithFormat:@"%.0f", [sender value]]];
+  _pview->getOptions()->nbIso = [sender value];
+  _pview->setChanged(true);
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
+{
+  return 1;
+}
+
+-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
+{
+  return 3;
+}
+
+-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
+{
+  NSArray *name = [[NSArray alloc] initWithObjects:@"Iso-values", @"Continuous map", @"Filled iso-values", nil];
+  return [name objectAtIndex:row];
+}
+
+-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
+{
+  _pview->getOptions()->intervalsType = 1+row;
+  _pview->setChanged(true);
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+- (void)slideRaiseZ:(UISlider*)sender
+{
+  _pview->getOptions()->raise[2] = sender.value;
+  _pview->setChanged(true);
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+}
+
+- (void)didReceiveMemoryWarning
+{
+  [super didReceiveMemoryWarning];
+  // Dispose of any resources that can be recreated.
+}
+
+-(BOOL)textFieldShouldEndEditing:(UITextField *)textField
+{
+  [_IntervalsStepper setValue:_pview->getOptions()->nbIso];
+  _pview->getOptions()->nbIso = [textField.text integerValue];
+  _pview->setChanged(true);
+  [[NSNotificationCenter defaultCenter] postNotificationName:@"requestRender" object:nil];
+  return YES;
+}
+
+-(BOOL)textFieldShouldReturn:(UITextField *)textField
+{
+  return [_Intervals endEditing:YES];
+}
+
+-(void)doneWithNumberPad
+{
+  [_Intervals endEditing:YES];
+}
+
+-(void)viewDidUnload
+{
+  [self setName:nil];
+  [self setRaiseZ:nil];
+  [self setIntervals:nil];
+  [self setIntervalsType:nil];
+  [self setName:nil];
+  [self setIntervalsStepper:nil];
+  [super viewDidUnload];
+}
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/SplitViewController.h b/contrib/mobile/iOS/Onelab/SplitViewController.h
new file mode 100644
index 0000000000000000000000000000000000000000..d882692bcb64b3476026498f80987899d87ef10d
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/SplitViewController.h
@@ -0,0 +1,13 @@
+#import <UIKit/UIKit.h>
+
+#import "ParametersViewController.h"
+#import "ModelViewController.h"
+
+@interface SplitViewController : UISplitViewController{
+  ModelViewController *modelViewController;
+  ParametersViewController *parametersViewController;
+}
+
+@property (nonatomic, retain) NSString *initialModel;
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/SplitViewController.mm b/contrib/mobile/iOS/Onelab/SplitViewController.mm
new file mode 100644
index 0000000000000000000000000000000000000000..a2bf7adbc658c05cf0fc44acd55f946c798360ec
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/SplitViewController.mm
@@ -0,0 +1,50 @@
+#import "SplitViewController.h"
+
+@interface SplitViewController ()
+
+@end
+
+@implementation SplitViewController
+
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
+{
+  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
+  if (self) {
+    // Custom initialization
+  }
+  return self;
+}
+
+- (void)viewDidLoad
+{
+  [super viewDidLoad];
+  // Do any additional setup after loading the view.
+  UINavigationController *right = [self.viewControllers objectAtIndex:1]; // right UINavigationController (Detail)
+  for(UIViewController *v in right.viewControllers){
+    if ([v isKindOfClass:[ModelViewController class]]) {
+      modelViewController = (ModelViewController *)v;
+    }
+  }
+  UINavigationController *left = [self.viewControllers objectAtIndex:0]; // left UINavigationController (Master)
+  for(UIViewController *v in left.viewControllers){
+    if ([v isKindOfClass:[ParametersViewController class]]) {
+      parametersViewController = (ParametersViewController *)v;
+    }
+  }
+  self.delegate = modelViewController;
+  [self setPresentsWithGesture:NO];
+}
+
+-(void)viewDidAppear:(BOOL)animated
+{
+  modelViewController.initialModel = self.initialModel;
+  [super viewDidAppear:animated];
+}
+
+- (void)didReceiveMemoryWarning
+{
+  [super didReceiveMemoryWarning];
+  // Dispose of any resources that can be recreated.
+}
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/Utils.h b/contrib/mobile/iOS/Onelab/Utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..965b8c06f18fcb8e52eeb4bbe8dbbef90429dc91
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Utils.h
@@ -0,0 +1,11 @@
+#import <Foundation/Foundation.h>
+
+@interface Utils : NSObject
+
++ (NSString *) getApplicationDocumentsDirectory;
++ (void) copyRes;
++ (void) openModelURL:(NSURL*)url;
++ (id) traverseResponderChainForUIViewController:(UIView *)v;
++ (NSString *) getStringFromCString:(const char*)s;
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/Utils.mm b/contrib/mobile/iOS/Onelab/Utils.mm
new file mode 100644
index 0000000000000000000000000000000000000000..24757f7ffa6cb35ef272debea3cd3b247a30f8a5
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/Utils.mm
@@ -0,0 +1,67 @@
+#import "Utils.h"
+
+#include <gmsh/OS.h>
+
+@implementation Utils
+
++ (NSString *) getApplicationDocumentsDirectory
+{
+  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+  NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
+  return basePath;
+}
+
++ (void) copyRes
+{
+  NSString *resPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"files"];
+  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+  NSString *docPath = [paths objectAtIndex:0]; //Get the docs directory
+  NSArray *resContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:resPath error:NULL];
+  for (NSString *obj in resContents){
+    NSLog(@"Replacing model %@", obj);
+    NSString *modelSrc = [[resPath stringByAppendingString:@"/"] stringByAppendingString:obj];
+    NSString *modelDst = [[docPath stringByAppendingString:@"/"] stringByAppendingString:obj];
+    [[NSFileManager defaultManager] removeItemAtPath:modelDst error:nil];
+    NSError *error = nil;
+    if (![[NSFileManager defaultManager] copyItemAtPath:modelSrc toPath:modelDst error:&error])
+      NSLog(@"Error: %@", error);
+    else if(![[NSURL fileURLWithPath:modelDst] setResourceValue: [NSNumber numberWithBool: YES] forKey: NSURLIsExcludedFromBackupKey error: &error])
+      NSLog(@"Error %@", error);
+  }
+}
+
++ (void) openModelURL:(NSURL*)url
+{
+  if(!url) return;
+  NSString *filepath = [url path];
+  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+  NSString *docPath = [paths objectAtIndex:0]; //Get the docs directory
+  NSString *extension = [filepath pathExtension];
+  if([extension isEqualToString:@"zip"] || [extension isEqualToString:@"ZIP"]){
+    NSLog(@"Unzipping %@", filepath);
+    UnzipFile([filepath UTF8String], [docPath UTF8String]);
+    NSLog(@"Removing %@", filepath);
+    [[NSFileManager defaultManager] removeItemAtPath:filepath error:nil];
+  }
+  else{
+    NSLog(@"Unknown model file extension: only .zip files are currently accepted");
+  }
+}
+
++ (UIViewController *) traverseResponderChainForUIViewController:(UIView *)v
+{
+  id nextResponder = [v nextResponder];
+  if ([nextResponder isKindOfClass:[UIViewController class]])
+    return nextResponder;
+  else if ([nextResponder isKindOfClass:[UIView class]])
+    return [Utils traverseResponderChainForUIViewController:nextResponder];
+  else
+    return nil;
+}
+
++ (NSString *) getStringFromCString:(const char*)s
+{
+  return [NSString stringWithCString:s encoding:NSUTF8StringEncoding];
+}
+
+@end
diff --git a/contrib/mobile/iOS/Onelab/emulatorFix.c b/contrib/mobile/iOS/Onelab/emulatorFix.c
new file mode 100644
index 0000000000000000000000000000000000000000..4841b5ede00fd667a889d14fd0b25f92da07173e
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/emulatorFix.c
@@ -0,0 +1,179 @@
+//
+//  file.c
+//  MobileGmsh
+//
+//  Created by Maxime Graulich on 21/02/13.
+//
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+
+int getrlimit$UNIX2003( int resource, struct rlimit *rlp)
+{
+    return getrlimit(resource, rlp);
+}
+
+FILE *fopen$UNIX2003(const char * filename, const char *mode)
+{
+    return fopen(filename, mode);
+}
+
+int fputs$UNIX2003(const char * filename, FILE *file)
+{
+  return fputs(filename, file);
+}
+
+int stat$INODE64(const char * filename, struct stat *buff)
+{
+    return stat(filename,buff);
+}
+
+DIR *opendir$INODE64$UNIX2003(const char *name)
+{
+    return opendir(name);
+}
+
+struct dirent *readdir$INODE64(DIR *dirp)
+{
+    return readdir(dirp);
+}
+
+int closedir$UNIX2003(DIR *dirp)
+{
+    return closedir(dirp);
+}
+
+int socketpair$UNIX2003(int domain, int type, int protocol, int socket_vector[2])
+{
+    return socketpair(domain, type, protocol, socket_vector);
+}
+
+int fcntl$UNIX2003(int fd, int cmd)
+{
+    return fcntl(fd, cmd);
+}
+
+size_t write$UNIX2003(int fildes, const void *buf, size_t nbytes)
+{
+    return write(fildes, buf, nbytes);
+}
+
+int read$UNIX2003(int handle, void *buffer, int nbyte)
+{
+    return (int)read(handle, buffer, nbyte);
+}
+
+int close$UNIX2003(int handle)
+{
+    return close(handle);
+}
+int pthread_cond_init$UNIX2003(pthread_cond_t *cond, const pthread_condattr_t *attr)
+{
+    return pthread_cond_init(cond, attr);
+}
+
+int usleep$UNIX2003(useconds_t usec)
+{
+    return usleep(usec);
+}
+
+int bind$UNIX2003(int sockfd, struct sockaddr *my_addr, socklen_t addrlen)
+{
+    return bind(sockfd, my_addr, addrlen);
+}
+
+int getsockname$UNIX2003( int socket, struct sockaddr *restrict adress, socklen_t *restrict adress_len)
+{
+    return getsockname(socket, adress, adress_len);
+}
+
+int listen$UNIX2003(int socket, int backlog)
+{
+    return listen(socket, backlog);
+}
+
+int connect$UNIX2003(int socket, const struct sockaddr *address, socklen_t adress_len)
+{
+    return connect(socket, address, adress_len);
+}
+
+int select$UNIX2003(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, struct timeval *restrict timeout)
+{
+    return select(nfds, readfds, readfds, writefds, timeout);
+}
+
+ssize_t recv$UNIX2003(int socket, void*buffer, size_t len, int flag)
+{
+    return recv(socket, buffer, len, flag);
+}
+
+int setrlimit$UNIX2003(int ressource, struct rlimit *rlim)
+{
+    return setrlimit(ressource, rlim);
+}
+size_t fwrite$UNIX2003(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+    return fwrite(ptr, size, nmemb, stream);
+}
+
+int fstat$INODE64(int fildes, struct stat *buf)
+{
+    return fstat(fildes, buf);
+}
+
+ssize_t send$UNIX2003(int socket, const void *buffer, size_t length, int flags)
+{
+    return send(socket, buffer, length, flags);
+}
+
+clock_t clock$UNIX2003()
+{
+    return clock();
+}
+
+int creat$UNIX2003(const char *path, mode_t mode)
+{
+  return creat(path, mode);
+}
+
+int nanosleep$UNIX2003(const struct timespec *rqtp, struct timespec *rmtp)
+{
+  return nanosleep(rqtp, rmtp);
+}
+
+FILE *popen$UNIX2003(const char *command, const char *mode)
+{
+  return popen(command, mode);
+}
+
+int setenv$UNIX2003(const char *name, const char *value, int overwrite)
+{
+  return setenv(name, value, overwrite);
+}
+
+unsigned int sleep$UNIX2003(unsigned int seconds)
+{
+  return sleep(seconds);
+}
+
+char *strerror$UNIX2003(int errnum)
+{
+  return strerror(errnum);
+}
+
+double strtod$UNIX2003(const char *restrict nptr, char **restrict endptr)
+{
+  return strtod(nptr, endptr);
+}
diff --git a/contrib/mobile/iOS/Onelab/iPadStoryboard.storyboard b/contrib/mobile/iOS/Onelab/iPadStoryboard.storyboard
new file mode 100644
index 0000000000000000000000000000000000000000..f29dd1a4f00d585511cabd556965a018c4593527
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/iPadStoryboard.storyboard
@@ -0,0 +1,376 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8191" systemVersion="15A282b" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="dwa-Pq-2vA">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
+    </dependencies>
+    <scenes>
+        <!--Navigation Controller-->
+        <scene sceneID="14">
+            <objects>
+                <navigationController toolbarHidden="NO" id="3" sceneMemberID="viewController">
+                    <simulatedNavigationBarMetrics key="simulatedTopBarMetrics"/>
+                    <simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
+                    <navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="6">
+                        <rect key="frame" x="0.0" y="-44" width="0.0" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                        <animations/>
+                    </navigationBar>
+                    <toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="YDU-Oo-Ie9">
+                        <rect key="frame" x="0.0" y="556" width="600" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                        <animations/>
+                    </toolbar>
+                    <connections>
+                        <segue destination="19" kind="relationship" relationship="rootViewController" id="25"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="13" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="142" y="-548"/>
+        </scene>
+        <!--ONELAB-->
+        <scene sceneID="16">
+            <objects>
+                <viewController storyboardIdentifier="ModelViewController" title="ONELAB" id="4" customClass="ModelViewController" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="NzN-7G-5eX"/>
+                        <viewControllerLayoutGuide type="bottom" id="EYA-0D-qDB"/>
+                    </layoutGuides>
+                    <view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="26" customClass="EAGLView">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HN2-fw-mfe">
+                                <rect key="frame" x="47" y="727" width="636" height="21"/>
+                                <animations/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <activityIndicatorView opaque="NO" contentMode="scaleToFill" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="BfX-qT-0mL">
+                                <rect key="frame" x="20" y="560" width="20" height="20"/>
+                                <animations/>
+                            </activityIndicatorView>
+                            <button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="right" contentVerticalAlignment="top" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GRU-nz-BNs">
+                                <rect key="frame" x="652" y="80" width="40" height="40"/>
+                                <animations/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="NTn-Hp-n22"/>
+                                    <constraint firstAttribute="width" constant="40" id="lvs-wL-1eu"/>
+                                </constraints>
+                                <state key="normal" image="icon_rotate.png">
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="startRotation:" destination="4" eventType="touchUpInside" id="Ufg-Lv-KVt"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <animations/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstAttribute="trailing" secondItem="GRU-nz-BNs" secondAttribute="trailing" constant="20" id="5SK-5e-5sv"/>
+                            <constraint firstItem="GRU-nz-BNs" firstAttribute="top" secondItem="NzN-7G-5eX" secondAttribute="bottom" constant="20" id="Brz-ez-tFA"/>
+                            <constraint firstItem="BfX-qT-0mL" firstAttribute="leading" secondItem="26" secondAttribute="leading" constant="20" id="O3e-fe-oVT"/>
+                            <constraint firstItem="HN2-fw-mfe" firstAttribute="leading" secondItem="BfX-qT-0mL" secondAttribute="trailing" constant="10" id="Q5d-FK-Rq5"/>
+                            <constraint firstItem="EYA-0D-qDB" firstAttribute="top" secondItem="HN2-fw-mfe" secondAttribute="bottom" constant="20" id="eo2-1G-DDU"/>
+                            <constraint firstItem="EYA-0D-qDB" firstAttribute="top" secondItem="BfX-qT-0mL" secondAttribute="bottom" constant="20" id="tlt-kB-Qwe"/>
+                        </constraints>
+                        <connections>
+                            <outletCollection property="gestureRecognizers" destination="Nb5-mS-uY7" appends="YES" id="aF3-Oi-e8B"/>
+                            <outletCollection property="gestureRecognizers" destination="Rg0-JN-2rY" appends="YES" id="Idn-LL-Eok"/>
+                            <outletCollection property="gestureRecognizers" destination="MRc-vF-047" appends="YES" id="BU4-EW-yFf"/>
+                        </connections>
+                    </view>
+                    <toolbarItems/>
+                    <navigationItem key="navigationItem" title="ONELAB" id="53"/>
+                    <connections>
+                        <outlet property="doubleTap" destination="MRc-vF-047" id="mdb-zu-B2d"/>
+                        <outlet property="glView" destination="26" id="i7d-I7-Zhz"/>
+                        <outlet property="progressIndicator" destination="BfX-qT-0mL" id="O9E-Bm-5zQ"/>
+                        <outlet property="progressLabel" destination="HN2-fw-mfe" id="s62-5y-B7S"/>
+                        <outlet property="singleTap" destination="Rg0-JN-2rY" id="eXQ-Gl-MxX"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="15" sceneMemberID="firstResponder"/>
+                <tapGestureRecognizer id="Rg0-JN-2rY">
+                    <connections>
+                        <action selector="singleTap:" destination="4" id="E0C-NP-r4x"/>
+                    </connections>
+                </tapGestureRecognizer>
+                <tapGestureRecognizer numberOfTapsRequired="2" id="MRc-vF-047">
+                    <connections>
+                        <action selector="doubleTap:" destination="4" id="ruZ-Qr-c1X"/>
+                    </connections>
+                </tapGestureRecognizer>
+                <pinchGestureRecognizer id="Nb5-mS-uY7">
+                    <connections>
+                        <action selector="pinch:" destination="4" id="4AO-rb-u23"/>
+                    </connections>
+                </pinchGestureRecognizer>
+            </objects>
+            <point key="canvasLocation" x="857" y="523"/>
+        </scene>
+        <!--Post-processing-->
+        <scene sceneID="i13-tZ-4on">
+            <objects>
+                <tableViewController storyboardIdentifier="PostProViewController" id="89M-FT-oxV" customClass="PostProViewController" sceneMemberID="viewController">
+                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="4WQ-dq-nwm">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <animations/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                        <prototypes>
+                            <tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="postProCell" id="rgB-ht-LVb">
+                                <rect key="frame" x="0.0" y="86" width="600" height="44"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="rgB-ht-LVb" id="4vv-Gp-Zf0">
+                                    <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                    <animations/>
+                                </tableViewCellContentView>
+                                <animations/>
+                            </tableViewCell>
+                        </prototypes>
+                        <connections>
+                            <outlet property="dataSource" destination="89M-FT-oxV" id="elT-4a-yV2"/>
+                            <outlet property="delegate" destination="89M-FT-oxV" id="arL-SY-mlu"/>
+                        </connections>
+                    </tableView>
+                    <navigationItem key="navigationItem" title="Post-processing" id="Zld-KM-OS3"/>
+                    <connections>
+                        <segue destination="59P-em-ZXH" kind="show" identifier="showPViewSegue" id="UaH-Jh-G3U"/>
+                    </connections>
+                </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="3Aw-Tm-Dq6" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="1621" y="-548"/>
+        </scene>
+        <!--Post Processing View Controller-->
+        <scene sceneID="inJ-Ob-bP0">
+            <objects>
+                <viewController storyboardIdentifier="PostProcessingViewController" id="59P-em-ZXH" customClass="PostProcessingViewController" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="rYq-Fd-aLL"/>
+                        <viewControllerLayoutGuide type="bottom" id="zl2-C9-6GO"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="bA2-5p-eQu">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                        <subviews>
+                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" text="PView Name" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zvL-DY-CD8">
+                                <rect key="frame" x="20" y="75" width="280" height="32"/>
+                                <animations/>
+                                <fontDescription key="fontDescription" type="system" pointSize="24"/>
+                                <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <pickerView contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KcQ-1V-heQ">
+                                <rect key="frame" x="0.0" y="95" width="320" height="216"/>
+                                <animations/>
+                            </pickerView>
+                            <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="21" borderStyle="roundedRect" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="NrM-45-dY4">
+                                <rect key="frame" x="111" y="318" width="65" height="30"/>
+                                <animations/>
+                                <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                <textInputTraits key="textInputTraits"/>
+                            </textField>
+                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" text="Intervals" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ol0-Ou-YBJ">
+                                <rect key="frame" x="0.0" y="322" width="89" height="21"/>
+                                <animations/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" text="Raise (Z)" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IVB-fp-jdZ">
+                                <rect key="frame" x="0.0" y="361" width="89" height="21"/>
+                                <animations/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <slider opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="-5" maxValue="5" translatesAutoresizingMaskIntoConstraints="NO" id="mjo-81-vMy">
+                                <rect key="frame" x="109" y="356" width="187" height="31"/>
+                                <animations/>
+                            </slider>
+                            <stepper opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="p1D-j1-egi">
+                                <rect key="frame" x="200" y="319" width="94" height="29"/>
+                                <animations/>
+                                <connections>
+                                    <action selector="stepperValueChanged:" destination="59P-em-ZXH" eventType="valueChanged" id="lia-6x-IaS"/>
+                                </connections>
+                            </stepper>
+                        </subviews>
+                        <animations/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                    <navigationItem key="navigationItem" id="Zf1-f8-ob0"/>
+                    <connections>
+                        <outlet property="Intervals" destination="NrM-45-dY4" id="xkE-Wc-sCj"/>
+                        <outlet property="IntervalsStepper" destination="p1D-j1-egi" id="iar-sb-lYV"/>
+                        <outlet property="IntervalsType" destination="KcQ-1V-heQ" id="RiS-30-aUS"/>
+                        <outlet property="Name" destination="zvL-DY-CD8" id="4Jw-ou-0yu"/>
+                        <outlet property="RaiseZ" destination="mjo-81-vMy" id="BJw-p9-bSq"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="URb-SK-Z2A" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="2350" y="-553"/>
+        </scene>
+        <!--Split View Controller-->
+        <scene sceneID="18">
+            <objects>
+                <splitViewController storyboardIdentifier="SplitViewController" id="5" customClass="SplitViewController" sceneMemberID="viewController">
+                    <toolbarItems/>
+                    <navigationItem key="navigationItem" id="Pnc-za-KDo"/>
+                    <simulatedStatusBarMetrics key="simulatedStatusBarMetrics"/>
+                    <simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
+                    <connections>
+                        <segue destination="3" kind="relationship" relationship="masterViewController" id="9"/>
+                        <segue destination="42" kind="relationship" relationship="detailViewController" id="51"/>
+                    </connections>
+                </splitViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="17" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-679" y="-548"/>
+        </scene>
+        <!--About View Controller-->
+        <scene sceneID="JzC-rN-mWe">
+            <objects>
+                <viewController id="VJs-Jj-MrK" customClass="AboutViewController" sceneMemberID="viewController">
+                    <webView key="view" contentMode="scaleToFill" id="5gX-R5-60b">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <animations/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
+                    </webView>
+                    <navigationItem key="navigationItem" id="Jgu-H8-Skt"/>
+                    <connections>
+                        <outlet property="aboutView" destination="5gX-R5-60b" id="C4Z-WU-Ydg"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="aMz-Ds-IMq" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-687" y="489"/>
+        </scene>
+        <!--Model list-->
+        <scene sceneID="af3-vl-hzw">
+            <objects>
+                <tableViewController id="dVI-Oo-Sq4" customClass="ModelListController" sceneMemberID="viewController">
+                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="OmH-vi-qhj">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <animations/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                        <prototypes>
+                            <tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="ModelsCell" id="dad-rm-zwY">
+                                <rect key="frame" x="0.0" y="86" width="600" height="44"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="dad-rm-zwY" id="smI-Gj-Wpi">
+                                    <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                    <animations/>
+                                </tableViewCellContentView>
+                                <animations/>
+                            </tableViewCell>
+                        </prototypes>
+                    </tableView>
+                    <navigationItem key="navigationItem" title="Model list" id="sdX-f6-lSz"/>
+                    <connections>
+                        <segue destination="VJs-Jj-MrK" kind="show" identifier="showAboutSegue" id="s46-1w-abC"/>
+                    </connections>
+                </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="zLU-RU-aDe" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-1493.75" y="-547.265625"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="Rhc-IE-crY">
+            <objects>
+                <navigationController definesPresentationContext="YES" id="dwa-Pq-2vA" sceneMemberID="viewController">
+                    <simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
+                    <nil key="simulatedBottomBarMetrics"/>
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" id="3bj-vr-d0C">
+                        <rect key="frame" x="0.0" y="-44" width="0.0" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                        <animations/>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="dVI-Oo-Sq4" kind="relationship" relationship="rootViewController" id="4oJ-sM-Hd3"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="4VW-NK-Xj1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-2259.375" y="-547.265625"/>
+        </scene>
+        <!--Master-->
+        <scene sceneID="24">
+            <objects>
+                <tableViewController title="Master" clearsSelectionOnViewWillAppear="NO" id="19" customClass="ParametersViewController" sceneMemberID="viewController">
+                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="20">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <animations/>
+                        <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
+                        <prototypes>
+                            <tableViewCell contentMode="scaleToFill" selectionStyle="gray" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="SettingsCell" textLabel="tIi-5l-S0i" style="IBUITableViewCellStyleDefault" id="ZSw-0O-9Pw">
+                                <rect key="frame" x="0.0" y="113.5" width="600" height="44"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ZSw-0O-9Pw" id="PXw-eV-JUQ">
+                                    <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                    <subviews>
+                                        <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tIi-5l-S0i">
+                                            <rect key="frame" x="15" y="0.0" width="570" height="43.5"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                            <animations/>
+                                            <fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
+                                            <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                            <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
+                                        </label>
+                                    </subviews>
+                                    <animations/>
+                                </tableViewCellContentView>
+                                <animations/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                            </tableViewCell>
+                        </prototypes>
+                        <sections/>
+                        <connections>
+                            <outlet property="dataSource" destination="19" id="22"/>
+                            <outlet property="delegate" destination="19" id="21"/>
+                        </connections>
+                    </tableView>
+                    <navigationItem key="navigationItem" title="Settings" id="40"/>
+                    <connections>
+                        <segue destination="89M-FT-oxV" kind="show" id="fTD-MC-twP"/>
+                    </connections>
+                </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="23" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="857" y="-548"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="50">
+            <objects>
+                <navigationController id="42" sceneMemberID="viewController">
+                    <simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
+                    <nil key="simulatedBottomBarMetrics"/>
+                    <navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="43">
+                        <rect key="frame" x="0.0" y="-44" width="0.0" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                        <animations/>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="4" kind="relationship" relationship="rootViewController" id="52"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="49" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="94" y="523"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="icon_rotate.png" width="512" height="512"/>
+    </resources>
+</document>
diff --git a/contrib/mobile/iOS/Onelab/iPhoneiPodStoryboard.storyboard b/contrib/mobile/iOS/Onelab/iPhoneiPodStoryboard.storyboard
new file mode 100644
index 0000000000000000000000000000000000000000..38ce6512219c3cc36106d2c04a8a8317e01cc6c7
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/iPhoneiPodStoryboard.storyboard
@@ -0,0 +1,337 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="vAG-uz-hfU">
+    <device id="retina5_9" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Models-->
+        <scene sceneID="Y4S-jQ-WHz">
+            <objects>
+                <tableViewController id="aNd-kg-MlN" customClass="ModelListController" sceneMemberID="viewController">
+                    <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="E1y-UA-Su2">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <prototypes>
+                            <tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="model" id="8lY-sK-IU2">
+                                <rect key="frame" x="0.0" y="22" width="375" height="44"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="8lY-sK-IU2" id="DGR-CE-eWT">
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                </tableViewCellContentView>
+                            </tableViewCell>
+                        </prototypes>
+                    </tableView>
+                    <navigationItem key="navigationItem" title="Models" id="l1y-cb-Mvd"/>
+                    <connections>
+                        <segue destination="NyB-7w-cP0" kind="show" identifier="showModelSegue" id="urr-vz-3XW"/>
+                        <segue destination="uZG-Bw-GZA" kind="show" identifier="showAboutSegue" id="Ctu-pA-E38"/>
+                    </connections>
+                </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="5qi-J8-Zn2" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-616.875" y="169.01408450704224"/>
+        </scene>
+        <!--ONELAB-->
+        <scene sceneID="ghQ-lt-1PF">
+            <objects>
+                <viewController storyboardIdentifier="ModelViewController" id="NyB-7w-cP0" customClass="ModelViewController" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="B2T-ui-MHS"/>
+                        <viewControllerLayoutGuide type="bottom" id="PQs-jS-IB8"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="zrj-Dd-WPc" customClass="EAGLView">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                        <subviews>
+                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yT7-IR-qUJ">
+                                <rect key="frame" x="45" y="777" width="310" height="20"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <activityIndicatorView opaque="NO" contentMode="scaleToFill" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="Fuh-zG-zVR">
+                                <rect key="frame" x="15" y="777" width="20" height="20"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="20" id="f9c-ZY-6q4"/>
+                                </constraints>
+                            </activityIndicatorView>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="right" contentVerticalAlignment="top" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ct3-iA-CZQ">
+                                <rect key="frame" x="335" y="98" width="30" height="30"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="30" id="59F-eB-IWj"/>
+                                    <constraint firstAttribute="width" constant="30" id="Cor-0l-wdz"/>
+                                </constraints>
+                                <state key="normal" image="icon_rotate.png">
+                                    <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                </state>
+                                <connections>
+                                    <action selector="startRotation:" destination="NyB-7w-cP0" eventType="touchUpInside" id="6fd-R7-3ts"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <gestureRecognizers/>
+                        <constraints>
+                            <constraint firstItem="yT7-IR-qUJ" firstAttribute="bottom" secondItem="Fuh-zG-zVR" secondAttribute="bottom" id="AP1-zU-goA"/>
+                            <constraint firstAttribute="bottom" secondItem="Fuh-zG-zVR" secondAttribute="bottom" constant="15" id="J47-7T-QdC"/>
+                            <constraint firstAttribute="trailing" secondItem="Ct3-iA-CZQ" secondAttribute="trailing" constant="10" id="Nep-AD-3aJ"/>
+                            <constraint firstAttribute="trailing" secondItem="yT7-IR-qUJ" secondAttribute="trailing" constant="20" symbolic="YES" id="P96-El-NrK"/>
+                            <constraint firstItem="yT7-IR-qUJ" firstAttribute="leading" secondItem="Fuh-zG-zVR" secondAttribute="trailing" constant="10" id="oc4-6x-CF7"/>
+                            <constraint firstItem="Fuh-zG-zVR" firstAttribute="top" secondItem="yT7-IR-qUJ" secondAttribute="top" id="qEd-Tx-ibj"/>
+                            <constraint firstItem="Fuh-zG-zVR" firstAttribute="leading" secondItem="zrj-Dd-WPc" secondAttribute="leading" constant="15" id="qfh-y2-BuW"/>
+                            <constraint firstItem="Ct3-iA-CZQ" firstAttribute="top" secondItem="B2T-ui-MHS" secondAttribute="bottom" constant="10" id="uxn-cS-R2c"/>
+                        </constraints>
+                        <connections>
+                            <outletCollection property="gestureRecognizers" destination="7R3-zZ-cpa" appends="YES" id="K8X-lL-YUb"/>
+                            <outletCollection property="gestureRecognizers" destination="qCm-Ub-mO2" appends="YES" id="yww-Ed-CcX"/>
+                            <outletCollection property="gestureRecognizers" destination="mEQ-72-g58" appends="YES" id="ft8-8t-WzA"/>
+                        </connections>
+                    </view>
+                    <navigationItem key="navigationItem" title="ONELAB" id="WX3-lU-bHf"/>
+                    <connections>
+                        <outlet property="doubleTap" destination="mEQ-72-g58" id="xrl-dg-7EI"/>
+                        <outlet property="glView" destination="zrj-Dd-WPc" id="sIP-YC-fg2"/>
+                        <outlet property="progressIndicator" destination="Fuh-zG-zVR" id="ifr-7g-7Dv"/>
+                        <outlet property="progressLabel" destination="yT7-IR-qUJ" id="onn-gw-Mnv"/>
+                        <outlet property="singleTap" destination="7R3-zZ-cpa" id="Xlw-Pp-Szf"/>
+                        <segue destination="0h7-h3-thM" kind="show" identifier="showSettingsSegue" id="cJz-7s-fgZ"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="aSF-6U-E6q" userLabel="First Responder" sceneMemberID="firstResponder"/>
+                <tapGestureRecognizer id="7R3-zZ-cpa">
+                    <connections>
+                        <action selector="singleTap:" destination="NyB-7w-cP0" id="ywu-41-GBg"/>
+                    </connections>
+                </tapGestureRecognizer>
+                <tapGestureRecognizer numberOfTapsRequired="2" id="mEQ-72-g58">
+                    <connections>
+                        <action selector="doubleTap:" destination="NyB-7w-cP0" id="95f-15-F18"/>
+                    </connections>
+                </tapGestureRecognizer>
+                <pinchGestureRecognizer id="qCm-Ub-mO2">
+                    <connections>
+                        <action selector="pinch:" destination="NyB-7w-cP0" id="zf5-07-MqY"/>
+                    </connections>
+                </pinchGestureRecognizer>
+            </objects>
+            <point key="canvasLocation" x="266.25" y="169.01408450704224"/>
+        </scene>
+        <!--About View Controller-->
+        <scene sceneID="3Qf-fF-DaH">
+            <objects>
+                <viewController id="uZG-Bw-GZA" customClass="AboutViewController" sceneMemberID="viewController">
+                    <webView key="view" contentMode="scaleToFill" id="EMj-bY-w8n">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    </webView>
+                    <navigationItem key="navigationItem" id="0mC-fS-N0z"/>
+                    <connections>
+                        <outlet property="aboutView" destination="EMj-bY-w8n" id="YZf-1O-mOo"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="RFr-BW-Q0P" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="311.25" y="978.16901408450701"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="YFg-Co-Dc2">
+            <objects>
+                <navigationController definesPresentationContext="YES" id="vAG-uz-hfU" sceneMemberID="viewController">
+                    <simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
+                    <nil key="simulatedBottomBarMetrics"/>
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" id="HV6-65-Zcg">
+                        <rect key="frame" x="0.0" y="44" width="375" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="aNd-kg-MlN" kind="relationship" relationship="rootViewController" id="Hxi-aR-q5B"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="Fbk-Ng-NrK" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-1618.125" y="169.01408450704224"/>
+        </scene>
+        <!--Parameters-->
+        <scene sceneID="WMF-B6-2e0">
+            <objects>
+                <tableViewController id="0h7-h3-thM" customClass="ParametersViewController" sceneMemberID="viewController">
+                    <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="XKe-Ex-Vcl">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <prototypes>
+                            <tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="setting" id="Ryv-6O-Xsf">
+                                <rect key="frame" x="0.0" y="22" width="375" height="44"/>
+                                <autoresizingMask key="autoresizingMask" widthSizable="YES"/>
+                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Ryv-6O-Xsf" id="e1l-jJ-ItW">
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
+                                    <autoresizingMask key="autoresizingMask" widthSizable="YES"/>
+                                </tableViewCellContentView>
+                            </tableViewCell>
+                        </prototypes>
+                        <connections>
+                            <outlet property="dataSource" destination="0h7-h3-thM" id="4Ef-Df-sYF"/>
+                            <outlet property="delegate" destination="0h7-h3-thM" id="Jfg-cG-L0F"/>
+                        </connections>
+                    </tableView>
+                    <navigationItem key="navigationItem" title="Parameters" id="FKO-Qr-UUm">
+                        <barButtonItem key="backBarButtonItem" title="Model" id="SL3-OD-Ie0"/>
+                    </navigationItem>
+                    <connections>
+                        <segue destination="tLo-wG-spu" kind="show" id="50g-yc-iQZ"/>
+                    </connections>
+                </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="l6Y-Ow-J7G" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="1192.5" y="169.01408450704224"/>
+        </scene>
+        <!--Options-->
+        <scene sceneID="hN1-wg-Dof">
+            <objects>
+                <tableViewController id="tLo-wG-spu" customClass="OptionsViewController" sceneMemberID="viewController">
+                    <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="AUf-bU-fWv">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <prototypes>
+                            <tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="postProCell" id="7gg-dw-9nK">
+                                <rect key="frame" x="0.0" y="22" width="375" height="44"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="7gg-dw-9nK" id="ejA-3y-SSJ">
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
+                                    <autoresizingMask key="autoresizingMask" widthSizable="YES"/>
+                                </tableViewCellContentView>
+                            </tableViewCell>
+                        </prototypes>
+                        <connections>
+                            <outlet property="dataSource" destination="tLo-wG-spu" id="pkx-eg-H3n"/>
+                            <outlet property="delegate" destination="tLo-wG-spu" id="XDV-ze-ULa"/>
+                        </connections>
+                    </tableView>
+                    <navigationItem key="navigationItem" title="Options" id="qUi-6A-krJ"/>
+                    <connections>
+                        <segue destination="pfu-w8-zq5" kind="show" id="DuR-Aa-VLN"/>
+                    </connections>
+                </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="WwS-KQ-5Gs" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="2096.25" y="169.01408450704224"/>
+        </scene>
+        <!--Post Processing View Controller-->
+        <scene sceneID="nXH-mg-3hY">
+            <objects>
+                <viewController storyboardIdentifier="PostProcessingViewController" id="pfu-w8-zq5" customClass="PostProcessingViewController" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="KBG-YI-tIA"/>
+                        <viewControllerLayoutGuide type="bottom" id="4M7-bd-GcK"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Lh8-gQ-vBl">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                        <subviews>
+                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="PView Name" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="z3z-Xo-nQF">
+                                <rect key="frame" x="20" y="94" width="335" height="36"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="36" id="zTU-8R-I6z"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="24"/>
+                                <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="h3e-8g-CWy">
+                                <rect key="frame" x="14" y="138" width="347" height="654"/>
+                                <subviews>
+                                    <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="Intervals" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="elK-Av-NO2">
+                                        <rect key="frame" x="-8" y="172" width="91" height="21"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" constant="91" id="g2R-zm-Dt4"/>
+                                        </constraints>
+                                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                        <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="rgB-dz-6XG">
+                                        <rect key="frame" x="91" y="168" width="71" height="30"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" constant="71" id="DMh-ns-x7e"/>
+                                        </constraints>
+                                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                        <textInputTraits key="textInputTraits"/>
+                                    </textField>
+                                    <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="Raise (Z)" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bfu-t9-dAj">
+                                        <rect key="frame" x="-8" y="217" width="91" height="21"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                        <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <slider opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="-5" maxValue="5" translatesAutoresizingMaskIntoConstraints="NO" id="BC5-E7-rEF">
+                                        <rect key="frame" x="89" y="213" width="185" height="31"/>
+                                    </slider>
+                                    <stepper opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="fq0-E8-V07">
+                                        <rect key="frame" x="178" y="168" width="94" height="29"/>
+                                        <connections>
+                                            <action selector="stepperValueChanged:" destination="pfu-w8-zq5" eventType="valueChanged" id="d8w-ZF-KcK"/>
+                                        </connections>
+                                    </stepper>
+                                    <pickerView contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xW0-cN-kxL">
+                                        <rect key="frame" x="17" y="0.0" width="538" height="162"/>
+                                    </pickerView>
+                                </subviews>
+                                <constraints>
+                                    <constraint firstItem="BC5-E7-rEF" firstAttribute="leading" secondItem="Bfu-t9-dAj" secondAttribute="trailing" constant="8" symbolic="YES" id="171-4w-6wl"/>
+                                    <constraint firstItem="Bfu-t9-dAj" firstAttribute="leading" secondItem="elK-Av-NO2" secondAttribute="leading" id="1t0-hf-2Hf"/>
+                                    <constraint firstItem="Bfu-t9-dAj" firstAttribute="top" secondItem="elK-Av-NO2" secondAttribute="bottom" constant="24" id="2cz-wD-n92"/>
+                                    <constraint firstItem="xW0-cN-kxL" firstAttribute="top" secondItem="h3e-8g-CWy" secondAttribute="top" id="6Tb-lU-QwA"/>
+                                    <constraint firstItem="elK-Av-NO2" firstAttribute="top" secondItem="xW0-cN-kxL" secondAttribute="bottom" constant="10" id="6dl-PR-DcO"/>
+                                    <constraint firstItem="xW0-cN-kxL" firstAttribute="leading" secondItem="h3e-8g-CWy" secondAttribute="leading" constant="17" id="DQh-yQ-mvn"/>
+                                    <constraint firstItem="elK-Av-NO2" firstAttribute="top" secondItem="rgB-dz-6XG" secondAttribute="top" constant="4" id="HEg-td-wvJ"/>
+                                    <constraint firstItem="xW0-cN-kxL" firstAttribute="centerX" secondItem="h3e-8g-CWy" secondAttribute="centerX" id="HHR-C5-tPS"/>
+                                    <constraint firstAttribute="bottom" secondItem="BC5-E7-rEF" secondAttribute="bottom" constant="136" id="Lwf-Ue-JYj"/>
+                                    <constraint firstItem="elK-Av-NO2" firstAttribute="leading" secondItem="h3e-8g-CWy" secondAttribute="leading" constant="-8" id="NO1-74-SYu"/>
+                                    <constraint firstItem="fq0-E8-V07" firstAttribute="trailing" secondItem="BC5-E7-rEF" secondAttribute="trailing" id="PvF-F6-GGY"/>
+                                    <constraint firstItem="BC5-E7-rEF" firstAttribute="top" secondItem="fq0-E8-V07" secondAttribute="bottom" constant="16" id="Qd9-fY-MEn"/>
+                                    <constraint firstItem="fq0-E8-V07" firstAttribute="leading" secondItem="rgB-dz-6XG" secondAttribute="trailing" constant="16" id="Uce-JP-GEZ"/>
+                                    <constraint firstItem="BC5-E7-rEF" firstAttribute="top" secondItem="rgB-dz-6XG" secondAttribute="bottom" constant="15" id="V0c-P0-Cga"/>
+                                    <constraint firstItem="rgB-dz-6XG" firstAttribute="leading" secondItem="elK-Av-NO2" secondAttribute="trailing" constant="8" symbolic="YES" id="j3z-BG-Bm9"/>
+                                    <constraint firstItem="rgB-dz-6XG" firstAttribute="leading" secondItem="BC5-E7-rEF" secondAttribute="leading" id="mX9-R6-657"/>
+                                    <constraint firstAttribute="trailing" secondItem="fq0-E8-V07" secondAttribute="trailing" constant="20" symbolic="YES" id="xfn-S1-D4x"/>
+                                </constraints>
+                            </scrollView>
+                        </subviews>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="z3z-Xo-nQF" firstAttribute="top" secondItem="KBG-YI-tIA" secondAttribute="bottom" constant="6" id="3sW-ft-cxl"/>
+                            <constraint firstItem="h3e-8g-CWy" firstAttribute="leading" secondItem="Lh8-gQ-vBl" secondAttribute="leading" constant="14" id="CST-ND-FJF"/>
+                            <constraint firstItem="h3e-8g-CWy" firstAttribute="centerX" secondItem="z3z-Xo-nQF" secondAttribute="centerX" id="TqQ-12-vnX"/>
+                            <constraint firstAttribute="bottom" secondItem="h3e-8g-CWy" secondAttribute="bottom" constant="20" symbolic="YES" id="anm-iu-ift"/>
+                            <constraint firstItem="z3z-Xo-nQF" firstAttribute="leading" secondItem="Lh8-gQ-vBl" secondAttribute="leading" constant="20" symbolic="YES" id="jF4-08-vhZ"/>
+                            <constraint firstAttribute="trailing" secondItem="z3z-Xo-nQF" secondAttribute="trailing" constant="20" symbolic="YES" id="wbV-Vl-wDT"/>
+                            <constraint firstItem="h3e-8g-CWy" firstAttribute="top" secondItem="z3z-Xo-nQF" secondAttribute="bottom" constant="8" symbolic="YES" id="zzS-9L-S8L"/>
+                        </constraints>
+                    </view>
+                    <navigationItem key="navigationItem" id="d15-oM-VRg"/>
+                    <connections>
+                        <outlet property="Intervals" destination="rgB-dz-6XG" id="JH1-PI-Mc7"/>
+                        <outlet property="IntervalsStepper" destination="fq0-E8-V07" id="T9Z-f2-nCm"/>
+                        <outlet property="IntervalsType" destination="xW0-cN-kxL" id="GMk-2Z-uHR"/>
+                        <outlet property="Name" destination="z3z-Xo-nQF" id="aNu-Yc-mNc"/>
+                        <outlet property="RaiseZ" destination="BC5-E7-rEF" id="Wnp-mG-Z0a"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="p63-tL-CUK" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="2992.5" y="169.01408450704224"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="icon_rotate.png" width="512" height="512"/>
+    </resources>
+</document>
diff --git a/contrib/mobile/iOS/Onelab/icon_onelab.png b/contrib/mobile/iOS/Onelab/icon_onelab.png
new file mode 100644
index 0000000000000000000000000000000000000000..262b7f517d3a65478f8bc319f9ae2eee05974024
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/icon_onelab.png differ
diff --git a/contrib/mobile/iOS/Onelab/icon_rotate.png b/contrib/mobile/iOS/Onelab/icon_rotate.png
new file mode 100644
index 0000000000000000000000000000000000000000..28ae466429a7acf9ea9e4b0ab3f17a84c9668473
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/icon_rotate.png differ
diff --git a/contrib/mobile/iOS/Onelab/icon_translate.png b/contrib/mobile/iOS/Onelab/icon_translate.png
new file mode 100644
index 0000000000000000000000000000000000000000..acde225dd957e6e02df7557448b4d2a66ebfb032
Binary files /dev/null and b/contrib/mobile/iOS/Onelab/icon_translate.png differ
diff --git a/contrib/mobile/iOS/Onelab/main.mm b/contrib/mobile/iOS/Onelab/main.mm
new file mode 100644
index 0000000000000000000000000000000000000000..d645c7246c42e45510cfcdcd7a7ff584a700d4c1
--- /dev/null
+++ b/contrib/mobile/iOS/Onelab/main.mm
@@ -0,0 +1,10 @@
+#import <UIKit/UIKit.h>
+
+#import "AppDelegate.h"
+
+int main(int argc, char *argv[])
+{
+  @autoreleasepool {
+    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+  }
+}
diff --git a/contrib/mobile/iosUtils.cpp b/contrib/mobile/iosUtils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aac9b23f22265a715c8b316ce48c71137620c2ab
--- /dev/null
+++ b/contrib/mobile/iosUtils.cpp
@@ -0,0 +1,15 @@
+#include "iosUtils.h"
+
+static void * objc;
+
+void getBitmapFromString(const char *text, int textsize, unsigned char **map,
+                         int *height, int *width, int *realWidth)
+{
+  getBitmap(objc, text, textsize, map, height, width, realWidth);
+}
+
+void setObjCBridge(void *objcObject)
+{
+  objc = objcObject;
+  Msg::SetCallback((GmshMessage*)new MobileMessage(objcObject));
+}
diff --git a/contrib/mobile/iosUtils.h b/contrib/mobile/iosUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..fc2d64db2be28d5ae8c2210b128fb448ecb46c07
--- /dev/null
+++ b/contrib/mobile/iosUtils.h
@@ -0,0 +1,29 @@
+#ifndef _IOS_UTILS_ONELAB_H_
+#define _IOS_UTILS_ONELAB_H_
+
+#include <iostream>
+#include <string>
+
+#include <Gmsh/GmshMessage.h>
+
+void messageFromCpp (void *self, std::string level, std::string msg);
+void getBitmap(void *self, const char *text, int textsize, unsigned char **map,
+               int *height, int *width, int *realWidth=NULL);
+void getBitmapFromString(const char *text, int textsize, unsigned char **map,
+                         int *height, int *width, int *realWidth=NULL);
+void setObjCBridge(void *objcObject);
+
+class MobileMessage : GmshMessage
+{
+private:
+  void* _objcObject;
+public:
+  MobileMessage(void* objcObject){_objcObject = objcObject;}
+  ~MobileMessage(){}
+  void operator()(std::string level, std::string message)
+  {
+    messageFromCpp(_objcObject, level, message);
+  }
+};
+
+#endif
diff --git a/contrib/mobile/utils/android_build.sh b/contrib/mobile/utils/android_build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2c2c127fffa661533dea001d042c2321c2c6fb73
--- /dev/null
+++ b/contrib/mobile/utils/android_build.sh
@@ -0,0 +1,163 @@
+#!/usr/bin/env bash
+
+appname=Onelab
+enable_occ=1
+enable_simulator=0
+
+while [[ $# -gt 0 ]]; do
+  key="$1"
+  case $key in
+    -n|--name)
+      appname=$2
+      enable_occ=0
+      echo "Rebranding Onelab app as ${appname}"
+      shift # past argument
+      ;;
+    -s|--simulator)
+      enable_simulator=1
+      ;;
+    --no-occ)
+      enable_occ=0
+      ;;
+    *)
+      echo "Usage: $0 [-n|--name appname] [-s|--simulator] [--no-occ]"
+      exit 1
+      ;;
+  esac
+  shift # past argument or value
+done
+
+android=android
+if [ $enable_simulator != 0 ]; then
+  android=androidsimulator
+fi
+
+gmsh_git="${HOME}/src/gmsh"
+getdp_git="${HOME}/src/getdp"
+frameworks_dir="${HOME}/src/gmsh/contrib/mobile/frameworks_${android}"
+
+petsc_lib="$frameworks_dir/petsc"
+slepc_lib="$frameworks_dir/slepc"
+occ_lib="$frameworks_dir/occt/lib"
+occ_inc="$frameworks_dir/occt/include/opencascade"
+android_ndk="${HOME}/Library/Android/sdk/ndk-bundle/"
+android_sdk="${HOME}/Library/Android/sdk/"
+
+if [ "$appname" != "Onelab" ] ; then
+  models_dir="${HOME}/src/getdp/benchmarks_private"
+else
+  models_dir="${HOME}/src/onelab_doc"
+fi
+if [ -f ${models_dir}/cleanup.sh ]; then
+  cd ${models_dir} && ./cleanup.sh
+fi
+
+if [ $enable_simulator != 0 ]; then
+  cmake_default="-DDEFAULT=0 -DENABLE_PRIVATE_API=1 -DCMAKE_TOOLCHAIN_FILE=${android_ndk}/build/cmake/android.toolchain.cmake -DANDROID_STL_FORCE_FEATURES=1 -DENABLE_BUILD_ANDROID=1 -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI=x86_64"
+else
+  cmake_default="-DDEFAULT=0 -DENABLE_PRIVATE_API=1 -DCMAKE_TOOLCHAIN_FILE=${android_ndk}/build/cmake/android.toolchain.cmake -DANDROID_STL_FORCE_FEATURES=1 -DENABLE_BUILD_ANDROID=1 -DCMAKE_BUILD_TYPE=Release"
+fi
+
+cmake_thread=6
+
+function check {
+  return_code=$?
+  if [ $return_code != 0 ]; then
+    echo "last command failed (return $return_code)"
+    exit $return_code
+  fi
+}
+
+# Gmsh
+cd $gmsh_git
+git pull
+if [ ! -d "$gmsh_git/build_${android}" ] || [ ! -f "$gmsh_git/build_${android}/CMakeCache.txt" ]; then
+  mkdir $gmsh_git/build_${android}
+fi
+cd $gmsh_git/build_${android}
+cmake $cmake_default -DENABLE_BLAS_LAPACK=1 -DENABLE_BUILD_SHARED=1 -DENABLE_MATHEX=1 -DENABLE_MESH=1 -DENABLE_ONELAB=1 -DENABLE_PARSER=1 -DENABLE_POST=1 -DENABLE_PLUGINS=1 -DENABLE_ANN=1 -DENABLE_KBIPACK=1 -DENABLE_GMP=0 -DENABLE_ZIPPER=1 -DBLAS_LAPACK_LIBRARIES="$petsc_lib/libf2cblas.so;$petsc_lib/libf2clapack.so" -DENABLE_OCC=$enable_occ -DOCC_LIBS="${occ_lib}/libTKSTEP.a;${occ_lib}/libTKSTEP209.a;${occ_lib}/libTKSTEPAttr.a;${occ_lib}/libTKSTEPBase.a;${occ_lib}/libTKIGES.a;${occ_lib}/libTKXSBase.a;${occ_lib}/libTKOffset.a;${occ_lib}/libTKFeat.a;${occ_lib}/libTKFillet.a;${occ_lib}/libTKBool.a;${occ_lib}/libTKMesh.a;${occ_lib}/libTKHLR.a;${occ_lib}/libTKBO.a;${occ_lib}/libTKShHealing.a;${occ_lib}/libTKPrim.a;${occ_lib}/libTKTopAlgo.a;${occ_lib}/libTKGeomAlgo.a;${occ_lib}/libTKBRep.a;${occ_lib}/libTKGeomBase.a;${occ_lib}/libTKG3d.a;${occ_lib}/libTKG2d.a;${occ_lib}/libTKMath.a;${occ_lib}/libTKernel.a" -DOCC_INC=${occ_inc} ..
+check
+make androidGmsh -j$cmake_thread
+check
+make get_headers
+check
+
+# GetDP
+cd $getdp_git
+git pull
+if [ ! -d "$getdp_git/build_${android}" ] || [ ! -f "$getdp_git/build_${android}/CMakeCache.txt" ]; then
+  mkdir $getdp_git/build_${android}
+fi
+cd $getdp_git/build_${android}
+PETSC_DIR= PETSC_ARCH= SLEPC_DIR= cmake $cmake_default -DENABLE_BLAS_LAPACK=1 -DENABLE_BUILD_SHARED=1 -DENABLE_GMSH=1 -DENABLE_KERNEL=1 -DENABLE_PETSC=1 -DPETSC_INC="$petsc_lib/Headers;$petsc_lib/Headers/mpiuni" -DPETSC_LIBS="$petsc_lib/libpetsc.so" -DENABLE_SLEPC=1 -DSLEPC_INC="$slepc_lib/Headers/" -DSLEPC_LIB="$slepc_lib/libslepc.so" -DGMSH_INC="$gmsh_git/build_${android}/Headers/" -DGMSH_LIB="$gmsh_git/build_${android}/libs/libgmsh.so" -DBLAS_LAPACK_LIBRARIES="$petsc_lib/libf2cblas.so;$petsc_lib/libf2clapack.so" ..
+check
+make androidGetdp -j$cmake_thread
+check
+make get_headers
+check
+
+# Onelab/Mobile interface
+if [ ! -d "$gmsh_git/contrib/mobile/build_${android}_${appname}" ]; then
+  mkdir $gmsh_git/contrib/mobile/build_${android}_${appname}
+fi
+cd $gmsh_git/contrib/mobile/build_${android}_${appname}
+
+cmake $cmake_default -DAPPNAME:STRING=${appname} \
+      -DMODELS_DIR="$models_dir" \
+      -DBLAS_LIB="$petsc_lib/libf2cblas.so" -DLAPACK_LIB="$petsc_lib/libf2clapack.so" \
+      -DPETSC_LIB="$petsc_lib/libpetsc.so" -DSLEPC_LIB="$slepc_lib/libslepc.so" \
+      -DGMSH_INC="$gmsh_git/build_${android}/Headers" -DGMSH_LIB="$gmsh_git/build_${android}/libs/libgmsh.so" \
+      -DGETDP_INC="$getdp_git/build_${android}/Headers" -DGETDP_LIB="$getdp_git/build_${android}/libs/libgetdp.so" ..
+check
+make androidOnelab -j$cmake_thread
+check
+make androidProject
+check
+
+cd $appname
+
+if [ "$appname" != "Onelab" ] ; then
+  # change package name
+  rm -rf app/src/main/java/org/geuz/$appname
+  mv app/src/main/java/org/geuz/onelab/ app/src/main/java/org/geuz/$appname
+  # Gmsh.java and StringTexture.java are accessed from C++ as onelab packages
+  mkdir app/src/main/java/org/geuz/onelab
+  mv app/src/main/java/org/geuz/$appname/Gmsh.java app/src/main/java/org/geuz/onelab
+  mv app/src/main/java/org/geuz/$appname/StringTexture.java app/src/main/java/org/geuz/onelab
+  sed -e "s/org\.geuz\.onelab/org\.geuz\.$appname/g" -i "" app/src/main/AndroidManifest.xml
+  find app/src/main/java/org/geuz/$appname -type f -name '*.java' -exec sed -e "s/package org\.geuz\.onelab/package org\.geuz\.$appname/g" -i "" {} \;
+  find app/src/main/res -type f -name '*.xml' -exec sed -e "s/org\.geuz\.onelab/org\.geuz\.$appname/g" -i "" {} \;
+  # change app name and icons
+  sed -e "s/Onelab/$appname/" -i "" app/src/main/res/values/strings.xml
+  sed -e "s/Onelab\/Mobile/$appname/" -i "" app/src/main/java/org/geuz/$appname/AboutActivity.java
+  if [ "$appname" == "BBEMG" ] ; then
+    sed -e "s/onelab\.info/www\.bbemg\.be/g" -i "" app/src/main/java/org/geuz/$appname/AboutActivity.java
+    cp $HOME/tex/proposals/bbemg/icons/bbemg-logo-128x128.png app/src/main/res/drawable-hdpi/ic_launcher.png
+    cp $HOME/tex/proposals/bbemg/icons/bbemg-logo-64x64.png app/src/main/res/drawable-mdpi/ic_launcher.png
+    cp $HOME/tex/proposals/bbemg/icons/bbemg-logo-48x48.png app/src/main/res/drawable-ldpi/ic_launcher.png
+  fi
+fi
+
+# we should generate this here:
+#echo "ndk.dir=${android_ndk}" > local.properties
+#echo "sdk.dir=${android_sdk}" >> local.properties
+# as well as the keystore config in app/build.gradle
+
+check
+gradle assembleRelease
+check
+
+# to install on the device:
+# ~/Library/Android/sdk/platform-tools/adb install -r build_android_Onelab/Onelab/app/build/outputs/apk/app-release.apk
+
+# to launch the app on the device:
+# ~/Library/Android/sdk/platform-tools/adb shell am start -n org.geuz.onelab/org.geuz.onelab.SplashScreen
+
+# to debug and check the log:
+# ~/Library/Android/sdk/tools/monitor
+
+# to see stack traces after crashes:
+# ~/Library/Android/sdk/platform-tools/adb logcat | ~/Library/Android/ndk-bundle/ndk-stack -sym build_android_Onelab/
+
+# to run the emulator (this one uses a real ARM image - slower but allows to test the actual build!)
+# ~/Library/Android/sdk/tools/emulator -netdelay none -netspeed full -avd Nexus_6P_API_25
diff --git a/contrib/mobile/utils/f2cblaslapack_android_cmakelists b/contrib/mobile/utils/f2cblaslapack_android_cmakelists
new file mode 100644
index 0000000000000000000000000000000000000000..a5cd8e2085d33d655150c9bc84fab9d66f0e143e
--- /dev/null
+++ b/contrib/mobile/utils/f2cblaslapack_android_cmakelists
@@ -0,0 +1,34 @@
+# run this with the android toolchain file, e.g.
+#
+# mkdir build
+# cmake -DCMAKE_TOOLCHAIN_FILE=/Users/geuzaine/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake ..
+
+cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
+project(f2cblaslapack C)
+
+add_definitions(-D_GLIBCXX_USE_C99_MATH=1)
+find_file(CMAKE_TOOLCHAIN_FILE "android.toolchain.cmake")
+if(NOT CMAKE_TOOLCHAIN_FILE)
+  message(FATAL_ERROR "Cannot compile f2cblaslapack for android without android-cmake")
+endif(NOT CMAKE_TOOLCHAIN_FILE)
+
+add_definitions(-DBUILD_ANDROID)
+set(CMAKE_BUILD_TYPE Release)
+
+set(BLAS_AUX pow_ii.c lsame.c xerbla_array.c xerbla.c)
+set(BLAS_SINGLE pow_si.c smaxloc.c sf__cabs.c caxpy.c ccopy.c cdotc.c cdotu.c cgbmv.c cgemm.c cgemv.c cgerc.c cgeru.c chbmv.c chemm.c chemv.c cher2.c cher2k.c cher.c cherk.c chpmv.c chpr2.c chpr.c crotg.c cscal.c csrot.c csscal.c cswap.c csymm.c csyr2k.c csyrk.c ctbmv.c ctbsv.c ctpmv.c ctpsv.c ctrmm.c ctrmv.c ctrsm.c ctrsv.c icamax.c isamax.c sasum.c saxpy.c scabs1.c scasum.c scnrm2.c scopy.c sdot.c sgbmv.c sgemm.c sgemv.c sger.c snrm2.c srot.c srotg.c srotm.c srotmg.c ssbmv.c sscal.c sspmv.c sspr2.c sspr.c sswap.c ssymm.c ssymv.c ssyr2.c ssyr2k.c ssyr.c ssyrk.c stbmv.c stbsv.c stpmv.c stpsv.c strmm.c strmv.c strsm.c strsv.c)
+set(BLAS_DOUBLE pow_di.c dmaxloc.c df__cabs.c dasum.c daxpy.c dcabs1.c dcopy.c ddot.c dgbmv.c dgemm.c dgemv.c dger.c dnrm2.c drot.c drotg.c drotm.c drotmg.c dsbmv.c dscal.c dsdot.c dspmv.c dspr2.c dspr.c dswap.c dsymm.c dsymv.c dsyr2.c dsyr2k.c dsyr.c dsyrk.c dtbmv.c dtbsv.c dtpmv.c dtpsv.c dtrmm.c dtrmv.c dtrsm.c dtrsv.c dzasum.c dznrm2.c idamax.c izamax.c sdsdot.c zaxpy.c zcopy.c zdotc.c zdotu.c zdrot.c zdscal.c zgbmv.c zgemm.c zgemv.c zgerc.c zgeru.c zhbmv.c zhemm.c zhemv.c zher2.c zher2k.c zher.c zherk.c zhpmv.c zhpr2.c zhpr.c zrotg.c zscal.c zswap.c zsymm.c zsyr2k.c zsyrk.c ztbmv.c ztbsv.c ztpmv.c ztpsv.c ztrmm.c ztrmv.c ztrsm.c ztrsv.c)
+set(LAPACK_SINGLE slamch.c cbbcsd.c cbdsqr.c cgbbrd.c cgbcon.c cgbequb.c cgbequ.c cgbrfs.c cgbsv.c cgbsvx.c cgbtf2.c cgbtrf.c cgbtrs.c cgebak.c cgebal.c cgebd2.c cgebrd.c cgecon.c cgeequb.c cgeequ.c cgees.c cgeesx.c cgeev.c cgeevx.c cgegs.c cgegv.c cgehd2.c cgehrd.c cgelq2.c cgelqf.c cgelsd.c cgels.c cgelss.c cgelsx.c cgelsy.c cgemqrt.c cgeql2.c cgeqlf.c cgeqp3.c cgeqpf.c cgeqr2.c cgeqr2p.c cgeqrf.c cgeqrfp.c cgeqrt2.c cgeqrt3.c cgeqrt.c cgerfs.c cgerq2.c cgerqf.c cgesc2.c cgesdd.c cgesvd.c cgesv.c cgesvx.c cgetc2.c cgetf2.c cgetrf.c cgetri.c cgetrs.c cggbak.c cggbal.c cgges.c cggesx.c cggev.c cggevx.c cggglm.c cgghrd.c cgglse.c cggqrf.c cggrqf.c cggsvd.c cggsvp.c cgtcon.c cgtrfs.c cgtsv.c cgtsvx.c cgttrf.c cgttrs.c cgtts2.c chbevd.c chbev.c chbevx.c chbgst.c chbgvd.c chbgv.c chbgvx.c chbtrd.c checon.c cheequb.c cheevd.c cheev.c cheevr.c cheevx.c chegs2.c chegst.c chegvd.c chegv.c chegvx.c cherfs.c chesv.c chesvx.c cheswapr.c chetd2.c chetf2.c chetrd.c chetrf.c chetri2.c chetri2x.c chetri.c chetrs2.c chetrs.c chfrk.c chgeqz.c chpcon.c chpevd.c chpev.c chpevx.c chpgst.c chpgvd.c chpgv.c chpgvx.c chprfs.c chpsv.c chpsvx.c chptrd.c chptrf.c chptri.c chptrs.c chsein.c chseqr.c clabrd.c clacgv.c clacn2.c clacon.c clacp2.c clacpy.c clacrm.c clacrt.c cladiv.c claed0.c claed7.c claed8.c claein.c claesy.c claev2.c clag2z.c cla_gerpvgrw.c clags2.c clagtm.c clahef.c clahqr.c clahr2.c clahrd.c claic1.c clals0.c clalsa.c clalsd.c clangb.c clange.c clangt.c clanhb.c clanhe.c clanhf.c clanhp.c clanhs.c clanht.c clansb.c clansp.c clansy.c clantb.c clantp.c clantr.c clapll.c clapmr.c clapmt.c claqgb.c claqge.c claqhb.c claqhe.c claqhp.c claqp2.c claqps.c claqr0.c claqr1.c claqr2.c claqr3.c claqr4.c claqr5.c claqsb.c claqsp.c claqsy.c clar1v.c clar2v.c clarcm.c clarfb.c clarf.c clarfg.c clarfgp.c clarft.c clarfx.c clargv.c clarnv.c clarrv.c clartg.c clartv.c clarzb.c clarz.c clarzt.c clascl.c claset.c clasr.c classq.c claswp.c clasyf.c clatbs.c clatdf.c clatps.c clatrd.c clatrs.c clatrz.c clatzm.c clauu2.c clauum.c cpbcon.c cpbequ.c cpbrfs.c cpbstf.c cpbsv.c cpbsvx.c cpbtf2.c cpbtrf.c cpbtrs.c cpftrf.c cpftri.c cpftrs.c cpocon.c cpoequb.c cpoequ.c cporfs.c cposv.c cposvx.c cpotf2.c cpotrf.c cpotri.c cpotrs.c cppcon.c cppequ.c cpprfs.c cppsv.c cppsvx.c cpptrf.c cpptri.c cpptrs.c cpstf2.c cpstrf.c cptcon.c cpteqr.c cptrfs.c cptsv.c cptsvx.c cpttrf.c cpttrs.c cptts2.c crot.c cspcon.c cspmv.c cspr.c csprfs.c cspsv.c cspsvx.c csptrf.c csptri.c csptrs.c csrscl.c cstedc.c cstegr.c cstein.c cstemr.c csteqr.c csycon.c csyconv.c csyequb.c csymv.c csyr.c csyrfs.c csysv.c csysvx.c csyswapr.c csytf2.c csytrf.c csytri2.c csytri2x.c csytri.c csytrs2.c csytrs.c ctbcon.c ctbrfs.c ctbtrs.c ctfsm.c ctftri.c ctfttp.c ctfttr.c ctgevc.c ctgex2.c ctgexc.c ctgsen.c ctgsja.c ctgsna.c ctgsy2.c ctgsyl.c ctpcon.c ctpmqrt.c ctpqrt2.c ctpqrt.c ctprfb.c ctprfs.c ctptri.c ctptrs.c ctpttf.c ctpttr.c ctrcon.c ctrevc.c ctrexc.c ctrrfs.c ctrsen.c ctrsna.c ctrsyl.c ctrti2.c ctrtri.c ctrtrs.c ctrttf.c ctrttp.c ctzrqf.c ctzrzf.c cunbdb.c cuncsd.c cung2l.c cung2r.c cungbr.c cunghr.c cungl2.c cunglq.c cungql.c cungqr.c cungr2.c cungrq.c cungtr.c cunm2l.c cunm2r.c cunmbr.c cunmhr.c cunml2.c cunmlq.c cunmql.c cunmqr.c cunmr2.c cunmr3.c cunmrq.c cunmrz.c cunmtr.c cupgtr.c cupmtr.c icmax1.c ilaclc.c ilaclr.c ilaslc.c ilaslr.c sbbcsd.c sbdsdc.c sbdsqr.c scsum1.c sdisna.c sgbbrd.c sgbcon.c sgbequb.c sgbequ.c sgbrfs.c sgbsv.c sgbsvx.c sgbtf2.c sgbtrf.c sgbtrs.c sgebak.c sgebal.c sgebd2.c sgebrd.c sgecon.c sgeequb.c sgeequ.c sgees.c sgeesx.c sgeev.c sgeevx.c sgegs.c sgegv.c sgehd2.c sgehrd.c sgejsv.c sgelq2.c sgelqf.c sgelsd.c sgels.c sgelss.c sgelsx.c sgelsy.c sgemqrt.c sgeql2.c sgeqlf.c sgeqp3.c sgeqpf.c sgeqr2.c sgeqr2p.c sgeqrf.c sgeqrfp.c sgeqrt2.c sgeqrt3.c sgeqrt.c sgerfs.c sgerq2.c sgerqf.c sgesc2.c sgesdd.c sgesvd.c sgesv.c sgesvj.c sgesvx.c sgetc2.c sgetf2.c sgetrf.c sgetri.c sgetrs.c sggbak.c sggbal.c sgges.c sggesx.c sggev.c sggevx.c sggglm.c sgghrd.c sgglse.c sggqrf.c sggrqf.c sggsvd.c sggsvp.c sgsvj0.c sgsvj1.c sgtcon.c sgtrfs.c sgtsv.c sgtsvx.c sgttrf.c sgttrs.c sgtts2.c shgeqz.c shsein.c shseqr.c sisnan.c slabad.c slabrd.c slacn2.c slacon.c slacpy.c sladiv.c slae2.c slaebz.c slaed0.c slaed1.c slaed2.c slaed3.c slaed4.c slaed5.c slaed6.c slaed7.c slaed8.c slaed9.c slaeda.c slaein.c slaev2.c slaexc.c slag2d.c slag2.c sla_gerpvgrw.c slags2.c slagtf.c slagtm.c slagts.c slagv2.c slahqr.c slahr2.c slahrd.c slaic1.c slaisnan.c slaln2.c slals0.c slalsa.c slalsd.c slamrg.c slaneg.c slangb.c slange.c slangt.c slanhs.c slansb.c slansf.c slansp.c slanst.c slansy.c slantb.c slantp.c slantr.c slanv2.c slapll.c slapmr.c slapmt.c slapy2.c slapy3.c slaqgb.c slaqge.c slaqp2.c slaqps.c slaqr0.c slaqr1.c slaqr2.c slaqr3.c slaqr4.c slaqr5.c slaqsb.c slaqsp.c slaqsy.c slaqtr.c slar1v.c slar2v.c slarfb.c slarf.c slarfg.c slarfgp.c slarft.c slarfx.c slargv.c slarnv.c slarra.c slarrb.c slarrc.c slarrd.c slarre.c slarrf.c slarrj.c slarrk.c slarrr.c slarrv.c slartg.c slartgp.c slartgs.c slartv.c slaruv.c slarzb.c slarz.c slarzt.c slas2.c slascl.c slasd0.c slasd1.c slasd2.c slasd3.c slasd4.c slasd5.c slasd6.c slasd7.c slasd8.c slasda.c slasdq.c slasdt.c slaset.c slasq1.c slasq2.c slasq3.c slasq4.c slasq5.c slasq6.c slasr.c slasrt.c slassq.c slasv2.c slaswp.c slasy2.c slasyf.c slatbs.c slatdf.c slatps.c slatrd.c slatrs.c slatrz.c slatzm.c slauu2.c slauum.c sopgtr.c sopmtr.c sorbdb.c sorcsd.c sorg2l.c sorg2r.c sorgbr.c sorghr.c sorgl2.c sorglq.c sorgql.c sorgqr.c sorgr2.c sorgrq.c sorgtr.c sorm2l.c sorm2r.c sormbr.c sormhr.c sorml2.c sormlq.c sormql.c sormqr.c sormr2.c sormr3.c sormrq.c sormrz.c sormtr.c spbcon.c spbequ.c spbrfs.c spbstf.c spbsv.c spbsvx.c spbtf2.c spbtrf.c spbtrs.c spftrf.c spftri.c spftrs.c spocon.c spoequb.c spoequ.c sporfs.c sposv.c sposvx.c spotf2.c spotrf.c spotri.c spotrs.c sppcon.c sppequ.c spprfs.c sppsv.c sppsvx.c spptrf.c spptri.c spptrs.c spstf2.c spstrf.c sptcon.c spteqr.c sptrfs.c sptsv.c sptsvx.c spttrf.c spttrs.c sptts2.c srscl.c ssbevd.c ssbev.c ssbevx.c ssbgst.c ssbgvd.c ssbgv.c ssbgvx.c ssbtrd.c ssfrk.c sspcon.c sspevd.c sspev.c sspevx.c sspgst.c sspgvd.c sspgv.c sspgvx.c ssprfs.c sspsv.c sspsvx.c ssptrd.c ssptrf.c ssptri.c ssptrs.c sstebz.c sstedc.c sstegr.c sstein.c sstemr.c ssteqr.c ssterf.c sstevd.c sstev.c sstevr.c sstevx.c ssycon.c ssyconv.c ssyequb.c ssyevd.c ssyev.c ssyevr.c ssyevx.c ssygs2.c ssygst.c ssygvd.c ssygv.c ssygvx.c ssyrfs.c ssysv.c ssysvx.c ssyswapr.c ssytd2.c ssytf2.c ssytrd.c ssytrf.c ssytri2.c ssytri2x.c ssytri.c ssytrs2.c ssytrs.c stbcon.c stbrfs.c stbtrs.c stfsm.c stftri.c stfttp.c stfttr.c stgevc.c stgex2.c stgexc.c stgsen.c stgsja.c stgsna.c stgsy2.c stgsyl.c stpcon.c stpmqrt.c stpqrt2.c stpqrt.c stprfb.c stprfs.c stptri.c stptrs.c stpttf.c stpttr.c strcon.c strevc.c strexc.c strrfs.c strsen.c strsna.c strsyl.c strti2.c strtri.c strtrs.c strttf.c strttp.c stzrqf.c stzrzf.c)
+set(LAPACK_DOUBLE dlamch.c dbbcsd.c dbdsdc.c dbdsqr.c ddisna.c dgbbrd.c dgbcon.c dgbequb.c dgbequ.c dgbrfs.c dgbsv.c dgbsvx.c dgbtf2.c dgbtrf.c dgbtrs.c dgebak.c dgebal.c dgebd2.c dgebrd.c dgecon.c dgeequb.c dgeequ.c dgees.c dgeesx.c dgeev.c dgeevx.c dgegs.c dgegv.c dgehd2.c dgehrd.c dgejsv.c dgelq2.c dgelqf.c dgelsd.c dgels.c dgelss.c dgelsx.c dgelsy.c dgemqrt.c dgeql2.c dgeqlf.c dgeqp3.c dgeqpf.c dgeqr2.c dgeqr2p.c dgeqrf.c dgeqrfp.c dgeqrt2.c dgeqrt3.c dgeqrt.c dgerfs.c dgerq2.c dgerqf.c dgesc2.c dgesdd.c dgesvd.c dgesv.c dgesvj.c dgesvx.c dgetc2.c dgetf2.c dgetrf.c dgetri.c dgetrs.c dggbak.c dggbal.c dgges.c dggesx.c dggev.c dggevx.c dggglm.c dgghrd.c dgglse.c dggqrf.c dggrqf.c dggsvd.c dggsvp.c dgsvj0.c dgsvj1.c dgtcon.c dgtrfs.c dgtsv.c dgtsvx.c dgttrf.c dgttrs.c dgtts2.c dhgeqz.c dhsein.c dhseqr.c disnan.c dlabad.c dlabrd.c dlacn2.c dlacon.c dlacpy.c dladiv.c dlae2.c dlaebz.c dlaed0.c dlaed1.c dlaed2.c dlaed3.c dlaed4.c dlaed5.c dlaed6.c dlaed7.c dlaed8.c dlaed9.c dlaeda.c dlaein.c dlaev2.c dlaexc.c dlag2.c dlag2s.c dla_gerpvgrw.c dlags2.c dlagtf.c dlagtm.c dlagts.c dlagv2.c dlahqr.c dlahr2.c dlahrd.c dlaic1.c dlaisnan.c dlaln2.c dlals0.c dlalsa.c dlalsd.c dlamrg.c dlaneg.c dlangb.c dlange.c dlangt.c dlanhs.c dlansb.c dlansf.c dlansp.c dlanst.c dlansy.c dlantb.c dlantp.c dlantr.c dlanv2.c dlapll.c dlapmr.c dlapmt.c dlapy2.c dlapy3.c dlaqgb.c dlaqge.c dlaqp2.c dlaqps.c dlaqr0.c dlaqr1.c dlaqr2.c dlaqr3.c dlaqr4.c dlaqr5.c dlaqsb.c dlaqsp.c dlaqsy.c dlaqtr.c dlar1v.c dlar2v.c dlarfb.c dlarf.c dlarfg.c dlarfgp.c dlarft.c dlarfx.c dlargv.c dlarnv.c dlarra.c dlarrb.c dlarrc.c dlarrd.c dlarre.c dlarrf.c dlarrj.c dlarrk.c dlarrr.c dlarrv.c dlartg.c dlartgp.c dlartgs.c dlartv.c dlaruv.c dlarzb.c dlarz.c dlarzt.c dlas2.c dlascl.c dlasd0.c dlasd1.c dlasd2.c dlasd3.c dlasd4.c dlasd5.c dlasd6.c dlasd7.c dlasd8.c dlasda.c dlasdq.c dlasdt.c dlaset.c dlasq1.c dlasq2.c dlasq3.c dlasq4.c dlasq5.c dlasq6.c dlasr.c dlasrt.c dlassq.c dlasv2.c dlaswp.c dlasy2.c dlasyf.c dlat2s.c dlatbs.c dlatdf.c dlatps.c dlatrd.c dlatrs.c dlatrz.c dlatzm.c dlauu2.c dlauum.c dopgtr.c dopmtr.c dorbdb.c dorcsd.c dorg2l.c dorg2r.c dorgbr.c dorghr.c dorgl2.c dorglq.c dorgql.c dorgqr.c dorgr2.c dorgrq.c dorgtr.c dorm2l.c dorm2r.c dormbr.c dormhr.c dorml2.c dormlq.c dormql.c dormqr.c dormr2.c dormr3.c dormrq.c dormrz.c dormtr.c dpbcon.c dpbequ.c dpbrfs.c dpbstf.c dpbsv.c dpbsvx.c dpbtf2.c dpbtrf.c dpbtrs.c dpftrf.c dpftri.c dpftrs.c dpocon.c dpoequb.c dpoequ.c dporfs.c dposv.c dposvx.c dpotf2.c dpotrf.c dpotri.c dpotrs.c dppcon.c dppequ.c dpprfs.c dppsv.c dppsvx.c dpptrf.c dpptri.c dpptrs.c dpstf2.c dpstrf.c dptcon.c dpteqr.c dptrfs.c dptsv.c dptsvx.c dpttrf.c dpttrs.c dptts2.c drscl.c dsbevd.c dsbev.c dsbevx.c dsbgst.c dsbgvd.c dsbgv.c dsbgvx.c dsbtrd.c dsfrk.c dsgesv.c dspcon.c dspevd.c dspev.c dspevx.c dspgst.c dspgvd.c dspgv.c dspgvx.c dsposv.c dsprfs.c dspsv.c dspsvx.c dsptrd.c dsptrf.c dsptri.c dsptrs.c dstebz.c dstedc.c dstegr.c dstein.c dstemr.c dsteqr.c dsterf.c dstevd.c dstev.c dstevr.c dstevx.c dsycon.c dsyconv.c dsyequb.c dsyevd.c dsyev.c dsyevr.c dsyevx.c dsygs2.c dsygst.c dsygvd.c dsygv.c dsygvx.c dsyrfs.c dsysv.c dsysvx.c dsyswapr.c dsytd2.c dsytf2.c dsytrd.c dsytrf.c dsytri2.c dsytri2x.c dsytri.c dsytrs2.c dsytrs.c dtbcon.c dtbrfs.c dtbtrs.c dtfsm.c dtftri.c dtfttp.c dtfttr.c dtgevc.c dtgex2.c dtgexc.c dtgsen.c dtgsja.c dtgsna.c dtgsy2.c dtgsyl.c dtpcon.c dtpmqrt.c dtpqrt2.c dtpqrt.c dtprfb.c dtprfs.c dtptri.c dtptrs.c dtpttf.c dtpttr.c dtrcon.c dtrevc.c dtrexc.c dtrrfs.c dtrsen.c dtrsna.c dtrsyl.c dtrti2.c dtrtri.c dtrtrs.c dtrttf.c dtrttp.c dtzrqf.c dtzrzf.c dzsum1.c iladlc.c iladlr.c ilazlc.c ilazlr.c izmax1.c zbbcsd.c zbdsqr.c zcgesv.c zcposv.c zdrscl.c zgbbrd.c zgbcon.c zgbequb.c zgbequ.c zgbrfs.c zgbsv.c zgbsvx.c zgbtf2.c zgbtrf.c zgbtrs.c zgebak.c zgebal.c zgebd2.c zgebrd.c zgecon.c zgeequb.c zgeequ.c zgees.c zgeesx.c zgeev.c zgeevx.c zgegs.c zgegv.c zgehd2.c zgehrd.c zgelq2.c zgelqf.c zgelsd.c zgels.c zgelss.c zgelsx.c zgelsy.c zgemqrt.c zgeql2.c zgeqlf.c zgeqp3.c zgeqpf.c zgeqr2.c zgeqr2p.c zgeqrf.c zgeqrfp.c zgeqrt2.c zgeqrt3.c zgeqrt.c zgerfs.c zgerq2.c zgerqf.c zgesc2.c zgesdd.c zgesvd.c zgesv.c zgesvx.c zgetc2.c zgetf2.c zgetrf.c zgetri.c zgetrs.c zggbak.c zggbal.c zgges.c zggesx.c zggev.c zggevx.c zggglm.c zgghrd.c zgglse.c zggqrf.c zggrqf.c zggsvd.c zggsvp.c zgtcon.c zgtrfs.c zgtsv.c zgtsvx.c zgttrf.c zgttrs.c zgtts2.c zhbevd.c zhbev.c zhbevx.c zhbgst.c zhbgvd.c zhbgv.c zhbgvx.c zhbtrd.c zhecon.c zheequb.c zheevd.c zheev.c zheevr.c zheevx.c zhegs2.c zhegst.c zhegvd.c zhegv.c zhegvx.c zherfs.c zhesv.c zhesvx.c zheswapr.c zhetd2.c zhetf2.c zhetrd.c zhetrf.c zhetri2.c zhetri2x.c zhetri.c zhetrs2.c zhetrs.c zhfrk.c zhgeqz.c zhpcon.c zhpevd.c zhpev.c zhpevx.c zhpgst.c zhpgvd.c zhpgv.c zhpgvx.c zhprfs.c zhpsv.c zhpsvx.c zhptrd.c zhptrf.c zhptri.c zhptrs.c zhsein.c zhseqr.c zlabrd.c zlacgv.c zlacn2.c zlacon.c zlacp2.c zlacpy.c zlacrm.c zlacrt.c zladiv.c zlaed0.c zlaed7.c zlaed8.c zlaein.c zlaesy.c zlaev2.c zlag2c.c zla_gerpvgrw.c zlags2.c zlagtm.c zlahef.c zlahqr.c zlahr2.c zlahrd.c zlaic1.c zlals0.c zlalsa.c zlalsd.c zlangb.c zlange.c zlangt.c zlanhb.c zlanhe.c zlanhf.c zlanhp.c zlanhs.c zlanht.c zlansb.c zlansp.c zlansy.c zlantb.c zlantp.c zlantr.c zlapll.c zlapmr.c zlapmt.c zlaqgb.c zlaqge.c zlaqhb.c zlaqhe.c zlaqhp.c zlaqp2.c zlaqps.c zlaqr0.c zlaqr1.c zlaqr2.c zlaqr3.c zlaqr4.c zlaqr5.c zlaqsb.c zlaqsp.c zlaqsy.c zlar1v.c zlar2v.c zlarcm.c zlarfb.c zlarf.c zlarfg.c zlarfgp.c zlarft.c zlarfx.c zlargv.c zlarnv.c zlarrv.c zlartg.c zlartv.c zlarzb.c zlarz.c zlarzt.c zlascl.c zlaset.c zlasr.c zlassq.c zlaswp.c zlasyf.c zlat2c.c zlatbs.c zlatdf.c zlatps.c zlatrd.c zlatrs.c zlatrz.c zlatzm.c zlauu2.c zlauum.c zpbcon.c zpbequ.c zpbrfs.c zpbstf.c zpbsv.c zpbsvx.c zpbtf2.c zpbtrf.c zpbtrs.c zpftrf.c zpftri.c zpftrs.c zpocon.c zpoequb.c zpoequ.c zporfs.c zposv.c zposvx.c zpotf2.c zpotrf.c zpotri.c zpotrs.c zppcon.c zppequ.c zpprfs.c zppsv.c zppsvx.c zpptrf.c zpptri.c zpptrs.c zpstf2.c zpstrf.c zptcon.c zpteqr.c zptrfs.c zptsv.c zptsvx.c zpttrf.c zpttrs.c zptts2.c zrot.c zspcon.c zspmv.c zspr.c zsprfs.c zspsv.c zspsvx.c zsptrf.c zsptri.c zsptrs.c zstedc.c zstegr.c zstein.c zstemr.c zsteqr.c zsycon.c zsyconv.c zsyequb.c zsymv.c zsyr.c zsyrfs.c zsysv.c zsysvx.c zsyswapr.c zsytf2.c zsytrf.c zsytri2.c zsytri2x.c zsytri.c zsytrs2.c zsytrs.c ztbcon.c ztbrfs.c ztbtrs.c ztfsm.c ztftri.c ztfttp.c ztfttr.c ztgevc.c ztgex2.c ztgexc.c ztgsen.c ztgsja.c ztgsna.c ztgsy2.c ztgsyl.c ztpcon.c ztpmqrt.c ztpqrt2.c ztpqrt.c ztprfb.c ztprfs.c ztptri.c ztptrs.c ztpttf.c ztpttr.c ztrcon.c ztrevc.c ztrexc.c ztrrfs.c ztrsen.c ztrsna.c ztrsyl.c ztrti2.c ztrtri.c ztrtrs.c ztrttf.c ztrttp.c ztzrqf.c ztzrzf.c zunbdb.c zuncsd.c zung2l.c zung2r.c zungbr.c zunghr.c zungl2.c zunglq.c zungql.c zungqr.c zungr2.c zungrq.c zungtr.c zunm2l.c zunm2r.c zunmbr.c zunmhr.c zunml2.c zunmlq.c zunmql.c zunmqr.c zunmr2.c zunmr3.c zunmrq.c zunmrz.c zunmtr.c zupgtr.c zupmtr.c)
+set(LAPACK_AUX chla_transtype.c ieeeck.c iladiag.c ilaenv.c ilaprec.c ilatrans.c ilauplo.c ilaver.c iparmq.c lsamen.c xerbla_array.c xerbla.c)
+foreach(FILE ${BLAS_SINGLE} ${BLAS_DOUBLE} ${BLAS_AUX})
+  list(APPEND BLAS_SRC blas/${FILE})
+endforeach()
+foreach(FILE ${LAPACK_SINGLE} ${LAPACK_DOUBLE} ${LAPACK_AUX})
+  list(APPEND LAPACK_SRC lapack/${FILE})
+endforeach()
+
+add_library(f2cblas SHARED ${BLAS_SRC})
+add_library(f2clapack SHARED ${LAPACK_SRC})
+target_link_libraries(f2clapack f2cblas)
+
diff --git a/contrib/mobile/utils/f2cblaslapack_ios_makefile b/contrib/mobile/utils/f2cblaslapack_ios_makefile
new file mode 100644
index 0000000000000000000000000000000000000000..d8698f13afbf8a41852965b70435db8e1e43031f
--- /dev/null
+++ b/contrib/mobile/utils/f2cblaslapack_ios_makefile
@@ -0,0 +1,45 @@
+
+
+########################################################################################
+# f2cblaslapack: BLAS/LAPACK in C
+########################################################################################
+
+ALL: blas_lib lapack_lib
+
+########################################################################################
+# Specify options to compile and create libraries
+########################################################################################
+CC         = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
+COPTFLAGS  = -O -arch armv7 -arch armv7s -arch arm64 -fembed-bitcode -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk -miphoneos-version-min=7.0
+CNOOPT     = -O0 -arch armv7 -arch armv7s -arch arm64 -fembed-bitcode -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk -miphoneos-version-min=7.0
+RM         = /bin/rm
+AR         = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar
+AR_FLAGS   = cr
+LIB_SUFFIX = a
+RANLIB     = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib
+
+########################################################################################
+# compile the source files and create the blas and lapack libs
+########################################################################################
+
+BLAS_LIB_NAME       = libf2cblas.$(LIB_SUFFIX)
+LAPACK_LIB_NAME     = libf2clapack.$(LIB_SUFFIX)
+MAKE_OPTIONS        =  CC="$(CC)" COPTFLAGS="$(COPTFLAGS)" CNOOPT="$(CNOOPT)" AR="$(AR)" AR_FLAGS="$(AR_FLAGS)" RM="$(RM)"
+MAKE_OPTIONS_BLAS   = $(MAKE_OPTIONS) LIBNAME="$(BLAS_LIB_NAME)"
+MAKE_OPTIONS_LAPACK = $(MAKE_OPTIONS) LIBNAME="$(LAPACK_LIB_NAME)"
+
+blas_lib:
+	-@cd blas;   $(MAKE) lib $(MAKE_OPTIONS_BLAS)
+	-@$(RANLIB) $(BLAS_LIB_NAME)
+
+lapack_lib:
+	-@cd lapack; $(MAKE) lib $(MAKE_OPTIONS_LAPACK)
+	-@$(RANLIB) $(LAPACK_LIB_NAME)
+
+clean: cleanblaslapck cleanlib
+
+cleanblaslapck:
+	$(RM) */*.o
+
+cleanlib:
+	$(RM) ./*.a ./*.lib
diff --git a/contrib/mobile/utils/iOS.cmake b/contrib/mobile/utils/iOS.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..abbe836dababd71de6a9c2fae0eab915637d2f68
--- /dev/null
+++ b/contrib/mobile/utils/iOS.cmake
@@ -0,0 +1,130 @@
+# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake
+# files which are included with CMake 2.8.4
+# It has been altered for iOS development
+
+# Options:
+#
+# IOS_PLATFORM = OS (default) or SIMULATOR
+#   This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders
+#   OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch.
+#   SIMULATOR - used to build for the Simulator platforms, which have an x86 arch.
+#
+# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder
+#   By default this location is automatcially chosen based on the IOS_PLATFORM value above.
+#   If set manually, it will override the default location and force the user of a particular Developer Platform
+#
+# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder
+#   By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value.
+#   In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path.
+#   If set manually, this will force the use of a specific SDK version
+
+# Standard settings
+set (CMAKE_SYSTEM_NAME Darwin)
+set (CMAKE_SYSTEM_VERSION 1 )
+set (UNIX True)
+set (APPLE True)
+set (IOS True)
+
+# Force the compilers to gcc for iOS - removed for Yosemite XCode
+#include (CMakeForceCompiler)
+#CMAKE_FORCE_C_COMPILER (gcc gcc)
+#CMAKE_FORCE_CXX_COMPILER (g++ g++)
+
+# Skip the platform compiler checks for cross compiling
+set (CMAKE_CXX_COMPILER_WORKS TRUE)
+set (CMAKE_C_COMPILER_WORKS TRUE)
+
+# All iOS/Darwin specific settings - some may be redundant
+set (CMAKE_SHARED_LIBRARY_PREFIX "lib")
+set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib")
+set (CMAKE_SHARED_MODULE_PREFIX "lib")
+set (CMAKE_SHARED_MODULE_SUFFIX ".so")
+set (CMAKE_MODULE_EXISTS 1)
+set (CMAKE_DL_LIBS "")
+
+set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
+set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ")
+set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}")
+set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
+
+# Hidden visibilty is required for cxx on iOS 
+#set (CMAKE_C_FLAGS "-O2")
+#set (CMAKE_CXX_FLAGS "-O2 -fvisibility=hidden -fvisibility-inlines-hidden")
+
+set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
+set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
+
+set (CMAKE_PLATFORM_HAS_INSTALLNAME 1)
+set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib")
+set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle")
+set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,")
+set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,")
+set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a")
+
+# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree
+# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache
+# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun)
+# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex
+if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
+	find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool)
+endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
+
+# Setup iOS platform
+if (NOT DEFINED IOS_PLATFORM)
+	set (IOS_PLATFORM "OS")
+endif (NOT DEFINED IOS_PLATFORM)
+set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform")
+
+# Check the platform selection and setup for developer root
+if (${IOS_PLATFORM} STREQUAL "OS")
+	set (IOS_PLATFORM_LOCATION "iPhoneOS.platform")
+elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR")
+	set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
+else (${IOS_PLATFORM} STREQUAL "OS")
+	message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR")
+endif (${IOS_PLATFORM} STREQUAL "OS")
+
+# Setup iOS developer location
+if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
+	set (CMAKE_IOS_DEVELOPER_ROOT "/Applications/Xcode.app/Contents/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
+endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
+set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform")
+
+# Find and use the most recent iOS sdk 
+if (NOT DEFINED CMAKE_IOS_SDK_ROOT)
+	file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*")
+	if (_CMAKE_IOS_SDKS) 
+		list (SORT _CMAKE_IOS_SDKS)
+		list (REVERSE _CMAKE_IOS_SDKS)
+		list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT)
+	else (_CMAKE_IOS_SDKS)
+		message (FATAL_ERROR "No iOS SDK's found in default seach path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.")
+	endif (_CMAKE_IOS_SDKS)
+	message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}")
+endif (NOT DEFINED CMAKE_IOS_SDK_ROOT)
+set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK")
+
+# Set the sysroot default to the most recent SDK
+set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support")
+
+# set the architecture for iOS - using ARCHS_STANDARD_32_BIT sets armv6,armv7 and appears to be XCode's standard. 
+# The other value that works is ARCHS_UNIVERSAL_IPHONE_OS but that sets armv7 only
+set (CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_32_BIT)" CACHE string  "Build architecture for iOS")
+
+# Set the find root to the iOS developer roots and to user defined paths
+set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string  "iOS find search path root")
+
+# default to searching for frameworks first
+set (CMAKE_FIND_FRAMEWORK FIRST)
+
+# set up the default search directories for frameworks
+set (CMAKE_SYSTEM_FRAMEWORK_PATH
+	${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks
+	${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks
+	${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks
+)
+
+# only search the iOS sdks, not the remainder of the host filesystem
+set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
+set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
diff --git a/contrib/mobile/utils/icon_rotate.svg b/contrib/mobile/utils/icon_rotate.svg
new file mode 100644
index 0000000000000000000000000000000000000000..179914a1bf7a1cef45e5b10b66933c670821257a
--- /dev/null
+++ b/contrib/mobile/utils/icon_rotate.svg
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Layer_3"
+   x="0px"
+   y="0px"
+   width="512"
+   height="512"
+   viewBox="0 0 512 512"
+   enable-background="new 0 0 100 100"
+   xml:space="preserve"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="icon_rotate.svg"><metadata
+   id="metadata9"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+   id="defs7" /><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="1222"
+   inkscape:window-height="811"
+   id="namedview5"
+   showgrid="true"
+   inkscape:zoom="1.1773328"
+   inkscape:cx="123.31917"
+   inkscape:cy="232.25322"
+   inkscape:window-x="101"
+   inkscape:window-y="13"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="Layer_3"
+   showguides="false"><inkscape:grid
+     type="xygrid"
+     id="grid3075"
+     empspacing="5"
+     visible="true"
+     enabled="true"
+     snapvisiblegridlinesonly="true" /></sodipodi:namedview>
+<g
+   id="g3034"
+   transform="translate(-1.3697408,411.47927)" />
+<path
+   style="fill:#007aff;fill-opacity:0.39215687"
+   inkscape:connector-curvature="0"
+   d="m 425.65,251.19 c 0,47.895 -21.025,93.085 -57.675,123.985 l -31.84,-37.77 c 25.49,-21.49 40.11,-52.915 40.11,-86.215 0,-59.895 -46.995,-108.875 -106.03,-112.355 H 246.05 l 20.35,51.52 c 0.685,1.725 0.125,3.695 -1.355,4.81 -1.48,1.12 -3.53,1.115 -5,-0.02 L 158.85,117.34 c -1.015,-0.785 -1.615,-1.985 -1.615,-3.275 0,-1.285 0.6,-2.495 1.615,-3.275 l 101.2,-77.8 c 0.74,-0.57 1.625,-0.85 2.515,-0.85 0.88,0 1.75,0.275 2.49,0.83 1.48,1.115 2.035,3.09 1.355,4.815 l -20.35,51.515 17.5,0.05 v -0.26 c 6.255,0 12.425,0.395 18.5,1.09 4.03,0.42 8.525,1.015 11.94,1.82 74.865,14.29 131.65,80.215 131.65,159.19 z m -177,66.74 c -1.47,-1.135 -3.52,-1.14 -5,-0.015 -1.48,1.115 -2.035,3.085 -1.355,4.81 l 20.35,51.52 H 238.48 c -59.025,-3.485 -106.025,-52.47 -106.025,-112.36 0,-31.165 12.44,-60.165 35.025,-81.655 l -34.06,-35.79 c -32.47999,30.91 -50.369989,72.615 -50.369989,117.445 0,78.97 56.779989,144.9 131.659989,159.19 3.42,0.805 7.915,1.4 11.94,1.82 6.075,0.695 12.24,1.09 18.5,1.09 v -0.26 l 17.5,0.05 -20.35,51.515 c -0.685,1.73 -0.125,3.705 1.355,4.815 0.74,0.555 1.61,0.83 2.49,0.83 0.89,0 1.77,-0.28 2.515,-0.85 L 349.85,402.28 c 1.015,-0.78 1.615,-1.99 1.615,-3.275 0,-1.285 -0.6,-2.49 -1.615,-3.27 L 248.65,317.93 z"
+   id="path3134" /></svg>
\ No newline at end of file
diff --git a/contrib/mobile/utils/icon_translate.svg b/contrib/mobile/utils/icon_translate.svg
new file mode 100644
index 0000000000000000000000000000000000000000..30650a9a34d969019b96d0ed2533df3e65366527
--- /dev/null
+++ b/contrib/mobile/utils/icon_translate.svg
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Layer_3"
+   x="0px"
+   y="0px"
+   width="512"
+   height="512"
+   viewBox="0 0 512 512"
+   enable-background="new 0 0 100 100"
+   xml:space="preserve"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="icon_translate.svg"><metadata
+   id="metadata9"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+   id="defs7" /><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="1223"
+   inkscape:window-height="851"
+   id="namedview5"
+   showgrid="true"
+   inkscape:zoom="1.1773328"
+   inkscape:cx="1.858191"
+   inkscape:cy="266.22832"
+   inkscape:window-x="101"
+   inkscape:window-y="13"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="Layer_3"
+   showguides="false"><inkscape:grid
+     type="xygrid"
+     id="grid3075"
+     empspacing="5"
+     visible="true"
+     enabled="true"
+     snapvisiblegridlinesonly="true" /></sodipodi:namedview>
+<g
+   id="g3034"
+   transform="translate(-1.3697408,411.47927)" />
+<path
+   style="fill:#007aff;fill-opacity:0.39215687"
+   inkscape:connector-curvature="0"
+   d="m 279.795,276.695 0,114.165 51.52,-20.35 c 1.725,-0.685 3.695,-0.125 4.81,1.355 1.12,1.48 1.115,3.53 -0.02,5 L 258.3,478.06 c -0.785,1.015 -1.985,1.615 -3.275,1.615 -1.285,0 -2.495,-0.6 -3.275,-1.615 l -77.8,-101.2 c -0.57,-0.74 -0.85,-1.625 -0.85,-2.515 0,-0.88 0.275,-1.75 0.83,-2.49 1.115,-1.48 3.09,-2.035 4.815,-1.355 l 51.515,20.35 0.05,-17.5 -0.26,-100 c 24.6704,-94.46905 5.19182,-149.0134 49.745,3.345 z m -49.54,-44.04 0,-114.165 -51.52,20.35 c -1.725,0.685 -3.695,0.125 -4.81,-1.355 -1.12,-1.48 -1.115,-3.53 0.02,-5 L 251.75,31.29 c 0.785,-1.015 1.985,-1.615 3.275,-1.615 1.285,0 2.495,0.6 3.275,1.615 l 77.8,101.2 c 0.57,0.74 0.85,1.625 0.85,2.515 0,0.88 -0.275,1.75 -0.83,2.49 -1.115,1.48 -3.09,2.035 -4.815,1.355 L 279.79,118.5 279.74,136 280,236 c -24.6704,94.46904 -5.19182,149.01342 -49.745,-3.345 z m 2.75,46.79 -114.165,0 20.35,51.52 c 0.685,1.725 0.125,3.695 -1.355,4.81 -1.48,1.12 -3.53,1.115 -5,-0.02 L 31.640001,257.95 c -1.015,-0.785 -1.615,-1.985 -1.615,-3.275 0,-1.285 0.6,-2.495 1.615,-3.275 L 132.84,173.6 c 0.74,-0.57 1.625,-0.85 2.515,-0.85 0.88,0 1.75,0.275 2.49,0.83 1.48,1.115 2.035,3.09 1.355,4.815 l -20.35,51.515 17.5,0.05 100,-0.26 c 94.46903,24.6704 149.01342,5.19182 -3.345,49.745 z m 44.04,-49.54 114.165,0 -20.35,-51.52 c -0.685,-1.725 -0.125,-3.695 1.355,-4.81 1.48,-1.12 3.53,-1.115 5,0.02 L 478.41,251.4 c 1.015,0.785 1.615,1.985 1.615,3.275 0,1.285 -0.6,2.495 -1.615,3.275 l -101.2,77.8 c -0.74,0.57 -1.625,0.85 -2.515,0.85 -0.88,0 -1.75,-0.275 -2.49,-0.83 -1.48,-1.115 -2.035,-3.09 -1.355,-4.815 l 20.35,-51.515 -17.5,-0.05 -100,0.26 c -94.46904,-24.6704 -149.01342,-5.19182 3.345,-49.745 z"
+   id="path3144" /></svg>
\ No newline at end of file
diff --git a/contrib/mobile/utils/ios_build.sh b/contrib/mobile/utils/ios_build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a68bd282414ed523873e39291d151446805e76fa
--- /dev/null
+++ b/contrib/mobile/utils/ios_build.sh
@@ -0,0 +1,119 @@
+#/usr/bin/env bash
+
+appname=Onelab
+enable_occ=1
+enable_simulator=0
+#buildtype=Debug
+buildtype=Release
+
+while [[ $# -gt 0 ]]; do
+  key="$1"
+  case $key in
+    -n|--name)
+      appname=$2
+      enable_occ=0
+      echo "Rebranding Onelab app as ${appname}"
+      shift # past argument
+      ;;
+    -s|--simulator)
+      enable_simulator=1
+      ;;
+    --no-occ)
+      enable_occ=0
+      ;;
+    *)
+      echo "Usage: $0 [-n|--name appname] [-s|--simulator] [--no-occ]"
+      exit 1
+      ;;
+  esac
+  shift # past argument or value
+done
+
+ios=ios
+iphoneos=iphoneos
+iphoneos_version_min=-miphoneos-version-min=8.0
+if [ $enable_simulator != 0 ]; then
+  ios=iossimulator
+  iphoneos=iphonesimulator
+  iphoneos_version_min=
+fi
+
+gmsh_git="${HOME}/src/gmsh/"
+getdp_git="${HOME}/src/getdp/"
+frameworks_dir="${HOME}/src/gmsh/contrib/mobile/frameworks_${ios}/"
+petsc_framework="$frameworks_dir/petsc.framework"
+slepc_framework="$frameworks_dir/slepc.framework"
+gmsh_framework="$frameworks_dir/gmsh.framework"
+getdp_framework="$frameworks_dir/getdp.framework"
+occt_framework="$frameworks_dir/occt.framework"
+
+if [ "$appname" != "Onelab" ] ; then
+  models_dir="${HOME}/src/getdp/benchmarks_private"
+else
+  models_dir="${HOME}/src/onelab_doc"
+fi
+if [ -f ${models_dir}/cleanup.sh ]; then
+  cd ${models_dir} && ./cleanup.sh
+fi
+
+if [ $enable_simulator != 0 ]; then
+  cmake_default="-DDEFAULT=0 -DENABLE_PRIVATE_API=1 -DCMAKE_TOOLCHAIN_FILE=$gmsh_git/contrib/mobile/utils/iOS.cmake -DIOS_PLATFORM=SIMULATOR -DENABLE_BUILD_IOS=1 -DCMAKE_BUILD_TYPE=${buildtype} -DCMAKE_OSX_ARCHITECTURES=x86_64 -GXcode"
+else
+  cmake_default="-DDEFAULT=0 -DENABLE_PRIVATE_API=1 -DCMAKE_TOOLCHAIN_FILE=$gmsh_git/contrib/mobile/utils/iOS.cmake -DIOS_PLATFORM=OS -DENABLE_BUILD_IOS=1 -DCMAKE_BUILD_TYPE=${buildtype} -DCMAKE_OSX_ARCHITECTURES=armv7;armv7s;arm64 -GXcode"
+fi
+
+build_cmd="xcodebuild -target lib -configuration ${buildtype}"
+headers_cmd="xcodebuild -target get_headers -configuration ${buildtype}"
+
+function check {
+  return_code=$?
+  if [ $return_code != 0 ]; then
+    echo "last command failed (return $return_code)"
+    exit $return_code
+  fi
+}
+
+# build gmsh framework
+cd $gmsh_git && git pull
+mkdir -p $gmsh_git/build_${ios}
+cd $gmsh_git/build_${ios}
+cmake $cmake_default -DENABLE_BLAS_LAPACK=1 -DENABLE_BUILD_LIB=1 -DENABLE_MATHEX=1 -DENABLE_MESH=1 -DENABLE_ONELAB=1 -DENABLE_PARSER=1 -DENABLE_POST=1 -DENABLE_PLUGINS=1 -DENABLE_ANN=1 -DENABLE_KBIPACK=1 -DENABLE_GMP=0 -DENABLE_ZIPPER=1 -DENABLE_OCC=$enable_occ -DOCC_LIBS="$occt_framework/occt" -DOCC_INC="$occt_framework/Headers/" ..
+check
+$build_cmd OTHER_CFLAGS="${iphoneos_version_min} -fembed-bitcode" OTHER_CPLUSPLUSFLAGS="${iphoneos_version_min} -fembed-bitcode -std=c++11"
+check
+$headers_cmd
+mkdir -p $gmsh_framework/Headers
+cp $gmsh_git/build_${ios}/${buildtype}-${iphoneos}/libgmsh.a $gmsh_framework/gmsh
+cd $gmsh_framework/Headers
+cp $gmsh_git/build_${ios}/Headers/*.h $gmsh_git/build_${ios}/Headers/gmsh/* .
+ln -s . gmsh
+
+# build getdp framework
+cd $getdp_git && git pull
+mkdir -p $getdp_git/build_${ios}
+cd $getdp_git/build_${ios}
+PETSC_DIR= PETSC_ARCH= SLEPC_DIR= cmake $cmake_default -DENABLE_BLAS_LAPACK=1 -DENABLE_BUILD_LIB=1 -DENABLE_GMSH=1 -DENABLE_KERNEL=1 -DENABLE_PETSC=1 -DPETSC_INC="$petsc_framework/Headers/" -DPETSC_LIBS="$petsc_framework/petsc" -DENABLE_SLEPC=1 -DSLEPC_INC="$slepc_framework/Headers/" -DSLEPC_LIB="$slepc_framework/slepc" -DGMSH_INC="$gmsh_framework/Headers/" -DGMSH_LIB="$gmsh_framework/gmsh" ..
+check
+$build_cmd OTHER_CFLAGS="${iphoneos_version_min} -fembed-bitcode" OTHER_CPLUSPLUSFLAGS="${iphoneos_version_min} -fembed-bitcode"
+check
+$headers_cmd
+mkdir -p $getdp_framework/Headers
+cp $getdp_git/build_${ios}/${buildtype}-${iphoneos}/libgetdp.a $getdp_framework/getdp
+cd $getdp_framework/Headers
+cp $getdp_git/build_${ios}/Headers/*.h $getdp_git/build_${ios}/Headers/getdp/* .
+
+# create xcode project
+mkdir $gmsh_git/contrib/mobile/build_${ios}_${appname}
+cd $gmsh_git/contrib/mobile/build_${ios}_${appname}
+cmake -DMODELS_DIR="$models_dir" -DCMAKE_INCLUDE_PATH="$frameworks_dir" -DAPPNAME:STRING=${appname} ..
+make xcodeProject
+
+if [ $enable_simulator != 0 ]; then
+  # change blas/lapack for simulator
+  sed -e "s|lastKnownFileType = archive.ar; name = libf2cblas.a; path = ${appname}/frameworks/petsc.framework/libf2cblas.a;|lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework;|" -i "" $gmsh_git/contrib/mobile/build_iossimulator_${appname}/${appname}/${appname}.xcodeproj/project.pbxproj;
+  sed -e "s|lastKnownFileType = archive.ar; name = libf2clapack.a; path = ${appname}/frameworks/petsc.framework/libf2clapack.a;|lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework;|" -i "" $gmsh_git/contrib/mobile/build_iossimulator_${appname}/${appname}/${appname}.xcodeproj/project.pbxproj;
+fi
+
+#TODO
+#xcodebuild -project "Onelab" -target "Onelab" -configuration Release
+#xcrun -sdk iphoneos PackageApplication -v "Onelab.app" -o "Onelab.ipa" --sign "iPhone Distribution: Your Signature\" --embed enterprise.mobileprovision
diff --git a/contrib/mobile/utils/make_icon_ios.sh b/contrib/mobile/utils/make_icon_ios.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3af911c87303952de480fc6514610909c7710d75
--- /dev/null
+++ b/contrib/mobile/utils/make_icon_ios.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+icon_source=../../../utils/icons/gmsh_mobile_1024x1024.png
+bg=black
+
+#icon_source=$HOME/tex/proposals/bbemg/icons/bbemg-logo-white.png
+#bg=white
+
+mkdir -p AppIcon.appiconset
+# App icons (iPad)
+convert -scale 152 ${icon_source} AppIcon.appiconset/icon_app_ipad_retina.png
+convert -scale 76 ${icon_source} AppIcon.appiconset/icon_app_ipad.png
+# App icons (iPad Pro)
+convert -scale 167 ${icon_source} AppIcon.appiconset/icon_app_ipad_pro.png
+# App icon (iPhone)
+convert -scale 120 ${icon_source} AppIcon.appiconset/icon_app_iphone_retina.png
+# App icon (iOS marketing)
+cp ${icon_source} AppIcon.appiconset/icon_app_ios_marketing.png
diff --git a/contrib/mobile/utils/make_mobile_zip_from_geo_files.sh b/contrib/mobile/utils/make_mobile_zip_from_geo_files.sh
new file mode 100755
index 0000000000000000000000000000000000000000..07597536e428f0e952af84ef40535af42cfe848a
--- /dev/null
+++ b/contrib/mobile/utils/make_mobile_zip_from_geo_files.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+if [ $# -lt 1 ]; then
+  echo "Usage: $0 file1.geo file2.geo ..." 1>&2;
+  exit 1;
+fi
+
+name=demos_boolean
+
+files=$*
+rm -rf ${name}
+mkdir ${name}
+cd $name
+
+cp ../*.step .
+cat <<EOT >> infos.xml
+<?xml version="1.0" encoding="utf-8"?>
+<models>
+EOT
+
+cat <<EOT >> screenshot.geo
+Print.Width = 1014;
+Print.Height = 1014;
+Print.Background = 1;
+General.TrackballQuaternion0 = -0.09134439936266693;
+General.TrackballQuaternion1 = 0.09382793879350552;
+General.TrackballQuaternion2 = 0.02293507983466721;
+General.TrackballQuaternion3 = 0.9911238574062343;
+General.Orthographic = 0;
+Mesh 2;
+Draw;
+Save StrCat("screenshot_", StrPrefix(StrRelative(General.FileName)), ".png") ;
+SystemCall StrCat("convert -scale 128 screenshot_", StrPrefix(StrRelative(General.FileName)),
+  ".png screenshot_", StrPrefix(StrRelative(General.FileName)), "_128.png");
+Exit;
+EOT
+
+for file in $files ; do
+    cp ../$file .
+    /Applications/Gmsh.app/Contents/MacOS/gmsh ${file} screenshot.geo
+    echo "<model>" >> infos.xml
+    echo "<title>OpenCASCADE demo: ${file%.geo}</title>" >> infos.xml
+    echo "<summary>gmsh/demos/boolean/${file}</summary>" >> infos.xml
+    echo "<file type=\"geo\">$file</file>" >> infos.xml
+    echo "<preview type=\"png\">screenshot_${file%.geo}_128.png</preview>" >> infos.xml
+    echo "</model>" >> infos.xml
+    rm -f screenshot_${file%.geo}.png
+    cat <<EOT > ${file%.geo}.pro
+DefineConstant[
+  R_ = {"", Name "GetDP/1ResolutionChoices", Visible 0},
+  C_ = {"", Name "GetDP/9ComputeCommand", Visible 0},
+  P_ = {"", Name "GetDP/2PostOperationChoices", Visible 0}
+];
+EOT
+done
+
+cat <<EOT >> infos.xml
+</models>
+EOT
+
+cd ..
+rm ${name}.zip 
+zip -r ${name}.zip ${name}
diff --git a/contrib/mobile/utils/make_screenshot.geo b/contrib/mobile/utils/make_screenshot.geo
new file mode 100644
index 0000000000000000000000000000000000000000..8860766c6b5e9594ebc2ea94a9f6adb3ca6a114f
--- /dev/null
+++ b/contrib/mobile/utils/make_screenshot.geo
@@ -0,0 +1,11 @@
+// Merge this once you have a nice view of the model to generate the master
+// 1024x1024 screenshot for the mobile app, as well as the scaled 128x128 thumbnail
+
+Print.Width = 1014;
+Print.Height = 1014;
+Print.Background = 1;
+
+model = AbsolutePath(General.FileName);
+dir = DirName(model);
+Save StrCat(dir, "infos_large.png");
+SystemCall StrCat("convert -scale 128 ", dir, "infos_large.png ", dir, "infos.png");
diff --git a/contrib/mobile/utils/merge_static_libs.sh b/contrib/mobile/utils/merge_static_libs.sh
new file mode 100755
index 0000000000000000000000000000000000000000..839ee7d6b1d4456cdf050d7f34962f87246f7934
--- /dev/null
+++ b/contrib/mobile/utils/merge_static_libs.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+archs="i386 x86_64"
+#archs="armv7 armv7s arm64"
+
+mkdir -p tmplib
+for arch in $archs; do
+  mkdir -p $arch
+  for i in $*; do
+    lipo $i -thin $arch -o ${i}_${arch}
+    cd $arch
+    ar x ../${i}_${arch}
+    cd ..
+    rm ${i}_${arch}
+  done
+  ar r tmplib/${arch}.a ${arch}/*.o
+  rm -rf ${arch}
+done
+lipo -create tmplib/*.a -output merged_petsc_lib.a
+rm -rf tmplib
diff --git a/contrib/mobile/utils/microlight.js b/contrib/mobile/utils/microlight.js
new file mode 100644
index 0000000000000000000000000000000000000000..e3f21fad2c80512746e16b0a23998e6a4be64f8e
--- /dev/null
+++ b/contrib/mobile/utils/microlight.js
@@ -0,0 +1,209 @@
+/**
+ * Modified version of microlight for GetDP/Gmsh - october 2016
+ *
+ * @fileoverview microlight - syntax highlightning library
+ * @version 0.0.7
+ *
+ * @license MIT, see http://github.com/asvd/microlight
+ * @copyright 2016 asvd <heliosframework@gmail.com>
+ *
+ * Code structure aims at minimizing the compressed library size
+ */
+
+
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        define(['exports'], factory);
+    } else if (typeof exports !== 'undefined') {
+        factory(exports);
+    } else {
+        factory((root.microlight = {}));
+    }
+}(this, function (exports) {
+    // for better compression
+    var _window       = window,
+        _document     = document,
+        appendChild   = 'appendChild',
+        test          = 'test',
+        // style and color templates
+        textShadow    = ';text-shadow:',
+        opacity       = 'opacity:.',
+        _0px_0px      = ' 0px 0px ',
+        _3px_0px_5    = '3px 0px 5',
+        brace         = ')',
+
+        i,
+        microlighted,
+        el;  // current microlighted element to run through
+
+
+    
+    var reset = function(cls) {
+        // nodes to highlight
+        microlighted = _document.getElementsByClassName(cls||'microlight');
+
+        for (i = 0; el = microlighted[i++];) {
+            var text  = el.textContent,
+                pos   = 0,       // current position
+                next1 = text[0], // next character
+                chr   = 1,       // current character
+                prev1,           // previous character
+                prev2,           // the one before the previous
+                token =          // current token content
+                el.innerHTML = '',  // (and cleaning the node)
+                
+                // current token type:
+                //  0: anything else (whitespaces / newlines)
+                //  1: operator or brace
+                //  2: closing braces (after which '/' is division not regex)
+                //  3: (key)word
+                //  4: regex
+                //  5: string starting with "
+                //  6: string starting with '
+                //  7: xml comment  <!-- -->
+                //  8: multiline comment /* */
+                //  9: single-line comment starting with two slashes //
+                // 10: single-line comment starting with hash #
+                tokenType = 0,
+
+                // kept to determine between regex and division
+                lastTokenType,
+                // flag determining if token is multi-character
+                multichar,
+                node,
+
+                // calculating the colors for the style templates
+                colorArr = /(\d*\, \d*\, \d*)(, ([.\d]*))?/g.exec(
+                    _window.getComputedStyle(el).color
+                ),
+                pxColor = 'px rgba('+colorArr[1]+',',
+                alpha = colorArr[3]||1;
+
+            // running through characters and highlighting
+            while (prev2 = prev1,
+                   // escaping if needed (with except for comments)
+                   // pervious character will not be therefore
+                   // recognized as a token finalize condition
+                   prev1 = tokenType < 7 && prev1 == '\\' ? 1 : chr
+            ) {
+                chr = next1;
+                next1=text[++pos];
+                multichar = token.length > 1;
+
+                // checking if current token should be finalized
+                if (!chr  || // end of content
+                    // types 9-10 (single-line comments) end with a
+                    // newline
+                    (tokenType > 8 && chr == '\n') ||
+                    [ // finalize conditions for other token types
+                        // 0: whitespaces
+                        /\S/[test](chr),  // merged together
+                        // 1: operators
+                        1,                // consist of a single character
+                        // 2: braces
+                        1,                // consist of a single character
+                        // 3: (key)word
+                        !/[$\w]/[test](chr),
+                        // 4: regex
+                        (prev1 == '/' || prev1 == '\n') && multichar,
+                        // 5: string with "
+                        prev1 == '"' && multichar,
+                        // 6: string with '
+                        prev1 == "'" && multichar,
+                        // 7: xml comment
+                        text[pos-4]+prev2+prev1 == '-->',
+                        // 8: multiline comment
+                        prev2+prev1 == '*/'
+                    ][tokenType]
+                ) {
+                    // appending the token to the result
+                    if (token) {
+                        // remapping token type into style
+                        // (some types are highlighted similarly)
+                        el[appendChild](
+                            node = _document.createElement('span')
+                        ).setAttribute('style', [
+                            // 0: not formatted
+                            '',
+                            // 1: keywords
+                            'font-weight:bold;color:#00c;',
+                            // 2: punctuation
+                            opacity + 6,
+                            // 3: strings and regexps
+                            'color: #a08;'+
+                            opacity + 8,
+                            // 4: comments
+                            'font-style:italic;color: #b00;'+
+                            opacity + 8
+                        ][
+                            // not formatted
+                            !tokenType ? 0 :
+                            // punctuation
+                            tokenType < 3 ? 2 :
+                            // comments
+                            tokenType > 6 ? 4 :
+                            // regex and strings
+                            tokenType > 3 ? 3 :
+                            // otherwise tokenType == 3, (key)word
+                            // (1 if regexp matches, 0 otherwise)
+                                + /^(If|Else|ElseIf|EndIf|Include|For|EndFor|Include|Macro|Return)$/[test](token)
+                        ]);
+
+                        node[appendChild](_document.createTextNode(token));
+                    }
+
+                    // saving the previous token type
+                    // (skipping whitespaces and comments)
+                    lastTokenType =
+                        (tokenType && tokenType < 7) ?
+                        tokenType : lastTokenType;
+
+                    // initializing a new token
+                    token = '';
+
+                    // determining the new token type (going up the
+                    // list until matching a token type start
+                    // condition)
+                    tokenType = 11;
+                    while (![
+                        1,                   //  0: whitespace
+                                             //  1: operator or braces
+                        /[\/{}[(\-+*=<>:;|\\.,?!&@~]/[test](chr),
+                        /[\])]/[test](chr),  //  2: closing brace
+                        /[$\w]/[test](chr),  //  3: (key)word
+                        chr == '/' &&        //  4: regex
+                            // previous token was an
+                            // opening brace or an
+                            // operator (otherwise
+                            // division, not a regex)
+                            (lastTokenType < 2) &&
+                            // workaround for xml
+                            // closing tags
+                            prev1 != '<',
+                        chr == '"',          //  5: string with "
+                        chr == "'",          //  6: string with '
+                                             //  7: xml comment
+                        chr+next1+text[pos+1]+text[pos+2] == '<!--',
+                        chr+next1 == '/*',   //  8: multiline comment
+                        chr+next1 == '//',   //  9: single-line comment
+                        chr+next1 == '//'    // 10: hash-style comment -- removed!
+                    ][--tokenType]);
+                }
+
+                token += chr;
+            }
+        }
+    }
+
+    exports.reset = reset;
+
+    if (_document.readyState == 'complete') {
+        reset();
+    } else {
+        _window.addEventListener('load', function(){reset()}, 0);
+    }
+}));
+
+// then compress with https://jscompress.com and escape \ " '
+
+//!function(a,b){\"function\"==typeof define&&define.amd?define([\"exports\"],b):b(\"undefined\"!=typeof exports?exports:a.microlight={})}(this,function(a){var k,l,m,b=window,c=document,d=\"appendChild\",e=\"test\",g=\"opacity:.\",n=function(a){for(l=c.getElementsByClassName(a||\"microlight\"),k=0;m=l[k++];){var n,o,r,s,t,f=m.textContent,h=0,i=f[0],j=1,p=m.innerHTML=\"\",q=0,u=/(\\d*\\, \\d*\\, \\d*)(, ([.\\d]*))?/g.exec(b.getComputedStyle(m).color);for(\"px rgba(\"+u[1]+\",\",u[3]||1;o=n,n=q<7&&\"\\\\\"==n?1:j;){if(j=i,i=f[++h],s=p.length>1,!j||q>8&&\"\\n\"==j||[/\\S/[e](j),1,1,!/[$\\w]/[e](j),(\"/\"==n||\"\\n\"==n)&&s,\'\"\'==n&&s,\"\'\"==n&&s,f[h-4]+o+n==\"-->\",o+n==\"*/\"][q])for(p&&(m[d](t=c.createElement(\"span\")).setAttribute(\"style\",[\"\",\"font-weight:bold;color:#00c;\",g+6,\"color: #a08;\"+g+8,\"font-style:italic;color: #b00;\"+g+8][q?q<3?2:q>6?4:q>3?3:+/^(If|Else|ElseIf|EndIf|Include|For|EndFor|Include|Macro|Return)$/[e](p):0]),t[d](c.createTextNode(p))),r=q&&q<7?q:r,p=\"\",q=11;![1,/[\\/{}[(\\-+*=<>:;|\\\\.,?!&@~]/[e](j),/[\\])]/[e](j),/[$\\w]/[e](j),\"/\"==j&&r<2&&\"<\"!=n,\'\"\'==j,\"\'\"==j,j+i+f[h+1]+f[h+2]==\"<!--\",j+i==\"/*\",j+i==\"//\",j+i==\"//\"][--q];);p+=j}}};a.reset=n,\"complete\"==c.readyState?n():b.addEventListener(\"load\",function(){n()},0)});
diff --git a/contrib/mobile/utils/occt_configuration.txt b/contrib/mobile/utils/occt_configuration.txt
new file mode 100644
index 0000000000000000000000000000000000000000..151c375bd94cbaeda06fe83ac50babcf900f2ce1
--- /dev/null
+++ b/contrib/mobile/utils/occt_configuration.txt
@@ -0,0 +1,21 @@
+####### configuration for OCCT 7.1 #######
+
+################# iOS: ####
+cmake -DCMAKE_TOOLCHAIN_FILE=~/src/gmsh/contrib/mobile/utils/iOS.cmake -DAppkit_LIB="" -DOpenGlLibs_LIB="" -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="armv7;armv7s;arm64" -DCMAKE_SIZEOF_VOID_P=8 -GXcode -DBUILD_MODULE_Draw=0 -DBUILD_MODULE_Visualization=0 -DBUILD_LIBRARY_TYPE=Static ..
+
+xcodebuild -configuration Release OTHER_CFLAGS="-miphoneos-version-min=8.0 -fembed-bitcode" OTHER_CPLUSPLUSFLAGS="-miphoneos-version-min=8.0 -fembed-bitcode -std=c++11"
+
+# Then combine all static libs using merge_static_libs.sh and put it in OCCT.framework
+
+################## iOS Simulator: ####
+cmake -DCMAKE_TOOLCHAIN_FILE=~/src/gmsh/contrib/mobile/utils/iOS.cmake -DAppkit_LIB="" -DOpenGlLibs_LIB="" -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="i386;x86_64" -DIOS_PLATFORM=SIMULATOR -DCMAKE_SIZEOF_VOID_P=8 -GXcode -DBUILD_MODULE_Draw=0 -DBUILD_MODULE_Visualization=0 -DBUILD_LIBRARY_TYPE=Static ..
+
+xcodebuild -configuration Release OTHER_CFLAGS="-miphoneos-version-min=8.0 -fembed-bitcode" OTHER_CPLUSPLUSFLAGS="-miphoneos-version-min=8.0 -fembed-bitcode -std=c++11"
+
+# Then combine all static libs using merge_static_libs.sh and put it in OCCT.framework
+
+################## Android ####
+cmake -DCMAKE_TOOLCHAIN_FILE=~/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_MODULE_Draw=0 -DBUILD_MODULE_Visualization=0 -DBUILD_MODULE_ApplicationFramework=0 -DBUILD_LIBRARY_TYPE=Static -DCMAKE_PREFIX_PATH=/opt/local ..
+
+make -j 8 -k  ### -k so that compilation will continue even when linking to freetype fails
+
diff --git a/contrib/mobile/utils/petsc_reconfigure_android_real.py b/contrib/mobile/utils/petsc_reconfigure_android_real.py
new file mode 100755
index 0000000000000000000000000000000000000000..354edd55886fbdeb6dd648878c349d707ba57635
--- /dev/null
+++ b/contrib/mobile/utils/petsc_reconfigure_android_real.py
@@ -0,0 +1,66 @@
+#!/usr/bin/python
+
+######## FOR PETSC 3.6.0
+######## I had to
+########   change Cxx.py in the PETSc 3.6.0 distrib to replace CXXCPP by CPP
+########   edit src/system/err.c to remove the exception throwing code
+########   edit android_real/lib/petsc/conf/petscvariables:
+########    1) change DSYMUTIL = ls
+########    2) change SL_LINKER_FUNCTION = -shared
+########    3) change SONAME_FUNCTION=$(1).$(2).so
+########    4) add "-Wl,--unresolved-symbols=ignore-all" to the PCC_LINKER_FLAGS
+
+if __name__ == '__main__':
+  import sys
+  import os
+  sys.path.insert(0, os.path.abspath('config'))
+  import configure
+  ndkroot='/Users/geuzaine/Library/Android/sdk/ndk-bundle/'
+  ndkbin=ndkroot + 'toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/'
+  ndklibs=ndkroot + 'toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/user/libs/'
+  ndklibs2=ndkroot + 'platforms/android-14/arch-arm/usr/lib/'
+  externallibs='/Users/geuzaine/src/gmsh/contrib/mobile/frameworks_android/petsc/'
+  configure_options = [
+    '--AR=' + ndkbin + 'arm-linux-androideabi-ar',
+    '--CC=' + ndkbin + 'arm-linux-androideabi-gcc',
+    '--CFLAGS=--sysroot=' + ndkroot + 'platforms/android-14/arch-arm',
+    '--CPP=' + ndkbin + 'arm-linux-androideabi-cpp',
+    '--CPPFLAGS=--sysroot=' + ndkroot + 'platforms/android-14/arch-arm',
+    '--CXX=' + ndkbin + 'arm-linux-androideabi-g++',
+    '--CXXFLAGS=--sysroot=' + ndkroot + 'platforms/android-14/arch-arm -fsigned-char -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -fdata-sections -ffunction-sections -fPIC -Wno-psabi -frtti -fexceptions -mthumb -O3 -fomit-frame-pointer -DNDEBUG -fPIC -isystem ' + ndkroot + 'platforms/android-14/arch-arm/usr/include -isystem ' + ndkroot + 'sources/cxx-stl/gnu-libstdc++/4.9/include -isystem ' + ndkroot + 'sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -lstdc++ -I' + ndkroot + 'sources/cxx-stl/gnu-libstdc++/4.9/include/ -I' + ndkroot + 'sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I' + ndkroot + 'sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi/include',
+    '--LDFLAGS= -L' + ndklibs + 'armeabi-v7a -L' + ndklibs2 + ' -lm',
+    '--LD_SHARED=' + ndkbin + 'arm-linux-androideabi-ld',
+    '--CLINKER=' + ndkbin + 'arm-linux-androideabi-gcc',
+    '--known-bits-per-byte=8',
+    '--known-endian=little',
+    '--known-level1-dcache-assoc=1',
+    '--known-level1-dcache-linesize=16',
+    '--known-level1-dcache-size=4000',
+    '--known-memcmp-ok=1',
+    '--known-sizeof-char=1',
+    '--known-sizeof-double=8',
+    '--known-sizeof-float=4',
+    '--known-sizeof-int=4',
+    '--known-sizeof-long-long=8',
+    '--known-sizeof-long=8',
+    '--known-sizeof-short=2',
+    '--known-sizeof-size_t=8',
+    '--known-sizeof-void-p=8',
+    '--with-blas-lib=' + externallibs + 'libf2cblas.so',
+    '--with-clanguage=cxx',
+    '--with-cmake=1',
+    '--with-debugging=0',
+    '--with-fc=0',
+    '--with-lapack-lib=' + externallibs + 'libf2clapack.so',
+    '--with-mpi=0',
+    '--download-suitesparse=yes',
+    '--with-shared-libraries=1',
+    '--with-x=0',
+    '-I' + ndkroot + 'sources/cxx-stl/gnu-libstdc++/4.9/include/',
+    '-I' + ndkroot + 'sources/cxx-stl/gnu-libstdc++/4.9/include/backward',
+    '-I' + ndkroot + 'sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi/include',
+    '-lstdc++',
+    '-with-batch=1',
+    'PETSC_ARCH=android_real',
+  ]
+  configure.petsc_configure(configure_options)
diff --git a/contrib/mobile/utils/petsc_reconfigure_ios_real.py b/contrib/mobile/utils/petsc_reconfigure_ios_real.py
new file mode 100755
index 0000000000000000000000000000000000000000..0931ffacaa5a2a145f2be8d5fb9369d871d47666
--- /dev/null
+++ b/contrib/mobile/utils/petsc_reconfigure_ios_real.py
@@ -0,0 +1,40 @@
+#!/Users/geuzaine/anaconda/bin/python
+if __name__ == '__main__':
+  import sys
+  import os
+  sys.path.insert(0, os.path.abspath('config'))
+  import configure
+  configure_options = [
+    '--CFLAGS=-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk -miphoneos-version-min=8.0 -arch armv7 -arch armv7s -arch arm64 -fembed-bitcode -DPETSC_BLASLAPACK_UNDERSCORE',
+    '--CXXFLAGS=-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk -miphoneos-version-min=8.0 -arch armv7 -arch armv7s -arch arm64 -fembed-bitcode -DPETSC_BLASLAPACK_UNDERSCORE',
+    '--download-suitesparse=yes',
+    '--known-bits-per-byte=8',
+    '--known-endian=little',
+    '--known-level1-dcache-assoc=1',
+    '--known-level1-dcache-linesize=16',
+    '--known-level1-dcache-size=4000',
+    '--known-memcmp-ok=1',
+    '--known-sizeof-char=1',
+    '--known-sizeof-double=8',
+    '--known-sizeof-float=4',
+    '--known-sizeof-int=4',
+    '--known-sizeof-long-long=8',
+    '--known-sizeof-long=8',
+    '--known-sizeof-short=2',
+    '--known-sizeof-size_t=8',
+    '--known-sizeof-void-p=8',
+    '--with-batch=1',
+    '--with-blas-lib=/Users/geuzaine/src/gmsh/contrib/mobile/frameworks_ios/petsc.framework/libf2cblas.a',
+    '--with-clanguage=cxx',
+    '--with-cmake=1',
+    '--with-debugging=0',
+    '--with-fc=0',
+    '--with-ios=1',
+    '--with-lapack-lib=/Users/geuzaine/src/gmsh/contrib/mobile/frameworks_ios/petsc.framework/libf2clapack.a',
+    '--with-mpi=0',
+    '--with-shared-libraries=0',
+    '--with-ssl=0',
+    '--with-x=0',
+    'PETSC_ARCH=ios_real',
+  ]
+  configure.petsc_configure(configure_options)
diff --git a/contrib/mobile/utils/petsc_reconfigure_iossimulator_real.py b/contrib/mobile/utils/petsc_reconfigure_iossimulator_real.py
new file mode 100755
index 0000000000000000000000000000000000000000..a784c1e5b3382b404736b29a9011c237032d70ed
--- /dev/null
+++ b/contrib/mobile/utils/petsc_reconfigure_iossimulator_real.py
@@ -0,0 +1,25 @@
+#!/Users/geuzaine/anaconda/bin/python
+if __name__ == '__main__':
+  import sys
+  import os
+  sys.path.insert(0, os.path.abspath('config'))
+  import configure
+  configure_options = [
+    '--CFLAGS=-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -miphoneos-version-min=8.0 -arch i386 -arch x86_64 -DPETSC_BLASLAPACK_UNDERSCORE',
+    '--CXXFLAGS=-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -miphoneos-version-min=8.0 -arch i386 -arch x86_64 -DPETSC_BLASLAPACK_UNDERSCORE',
+    '--download-suitesparse=yes',
+    '--with-batch=1',
+    '--with-blas-lib=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libBLAS.dylib',
+    '--with-clanguage=cxx',
+    '--with-cmake=1',
+    '--with-debugging=0',
+    '--with-fc=0',
+    '--with-ios=1',
+    '--with-lapack-lib=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libLAPACK.dylib',
+    '--with-mpi=0',
+    '--with-shared-libraries=0',
+    '--with-ssl=0',
+    '--with-x=0',
+    'PETSC_ARCH=iossimulator_real',
+  ]
+  configure.petsc_configure(configure_options)
diff --git a/demos/post_processing/compute_area_volume.geo b/demos/post_processing/compute_area_volume.geo
new file mode 100644
index 0000000000000000000000000000000000000000..67fd6d8cf7a35cc0544ebce8cb9cdd3f36dd9f50
--- /dev/null
+++ b/demos/post_processing/compute_area_volume.geo
@@ -0,0 +1,35 @@
+SetFactory("OpenCASCADE");
+
+r = 1;
+Sphere(1) = {0,0,0, r};
+Sphere(2) = {2,0,0, r};
+
+Mesh.CharacteristicLengthMin = 0.1;
+Mesh.CharacteristicLengthMax = 0.1;
+
+// hide volume 2 (to test Plugin(Integrate)'s ability to only integrate on
+// what's visible)
+Recursive Hide { Volume{2}; }
+
+// mesh
+Mesh 3;
+
+// compute post-processing data view with constant value 1 on the mesh
+Plugin(NewView).Run;
+Plugin(ModifyComponents).View = 0;
+Plugin(ModifyComponents).Expression0 = "1";
+Plugin(ModifyComponents).Run;
+
+// compute surface of sphere 1
+Plugin(Integrate).View = 0;
+Plugin(Integrate).Visible = 1; // only integrate on what's visible
+Plugin(Integrate).Dimension = 2; // only integrate on 2D elements (triangles)
+Plugin(Integrate).Run;
+Printf("Area = %g (exact = %g)", View[PostProcessing.NbViews-1].Max, 4*Pi*r^2);
+
+// compute volume of sphere 1
+Plugin(Integrate).View = 0;
+Plugin(Integrate).Visible = 1; // only integrate on what's visible
+Plugin(Integrate).Dimension = 3; // only integrate on 3D elements (tetrahedra)
+Plugin(Integrate).Run;
+Printf("Volume = %g (exact = %g)", View[PostProcessing.NbViews-1].Max, 4/3*Pi*r^3);